if (services->config->process_avail == 0) {
/* we can't reload config if there's no config process. */
- if (service_process_create(services->config) == NULL) {
+ if (service_process_create(services->config, -1, NULL) == NULL) {
i_error("Can't reload configuration because "
"we couldn't create a config process");
i_sd_notify(0, "READY=1");
service_list_anvil_discard_input(service_anvil_global);
else {
service = service_lookup_type(service_list, SERVICE_TYPE_ANVIL);
- (void)service_process_create(service);
+ (void)service_process_create(service, -1, NULL);
}
}
static void service_accept(struct service_listener *l)
{
struct service *service = l->service;
+ int fd = -1;
i_assert(service->process_avail == 0);
return;
}
- /* create a child process and let it accept() this connection */
- if (service_process_create(service) == NULL)
+ if (service->client_limit == 1 &&
+ (l->type == SERVICE_LISTENER_INET ||
+ (l->type == SERVICE_LISTENER_UNIX &&
+ !l->set.fileset.pid_listener))) {
+ /* pre-accept() a client fd for services with client_limit=1,
+ so we can rapidly create new processes as needed. */
+ fd = net_accept(l->fd, NULL, NULL);
+ if (fd == -1) {
+ if (!NET_ACCEPT_ENOCONN(errno))
+ e_error(service->event, "net_accept() failed: %m");
+ return;
+ }
+ fd_close_on_exec(fd, TRUE);
+ } else {
+ /* the created child process will accept() the connection */
+ }
+
+ if (service_process_create(service, fd, l) == NULL) {
+ /* failed to create the process */
service_monitor_throttle(service);
- else
+ } else if (fd == -1) {
service_monitor_listen_stop(service);
+ }
+ if (fd != -1)
+ net_disconnect(fd);
}
static bool
count = limit;
for (i = 0; i < count; i++) {
- if (service_process_create(service) == NULL) {
+ if (service_process_create(service, -1, NULL) == NULL) {
service_monitor_throttle(service);
break;
}
service_monitor_start_extra_avail(service);
if (service_list->log->status_fd[0] != -1) {
- if (service_process_create(service_list->log) != NULL)
+ if (service_process_create(service_list->log, -1, NULL) != NULL)
service_monitor_listen_stop(service_list->log);
}
array_foreach_elem(&service_list->services, service) {
if (service->type == SERVICE_TYPE_STARTUP &&
service->status_fd[0] != -1) {
- if (service_process_create(service) != NULL)
+ if (service_process_create(service, -1, NULL) != NULL)
service_monitor_listen_stop(service);
}
}
} else if (service == service->list->log &&
service->process_count == 0) {
/* log service must always be running */
- if (service_process_create(service) == NULL)
+ if (service_process_create(service, -1, NULL) == NULL)
service_monitor_throttle(service);
} else {
service_monitor_listen_start(service);
}
static void
-service_dup_fds(struct service *service)
+service_dup_fds(struct service *service, int accepted_fd,
+ const struct service_listener *accepted_listener,
+ int *accepted_listener_fd_r)
{
struct service_listener *const *listeners;
ARRAY_TYPE(dup2) dups;
int fd = MASTER_LISTEN_FD_FIRST;
unsigned int i, count, socket_listener_count;
+ *accepted_listener_fd_r = -1;
+
/* stdin/stdout is already redirected to /dev/null. Other master fds
should have been opened with fd_close_on_exec() so we don't have to
worry about them.
listeners = array_get(&service->listeners, &count);
t_array_init(&dups, count + 10);
+ if (accepted_fd != -1) {
+ /* we have a pre-accepted connection */
+ dup2_append(&dups, accepted_fd,
+ MASTER_ACCEPTED_CLIENT_FD);
+ }
+
switch (service->type) {
case SERVICE_TYPE_LOG:
i_assert(fd == MASTER_LISTEN_FD_FIRST);
}
}
+ if (listeners[i] == accepted_listener)
+ *accepted_listener_fd_r = fd;
dup2_append(&dups, listeners[i]->fd, fd++);
env_put(t_strdup_printf("SOCKET%d_SETTINGS",
timeout_remove(&process->to_status);
}
-struct service_process *service_process_create(struct service *service)
+struct service_process *
+service_process_create(struct service *service, int accepted_fd,
+ const struct service_listener *accepted_listener)
{
static unsigned int uid_counter = 0;
struct service_process *process;
}
if (pid == 0) {
/* child */
+ int accepted_listener_fd;
service_process_setup_environment(service, uid, hostdomain);
service_reopen_inet_listeners(service);
- service_dup_fds(service);
+ service_dup_fds(service, accepted_fd, accepted_listener,
+ &accepted_listener_fd);
+ if (accepted_fd != -1) {
+ i_assert(accepted_listener_fd > 0);
+ env_put(DOVECOT_ACCEPTED_CLIENT_LISTENER_FD_ENV,
+ dec2str(accepted_listener_fd));
+ }
drop_privileges(service);
process_exec(service->executable);
}
service_process_status_timeout, process);
}
- process->available_count = service->client_limit;
- process->idle_start = ioloop_time;
service->process_count_total++;
service->process_count++;
- service->process_avail++;
- service->process_idling++;
- DLLIST2_APPEND(&service->idle_processes_head,
- &service->idle_processes_tail, process);
+ if (accepted_fd == -1) {
+ process->available_count = service->client_limit;
+ process->idle_start = ioloop_time;
+ service->process_avail++;
+ service->process_idling++;
+ DLLIST2_APPEND(&service->idle_processes_head,
+ &service->idle_processes_tail, process);
+ } else {
+ i_assert(service->client_limit == 1);
+ DLLIST_PREPEND(&service->busy_processes, process);
+ }
service_list_ref(service->list);
hash_table_insert(service_pids, POINTER_CAST(process->pid), process);
#define SERVICE_PROCESS_IS_INITIALIZED(process) \
((process)->to_status == NULL)
-struct service_process *service_process_create(struct service *service);
+struct service_process *
+service_process_create(struct service *service, int accepted_fd,
+ const struct service_listener *accepted_listener);
void service_process_destroy(struct service_process *process);
void service_process_ref(struct service_process *process);
if (strstr(unix_listeners[i]->path, "%{pid}") == NULL)
array_push_back(&service->listeners, &l);
- else
+ else {
+ l->set.fileset.pid_listener = TRUE;
array_push_back(&service->unix_pid_listeners, &l);
+ }
}
for (i = 0; i < fifo_count; i++) {
if (fifo_listeners[i]->mode == 0) {
const struct file_listener_settings *set;
uid_t uid;
gid_t gid;
+ bool pid_listener;
} fileset;
struct {
const struct inet_listener_settings *set;