extern bool auth_success_written;
extern bool core_dumps_disabled;
extern int null_fd;
+extern struct service_list *services;
void process_exec(const char *cmd, const char *extra_args[]) ATTR_NORETURN;
bool auth_success_written;
bool core_dumps_disabled;
int null_fd;
+struct service_list *services;
static char *pidfile_path;
-static struct service_list *services;
static fatal_failure_callback_t *orig_fatal_callback;
static const char *child_process_env[3]; /* @UNSAFE */
return request;
}
+static struct service *
+auth_process_get_dest_service(struct service_process_auth_source *process)
+{
+ struct service *service = process->process.service;
+
+ if (!service->list->destroyed)
+ return service->auth_dest_service;
+
+ service = service_lookup(services, service->set->auth_dest_service);
+ if (service == NULL) {
+ i_warning("service(%s): Lost destination service %s",
+ service->set->name, service->set->auth_dest_service);
+ }
+ return service;
+}
+
static int
auth_process_input_user(struct service_process_auth_server *process, const char *args)
{
request = auth_process_lookup_request(process, id);
if (request != NULL) {
- struct service *dest_service =
- request->process->process.service->auth_dest_service;
+ struct service *dest_service;
struct service_process *dest_process;
- dest_process = service_process_create(dest_service, list + 1,
- request);
+ dest_service = auth_process_get_dest_service(request->process);
+ dest_process = dest_service == NULL ? NULL :
+ service_process_create(dest_service, list + 1, request);
status = dest_process != NULL ?
MASTER_AUTH_STATUS_OK :
MASTER_AUTH_STATUS_INTERNAL_ERROR;
switch (l1->type) {
case SERVICE_LISTENER_UNIX:
case SERVICE_LISTENER_FIFO:
- if (strcmp(l1->set.fileset.set->path,
- l2->set.fileset.set->path) != 0)
- return FALSE;
- if (l1->set.fileset.set->mode != l2->set.fileset.set->mode)
- return FALSE;
- if (l1->set.fileset.uid != l2->set.fileset.uid)
- return FALSE;
- if (l1->set.fileset.gid != l2->set.fileset.gid)
- return FALSE;
- return TRUE;
+ /* We could just keep using the same listener, but it's more
+ likely to cause problems if old process accepts a connection
+ before it knows that it should die. So just always unlink
+ and recreate unix/fifo listeners. */
+ return FALSE;
case SERVICE_LISTENER_INET:
if (memcmp(&l1->set.inetset.ip, &l2->set.inetset.ip,
sizeof(l1->set.inetset.ip)) != 0)
if (close(old_listeners[j]->fd) < 0)
i_error("close(listener) failed: %m");
}
+ switch (old_listeners[j]->type) {
+ case SERVICE_LISTENER_UNIX:
+ case SERVICE_LISTENER_FIFO: {
+ const char *path =
+ old_listeners[j]->set.fileset.set->path;
+ if (unlink(path) < 0)
+ i_error("unlink(%s) failed: %m", path);
+ break;
+ }
+ case SERVICE_LISTENER_INET:
+ break;
+ }
}
/* and let services_listen() deal with the remaining fds */
struct service *service;
pid_t pid;
int status;
+ bool service_destroyed;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
process = hash_table_lookup(service_pids, &pid);
} else {
service_process_failure(process, status);
}
+ service_destroyed = service->list->destroyed;
service_process_destroy(process);
- service_monitor_start_extra_avail(service);
- if (service->to_throttle == NULL)
- service_monitor_listen_start(service);
+ if (!service_destroyed) {
+ service_monitor_start_extra_avail(service);
+ if (service->to_throttle == NULL)
+ service_monitor_listen_start(service);
+ }
}
}
*pid1 > *pid2 ? 1 : 0;
}
-static struct service *
+struct service *
service_lookup(struct service_list *service_list, const char *name)
{
struct service *const *services;
static void services_kill_timeout(struct service_list *service_list)
{
- struct service *const *services;
+ struct service *const *services, *log_service;
unsigned int i, count;
+ bool sigterm_log;
int sig;
- if (!service_list->sigterm_sent)
+ if (!service_list->sigterm_sent || !service_list->sigterm_sent_to_log)
sig = SIGTERM;
else
sig = SIGKILL;
+ sigterm_log = service_list->sigterm_sent;
service_list->sigterm_sent = TRUE;
i_warning("Processes aren't dying after reload, sending %s.",
sig == SIGTERM ? "SIGTERM" : "SIGKILL");
+ log_service = NULL;
services = array_get(&service_list->services, &count);
- for (i = 0; i < count; i++)
- service_signal(services[i], sig);
+ for (i = 0; i < count; i++) {
+ if (services[i]->type == SERVICE_TYPE_LOG)
+ log_service = services[i];
+ else
+ service_signal(services[i], sig);
+ }
+ /* kill log service later so it could still have a chance of logging
+ something */
+ if (log_service != NULL && sigterm_log) {
+ service_signal(log_service, sig);
+ service_list->sigterm_sent_to_log = TRUE;
+ }
}
void services_destroy(struct service_list *service_list)
services_kill_timeout, service_list);
}
+ service_list->destroyed = TRUE;
service_list_unref(service_list);
}
ARRAY_DEFINE(services, struct service *);
+ unsigned int destroyed:1;
unsigned int sigterm_sent:1;
+ unsigned int sigterm_sent_to_log:1;
};
extern struct hash_table *service_pids;
void services_throttle_time_sensitives(struct service_list *list,
unsigned int secs);
+/* Find a service by name. */
+struct service *
+service_lookup(struct service_list *service_list, const char *name);
+
void service_error(struct service *service, const char *format, ...)
ATTR_FORMAT(2, 3);