changeset 5843:f655c4d4a419 HEAD

Moved child process handling to child-process.[ch]. The hash table now uses pointers to structures instead of a casted process type. This allowed removing another login-processes hash table.
author Timo Sirainen <tss@iki.fi>
date Sat, 30 Jun 2007 17:38:17 +0300
parents 77a399a5eb2a
children 7efc025c7e9f
files src/master/Makefile.am src/master/auth-process.c src/master/child-process.c src/master/child-process.h src/master/common.h src/master/dict-process.c src/master/dict-process.h src/master/login-process.c src/master/login-process.h src/master/mail-process.c src/master/mail-process.h src/master/main.c src/master/ssl-init.c src/master/ssl-init.h
diffstat 14 files changed, 322 insertions(+), 222 deletions(-) [+]
line wrap: on
line diff
--- a/src/master/Makefile.am	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/Makefile.am	Sat Jun 30 17:38:17 2007 +0300
@@ -22,6 +22,7 @@
 	auth-process.c \
 	askpass.c \
 	capabilities-posix.c \
+	child-process.c \
 	dict-process.c \
 	log.c \
 	login-process.c \
@@ -35,6 +36,7 @@
 	auth-process.h \
 	askpass.h \
 	capabilities.h \
+	child-process.h \
 	dict-process.h \
 	common.h \
 	log.h \
--- a/src/master/auth-process.c	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/auth-process.c	Sat Jun 30 17:38:17 2007 +0300
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "hash.h"
 #include "ioloop.h"
 #include "env-util.h"
 #include "fd-close-on-exec.h"
@@ -12,6 +13,7 @@
 #include "restrict-access.h"
 #include "restrict-process-size.h"
 #include "auth-process.h"
+#include "child-process.h"
 #include "../auth/auth-master-interface.h"
 #include "log.h"
 
@@ -56,6 +58,11 @@
 
 bool have_initialized_auth_processes = FALSE;
 
+static struct child_process auth_child_process =
+	{ PROCESS_TYPE_AUTH };
+static struct child_process auth_worker_child_process =
+	{ PROCESS_TYPE_AUTH_WORKER };
+
 static struct timeout *to;
 static unsigned int auth_tag;
 static struct auth_process_group *process_groups;
@@ -283,7 +290,7 @@
 	const char *path, *handshake;
 
 	if (pid != 0)
-		PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH);
+		child_process_add(pid, &auth_child_process);
 
 	p = i_new(struct auth_process, 1);
 	p->group = group;
@@ -603,7 +610,7 @@
 
 	if (pid != 0) {
 		/* master */
-		PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_AUTH_WORKER);
+		child_process_add(pid, &auth_worker_child_process);
 		prefix = t_strdup_printf("auth-worker(%s): ",
 					 process->group->set->name);
 		log_set_prefix(log, prefix);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/child-process.c	Sat Jun 30 17:38:17 2007 +0300
@@ -0,0 +1,184 @@
+/* Copyright (C) 2002-2007 Timo Sirainen */
+
+#include "common.h"
+#include "lib-signals.h"
+#include "hash.h"
+#include "env-util.h"
+#include "syslog-util.h"
+#include "child-process.h"
+
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/wait.h>
+
+const char *process_names[PROCESS_TYPE_MAX] = {
+	"unknown",
+	"auth",
+	"auth-worker",
+	"login",
+	"imap",
+	"pop3",
+	"ssl-build-param",
+	"dict"
+};
+
+struct hash_table *processes;
+static child_process_destroy_callback_t *destroy_callbacks[PROCESS_TYPE_MAX];
+
+struct child_process *child_process_lookup(pid_t pid)
+{
+	return hash_lookup(processes, POINTER_CAST(pid));
+}
+
+void child_process_add(pid_t pid, struct child_process *process)
+{
+	hash_insert(processes, POINTER_CAST(pid), process);
+}
+
+void child_process_remove(pid_t pid)
+{
+	hash_remove(processes, POINTER_CAST(pid));
+}
+
+void child_process_init_env(void)
+{
+	int facility;
+
+	/* remove all environment, we don't need them */
+	env_clean();
+
+	/* we'll log through master process */
+	env_put("LOG_TO_MASTER=1");
+	if (env_tz != NULL)
+		env_put(t_strconcat("TZ=", env_tz, NULL));
+
+	if (settings_root == NULL ||
+	    !syslog_facility_find(settings_root->defaults->syslog_facility,
+				  &facility))
+		facility = LOG_MAIL;
+	env_put(t_strdup_printf("SYSLOG_FACILITY=%d", facility));
+
+	if (settings_root != NULL && !settings_root->defaults->version_ignore)
+		env_put("DOVECOT_VERSION="PACKAGE_VERSION);
+#ifdef DEBUG
+	if (gdb) env_put("GDB=1");
+#endif
+}
+
+void client_process_exec(const char *cmd, const char *title)
+{
+	const char *executable, *p, **argv;
+
+	/* very simple argument splitting. */
+	if (*title == '\0')
+		argv = t_strsplit(cmd, " ");
+	else
+		argv = t_strsplit(t_strconcat(cmd, " ", title, NULL), " ");
+
+	executable = argv[0];
+
+	/* hide the path, it's ugly */
+	p = strrchr(argv[0], '/');
+	if (p != NULL) argv[0] = p+1;
+
+	execv(executable, (char **)argv);
+}
+
+static const char *get_exit_status_message(enum fatal_exit_status status)
+{
+	switch (status) {
+	case FATAL_LOGOPEN:
+		return "Can't open log file";
+	case FATAL_LOGWRITE:
+		return "Can't write to log file";
+	case FATAL_LOGERROR:
+		return "Internal logging error";
+	case FATAL_OUTOFMEM:
+		return "Out of memory";
+	case FATAL_EXEC:
+		return "exec() failed";
+
+	case FATAL_DEFAULT:
+		return NULL;
+	}
+
+	return NULL;
+}
+
+static void sigchld_handler(int signo __attr_unused__,
+			    void *context __attr_unused__)
+{
+	struct child_process *process;
+	const char *process_type_name, *msg;
+	enum process_type process_type;
+	pid_t pid;
+	int status;
+	bool abnormal_exit;
+
+	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+		/* get the type and remove from hash */
+		process = child_process_lookup(pid);
+		if (process == NULL)
+			process_type = PROCESS_TYPE_UNKNOWN;
+		else {
+			process_type = process->type;
+			child_process_remove(pid);
+		}
+		abnormal_exit = TRUE;
+
+		/* write errors to syslog */
+		process_type_name = process_names[process_type];
+		if (WIFEXITED(status)) {
+			status = WEXITSTATUS(status);
+			if (status == 0) {
+				abnormal_exit = FALSE;
+				if (process_type == PROCESS_TYPE_UNKNOWN) {
+					i_error("unknown child %s exited "
+						"successfully", dec2str(pid));
+				}
+			} else if (status == 1 &&
+				   process_type == PROCESS_TYPE_SSL_PARAM) {
+				/* kludgy. hide this failure. */
+			} else {
+				msg = get_exit_status_message(status);
+				msg = msg == NULL ? "" :
+					t_strconcat(" (", msg, ")", NULL);
+				i_error("child %s (%s) returned error %d%s",
+					dec2str(pid), process_type_name,
+					status, msg);
+			}
+		} else if (WIFSIGNALED(status)) {
+			i_error("child %s (%s) killed with signal %d",
+				dec2str(pid), process_type_name,
+				WTERMSIG(status));
+		}
+
+		if (destroy_callbacks[process_type] != NULL)
+			destroy_callbacks[process_type](process, abnormal_exit);
+	}
+
+	if (pid == -1 && errno != EINTR && errno != ECHILD)
+		i_warning("waitpid() failed: %m");
+}
+
+void child_process_set_destroy_callback(enum process_type type,
+					child_process_destroy_callback_t *cb)
+{
+	i_assert(type < PROCESS_TYPE_MAX);
+
+	destroy_callbacks[type] = cb;
+}
+
+void child_processes_init(void)
+{
+	processes = hash_create(default_pool, default_pool, 128, NULL, NULL);
+	lib_signals_set_handler(SIGCHLD, TRUE, sigchld_handler, NULL);
+}
+
+void child_processes_deinit(void)
+{
+	/* make sure we log if child processes died unexpectedly */
+	sigchld_handler(SIGCHLD, NULL);
+	lib_signals_unset_handler(SIGCHLD, sigchld_handler, NULL);
+	hash_destroy(processes);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/child-process.h	Sat Jun 30 17:38:17 2007 +0300
@@ -0,0 +1,40 @@
+#ifndef __CHILD_PROCESS_H
+#define __CHILD_PROCESS_H
+
+enum process_type {
+	PROCESS_TYPE_UNKNOWN,
+	PROCESS_TYPE_AUTH,
+	PROCESS_TYPE_AUTH_WORKER,
+	PROCESS_TYPE_LOGIN,
+	PROCESS_TYPE_IMAP,
+	PROCESS_TYPE_POP3,
+	PROCESS_TYPE_SSL_PARAM,
+	PROCESS_TYPE_DICT,
+
+	PROCESS_TYPE_MAX
+};
+
+struct child_process {
+	enum process_type type;
+};
+
+typedef void child_process_destroy_callback_t(struct child_process *,
+					      bool abnormal_exit);
+
+extern const char *process_names[];
+extern struct hash_table *processes;
+
+struct child_process *child_process_lookup(pid_t pid);
+void child_process_add(pid_t pid, struct child_process *process);
+void child_process_remove(pid_t pid);
+
+void child_process_init_env(void);
+void client_process_exec(const char *cmd, const char *title);
+
+void child_process_set_destroy_callback(enum process_type type,
+					child_process_destroy_callback_t *cb);
+
+void child_processes_init(void);
+void child_processes_deinit(void);
+
+#endif
--- a/src/master/common.h	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/common.h	Sat Jun 30 17:38:17 2007 +0300
@@ -4,46 +4,21 @@
 struct ip_addr;
 
 #include "lib.h"
-#include "hash.h"
 #include "master-settings.h"
 
-enum process_type {
-	PROCESS_TYPE_UNKNOWN,
-	PROCESS_TYPE_AUTH,
-	PROCESS_TYPE_AUTH_WORKER,
-	PROCESS_TYPE_LOGIN,
-	PROCESS_TYPE_IMAP,
-	PROCESS_TYPE_POP3,
-	PROCESS_TYPE_SSL_PARAM,
-	PROCESS_TYPE_DICT,
-
-	PROCESS_TYPE_MAX
-};
-
 extern struct ioloop *ioloop;
-extern struct hash_table *pids;
 extern int null_fd, inetd_login_fd;
 extern uid_t master_uid;
 extern char program_path[];
-extern const char *process_names[];
 extern char ssl_manual_key_password[];
+extern const char *env_tz;
+#ifdef DEBUG
+extern bool gdb;
+#endif
 
 #define IS_INETD() \
 	(inetd_login_fd != -1)
 
-/* processes */
-#define PID_GET_PROCESS_TYPE(pid) \
-	POINTER_CAST_TO(hash_lookup(pids, POINTER_CAST(pid)), pid_t)
-
-#define PID_ADD_PROCESS_TYPE(pid, type) \
-	hash_insert(pids, POINTER_CAST(pid), POINTER_CAST(type))
-
-#define PID_REMOVE_PROCESS_TYPE(pid) \
-	hash_remove(pids, POINTER_CAST(pid))
-
-void child_process_init_env(void);
-void client_process_exec(const char *cmd, const char *title);
-
 /* misc */
 #define VALIDATE_STR(str) \
 	validate_str(str, sizeof(str))
--- a/src/master/dict-process.c	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/dict-process.c	Sat Jun 30 17:38:17 2007 +0300
@@ -7,6 +7,7 @@
 #include "fd-close-on-exec.h"
 #include "env-util.h"
 #include "log.h"
+#include "child-process.h"
 #include "dict-process.h"
 
 #include <syslog.h>
@@ -16,6 +17,7 @@
 #define DICT_SERVER_SOCKET_NAME "dict-server"
 
 struct dict_process {
+	struct child_process process;
 	char *path;
 	int fd;
 
@@ -51,7 +53,7 @@
 
 	if (pid != 0) {
 		/* master */
-		PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_DICT);
+		child_process_add(pid, &process->process);
 		log_set_prefix(log, "dict: ");
 		(void)close(log_fd);
 
@@ -155,13 +157,26 @@
 	process->fd = -1;
 }
 
+static void
+dict_process_destroyed(struct child_process *process,
+		       bool abnormal_exit __attr_unused__)
+{
+	struct dict_process *p = (struct dict_process *)process;
+
+	(void)dict_process_listen(p);
+}
+
 void dict_process_init(void)
 {
 	process = i_new(struct dict_process, 1);
+	process->process.type = PROCESS_TYPE_DICT;
 	process->fd = -1;
 	process->path = i_strconcat(settings_root->defaults->base_dir,
 				    "/"DICT_SERVER_SOCKET_NAME, NULL);
 	(void)dict_process_listen(process);
+
+	child_process_set_destroy_callback(PROCESS_TYPE_DICT,
+					   dict_process_destroyed);
 }
 
 void dict_process_deinit(void)
@@ -173,12 +188,6 @@
 	i_free(process);
 }
 
-void dict_process_restart(void)
-{
-	dict_process_deinit();
-	dict_process_init();
-}
-
 void dict_process_kill(void)
 {
 	if (process->log != NULL) {
--- a/src/master/dict-process.h	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/dict-process.h	Sat Jun 30 17:38:17 2007 +0300
@@ -3,7 +3,6 @@
 
 void dict_process_init(void);
 void dict_process_deinit(void);
-void dict_process_restart(void);
 void dict_process_kill(void);
 
 #endif
--- a/src/master/login-process.c	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/login-process.c	Sat Jun 30 17:38:17 2007 +0300
@@ -2,6 +2,7 @@
 
 #include "common.h"
 #include "ioloop.h"
+#include "hash.h"
 #include "network.h"
 #include "ostream.h"
 #include "fdpass.h"
@@ -21,6 +22,8 @@
 #include <sys/stat.h>
 
 struct login_process {
+	struct child_process process;
+
 	struct login_group *group;
 	struct login_process *prev_prelogin, *next_prelogin;
 	int refcount;
@@ -51,7 +54,6 @@
 static struct io *io_listen;
 static bool logins_stalled = FALSE;
 
-static struct hash_table *processes;
 static struct login_group *login_groups;
 
 static void login_processes_stall(void);
@@ -67,7 +69,7 @@
 	group = i_new(struct login_group, 1);
 	group->refcount = 1;
 	group->set = set;
-	group->process_type = set->protocol == MAIL_PROTOCOL_IMAP ?
+	group->mail_process_type = set->protocol == MAIL_PROTOCOL_IMAP ?
 		PROCESS_TYPE_IMAP : PROCESS_TYPE_POP3;
 
 	group->next = login_groups;
@@ -98,7 +100,8 @@
 
 		t_push();
 		master_reply.success =
-			create_mail_process(group->process_type, group->set,
+			create_mail_process(group->mail_process_type,
+					    group->set,
 					    request->fd, &request->local_ip,
 					    &request->remote_ip, user, args,
 					    FALSE);
@@ -438,6 +441,7 @@
 	i_assert(pid != 0);
 
 	p = i_new(struct login_process, 1);
+	p->process.type = PROCESS_TYPE_LOGIN;
 	p->group = group;
 	p->refcount = 2; /* once for fd close, another for process exit */
 	p->pid = pid;
@@ -446,9 +450,7 @@
 	p->output = o_stream_create_file(fd, default_pool,
 					 sizeof(struct master_login_reply)*10,
 					 FALSE);
-
-	PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_LOGIN);
-	hash_insert(processes, POINTER_CAST(pid), p);
+	child_process_add(pid, &p->process);
 
 	p->state = LOGIN_STATE_LISTENING;
 
@@ -465,7 +467,6 @@
 	if (p->group != NULL)
 		p->group->processes--;
 
-	hash_remove(processes, POINTER_CAST(p->pid));
 	login_process_unref(p);
 }
 
@@ -570,7 +571,7 @@
 	if (set->login_greeting_capability)
 		env_put("GREETING_CAPABILITY=1");
 
-	if (group->process_type == PROCESS_TYPE_IMAP) {
+	if (group->mail_process_type == PROCESS_TYPE_IMAP) {
 		env_put(t_strconcat("CAPABILITY_STRING=",
 				    *set->imap_capability != '\0' ?
 				    set->imap_capability :
@@ -616,7 +617,7 @@
 	if (pid != 0) {
 		/* master */
 		prefix = t_strdup_printf("%s-login: ",
-					 process_names[group->process_type]);
+				process_names[group->mail_process_type]);
 		log_set_prefix(log, prefix);
 
 		net_set_nonblock(fd[0], TRUE);
@@ -628,7 +629,7 @@
 	}
 
 	prefix = t_strdup_printf("master-%s-login: ",
-				 process_names[group->process_type]);
+				 process_names[group->mail_process_type]);
 	log_set_prefix(log, prefix);
 
 	/* move the listen handle */
@@ -681,13 +682,11 @@
 	return -1;
 }
 
-void login_process_destroyed(pid_t pid, bool abnormal_exit)
+static void
+login_process_destroyed(struct child_process *process, bool abnormal_exit)
 {
-	struct login_process *p;
+	struct login_process *p = (struct login_process *)process;
 
-	p = hash_lookup(processes, POINTER_CAST(pid));
-	if (p == NULL)
-		i_panic("Lost login process PID %s", dec2str(pid));
 	i_assert(!p->inetd_child);
 
 	if (abnormal_exit) {
@@ -706,8 +705,12 @@
 	void *key, *value;
 
 	iter = hash_iterate_init(processes);
-	while (hash_iterate(iter, &key, &value))
-		login_process_destroy(value);
+	while (hash_iterate(iter, &key, &value)) {
+		struct login_process *p = value;
+
+		if (p->process.type == PROCESS_TYPE_LOGIN)
+			login_process_destroy(p);
+	}
 	hash_iterate_deinit(iter);
 
 	while (login_groups != NULL) {
@@ -730,7 +733,7 @@
 	while (hash_iterate(iter, &key, &value)) {
 		struct login_process *p = value;
 
-		if (p->group == group)
+		if (p->process.type == PROCESS_TYPE_LOGIN && p->group == group)
 			(void)o_stream_send(p->output, &reply, sizeof(reply));
 	}
 	hash_iterate_deinit(iter);
@@ -892,7 +895,9 @@
         login_pid_counter = 0;
 	login_groups = NULL;
 
-	processes = hash_create(default_pool, default_pool, 128, NULL, NULL);
+	child_process_set_destroy_callback(PROCESS_TYPE_LOGIN,
+					   login_process_destroyed);
+
 	if (!IS_INETD()) {
 		to = timeout_add(1000, login_processes_start_missing, NULL);
 		io_listen = NULL;
@@ -906,7 +911,6 @@
 void login_processes_deinit(void)
 {
         login_processes_destroy_all();
-	hash_destroy(processes);
 
 	if (to != NULL)
 		timeout_remove(&to);
--- a/src/master/login-process.h	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/login-process.h	Sat Jun 30 17:38:17 2007 +0300
@@ -1,11 +1,13 @@
 #ifndef __LOGIN_PROCESS_H
 #define __LOGIN_PROCESS_H
 
+#include "child-process.h"
+
 struct login_group {
 	struct login_group *next;
 	int refcount;
 
-	enum process_type process_type;
+	enum process_type mail_process_type;
 	struct settings *set;
 
 	unsigned int processes;
@@ -18,8 +20,6 @@
 	struct login_process *newest_prelogin_process;
 };
 
-void login_process_destroyed(pid_t pid, bool abnormal_exit);
-
 void login_processes_destroy_all(void);
 
 void login_processes_init(void);
--- a/src/master/mail-process.c	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/mail-process.c	Sat Jun 30 17:38:17 2007 +0300
@@ -31,6 +31,9 @@
    many seconds to finish. */
 #define CHDIR_WARN_SECS 10
 
+static struct child_process imap_child_process = { PROCESS_TYPE_IMAP };
+static struct child_process pop3_child_process = { PROCESS_TYPE_POP3 };
+
 static unsigned int mail_process_count = 0;
 
 static bool validate_uid_gid(struct settings *set, uid_t uid, gid_t gid,
@@ -439,6 +442,9 @@
 	int ret, log_fd, nice, chdir_errno;
 	bool home_given, nfs_check;
 
+	i_assert(process_type == PROCESS_TYPE_IMAP ||
+		 process_type == PROCESS_TYPE_POP3);
+
 	// FIXME: per-group
 	if (mail_process_count == set->max_mail_processes) {
 		i_error("Maximum number of mail processes exceeded");
@@ -554,7 +560,10 @@
 		mail_process_count++;
 		if (!dump_capability) {
 			log_set_prefix(log, str_c(str));
-			PID_ADD_PROCESS_TYPE(pid, process_type);
+			child_process_add(pid,
+					  process_type == PROCESS_TYPE_IMAP ?
+					  &imap_child_process :
+					  &pop3_child_process);
 		}
 		(void)close(log_fd);
 		return TRUE;
@@ -709,7 +718,17 @@
 	return FALSE;
 }
 
-void mail_process_destroyed(pid_t pid __attr_unused__)
+static void
+mail_process_destroyed(struct child_process *process __attr_unused__,
+		       bool abnormal_exit __attr_unused__)
 {
 	mail_process_count--;
 }
+
+void mail_processes_init(void)
+{
+	child_process_set_destroy_callback(PROCESS_TYPE_IMAP,
+					   mail_process_destroyed);
+	child_process_set_destroy_callback(PROCESS_TYPE_POP3,
+					   mail_process_destroyed);
+}
--- a/src/master/mail-process.h	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/mail-process.h	Sat Jun 30 17:38:17 2007 +0300
@@ -1,6 +1,8 @@
 #ifndef __MAIL_PROCESS_H
 #define __MAIL_PROCESS_H
 
+#include "child-process.h"
+
 struct login_group;
 struct auth_master_reply;
 
@@ -13,6 +15,6 @@
 			 const char *user, const char *const *args,
 			 bool dump_capability);
 
-void mail_process_destroyed(pid_t pid);
+void mail_processes_init(void);
 
 #endif
--- a/src/master/main.c	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/main.c	Sat Jun 30 17:38:17 2007 +0300
@@ -24,30 +24,17 @@
 #include <fcntl.h>
 #include <syslog.h>
 #include <sys/stat.h>
-#include <sys/wait.h>
-
-const char *process_names[PROCESS_TYPE_MAX] = {
-	"unknown",
-	"auth",
-	"auth-worker",
-	"login",
-	"imap",
-	"pop3",
-	"ssl-build-param",
-	"dict"
-};
 
 static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf";
-static const char *env_tz;
 
 struct ioloop *ioloop;
-struct hash_table *pids;
 int null_fd = -1, inetd_login_fd;
 uid_t master_uid;
 char program_path[PATH_MAX];
 char ssl_manual_key_password[100];
+const char *env_tz;
 #ifdef DEBUG
-static bool gdb;
+bool gdb;
 #endif
 
 static void listen_fds_open(bool retry);
@@ -65,50 +52,6 @@
 	return FALSE;
 }
 
-void child_process_init_env(void)
-{
-	int facility;
-
-	/* remove all environment, we don't need them */
-	env_clean();
-
-	/* we'll log through master process */
-	env_put("LOG_TO_MASTER=1");
-	if (env_tz != NULL)
-		env_put(t_strconcat("TZ=", env_tz, NULL));
-
-	if (settings_root == NULL ||
-	    !syslog_facility_find(settings_root->defaults->syslog_facility,
-				  &facility))
-		facility = LOG_MAIL;
-	env_put(t_strdup_printf("SYSLOG_FACILITY=%d", facility));
-
-	if (settings_root != NULL && !settings_root->defaults->version_ignore)
-		env_put("DOVECOT_VERSION="PACKAGE_VERSION);
-#ifdef DEBUG
-	if (gdb) env_put("GDB=1");
-#endif
-}
-
-void client_process_exec(const char *cmd, const char *title)
-{
-	const char *executable, *p, **argv;
-
-	/* very simple argument splitting. */
-	if (*title == '\0')
-		argv = t_strsplit(cmd, " ");
-	else
-		argv = t_strsplit(t_strconcat(cmd, " ", title, NULL), " ");
-
-	executable = argv[0];
-
-	/* hide the path, it's ugly */
-	p = strrchr(argv[0], '/');
-	if (p != NULL) argv[0] = p+1;
-
-	execv(executable, (char **)argv);
-}
-
 static void set_logfile(struct settings *set)
 {
 	int facility;
@@ -172,91 +115,6 @@
 	set_logfile(settings_root->defaults);
 }
 
-static const char *get_exit_status_message(enum fatal_exit_status status)
-{
-	switch (status) {
-	case FATAL_LOGOPEN:
-		return "Can't open log file";
-	case FATAL_LOGWRITE:
-		return "Can't write to log file";
-	case FATAL_LOGERROR:
-		return "Internal logging error";
-	case FATAL_OUTOFMEM:
-		return "Out of memory";
-	case FATAL_EXEC:
-		return "exec() failed";
-
-	case FATAL_DEFAULT:
-		return NULL;
-	}
-
-	return NULL;
-}
-
-static void sigchld_handler(int signo __attr_unused__,
-			    void *context __attr_unused__)
-{
-	const char *process_type_name, *msg;
-	pid_t pid;
-	int status, process_type;
-	bool abnormal_exit;
-
-	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
-		/* get the type and remove from hash */
-		process_type = PID_GET_PROCESS_TYPE(pid);
-		if (process_type != PROCESS_TYPE_UNKNOWN)
-			PID_REMOVE_PROCESS_TYPE(pid);
-
-		abnormal_exit = TRUE;
-
-		/* write errors to syslog */
-		process_type_name = process_names[process_type];
-		if (WIFEXITED(status)) {
-			status = WEXITSTATUS(status);
-			if (status == 0) {
-				abnormal_exit = FALSE;
-				if (process_type == PROCESS_TYPE_UNKNOWN) {
-					i_error("unknown child %s exited "
-						"successfully", dec2str(pid));
-				}
-			} else if (status == 1 &&
-				   process_type == PROCESS_TYPE_SSL_PARAM) {
-				/* kludgy. hide this failure. */
-			} else {
-				msg = get_exit_status_message(status);
-				msg = msg == NULL ? "" :
-					t_strconcat(" (", msg, ")", NULL);
-				i_error("child %s (%s) returned error %d%s",
-					dec2str(pid), process_type_name,
-					status, msg);
-			}
-		} else if (WIFSIGNALED(status)) {
-			i_error("child %s (%s) killed with signal %d",
-				dec2str(pid), process_type_name,
-				WTERMSIG(status));
-		}
-
-		switch (process_type) {
-		case PROCESS_TYPE_LOGIN:
-			login_process_destroyed(pid, abnormal_exit);
-			break;
-		case PROCESS_TYPE_IMAP:
-		case PROCESS_TYPE_POP3:
-			mail_process_destroyed(pid);
-			break;
-		case PROCESS_TYPE_SSL_PARAM:
-			ssl_parameter_process_destroyed(abnormal_exit);
-			break;
-		case PROCESS_TYPE_DICT:
-			dict_process_restart();
-			break;
-		}
-	}
-
-	if (pid == -1 && errno != EINTR && errno != ECHILD)
-		i_warning("waitpid() failed: %m");
-}
-
 static void resolve_ip(const char *set_name, const char *name,
 		       struct ip_addr *ip, unsigned int *port)
 {
@@ -598,14 +456,13 @@
         lib_signals_set_handler(SIGHUP, TRUE, sig_reload_settings, NULL);
         lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL);
 
-	pids = hash_create(default_pool, default_pool, 128, NULL, NULL);
-	lib_signals_set_handler(SIGCHLD, TRUE, sigchld_handler, NULL);
-
+	child_processes_init();
 	log_init();
 	ssl_init();
 	dict_process_init();
 	auth_processes_init();
 	login_processes_init();
+	mail_processes_init();
 
 	create_pid_file(t_strconcat(settings_root->defaults->base_dir,
 				    "/master.pid", NULL));
@@ -616,20 +473,15 @@
 	(void)unlink(t_strconcat(settings_root->defaults->base_dir,
 				 "/master.pid", NULL));
 
-	/* make sure we log if child processes died unexpectedly */
-	sigchld_handler(SIGCHLD, NULL);
-
 	login_processes_deinit();
 	auth_processes_deinit();
 	dict_process_deinit();
 	ssl_deinit();
-
-	lib_signals_unset_handler(SIGCHLD, sigchld_handler, NULL);
+	child_processes_deinit();
 
 	if (close(null_fd) < 0)
 		i_error("close(null_fd) failed: %m");
 
-	hash_destroy(pids);
 	lib_signals_deinit();
 	log_deinit();
 	closelog();
--- a/src/master/ssl-init.c	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/ssl-init.c	Sat Jun 30 17:38:17 2007 +0300
@@ -5,6 +5,7 @@
 #include "env-util.h"
 #include "file-copy.h"
 #include "log.h"
+#include "child-process.h"
 #include "ssl-init.h"
 
 #ifdef HAVE_SSL
@@ -16,6 +17,9 @@
 #include <utime.h>
 #include <sys/stat.h>
 
+static struct child_process ssl_param_child_process =
+	{ PROCESS_TYPE_SSL_PARAM };
+
 static struct timeout *to;
 static char *generating_path = NULL;
 
@@ -46,7 +50,7 @@
 		/* parent */
 		i_assert(generating_path == NULL);
 		generating_path = i_strdup(fname);
-		PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_SSL_PARAM);
+		child_process_add(pid, &ssl_param_child_process);
 		(void)close(log_fd);
 		return;
 	}
@@ -61,7 +65,9 @@
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", binpath);
 }
 
-void ssl_parameter_process_destroyed(bool abnormal_exit)
+static void
+ssl_parameter_process_destroyed(struct child_process *process __attr_unused__,
+				bool abnormal_exit)
 {
 	if (!abnormal_exit) {
 		if (file_copy(SSL_PARAMETERS_PERM_PATH,
@@ -155,6 +161,9 @@
 {
 	generating_path = NULL;
 
+	child_process_set_destroy_callback(PROCESS_TYPE_SSL_PARAM,
+					   ssl_parameter_process_destroyed);
+
 	/* check every 10 mins */
 	to = timeout_add(600 * 1000, check_parameters_file_timeout, NULL);
 
--- a/src/master/ssl-init.h	Sat Jun 30 00:48:10 2007 +0300
+++ b/src/master/ssl-init.h	Sat Jun 30 17:38:17 2007 +0300
@@ -3,8 +3,6 @@
 
 #define SSL_PARAMETERS_FILENAME "ssl-parameters.dat"
 
-void ssl_parameter_process_destroyed(bool abnormal_exit);
-
 void ssl_check_parameters_file(void);
 void _ssl_generate_parameters(int fd, const char *fname);