Commit Diff


commit - 666bd316da38eef77379216e92680abfa93b938a
commit + dbef7d4f82e95c334a131206f7d2ac02d3ef21bd
blob - 30a7fa88d41a8d7256095df23846cfd85e45b995
blob + 158491944da3175eea0b36906f2bc3308f1e4a12
--- Makefile
+++ Makefile
@@ -1,10 +1,10 @@
-#	$OpenBSD: Makefile,v 1.20 2010/05/26 16:44:32 nicm Exp $
+#	$OpenBSD: Makefile,v 1.21 2011/05/09 12:08:47 reyk Exp $
 
 PROG=		relayd
 SRCS=		parse.y log.c control.c ssl.c ssl_privsep.c \
 		relayd.c pfe.c pfe_filter.c pfe_route.c hce.c relay.c \
 		relay_udp.c carp.c check_icmp.c check_tcp.c check_script.c \
-		name2id.c snmp.c shuffle.c
+		name2id.c snmp.c shuffle.c proc.c
 MAN=		relayd.8 relayd.conf.5
 
 LDADD=		-levent -lssl -lcrypto -lutil
blob - 9a399a784c0ee728999e41bbacab1c7d5addbcdb
blob + df017f01a5f35e94c10c21b77d417eb50a3cb0b1
--- check_icmp.c
+++ check_icmp.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: check_icmp.c,v 1.30 2011/05/05 12:01:43 reyk Exp $	*/
+/*	$OpenBSD: check_icmp.c,v 1.31 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -59,7 +59,7 @@ icmp_setup(struct relayd *env, struct ctl_icmp_event *
 		proto = IPPROTO_ICMPV6;
 	if ((cie->s = socket(af, SOCK_RAW, proto)) < 0)
 		fatal("icmp_init: socket");
-	session_socket_blockmode(cie->s, BM_NONBLOCK);
+	socket_set_blockmode(cie->s, BM_NONBLOCK);
 	cie->env = env;
 	cie->af = af;
 }
blob - fff3998e75a89d6853246c19de03f42b82994c58
blob + 5dff88d011347d54570ac4152aaa4d1b36ec76a0
--- check_script.c
+++ check_script.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: check_script.c,v 1.11 2011/02/28 00:09:11 sthen Exp $	*/
+/*	$OpenBSD: check_script.c,v 1.12 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -39,11 +39,10 @@
 
 void	 script_sig_alarm(int);
 
-extern struct imsgev		*iev_main;
-pid_t				 child = -1;
+pid_t			 child = -1;
 
 void
-check_script(struct host *host)
+check_script(struct relayd *env, struct host *host)
 {
 	struct ctl_script	 scr;
 
@@ -51,7 +50,8 @@ check_script(struct host *host)
 	host->flags &= ~(F_CHECK_SENT|F_CHECK_DONE);
 
 	scr.host = host->conf.id;
-	imsg_compose_event(iev_main, IMSG_SCRIPT, 0, 0, -1, &scr, sizeof(scr));
+	proc_compose_imsg(env->sc_ps, PROC_PARENT, 0, IMSG_SCRIPT,
+	    -1, &scr, sizeof(scr));
 }
 
 void
blob - 8b1d8da936b2fc3743d91ed1aec8e46e99db26d8
blob + 9c9a3cc02e48c12fb72d8d325335093a1522a2f1
--- control.c
+++ control.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: control.c,v 1.37 2011/05/05 12:01:43 reyk Exp $	*/
+/*	$OpenBSD: control.c,v 1.38 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -40,86 +40,94 @@
 
 struct ctl_connlist ctl_conns;
 
-struct ctl_conn	*control_connbyfd(int);
+void		 control_accept(int, short, void *);
 void		 control_close(int);
 
-struct imsgev	*iev_main = NULL;
-struct imsgev	*iev_hce = NULL;
-
 int
-control_init(void)
+control_init(struct privsep *ps, struct control_sock *cs)
 {
+	struct relayd		*env = ps->ps_env;
 	struct sockaddr_un	 sun;
 	int			 fd;
-	mode_t			 old_umask;
+	mode_t			 old_umask, mode;
 
+	if (cs->cs_name == NULL)
+		return (0);
+
 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
 		log_warn("%s: socket", __func__);
 		return (-1);
 	}
 
 	sun.sun_family = AF_UNIX;
-	if (strlcpy(sun.sun_path, RELAYD_SOCKET,
+	if (strlcpy(sun.sun_path, cs->cs_name,
 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
-		log_warn("%s: %s name too long", __func__, RELAYD_SOCKET);
+		log_warn("%s: %s name too long", __func__, cs->cs_name);
 		close(fd);
 		return (-1);
 	}
 
-	if (unlink(RELAYD_SOCKET) == -1)
+	if (unlink(cs->cs_name) == -1)
 		if (errno != ENOENT) {
-			log_warn("%s: unlink %s", __func__, RELAYD_SOCKET);
+			log_warn("%s: unlink %s", __func__, cs->cs_name);
 			close(fd);
 			return (-1);
 		}
 
-	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+	if (cs->cs_restricted) {
+		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
+		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+	} else {
+		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
+	}
+
 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
-		log_warn("%s: bind: %s", __func__, RELAYD_SOCKET);
+		log_warn("%s: bind: %s", __func__, cs->cs_name);
 		close(fd);
 		(void)umask(old_umask);
 		return (-1);
 	}
 	(void)umask(old_umask);
 
-	if (chmod(RELAYD_SOCKET, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+	if (chmod(cs->cs_name, mode) == -1) {
 		log_warn("%s: chmod", __func__);
 		close(fd);
-		(void)unlink(RELAYD_SOCKET);
+		(void)unlink(cs->cs_name);
 		return (-1);
 	}
 
-	session_socket_blockmode(fd, BM_NONBLOCK);
-	control_state.fd = fd;
-	TAILQ_INIT(&ctl_conns);
+	socket_set_blockmode(fd, BM_NONBLOCK);
+	cs->cs_fd = fd;
+	cs->cs_env = env;
 
 	return (0);
 }
 
 int
-control_listen(struct relayd *env, struct imsgev *i_main,
-    struct imsgev *i_hce)
+control_listen(struct control_sock *cs)
 {
+	if (cs->cs_name == NULL)
+		return (0);
 
-	iev_main = i_main;
-	iev_hce = i_hce;
-
-	if (listen(control_state.fd, CONTROL_BACKLOG) == -1) {
+	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
 		log_warn("%s: listen", __func__);
 		return (-1);
 	}
 
-	event_set(&control_state.ev, control_state.fd, EV_READ | EV_PERSIST,
-	    control_accept, env);
-	event_add(&control_state.ev, NULL);
+	event_set(&cs->cs_ev, cs->cs_fd, EV_READ | EV_PERSIST,
+	    control_accept, cs->cs_env);
+	event_add(&cs->cs_ev, NULL);
 
 	return (0);
 }
 
 void
-control_cleanup(void)
+control_cleanup(struct control_sock *cs)
 {
-	(void)unlink(RELAYD_SOCKET);
+	if (cs->cs_name == NULL)
+		return;
+	(void)unlink(cs->cs_name);
 }
 
 /* ARGSUSED */
@@ -140,7 +148,7 @@ control_accept(int listenfd, short event, void *arg)
 		return;
 	}
 
-	session_socket_blockmode(connfd, BM_NONBLOCK);
+	socket_set_blockmode(connfd, BM_NONBLOCK);
 
 	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
 		close(connfd);
@@ -323,8 +331,8 @@ control_dispatch_imsg(int fd, short event, void *arg)
 			    0, 0, -1, NULL, 0);
 			break;
 		case IMSG_CTL_POLL:
-			imsg_compose_event(iev_hce, IMSG_CTL_POLL,
-			    0, 0,-1, NULL, 0);
+			proc_compose_imsg(env->sc_ps, PROC_HCE, -1,
+			    IMSG_CTL_POLL, -1, NULL, 0);
 			imsg_compose_event(&c->iev, IMSG_CTL_OK,
 			    0, 0, -1, NULL, 0);
 			break;
@@ -334,8 +342,8 @@ control_dispatch_imsg(int fd, short event, void *arg)
 				    0, 0, -1, NULL, 0);
 				break;
 			}
-			imsg_compose_event(iev_main, IMSG_CTL_RELOAD,
-			    0, 0, -1, NULL, 0);
+			proc_compose_imsg(env->sc_ps, PROC_PARENT, -1, IMSG_CTL_RELOAD,
+			    -1, NULL, 0);
 			/*
 			 * we unconditionnaly return a CTL_OK imsg because
 			 * we have no choice.
@@ -358,20 +366,17 @@ control_dispatch_imsg(int fd, short event, void *arg)
 			}
 			c->flags |= CTL_CONN_NOTIFY;
 			break;
-		case IMSG_CTL_LOG_VERBOSE:
-			if (imsg.hdr.len != IMSG_HEADER_SIZE +
-			    sizeof(verbose))
-				break;
+		case IMSG_CTL_VERBOSE:
+			IMSG_SIZE_CHECK(&imsg, &verbose);
 
 			memcpy(&verbose, imsg.data, sizeof(verbose));
 
-			imsg_compose_event(iev_hce, IMSG_CTL_LOG_VERBOSE,
-			    0, 0, -1, &verbose, sizeof(verbose));
-			imsg_compose_event(iev_main, IMSG_CTL_LOG_VERBOSE,
-			    0, 0, -1, &verbose, sizeof(verbose));
+			proc_forward_imsg(env->sc_ps, &imsg, PROC_PARENT, 0);
+			proc_forward_imsg(env->sc_ps, &imsg, PROC_HCE, 0);
+			proc_forward_imsg(env->sc_ps, &imsg, PROC_RELAY, -1);
+
 			memcpy(imsg.data, &verbose, sizeof(verbose));
 			control_imsg_forward(&imsg);
-
 			log_verbose(verbose);
 			break;
 		default:
@@ -398,7 +403,7 @@ control_imsg_forward(struct imsg *imsg)
 }
 
 void
-session_socket_blockmode(int fd, enum blockmodes bm)
+socket_set_blockmode(int fd, enum blockmodes bm)
 {
 	int	flags;
 
blob - 2eb5b0c5542a31a13e421c5a786cddc241720099
blob + ab80166c4bb7927bacf7f67946bd0f9f98a0a9d4
--- hce.c
+++ hce.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: hce.c,v 1.58 2011/05/05 12:01:43 reyk Exp $	*/
+/*	$OpenBSD: hce.c,v 1.59 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -41,135 +41,45 @@
 
 #include "relayd.h"
 
-__dead void hce_shutdown(void);
-void	hce_sig_handler(int sig, short, void *);
-void	hce_dispatch_imsg(int, short, void *);
-void	hce_dispatch_parent(int, short, void *);
-void	hce_launch_checks(int, short, void *);
-void	hce_setup_events(void);
-void	hce_disable_events(void);
+void	 hce_init(struct privsep *, struct privsep_proc *p, void *);
+void	 hce_sig_handler(int sig, short, void *);
+void	 hce_launch_checks(int, short, void *);
+void	 hce_setup_events(void);
+void	 hce_disable_events(void);
 
+int	 hce_dispatch_parent(int, struct privsep_proc *, struct imsg *);
+int	 hce_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
+
 static struct relayd *env = NULL;
-struct imsgev		*iev_pfe;
-struct imsgev		*iev_main;
 int			 running = 0;
 
-void
-hce_sig_handler(int sig, short event, void *arg)
-{
-	switch (sig) {
-	case SIGINT:
-	case SIGTERM:
-		hce_shutdown();
-		break;
-	case SIGCHLD:
-	case SIGHUP:
-	case SIGPIPE:
-		/* ignore */
-		break;
-	default:
-		fatalx("hce_sig_handler: unexpected signal");
-	}
-}
+static struct privsep_proc procs[] = {
+	{ "parent",	PROC_PARENT,	hce_dispatch_parent },
+	{ "pfe",	PROC_PFE,	hce_dispatch_pfe },
+};
 
 pid_t
-hce(struct relayd *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
-    int pipe_parent2relay[RELAY_MAXPROC][2], int pipe_pfe2hce[2],
-    int pipe_pfe2relay[RELAY_MAXPROC][2])
+hce(struct privsep *ps, struct privsep_proc *p)
 {
-	pid_t		 pid;
-	struct passwd	*pw;
-	int		 i;
+	env = ps->ps_env;
 
-	switch (pid = fork()) {
-	case -1:
-		fatal("hce: cannot fork");
-	case 0:
-		break;
-	default:
-		return (pid);
-	}
-
-	env = x_env;
-	purge_config(env, PURGE_RDRS|PURGE_RELAYS|PURGE_PROTOS);
-
-	if ((pw = getpwnam(RELAYD_USER)) == NULL)
-		fatal("hce: getpwnam");
-
-#ifndef DEBUG
-	if (chroot(pw->pw_dir) == -1)
-		fatal("hce: chroot");
-	if (chdir("/") == -1)
-		fatal("hce: chdir(\"/\")");
-#else
-#warning disabling privilege revocation and chroot in DEBUG mode
-#endif
-
-	setproctitle("host check engine");
-	relayd_process = PROC_HCE;
-
 	/* this is needed for icmp tests */
 	icmp_init(env);
 
-#ifndef DEBUG
-	if (setgroups(1, &pw->pw_gid) ||
-	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
-	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
-		fatal("hce: can't drop privileges");
-#endif
+	return (proc_run(ps, p, procs, nitems(procs), hce_init, NULL));
+}
 
-	event_init();
+void
+hce_init(struct privsep *ps, struct privsep_proc *p, void *arg)
+{
+	purge_config(env, PURGE_RDRS|PURGE_RELAYS|PURGE_PROTOS);
 
+	env->sc_id = getpid() & 0xffff;
+
 	/* Allow maximum available sockets for TCP checks */
 	socket_rlimit(-1);
 
-	if ((iev_pfe = calloc(1, sizeof(struct imsgev))) == NULL ||
-	    (iev_main = calloc(1, sizeof(struct imsgev))) == NULL)
-		fatal("hce");
-	imsg_init(&iev_pfe->ibuf, pipe_pfe2hce[0]);
-	iev_pfe->handler = hce_dispatch_imsg;
-	imsg_init(&iev_main->ibuf, pipe_parent2hce[1]);
-	iev_main->handler = hce_dispatch_parent;
-
-	iev_pfe->events = EV_READ;
-	event_set(&iev_pfe->ev, iev_pfe->ibuf.fd, iev_pfe->events,
-	    iev_pfe->handler, iev_pfe);
-	event_add(&iev_pfe->ev, NULL);
-
-	iev_main->events = EV_READ;
-	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
-	    iev_main->handler, iev_main);
-	event_add(&iev_main->ev, NULL);
-
-	signal_set(&env->sc_evsigint, SIGINT, hce_sig_handler, env);
-	signal_set(&env->sc_evsigterm, SIGTERM, hce_sig_handler, env);
-	signal_set(&env->sc_evsigchld, SIGCHLD, hce_sig_handler, env);
-	signal_set(&env->sc_evsighup, SIGHUP, hce_sig_handler, env);
-	signal_set(&env->sc_evsigpipe, SIGPIPE, hce_sig_handler, env);
-
-	signal_add(&env->sc_evsigint, NULL);
-	signal_add(&env->sc_evsigterm, NULL);
-	signal_add(&env->sc_evsigchld, NULL);
-	signal_add(&env->sc_evsighup, NULL);
-	signal_add(&env->sc_evsigpipe, NULL);
-
-	/* setup pipes */
-	close(pipe_pfe2hce[1]);
-	close(pipe_parent2hce[0]);
-	close(pipe_parent2pfe[0]);
-	close(pipe_parent2pfe[1]);
-	for (i = 0; i < env->sc_prefork_relay; i++) {
-		close(pipe_parent2relay[i][0]);
-		close(pipe_parent2relay[i][1]);
-		close(pipe_pfe2relay[i][0]);
-		close(pipe_pfe2relay[i][1]);
-	}
-
 	hce_setup_events();
-	event_dispatch();
-	hce_shutdown();
-
-	return (0);
 }
 
 void
@@ -178,7 +88,7 @@ hce_setup_events(void)
 	struct timeval	 tv;
 	struct table	*table;
 
-	snmp_init(env, iev_main);
+	snmp_init(env, PROC_PARENT);
 
 	if (!TAILQ_EMPTY(env->sc_tables)) {
 		evtimer_set(&env->sc_ev, hce_launch_checks, env);
@@ -230,7 +140,7 @@ hce_launch_checks(int fd, short event, void *arg)
 	/*
 	 * notify pfe checks are done and schedule next check
 	 */
-	imsg_compose_event(iev_pfe, IMSG_SYNC, 0, 0, -1, NULL, 0);
+	proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_SYNC, -1, NULL, 0);
 	TAILQ_FOREACH(table, env->sc_tables, entry) {
 		TAILQ_FOREACH(host, &table->hosts, entry) {
 			if ((host->flags & F_CHECK_DONE) == 0)
@@ -265,7 +175,7 @@ hce_launch_checks(int fd, short event, void *arg)
 				schedule_icmp(env, host);
 				break;
 			case CHECK_SCRIPT:
-				check_script(host);
+				check_script(env, host);
 				break;
 			default:
 				/* Any other TCP-style checks */
@@ -320,8 +230,8 @@ hce_notify_done(struct host *host, enum host_error he)
 	if (msg)
 		log_debug("%s: %s (%s)", __func__, host->conf.name, msg);
 
-	imsg_compose_event(iev_pfe, IMSG_HOST_STATUS,
-	    0, 0, -1, &st, sizeof(st));
+	proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_HOST_STATUS,
+	    -1, &st, sizeof(st));
 	if (host->up != host->last_up)
 		logopt = RELAYD_OPT_LOGUPDATE;
 	else
@@ -348,7 +258,7 @@ hce_notify_done(struct host *host, enum host_error he)
 	}
 
 	if (host->last_up != host->up)
-		snmp_hosttrap(table, host);
+		snmp_hosttrap(env, table, host);
 
 	host->last_up = host->up;
 
@@ -362,200 +272,119 @@ hce_notify_done(struct host *host, enum host_error he)
 	}
 }
 
-void
-hce_shutdown(void)
+int
+hce_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	log_info("host check engine exiting");
-	_exit(0);
-}
-
-void
-hce_dispatch_imsg(int fd, short event, void *ptr)
-{
-	struct imsgev		*iev;
-	struct imsgbuf		*ibuf;
-	struct imsg		 imsg;
-	ssize_t			 n;
 	objid_t			 id;
 	struct host		*host;
 	struct table		*table;
-	int			 verbose;
 
-	iev = ptr;
-	ibuf = &iev->ibuf;
-
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("hce_dispatch_imsg: imsg_read_error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
-		}
-	}
-
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("hce_dispatch_imsg: msgbuf_write");
-	}
-
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("hce_dispatch_imsg: imsg_read error");
-		if (n == 0)
-			break;
-
-		switch (imsg.hdr.type) {
-		case IMSG_HOST_DISABLE:
-			memcpy(&id, imsg.data, sizeof(id));
-			if ((host = host_find(env, id)) == NULL)
-				fatalx("hce_dispatch_imsg: desynchronized");
-			host->flags |= F_DISABLE;
+	switch (imsg->hdr.type) {
+	case IMSG_HOST_DISABLE:
+		memcpy(&id, imsg->data, sizeof(id));
+		if ((host = host_find(env, id)) == NULL)
+			fatalx("hce_dispatch_imsg: desynchronized");
+		host->flags |= F_DISABLE;
+		host->up = HOST_UNKNOWN;
+		host->check_cnt = 0;
+		host->up_cnt = 0;
+		host->he = HCE_NONE;
+		break;
+	case IMSG_HOST_ENABLE:
+		memcpy(&id, imsg->data, sizeof(id));
+		if ((host = host_find(env, id)) == NULL)
+			fatalx("hce_dispatch_imsg: desynchronized");
+		host->flags &= ~(F_DISABLE);
+		host->up = HOST_UNKNOWN;
+		host->he = HCE_NONE;
+		break;
+	case IMSG_TABLE_DISABLE:
+		memcpy(&id, imsg->data, sizeof(id));
+		if ((table = table_find(env, id)) == NULL)
+			fatalx("hce_dispatch_imsg: desynchronized");
+		table->conf.flags |= F_DISABLE;
+		TAILQ_FOREACH(host, &table->hosts, entry)
 			host->up = HOST_UNKNOWN;
-			host->check_cnt = 0;
-			host->up_cnt = 0;
-			host->he = HCE_NONE;
-			break;
-		case IMSG_HOST_ENABLE:
-			memcpy(&id, imsg.data, sizeof(id));
-			if ((host = host_find(env, id)) == NULL)
-				fatalx("hce_dispatch_imsg: desynchronized");
-			host->flags &= ~(F_DISABLE);
+		break;
+	case IMSG_TABLE_ENABLE:
+		memcpy(&id, imsg->data, sizeof(id));
+		if ((table = table_find(env, id)) == NULL)
+			fatalx("hce_dispatch_imsg: desynchronized");
+		table->conf.flags &= ~(F_DISABLE);
+		TAILQ_FOREACH(host, &table->hosts, entry)
 			host->up = HOST_UNKNOWN;
-			host->he = HCE_NONE;
-			break;
-		case IMSG_TABLE_DISABLE:
-			memcpy(&id, imsg.data, sizeof(id));
-			if ((table = table_find(env, id)) == NULL)
-				fatalx("hce_dispatch_imsg: desynchronized");
-			table->conf.flags |= F_DISABLE;
-			TAILQ_FOREACH(host, &table->hosts, entry)
-				host->up = HOST_UNKNOWN;
-			break;
-		case IMSG_TABLE_ENABLE:
-			memcpy(&id, imsg.data, sizeof(id));
-			if ((table = table_find(env, id)) == NULL)
-				fatalx("hce_dispatch_imsg: desynchronized");
-			table->conf.flags &= ~(F_DISABLE);
-			TAILQ_FOREACH(host, &table->hosts, entry)
-				host->up = HOST_UNKNOWN;
-			break;
-		case IMSG_CTL_POLL:
-			evtimer_del(&env->sc_ev);
-			TAILQ_FOREACH(table, env->sc_tables, entry)
-				table->skipped = 0;
-			hce_launch_checks(-1, EV_TIMEOUT, env);
-			break;
-		case IMSG_CTL_LOG_VERBOSE:
-			memcpy(&verbose, imsg.data, sizeof(verbose));
-			log_verbose(verbose);
-			break;
-		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
-		}
-		imsg_free(&imsg);
+		break;
+	case IMSG_CTL_POLL:
+		evtimer_del(&env->sc_ev);
+		TAILQ_FOREACH(table, env->sc_tables, entry)
+			table->skipped = 0;
+		hce_launch_checks(-1, EV_TIMEOUT, env);
+		break;
+	default:
+		return (-1);
 	}
-	imsg_event_add(iev);
+
+	return (0);
 }
 
-void
-hce_dispatch_parent(int fd, short event, void * ptr)
+int
+hce_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	struct imsgev		*iev;
-	struct imsgbuf		*ibuf;
-	struct imsg		 imsg;
 	struct ctl_script	 scr;
-	ssize_t			 n;
 	size_t			 len;
 	static struct table	*table = NULL;
 	struct host		*host, *parent;
 
-	iev = ptr;
-	ibuf = &iev->ibuf;
+	switch (imsg->hdr.type) {
+	case IMSG_SCRIPT:
+		IMSG_SIZE_CHECK(imsg, &scr);
+		bcopy(imsg->data, &scr, sizeof(scr));
+		script_done(env, &scr);
+		break;
+	case IMSG_RECONF:
+		IMSG_SIZE_CHECK(imsg, env);
+		log_debug("%s: reloading configuration", __func__);
+		hce_disable_events();
+		purge_config(env, PURGE_TABLES);
+		merge_config(env, (struct relayd *)imsg->data);
 
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("hce_dispatch_parent: imsg_read error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
-		}
-	}
+		env->sc_tables = calloc(1, sizeof(*env->sc_tables));
+		if (env->sc_tables == NULL)
+			fatal(NULL);
 
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("hce_dispatch_parent: msgbuf_write");
-	}
-
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("hce_dispatch_parent: imsg_read error");
-		if (n == 0)
-			break;
-
-		switch (imsg.hdr.type) {
-		case IMSG_SCRIPT:
-			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
-			    sizeof(scr))
-				fatalx("hce_dispatch_parent: "
-				    "invalid size of script request");
-			bcopy(imsg.data, &scr, sizeof(scr));
-			script_done(env, &scr);
-			break;
-		case IMSG_RECONF:
-			log_debug("%s: reloading configuration", __func__);
-			if (imsg.hdr.len !=
-			    sizeof(struct relayd) + IMSG_HEADER_SIZE)
-				fatalx("corrupted reload data");
-			hce_disable_events();
-			purge_config(env, PURGE_TABLES);
-			merge_config(env, (struct relayd *)imsg.data);
-
-			env->sc_tables = calloc(1, sizeof(*env->sc_tables));
-			if (env->sc_tables == NULL)
-				fatal(NULL);
-
-			TAILQ_INIT(env->sc_tables);
-			break;
-		case IMSG_RECONF_TABLE:
-			if ((table = calloc(1, sizeof(*table))) == NULL)
-				fatal(NULL);
-			memcpy(&table->conf, imsg.data, sizeof(table->conf));
-			TAILQ_INIT(&table->hosts);
-			TAILQ_INSERT_TAIL(env->sc_tables, table, entry);
-			break;
-		case IMSG_RECONF_SENDBUF:
-			len = imsg.hdr.len - IMSG_HEADER_SIZE;
-			table->sendbuf = calloc(1, len);
-			(void)strlcpy(table->sendbuf, (char *)imsg.data, len);
-			break;
-		case IMSG_RECONF_HOST:
-			if ((host = calloc(1, sizeof(*host))) == NULL)
-				fatal(NULL);
-			memcpy(&host->conf, imsg.data, sizeof(host->conf));
-			host->tablename = table->conf.name;
-			TAILQ_INSERT_TAIL(&table->hosts, host, entry);
-			if (host->conf.parentid) {
-				parent = host_find(env, host->conf.parentid);
-				SLIST_INSERT_HEAD(&parent->children,
-				    host, child);
-			}
-			break;
-		case IMSG_RECONF_END:
-			log_warnx("%s: configuration reloaded", __func__);
-			hce_setup_events();
-			break;
-		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
+		TAILQ_INIT(env->sc_tables);
+		break;
+	case IMSG_RECONF_TABLE:
+		if ((table = calloc(1, sizeof(*table))) == NULL)
+			fatal(NULL);
+		memcpy(&table->conf, imsg->data, sizeof(table->conf));
+		TAILQ_INIT(&table->hosts);
+		TAILQ_INSERT_TAIL(env->sc_tables, table, entry);
+		break;
+	case IMSG_RECONF_SENDBUF:
+		len = imsg->hdr.len - IMSG_HEADER_SIZE;
+		table->sendbuf = calloc(1, len);
+		(void)strlcpy(table->sendbuf, (char *)imsg->data, len);
+		break;
+	case IMSG_RECONF_HOST:
+		if ((host = calloc(1, sizeof(*host))) == NULL)
+			fatal(NULL);
+		memcpy(&host->conf, imsg->data, sizeof(host->conf));
+		host->tablename = table->conf.name;
+		TAILQ_INSERT_TAIL(&table->hosts, host, entry);
+		if (host->conf.parentid) {
+			parent = host_find(env, host->conf.parentid);
+			SLIST_INSERT_HEAD(&parent->children,
+			    host, child);
 		}
-		imsg_free(&imsg);
+		break;
+	case IMSG_RECONF_END:
+		log_warnx("%s: configuration reloaded", __func__);
+		hce_setup_events();
+		break;
+	default:
+		return (-1);
 	}
-	imsg_event_add(iev);
+
+	return (0);
 }
blob - fcb0b55f9c56e1420514510953f04a1fa89f9b45
blob + c03b54c4264e2e3e86043000ab8664981b16ae50
--- parse.y
+++ parse.y
@@ -1,4 +1,4 @@
-/*	$OpenBSD: parse.y,v 1.154 2011/05/05 12:01:43 reyk Exp $	*/
+/*	$OpenBSD: parse.y,v 1.155 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -1253,6 +1253,7 @@ relay		: RELAY STRING	{
 			conf->sc_relaycount++;
 			SPLAY_INIT(&rlay->rl_sessions);
 			TAILQ_INSERT_TAIL(conf->sc_relays, rlay, rl_entry);
+
 			tableport = 0;
 
 			while ((r = TAILQ_FIRST(&relays)) != NULL) {
blob - a9d376d42e19562579241fd9c368a27c035a773b
blob + ba3a6260390e37cf546a675d139e7ada36208f62
--- pfe.c
+++ pfe.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pfe.c,v 1.67 2011/05/05 12:01:43 reyk Exp $	*/
+/*	$OpenBSD: pfe.c,v 1.68 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -35,180 +35,56 @@
 
 #include "relayd.h"
 
-void	pfe_sig_handler(int sig, short, void *);
-void	pfe_shutdown(void);
-void	pfe_setup_events(void);
-void	pfe_disable_events(void);
-void	pfe_dispatch_imsg(int, short, void *);
-void	pfe_dispatch_parent(int, short, void *);
-void	pfe_dispatch_relay(int, short, void *);
-void	pfe_sync(void);
-void	pfe_statistics(int, short, void *);
+void	 pfe_init(struct privsep *, struct privsep_proc *p, void *);
+void	 pfe_shutdown(void);
+void	 pfe_setup_events(void);
+void	 pfe_disable_events(void);
+void	 pfe_sync(void);
+void	 pfe_statistics(int, short, void *);
 
+int	 pfe_dispatch_parent(int, struct privsep_proc *, struct imsg *);
+int	 pfe_dispatch_hce(int, struct privsep_proc *, struct imsg *);
+int	 pfe_dispatch_relay(int, struct privsep_proc *, struct imsg *);
+
 static struct relayd	*env = NULL;
 
-struct imsgev	*iev_main;
-struct imsgev	*iev_hce;
-struct imsgev	*iev_relay;
+static struct privsep_proc procs[] = {
+	{ "parent",	PROC_PARENT,	pfe_dispatch_parent },
+	{ "relay",	PROC_RELAY,	pfe_dispatch_relay },
+	{ "hce",	PROC_HCE,	pfe_dispatch_hce }
+};
 
-void
-pfe_sig_handler(int sig, short event, void *arg)
-{
-	switch (sig) {
-	case SIGINT:
-	case SIGTERM:
-		pfe_shutdown();
-		break;
-	case SIGCHLD:
-	case SIGHUP:
-	case SIGPIPE:
-		/* ignore */
-		break;
-	default:
-		fatalx("pfe_sig_handler: unexpected signal");
-	}
-}
-
 pid_t
-pfe(struct relayd *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
-    int pipe_parent2relay[RELAY_MAXPROC][2], int pipe_pfe2hce[2],
-    int pipe_pfe2relay[RELAY_MAXPROC][2])
+pfe(struct privsep *ps, struct privsep_proc *p)
 {
-	pid_t		 pid;
-	struct passwd	*pw;
-	int		 i;
-	size_t		 size;
+	env = ps->ps_env;
 
-	switch (pid = fork()) {
-	case -1:
-		fatal("pfe: cannot fork");
-	case 0:
-		break;
-	default:
-		return (pid);
-	}
-
-	env = x_env;
-	purge_config(env, PURGE_PROTOS);
-
-	if (control_init() == -1)
-		fatalx("pfe: control socket setup failed");
-
 	init_filter(env);
 	init_tables(env);
 
-	if ((pw = getpwnam(RELAYD_USER)) == NULL)
-		fatal("pfe: getpwnam");
+	return (proc_run(ps, p, procs, nitems(procs), pfe_init, NULL));
+}
 
-#ifndef DEBUG
-	if (chroot(pw->pw_dir) == -1)
-		fatal("pfe: chroot");
-	if (chdir("/") == -1)
-		fatal("pfe: chdir(\"/\")");
-#else
-#warning disabling privilege revocation and chroot in DEBUG mode
-#endif
-
-	setproctitle("pf update engine");
-	relayd_process = PROC_PFE;
-
-#ifndef DEBUG
-	if (setgroups(1, &pw->pw_gid) ||
-	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
-	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
-		fatal("pfe: cannot drop privileges");
-#endif
-
-	event_init();
-
-	signal_set(&env->sc_evsigint, SIGINT, pfe_sig_handler, env);
-	signal_set(&env->sc_evsigterm, SIGTERM, pfe_sig_handler, env);
-	signal_set(&env->sc_evsigchld, SIGCHLD, pfe_sig_handler, env);
-	signal_set(&env->sc_evsighup, SIGHUP, pfe_sig_handler, env);
-	signal_set(&env->sc_evsigpipe, SIGPIPE, pfe_sig_handler, env);
-
-	signal_add(&env->sc_evsigint, NULL);
-	signal_add(&env->sc_evsigterm, NULL);
-	signal_add(&env->sc_evsigchld, NULL);
-	signal_add(&env->sc_evsighup, NULL);
-	signal_add(&env->sc_evsigpipe, NULL);
-
-	/* setup pipes */
-	close(pipe_pfe2hce[0]);
-	close(pipe_parent2pfe[0]);
-	close(pipe_parent2hce[0]);
-	close(pipe_parent2hce[1]);
-	for (i = 0; i < env->sc_prefork_relay; i++) {
-		close(pipe_parent2relay[i][0]);
-		close(pipe_parent2relay[i][1]);
-		close(pipe_pfe2relay[i][0]);
-	}
-
-	size = sizeof(struct imsgev);
-	if ((iev_hce = calloc(1, size)) == NULL ||
-	    (iev_relay = calloc(env->sc_prefork_relay, size)) == NULL ||
-	    (iev_main = calloc(1, size)) == NULL)
-		fatal("pfe");
-
-	imsg_init(&iev_hce->ibuf, pipe_pfe2hce[1]);
-	iev_hce->handler = pfe_dispatch_imsg;
-	imsg_init(&iev_main->ibuf, pipe_parent2pfe[1]);
-	iev_main->handler = pfe_dispatch_parent;
-	for (i = 0; i < env->sc_prefork_relay; i++) {
-		imsg_init(&iev_relay[i].ibuf, pipe_pfe2relay[i][1]);
-		iev_relay[i].handler = pfe_dispatch_relay;
-	}
-
-	iev_main->events = EV_READ;
-	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
-	    iev_main->handler, iev_main);
-	event_add(&iev_main->ev, NULL);
-
+void
+pfe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
+{
+	p->p_shutdown = pfe_shutdown;
+	purge_config(env, PURGE_PROTOS);
 	pfe_setup_events();
-
-	TAILQ_INIT(&ctl_conns);
-
-	if (control_listen(env, iev_main, iev_hce) == -1)
-		fatalx("pfe: control socket listen failed");
-
-	/* Initial sync */
 	pfe_sync();
-
-	event_dispatch();
-	pfe_shutdown();
-
-	return (0);
 }
 
 void
 pfe_shutdown(void)
 {
 	flush_rulesets(env);
-	log_info("pf update engine exiting");
-	_exit(0);
 }
 
 void
 pfe_setup_events(void)
 {
-	int		 i;
-	struct imsgev	*iev;
 	struct timeval	 tv;
 
-	iev_hce->events = EV_READ;
-	event_set(&iev_hce->ev, iev_hce->ibuf.fd, iev_hce->events,
-	    iev_hce->handler, iev_hce);
-	event_add(&iev_hce->ev, NULL);
-
-	for (i = 0; i < env->sc_prefork_relay; i++) {
-		iev = &iev_relay[i];
-
-		iev->events = EV_READ;
-		event_set(&iev->ev, iev->ibuf.fd, iev->events,
-		    iev->handler, iev);
-		event_add(&iev->ev, NULL);
-	}
-
 	/* Schedule statistics timer */
 	evtimer_set(&env->sc_statev, pfe_statistics, NULL);
 	bcopy(&env->sc_statinterval, &tv, sizeof(tv));
@@ -218,318 +94,209 @@ pfe_setup_events(void)
 void
 pfe_disable_events(void)
 {
-	int	i;
-
-	event_del(&iev_hce->ev);
-
-	for (i = 0; i < env->sc_prefork_relay; i++)
-		event_del(&iev_relay[i].ev);
-
 	event_del(&env->sc_statev);
 }
 
-void
-pfe_dispatch_imsg(int fd, short event, void *ptr)
+int
+pfe_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	struct imsgev		*iev;
-	struct imsgbuf		*ibuf;
-	struct imsg		 imsg;
-	ssize_t			 n;
-
 	struct host		*host;
 	struct table		*table;
 	struct ctl_status	 st;
 
-	iev = ptr;
-	ibuf = &iev->ibuf;
+	control_imsg_forward(imsg);
 
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("pfe_dispatch_imsg: imsg_read_error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
+	switch (imsg->hdr.type) {
+	case IMSG_HOST_STATUS:
+		IMSG_SIZE_CHECK(imsg, &st);
+		memcpy(&st, imsg->data, sizeof(st));
+		if ((host = host_find(env, st.id)) == NULL)
+			fatalx("pfe_dispatch_imsg: invalid host id");
+		host->he = st.he;
+		if (host->flags & F_DISABLE)
+			break;
+		host->retry_cnt = st.retry_cnt;
+		if (st.up != HOST_UNKNOWN) {
+			host->check_cnt++;
+			if (st.up == HOST_UP)
+				host->up_cnt++;
 		}
-	}
+		if (host->check_cnt != st.check_cnt) {
+			log_debug("%s: host %d => %d", __func__,
+			    host->conf.id, host->up);
+			fatalx("pfe_dispatch_imsg: desynchronized");
+		}
 
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("pfe_dispatch_imsg: msgbuf_write");
-	}
-
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("pfe_dispatch_imsg: imsg_read error");
-		if (n == 0)
+		if (host->up == st.up)
 			break;
 
-		control_imsg_forward(&imsg);
-		switch (imsg.hdr.type) {
-		case IMSG_HOST_STATUS:
-			if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(st))
-				fatalx("pfe_dispatch_imsg: invalid request");
-			memcpy(&st, imsg.data, sizeof(st));
-			if ((host = host_find(env, st.id)) == NULL)
-				fatalx("pfe_dispatch_imsg: invalid host id");
-			host->he = st.he;
-			if (host->flags & F_DISABLE)
-				break;
-			host->retry_cnt = st.retry_cnt;
-			if (st.up != HOST_UNKNOWN) {
-				host->check_cnt++;
-				if (st.up == HOST_UP)
-					host->up_cnt++;
-			}
-			if (host->check_cnt != st.check_cnt) {
-				log_debug("%s: host %d => %d", __func__,
-				    host->conf.id, host->up);
-				fatalx("pfe_dispatch_imsg: desynchronized");
-			}
+		/* Forward to relay engine(s) */
+		proc_compose_imsg(env->sc_ps, PROC_RELAY, -1,
+		    IMSG_HOST_STATUS, -1, &st, sizeof(st));
 
-			if (host->up == st.up)
-				break;
+		if ((table = table_find(env, host->conf.tableid))
+		    == NULL)
+			fatalx("pfe_dispatch_imsg: invalid table id");
 
-			/* Forward to relay engine(s) */
-			for (n = 0; n < env->sc_prefork_relay; n++)
-				imsg_compose_event(&iev_relay[n],
-				    IMSG_HOST_STATUS, 0, 0, -1, &st,
-				    sizeof(st));
+		log_debug("%s: state %d for host %u %s", __func__,
+		    st.up, host->conf.id, host->conf.name);
 
-			if ((table = table_find(env, host->conf.tableid))
-			    == NULL)
-				fatalx("pfe_dispatch_imsg: invalid table id");
-
-			log_debug("%s: state %d for host %u %s", __func__,
-			    st.up, host->conf.id, host->conf.name);
-
-			/*
-			 * Do not change the table state when the host
-			 * state switches between UNKNOWN and DOWN.
-			 */
-			if (HOST_ISUP(st.up)) {
-				table->conf.flags |= F_CHANGED;
-				table->up++;
-				host->flags |= F_ADD;
-				host->flags &= ~(F_DEL);
-			} else if (HOST_ISUP(host->up)) {
-				table->up--;
-				table->conf.flags |= F_CHANGED;
-				host->flags |= F_DEL;
-				host->flags &= ~(F_ADD);
-			}
-
-			host->up = st.up;
-			break;
-		case IMSG_SYNC:
-			pfe_sync();
-			break;
-		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
+		/*
+		 * Do not change the table state when the host
+		 * state switches between UNKNOWN and DOWN.
+		 */
+		if (HOST_ISUP(st.up)) {
+			table->conf.flags |= F_CHANGED;
+			table->up++;
+			host->flags |= F_ADD;
+			host->flags &= ~(F_DEL);
+		} else if (HOST_ISUP(host->up)) {
+			table->up--;
+			table->conf.flags |= F_CHANGED;
+			host->flags |= F_DEL;
+			host->flags &= ~(F_ADD);
 		}
-		imsg_free(&imsg);
+
+		host->up = st.up;
+		break;
+	case IMSG_SYNC:
+		pfe_sync();
+		break;
+	default:
+		return (-1);
 	}
-	imsg_event_add(iev);
+
+	return (0);
 }
 
-void
-pfe_dispatch_parent(int fd, short event, void * ptr)
+int
+pfe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	struct imsgev	*iev;
-	struct imsgbuf	*ibuf;
-	struct imsg	 imsg;
-	ssize_t		 n;
-
 	static struct rdr	*rdr = NULL;
 	static struct table	*table = NULL;
 	struct host		*host, *parent;
 	struct address		*virt;
 
-	iev = ptr;
-	ibuf = &iev->ibuf;
+	switch (imsg->hdr.type) {
+	case IMSG_RECONF:
+		IMSG_SIZE_CHECK(imsg, env);
 
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("imsg_read error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
-		}
-	}
+		log_debug("%s: reloading configuration", __func__);
 
+		pfe_disable_events();
+		purge_config(env, PURGE_RDRS|PURGE_TABLES);
+		merge_config(env, (struct relayd *)imsg->data);
 
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("msgbuf_write");
-	}
+		/*
+		 * no relays when reconfiguring yet.
+		 */
+		env->sc_relays = NULL;
+		env->sc_protos = NULL;
 
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("pfe_dispatch_parent: imsg_read error");
-		if (n == 0)
-			break;
+		env->sc_tables = calloc(1, sizeof(*env->sc_tables));
+		env->sc_rdrs = calloc(1, sizeof(*env->sc_rdrs));
+		if (env->sc_tables == NULL || env->sc_rdrs == NULL)
+			fatal(NULL);
 
-		switch (imsg.hdr.type) {
-		case IMSG_RECONF:
-			log_debug("%s: reloading configuration", __func__);
-			if (imsg.hdr.len !=
-			    sizeof(struct relayd) + IMSG_HEADER_SIZE)
-				fatalx("corrupted reload data");
-			pfe_disable_events();
-			purge_config(env, PURGE_RDRS|PURGE_TABLES);
-			merge_config(env, (struct relayd *)imsg.data);
-			/*
-			 * no relays when reconfiguring yet.
-			 */
-			env->sc_relays = NULL;
-			env->sc_protos = NULL;
-
-			env->sc_tables = calloc(1, sizeof(*env->sc_tables));
-			env->sc_rdrs = calloc(1, sizeof(*env->sc_rdrs));
-			if (env->sc_tables == NULL || env->sc_rdrs == NULL)
-				fatal(NULL);
-
-			TAILQ_INIT(env->sc_tables);
-			TAILQ_INIT(env->sc_rdrs);
-			break;
-		case IMSG_RECONF_TABLE:
-			if ((table = calloc(1, sizeof(*table))) == NULL)
-				fatal(NULL);
-			memcpy(&table->conf, imsg.data, sizeof(table->conf));
-			TAILQ_INIT(&table->hosts);
-			TAILQ_INSERT_TAIL(env->sc_tables, table, entry);
-			break;
-		case IMSG_RECONF_HOST:
-			if ((host = calloc(1, sizeof(*host))) == NULL)
-				fatal(NULL);
-			memcpy(&host->conf, imsg.data, sizeof(host->conf));
-			host->tablename = table->conf.name;
-			TAILQ_INSERT_TAIL(&table->hosts, host, entry);
-			if (host->conf.parentid) {
-				parent = host_find(env, host->conf.parentid);
-				SLIST_INSERT_HEAD(&parent->children,
-				    host, child);
-			}
-			break;
-		case IMSG_RECONF_RDR:
-			if ((rdr = calloc(1, sizeof(*rdr))) == NULL)
-				fatal(NULL);
-			memcpy(&rdr->conf, imsg.data,
-			    sizeof(rdr->conf));
-			rdr->table = table_find(env,
-			     rdr->conf.table_id);
-			if (rdr->conf.backup_id == EMPTY_TABLE)
-				rdr->backup = &env->sc_empty_table;
-			else
-				rdr->backup = table_find(env,
-				    rdr->conf.backup_id);
-			if (rdr->table == NULL || rdr->backup == NULL)
-				fatal("pfe_dispatch_parent:"
-				    " corrupted configuration");
-			log_debug("%s: redirect table %s, backup %s",
-			    __func__,
-			    rdr->table->conf.name,
-			    rdr->backup->conf.name);
-			TAILQ_INIT(&rdr->virts);
-			TAILQ_INSERT_TAIL(env->sc_rdrs, rdr, entry);
-			break;
-		case IMSG_RECONF_VIRT:
-			if ((virt = calloc(1, sizeof(*virt))) == NULL)
-				fatal(NULL);
-			memcpy(virt, imsg.data, sizeof(*virt));
-			TAILQ_INSERT_TAIL(&rdr->virts, virt, entry);
-			break;
-		case IMSG_RECONF_END:
-			log_warnx("%s: configuration reloaded", __func__);
-			init_tables(env);
-			pfe_setup_events();
-			pfe_sync();
-			break;
-		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
+		TAILQ_INIT(env->sc_tables);
+		TAILQ_INIT(env->sc_rdrs);
+		break;
+	case IMSG_RECONF_TABLE:
+		if ((table = calloc(1, sizeof(*table))) == NULL)
+			fatal(NULL);
+		memcpy(&table->conf, imsg->data, sizeof(table->conf));
+		TAILQ_INIT(&table->hosts);
+		TAILQ_INSERT_TAIL(env->sc_tables, table, entry);
+		break;
+	case IMSG_RECONF_HOST:
+		if ((host = calloc(1, sizeof(*host))) == NULL)
+			fatal(NULL);
+		memcpy(&host->conf, imsg->data, sizeof(host->conf));
+		host->tablename = table->conf.name;
+		TAILQ_INSERT_TAIL(&table->hosts, host, entry);
+		if (host->conf.parentid) {
+			parent = host_find(env, host->conf.parentid);
+			SLIST_INSERT_HEAD(&parent->children,
+			    host, child);
 		}
-		imsg_free(&imsg);
+		break;
+	case IMSG_RECONF_RDR:
+		if ((rdr = calloc(1, sizeof(*rdr))) == NULL)
+			fatal(NULL);
+		memcpy(&rdr->conf, imsg->data,
+		    sizeof(rdr->conf));
+		rdr->table = table_find(env,
+		     rdr->conf.table_id);
+		if (rdr->conf.backup_id == EMPTY_TABLE)
+			rdr->backup = &env->sc_empty_table;
+		else
+			rdr->backup = table_find(env,
+			    rdr->conf.backup_id);
+		if (rdr->table == NULL || rdr->backup == NULL)
+			fatal("pfe_dispatch_parent:"
+			    " corrupted configuration");
+		log_debug("%s: redirect table %s, backup %s",
+		    __func__,
+		    rdr->table->conf.name,
+		    rdr->backup->conf.name);
+		TAILQ_INIT(&rdr->virts);
+		TAILQ_INSERT_TAIL(env->sc_rdrs, rdr, entry);
+		break;
+	case IMSG_RECONF_VIRT:
+		if ((virt = calloc(1, sizeof(*virt))) == NULL)
+			fatal(NULL);
+		memcpy(virt, imsg->data, sizeof(*virt));
+		TAILQ_INSERT_TAIL(&rdr->virts, virt, entry);
+		break;
+	case IMSG_RECONF_END:
+		log_warnx("%s: configuration reloaded", __func__);
+		init_tables(env);
+		pfe_setup_events();
+		pfe_sync();
+		break;
+	default:
+		return (-1);
 	}
-	imsg_event_add(iev);
+
+	return (0);
 }
 
-void
-pfe_dispatch_relay(int fd, short event, void * ptr)
+int
+pfe_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	struct imsgev		*iev;
-	struct imsgbuf		*ibuf;
-	struct imsg		 imsg;
-	ssize_t			 n;
 	struct ctl_natlook	 cnl;
 	struct ctl_stats	 crs;
 	struct relay		*rlay;
 
-	iev = ptr;
-	ibuf = &iev->ibuf;
-
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("imsg_read error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
-		}
+	switch (imsg->hdr.type) {
+	case IMSG_NATLOOK:
+		IMSG_SIZE_CHECK(imsg, &cnl);
+		bcopy(imsg->data, &cnl, sizeof(cnl));
+		if (cnl.proc > env->sc_prefork_relay)
+			fatalx("pfe_dispatch_relay: "
+			    "invalid relay proc");
+		if (natlook(env, &cnl) != 0)
+			cnl.in = -1;
+		proc_compose_imsg(env->sc_ps, PROC_RELAY, cnl.proc,
+		    IMSG_NATLOOK, -1, &cnl, sizeof(cnl));
+		break;
+	case IMSG_STATISTICS:
+		IMSG_SIZE_CHECK(imsg, &crs);
+		bcopy(imsg->data, &crs, sizeof(crs));
+		if (crs.proc > env->sc_prefork_relay)
+			fatalx("pfe_dispatch_relay: "
+			    "invalid relay proc");
+		if ((rlay = relay_find(env, crs.id)) == NULL)
+			fatalx("pfe_dispatch_relay: invalid relay id");
+		bcopy(&crs, &rlay->rl_stats[crs.proc], sizeof(crs));
+		rlay->rl_stats[crs.proc].interval =
+		    env->sc_statinterval.tv_sec;
+		break;
+	default:
+		return (-1);
 	}
 
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("msgbuf_write");
-	}
-
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("pfe_dispatch_relay: imsg_read error");
-		if (n == 0)
-			break;
-
-		switch (imsg.hdr.type) {
-		case IMSG_NATLOOK:
-			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(cnl))
-				fatalx("invalid imsg header len");
-			bcopy(imsg.data, &cnl, sizeof(cnl));
-			if (cnl.proc > env->sc_prefork_relay)
-				fatalx("pfe_dispatch_relay: "
-				    "invalid relay proc");
-			if (natlook(env, &cnl) != 0)
-				cnl.in = -1;
-			imsg_compose_event(&iev_relay[cnl.proc], IMSG_NATLOOK,
-			    0, 0, -1, &cnl, sizeof(cnl));
-			break;
-		case IMSG_STATISTICS:
-			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(crs))
-				fatalx("invalid imsg header len");
-			bcopy(imsg.data, &crs, sizeof(crs));
-			if (crs.proc > env->sc_prefork_relay)
-				fatalx("pfe_dispatch_relay: "
-				    "invalid relay proc");
-			if ((rlay = relay_find(env, crs.id)) == NULL)
-				fatalx("pfe_dispatch_relay: invalid relay id");
-			bcopy(&crs, &rlay->rl_stats[crs.proc], sizeof(crs));
-			rlay->rl_stats[crs.proc].interval =
-			    env->sc_statinterval.tv_sec;
-			break;
-		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
-		}
-		imsg_free(&imsg);
-	}
-	imsg_event_add(iev);
+	return (0);
 }
 
 void
@@ -624,19 +391,21 @@ end:
 void
 show_sessions(struct ctl_conn *c)
 {
-	int		 n, proc, done;
-	struct imsg	 imsg;
+	int			 n, proc, done;
+	struct imsg		 imsg;
+	struct imsgbuf		*ibuf;
+	struct rsession		 con;
 
 	for (proc = 0; proc < env->sc_prefork_relay; proc++) {
+		ibuf = proc_ibuf(env->sc_ps, PROC_RELAY, proc);
+
 		/*
 		 * Request all the running sessions from the process
 		 */
-		imsg_compose_event(&iev_relay[proc],
-		    IMSG_CTL_SESSION, 0, 0, -1, NULL, 0);
-		while (iev_relay[proc].ibuf.w.queued)
-			if (msgbuf_write(&iev_relay[proc].ibuf.w) < 0)
-				fatalx("write error");
-
+		proc_compose_imsg(env->sc_ps, PROC_RELAY, proc,
+		    IMSG_CTL_SESSION, -1, NULL, 0);
+		proc_flush_imsg(env->sc_ps, PROC_RELAY, proc);
+		
 		/*
 		 * Wait for the reply and forward the messages to the
 		 * control connection.
@@ -644,20 +413,23 @@ show_sessions(struct ctl_conn *c)
 		done = 0;
 		while (!done) {
 			do {
-				if ((n = imsg_read(&iev_relay[proc].ibuf)) == -1)
+				if ((n = imsg_read(ibuf)) == -1)
 					fatalx("imsg_read error");
 			} while (n == -2); /* handle non-blocking I/O */
 			while (!done) {
-				if ((n = imsg_get(&iev_relay[proc].ibuf,
-				    &imsg)) == -1)
+				if ((n = imsg_get(ibuf, &imsg)) == -1)
 					fatalx("imsg_get error");
 				if (n == 0)
 					break;
+
 				switch (imsg.hdr.type) {
 				case IMSG_CTL_SESSION:
+					IMSG_SIZE_CHECK(&imsg, &con);
+					memcpy(&con, imsg.data, sizeof(con));
+
 					imsg_compose_event(&c->iev,
-					    IMSG_CTL_SESSION, proc, 0, -1,
-					    imsg.data, sizeof(struct rsession));
+					    IMSG_CTL_SESSION, 0, 0, -1,
+					    &con, sizeof(con));
 					break;
 				case IMSG_CTL_END:
 					done = 1;
@@ -740,7 +512,6 @@ disable_table(struct ctl_conn *c, struct ctl_id *id)
 {
 	struct table	*table;
 	struct host	*host;
-	int		 n;
 
 	if (id->id == EMPTY_ID)
 		table = table_findbyname(env, id->name);
@@ -758,13 +529,13 @@ disable_table(struct ctl_conn *c, struct ctl_id *id)
 	table->up = 0;
 	TAILQ_FOREACH(host, &table->hosts, entry)
 		host->up = HOST_UNKNOWN;
-	imsg_compose_event(iev_hce, IMSG_TABLE_DISABLE, 0, 0, -1,
+	proc_compose_imsg(env->sc_ps, PROC_HCE, -1, IMSG_TABLE_DISABLE, -1,
 	    &table->conf.id, sizeof(table->conf.id));
+
 	/* Forward to relay engine(s) */
-	for (n = 0; n < env->sc_prefork_relay; n++)
-		imsg_compose_event(&iev_relay[n],
-		    IMSG_TABLE_DISABLE, 0, 0, -1,
-		    &table->conf.id, sizeof(table->conf.id));
+	proc_compose_imsg(env->sc_ps, PROC_RELAY, -1, IMSG_TABLE_DISABLE, -1,
+	    &table->conf.id, sizeof(table->conf.id));
+
 	log_debug("%s: table %d", __func__, table->conf.id);
 	pfe_sync();
 	return (0);
@@ -775,7 +546,6 @@ enable_table(struct ctl_conn *c, struct ctl_id *id)
 {
 	struct table	*table;
 	struct host	*host;
-	int		 n;
 
 	if (id->id == EMPTY_ID)
 		table = table_findbyname(env, id->name);
@@ -795,13 +565,13 @@ enable_table(struct ctl_conn *c, struct ctl_id *id)
 	table->up = 0;
 	TAILQ_FOREACH(host, &table->hosts, entry)
 		host->up = HOST_UNKNOWN;
-	imsg_compose_event(iev_hce, IMSG_TABLE_ENABLE, 0, 0, -1,
+	proc_compose_imsg(env->sc_ps, PROC_HCE, -1, IMSG_TABLE_ENABLE, -1,
 	    &table->conf.id, sizeof(table->conf.id));
+
 	/* Forward to relay engine(s) */
-	for (n = 0; n < env->sc_prefork_relay; n++)
-		imsg_compose_event(&iev_relay[n],
-		    IMSG_TABLE_ENABLE, 0, 0, -1,
-		    &table->conf.id, sizeof(table->conf.id));
+	proc_compose_imsg(env->sc_ps, PROC_RELAY, -1, IMSG_TABLE_ENABLE, -1,
+	    &table->conf.id, sizeof(table->conf.id));
+
 	log_debug("%s: table %d", __func__, table->conf.id);
 	pfe_sync();
 	return (0);
@@ -812,7 +582,6 @@ disable_host(struct ctl_conn *c, struct ctl_id *id, st
 {
 	struct host	*h;
 	struct table	*table;
-	int		 n;
 
 	if (host == NULL) {
 		if (id->id == EMPTY_ID)
@@ -841,13 +610,12 @@ disable_host(struct ctl_conn *c, struct ctl_id *id, st
 	host->check_cnt = 0;
 	host->up_cnt = 0;
 
-	imsg_compose_event(iev_hce, IMSG_HOST_DISABLE, 0, 0, -1,
+	proc_compose_imsg(env->sc_ps, PROC_HCE, -1, IMSG_HOST_DISABLE, -1,
 	    &host->conf.id, sizeof(host->conf.id));
+
 	/* Forward to relay engine(s) */
-	for (n = 0; n < env->sc_prefork_relay; n++)
-		imsg_compose_event(&iev_relay[n],
-		    IMSG_HOST_DISABLE, 0, 0, -1,
-		    &host->conf.id, sizeof(host->conf.id));
+	proc_compose_imsg(env->sc_ps, PROC_RELAY, -1, IMSG_HOST_DISABLE, -1,
+	    &host->conf.id, sizeof(host->conf.id));
 	log_debug("%s: host %d", __func__, host->conf.id);
 
 	if (!host->conf.parentid) {
@@ -863,7 +631,6 @@ int
 enable_host(struct ctl_conn *c, struct ctl_id *id, struct host *host)
 {
 	struct host	*h;
-	int		 n;
 
 	if (host == NULL) {
 		if (id->id == EMPTY_ID)
@@ -883,13 +650,13 @@ enable_host(struct ctl_conn *c, struct ctl_id *id, str
 	host->flags &= ~(F_DEL);
 	host->flags &= ~(F_ADD);
 
-	imsg_compose_event(iev_hce, IMSG_HOST_ENABLE, 0, 0, -1,
+	proc_compose_imsg(env->sc_ps, PROC_HCE, -1, IMSG_HOST_ENABLE, -1,
 	    &host->conf.id, sizeof (host->conf.id));
+
 	/* Forward to relay engine(s) */
-	for (n = 0; n < env->sc_prefork_relay; n++)
-		imsg_compose_event(&iev_relay[n],
-		    IMSG_HOST_ENABLE, 0, 0, -1,
-		    &host->conf.id, sizeof(host->conf.id));
+	proc_compose_imsg(env->sc_ps, PROC_RELAY, -1, IMSG_HOST_ENABLE, -1,
+	    &host->conf.id, sizeof(host->conf.id));
+
 	log_debug("%s: host %d", __func__, host->conf.id);
 
 	if (!host->conf.parentid) {
@@ -999,7 +766,7 @@ pfe_sync(void)
 		    demote.level, table->conf.name, table->conf.demote_group);
 		(void)strlcpy(demote.group, table->conf.demote_group,
 		    sizeof(demote.group));
-		imsg_compose_event(iev_main, IMSG_DEMOTE, 0, 0, -1,
+		proc_compose_imsg(env->sc_ps, PROC_PARENT, -1, IMSG_DEMOTE, -1,
 		    &demote, sizeof(demote));
 	}
 }
blob - ef16d7e4302742e9e38ce7b6adf72ab55e5c35e5
blob + 0fae0dfd0a065c2b10d47febb03cf2687fb0725c
--- pfe_route.c
+++ pfe_route.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pfe_route.c,v 1.3 2011/05/05 12:01:44 reyk Exp $	*/
+/*	$OpenBSD: pfe_route.c,v 1.4 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2009 Reyk Floeter <reyk@openbsd.org>
@@ -36,8 +36,6 @@
 
 #include "relayd.h"
 
-extern struct imsgev	*iev_main;
-
 struct relay_rtmsg {
 	struct rt_msghdr	rm_hdr;
 	union {
@@ -102,8 +100,8 @@ sync_routes(struct relayd *env, struct router *rt)
 			crt.hostid = host->conf.id;
 			crt.up = host->up;
 
-			imsg_compose_event(iev_main, IMSG_RTMSG,
-			    0, 0, -1, &crt, sizeof(crt));
+			proc_compose_imsg(env->sc_ps, PROC_PARENT, -1,
+			    IMSG_RTMSG, -1, &crt, sizeof(crt));
 		}
 	}
 }
blob - 2fa1a144d74fbcabb6c7779369b7a49d8f0acaa8
blob + d458a4de7a6ef21a6b30c053be1ee56ce854485c
--- relay.c
+++ relay.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relay.c,v 1.135 2011/05/05 12:01:44 reyk Exp $	*/
+/*	$OpenBSD: relay.c,v 1.136 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -47,16 +47,17 @@
 
 #include "relayd.h"
 
-void		 relay_sig_handler(int sig, short, void *);
 void		 relay_statistics(int, short, void *);
-void		 relay_dispatch_pfe(int, short, void *);
-void		 relay_dispatch_parent(int, short, void *);
+int		 relay_dispatch_parent(int, struct privsep_proc *,
+		    struct imsg *);
+int		 relay_dispatch_pfe(int, struct privsep_proc *,
+		    struct imsg *);
 void		 relay_shutdown(void);
 
 void		 relay_privinit(void);
 void		 relay_nodedebug(const char *, struct protonode *);
 void		 relay_protodebug(struct relay *);
-void		 relay_init(void);
+void		 relay_init(struct privsep *, struct privsep_proc *p, void *);
 void		 relay_launch(void);
 int		 relay_socket(struct sockaddr_storage *, in_port_t,
 		    struct protocol *, int, int);
@@ -134,142 +135,22 @@ volatile sig_atomic_t relay_sessions;
 objid_t relay_conid;
 
 static struct relayd		*env = NULL;
-struct imsgev			*iev_pfe;
-struct imsgev			*iev_main;
 int				 proc_id;
 
-void
-relay_sig_handler(int sig, short event, void *arg)
-{
-	switch (sig) {
-	case SIGTERM:
-	case SIGINT:
-		(void)event_loopexit(NULL);
-		break;
-	case SIGCHLD:
-	case SIGHUP:
-	case SIGPIPE:
-		/* ignore */
-		break;
-	default:
-		fatalx("relay_sig_handler: unexpected signal");
-	}
-}
+static struct privsep_proc procs[] = {
+	{ "parent",	PROC_PARENT,	relay_dispatch_parent },
+	{ "pfe",	PROC_PFE,	relay_dispatch_pfe },
+};
 
 pid_t
-relay(struct relayd *x_env, int pipe_parent2pfe[2], int pipe_parent2hce[2],
-    int pipe_parent2relay[RELAY_MAXPROC][2], int pipe_pfe2hce[2],
-    int pipe_pfe2relay[RELAY_MAXPROC][2])
+relay(struct privsep *ps, struct privsep_proc *p)
 {
-	pid_t		 pid;
-	struct passwd	*pw;
-	int		 i;
+	env = ps->ps_env;
 
-	switch (pid = fork()) {
-	case -1:
-		fatal("relay: cannot fork");
-	case 0:
-		break;
-	default:
-		return (pid);
-	}
-
-	env = x_env;
-	purge_config(env, PURGE_RDRS);
-
 	/* Need root privileges for relay initialization */
 	relay_privinit();
 
-	if ((pw = getpwnam(RELAYD_USER)) == NULL)
-		fatal("relay: getpwnam");
-
-#ifndef DEBUG
-	if (chroot(pw->pw_dir) == -1)
-		fatal("relay: chroot");
-	if (chdir("/") == -1)
-		fatal("relay: chdir(\"/\")");
-
-#else
-#warning disabling privilege revocation and chroot in DEBUG mode
-#endif
-
-	setproctitle("socket relay engine");
-	relayd_process = PROC_RELAY;
-
-#ifndef DEBUG
-	if (setgroups(1, &pw->pw_gid) ||
-	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
-	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
-		fatal("relay: can't drop privileges");
-#endif
-
-	/* Fork child handlers */
-	for (i = 1; i < env->sc_prefork_relay; i++) {
-		if (fork() == 0) {
-			proc_id = i;
-			break;
-		}
-	}
-
-	event_init();
-
-	/* Per-child initialization */
-	relay_init();
-
-	signal_set(&env->sc_evsigint, SIGINT, relay_sig_handler, env);
-	signal_set(&env->sc_evsigterm, SIGTERM, relay_sig_handler, env);
-	signal_set(&env->sc_evsigchld, SIGCHLD, relay_sig_handler, env);
-	signal_set(&env->sc_evsighup, SIGHUP, relay_sig_handler, env);
-	signal_set(&env->sc_evsigpipe, SIGPIPE, relay_sig_handler, env);
-
-	signal_add(&env->sc_evsigint, NULL);
-	signal_add(&env->sc_evsigterm, NULL);
-	signal_add(&env->sc_evsigchld, NULL);
-	signal_add(&env->sc_evsighup, NULL);
-	signal_add(&env->sc_evsigpipe, NULL);
-
-	/* setup pipes */
-	close(pipe_pfe2hce[0]);
-	close(pipe_pfe2hce[1]);
-	close(pipe_parent2hce[0]);
-	close(pipe_parent2hce[1]);
-	close(pipe_parent2pfe[0]);
-	close(pipe_parent2pfe[1]);
-	for (i = 0; i < env->sc_prefork_relay; i++) {
-		if (i == proc_id)
-			continue;
-		close(pipe_parent2relay[i][0]);
-		close(pipe_parent2relay[i][1]);
-		close(pipe_pfe2relay[i][0]);
-		close(pipe_pfe2relay[i][1]);
-	}
-	close(pipe_parent2relay[proc_id][1]);
-	close(pipe_pfe2relay[proc_id][1]);
-
-	if ((iev_pfe = calloc(1, sizeof(struct imsgev))) == NULL ||
-	    (iev_main = calloc(1, sizeof(struct imsgev))) == NULL)
-		fatal("relay");
-	imsg_init(&iev_pfe->ibuf, pipe_pfe2relay[proc_id][0]);
-	imsg_init(&iev_main->ibuf, pipe_parent2relay[proc_id][0]);
-	iev_pfe->handler = relay_dispatch_pfe;
-	iev_main->handler = relay_dispatch_parent;
-
-	iev_pfe->events = EV_READ;
-	event_set(&iev_pfe->ev, iev_pfe->ibuf.fd, iev_pfe->events,
-	    iev_pfe->handler, iev_pfe);
-	event_add(&iev_pfe->ev, NULL);
-
-	iev_main->events = EV_READ;
-	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
-	    iev_main->handler, iev_main);
-	event_add(&iev_main->ev, NULL);
-
-	relay_launch();
-
-	event_dispatch();
-	relay_shutdown();
-
-	return (0);
+	return (proc_run(ps, p, procs, nitems(procs), relay_init, NULL));
 }
 
 void
@@ -286,8 +167,6 @@ relay_shutdown(void)
 			relay_close(con, "shutdown");
 	}
 	usleep(200);	/* XXX relay needs to shutdown last */
-	log_info("socket relay engine exiting");
-	_exit(0);
 }
 
 void
@@ -462,12 +341,17 @@ relay_privinit(void)
 }
 
 void
-relay_init(void)
+relay_init(struct privsep *ps, struct privsep_proc *p, void *arg)
 {
 	struct relay	*rlay;
 	struct host	*host;
 	struct timeval	 tv;
 
+	/* We use a custom shutdown callback */
+	p->p_shutdown = relay_shutdown;
+
+	purge_config(env, PURGE_RDRS);
+
 	/* Unlimited file descriptors (use system limits) */
 	socket_rlimit(-1);
 
@@ -518,6 +402,8 @@ relay_init(void)
 	evtimer_set(&env->sc_statev, relay_statistics, NULL);
 	bcopy(&env->sc_statinterval, &tv, sizeof(tv));
 	evtimer_add(&env->sc_statev, &tv);
+
+	relay_launch();
 }
 
 void
@@ -566,7 +452,7 @@ relay_statistics(int fd, short events, void *arg)
 
 		crs.id = rlay->rl_conf.id;
 		crs.proc = proc_id;
-		imsg_compose_event(iev_pfe, IMSG_STATISTICS, 0, 0, -1,
+		proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_STATISTICS, -1,
 		    &crs, sizeof(crs));
 
 		for (con = SPLAY_ROOT(&rlay->rl_sessions);
@@ -2151,7 +2037,7 @@ relay_accept(int fd, short sig, void *arg)
 			return;
 		}
 
-		imsg_compose_event(iev_pfe, IMSG_NATLOOK, 0, 0, -1, cnl,
+		proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_NATLOOK, -1, cnl,
 		    sizeof(*cnl));
 
 		/* Schedule timeout */
@@ -2330,8 +2216,8 @@ relay_bindanyreq(struct rsession *con, in_port_t port,
 	bnd.bnd_port = port;
 	bnd.bnd_proto = proto;
 	bcopy(&con->se_in.ss, &bnd.bnd_ss, sizeof(bnd.bnd_ss));
-	imsg_compose_event(iev_main, IMSG_BINDANY,
-	    0, 0, -1, &bnd, sizeof(bnd));
+	proc_compose_imsg(env->sc_ps, PROC_PARENT, -1, IMSG_BINDANY,
+	    -1, &bnd, sizeof(bnd));
 
 	/* Schedule timeout */
 	evtimer_set(&con->se_ev, relay_bindany, con);
@@ -2442,7 +2328,8 @@ relay_close(struct rsession *con, const char *msg)
 		if (EVBUFFER_LENGTH(con->se_log) &&
 		    evbuffer_add_printf(con->se_log, "\r\n") != -1)
 			ptr = evbuffer_readline(con->se_log);
-		log_info("relay %s, session %d (%d active), %d, %s -> %s:%d, "
+		log_info("relay %s, "
+		    "session %d (%d active), %d, %s -> %s:%d, "
 		    "%s%s%s", rlay->rl_conf.name, con->se_id, relay_sessions,
 		    con->se_mark, ibuf, obuf, ntohs(con->se_out.port), msg,
 		    ptr == NULL ? "" : ",", ptr == NULL ? "" : ptr);
@@ -2495,7 +2382,7 @@ relay_close(struct rsession *con, const char *msg)
 
 	if (con->se_cnl != NULL) {
 #if 0
-		imsg_compose_event(iev_pfe, IMSG_KILLSTATES, 0, 0, -1,
+		proc_compose_imsg(env->sc_ps, PROC_PFE, -1, IMSG_KILLSTATES, -1,
 		    cnl, sizeof(*cnl));
 #endif
 		free(con->se_cnl);
@@ -2505,13 +2392,9 @@ relay_close(struct rsession *con, const char *msg)
 	relay_sessions--;
 }
 
-void
-relay_dispatch_pfe(int fd, short event, void *ptr)
+int
+relay_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	struct imsgev		*iev;
-	struct imsgbuf		*ibuf;
-	struct imsg		 imsg;
-	ssize_t			 n;
 	struct relay		*rlay;
 	struct rsession		*con;
 	struct ctl_natlook	 cnl;
@@ -2520,203 +2403,139 @@ relay_dispatch_pfe(int fd, short event, void *ptr)
 	struct table		*table;
 	struct ctl_status	 st;
 	objid_t			 id;
-	int			 verbose;
 
-	iev = ptr;
-	ibuf = &iev->ibuf;
-
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("relay_dispatch_pfe: imsg_read_error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
+	switch (imsg->hdr.type) {
+	case IMSG_HOST_DISABLE:
+		memcpy(&id, imsg->data, sizeof(id));
+		if ((host = host_find(env, id)) == NULL)
+			fatalx("relay_dispatch_pfe: desynchronized");
+		if ((table = table_find(env, host->conf.tableid)) ==
+		    NULL)
+			fatalx("relay_dispatch_pfe: invalid table id");
+		if (host->up == HOST_UP)
+			table->up--;
+		host->flags |= F_DISABLE;
+		host->up = HOST_UNKNOWN;
+		break;
+	case IMSG_HOST_ENABLE:
+		memcpy(&id, imsg->data, sizeof(id));
+		if ((host = host_find(env, id)) == NULL)
+			fatalx("relay_dispatch_pfe: desynchronized");
+		host->flags &= ~(F_DISABLE);
+		host->up = HOST_UNKNOWN;
+		break;
+	case IMSG_TABLE_DISABLE:
+		memcpy(&id, imsg->data, sizeof(id));
+		if ((table = table_find(env, id)) == NULL)
+			fatalx("relay_dispatch_pfe: desynchronized");
+		table->conf.flags |= F_DISABLE;
+		table->up = 0;
+		TAILQ_FOREACH(host, &table->hosts, entry)
+			host->up = HOST_UNKNOWN;
+		break;
+	case IMSG_TABLE_ENABLE:
+		memcpy(&id, imsg->data, sizeof(id));
+		if ((table = table_find(env, id)) == NULL)
+			fatalx("relay_dispatch_pfe: desynchronized");
+		table->conf.flags &= ~(F_DISABLE);
+		table->up = 0;
+		TAILQ_FOREACH(host, &table->hosts, entry)
+			host->up = HOST_UNKNOWN;
+		break;
+	case IMSG_HOST_STATUS:
+		IMSG_SIZE_CHECK(imsg, &st);
+		memcpy(&st, imsg->data, sizeof(st));
+		if ((host = host_find(env, st.id)) == NULL)
+			fatalx("relay_dispatch_pfe: invalid host id");
+		if (host->flags & F_DISABLE)
+			break;
+		if (host->up == st.up) {
+			log_debug("%s: host %d => %d", __func__,
+			    host->conf.id, host->up);
+			fatalx("relay_dispatch_pfe: desynchronized");
 		}
-	}
 
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("relay_dispatch_pfe: msgbuf_write");
-	}
+		if ((table = table_find(env, host->conf.tableid))
+		    == NULL)
+			fatalx("relay_dispatch_pfe: invalid table id");
 
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("relay_dispatch_pfe: imsg_read error");
-		if (n == 0)
-			break;
+		DPRINTF("%s: [%d] state %d for "
+		    "host %u %s", __func__, proc_id, st.up,
+		    host->conf.id, host->conf.name);
 
-		switch (imsg.hdr.type) {
-		case IMSG_HOST_DISABLE:
-			memcpy(&id, imsg.data, sizeof(id));
-			if ((host = host_find(env, id)) == NULL)
-				fatalx("relay_dispatch_pfe: desynchronized");
-			if ((table = table_find(env, host->conf.tableid)) ==
-			    NULL)
-				fatalx("relay_dispatch_pfe: invalid table id");
-			if (host->up == HOST_UP)
-				table->up--;
-			host->flags |= F_DISABLE;
-			host->up = HOST_UNKNOWN;
-			break;
-		case IMSG_HOST_ENABLE:
-			memcpy(&id, imsg.data, sizeof(id));
-			if ((host = host_find(env, id)) == NULL)
-				fatalx("relay_dispatch_pfe: desynchronized");
-			host->flags &= ~(F_DISABLE);
-			host->up = HOST_UNKNOWN;
-			break;
-		case IMSG_TABLE_DISABLE:
-			memcpy(&id, imsg.data, sizeof(id));
-			if ((table = table_find(env, id)) == NULL)
-				fatalx("relay_dispatch_pfe: desynchronized");
-			table->conf.flags |= F_DISABLE;
-			table->up = 0;
-			TAILQ_FOREACH(host, &table->hosts, entry)
-				host->up = HOST_UNKNOWN;
-			break;
-		case IMSG_TABLE_ENABLE:
-			memcpy(&id, imsg.data, sizeof(id));
-			if ((table = table_find(env, id)) == NULL)
-				fatalx("relay_dispatch_pfe: desynchronized");
-			table->conf.flags &= ~(F_DISABLE);
-			table->up = 0;
-			TAILQ_FOREACH(host, &table->hosts, entry)
-				host->up = HOST_UNKNOWN;
-			break;
-		case IMSG_HOST_STATUS:
-			if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(st))
-				fatalx("relay_dispatch_pfe: invalid request");
-			memcpy(&st, imsg.data, sizeof(st));
-			if ((host = host_find(env, st.id)) == NULL)
-				fatalx("relay_dispatch_pfe: invalid host id");
-			if (host->flags & F_DISABLE)
-				break;
-			if (host->up == st.up) {
-				log_debug("%s: host %d => %d", __func__,
-				    host->conf.id, host->up);
-				fatalx("relay_dispatch_pfe: desynchronized");
-			}
-
-			if ((table = table_find(env, host->conf.tableid))
-			    == NULL)
-				fatalx("relay_dispatch_pfe: invalid table id");
-
-			DPRINTF("%s: [%d] state %d for "
-			    "host %u %s", __func__, proc_id, st.up,
-			    host->conf.id, host->conf.name);
-
-			if ((st.up == HOST_UNKNOWN && host->up == HOST_DOWN) ||
-			    (st.up == HOST_DOWN && host->up == HOST_UNKNOWN)) {
-				host->up = st.up;
-				break;
-			}
-			if (st.up == HOST_UP)
-				table->up++;
-			else
-				table->up--;
+		if ((st.up == HOST_UNKNOWN && host->up == HOST_DOWN) ||
+		    (st.up == HOST_DOWN && host->up == HOST_UNKNOWN)) {
 			host->up = st.up;
 			break;
-		case IMSG_NATLOOK:
-			bcopy(imsg.data, &cnl, sizeof(cnl));
-			if ((con = session_find(env, cnl.id)) == NULL ||
-			    con->se_cnl == NULL) {
-				log_debug("%s: session %d: expired",
-				    __func__, cnl.id);
-				break;
+		}
+		if (st.up == HOST_UP)
+			table->up++;
+		else
+			table->up--;
+		host->up = st.up;
+		break;
+	case IMSG_NATLOOK:
+		bcopy(imsg->data, &cnl, sizeof(cnl));
+		if ((con = session_find(env, cnl.id)) == NULL ||
+		    con->se_cnl == NULL) {
+			log_debug("%s: session %d: expired",
+			    __func__, cnl.id);
+			break;
+		}
+		bcopy(&cnl, con->se_cnl, sizeof(*con->se_cnl));
+		evtimer_del(&con->se_ev);
+		evtimer_set(&con->se_ev, relay_natlook, con);
+		bzero(&tv, sizeof(tv));
+		evtimer_add(&con->se_ev, &tv);
+		break;
+	case IMSG_CTL_SESSION:
+		TAILQ_FOREACH(rlay, env->sc_relays, rl_entry) {
+			SPLAY_FOREACH(con, session_tree,
+			    &rlay->rl_sessions) {
+				proc_compose_imsg(env->sc_ps, p->p_id, -1,
+				    IMSG_CTL_SESSION,
+				    -1, con, sizeof(*con));
 			}
-			bcopy(&cnl, con->se_cnl, sizeof(*con->se_cnl));
-			evtimer_del(&con->se_ev);
-			evtimer_set(&con->se_ev, relay_natlook, con);
-			bzero(&tv, sizeof(tv));
-			evtimer_add(&con->se_ev, &tv);
-			break;
-		case IMSG_CTL_SESSION:
-			TAILQ_FOREACH(rlay, env->sc_relays, rl_entry)
-				SPLAY_FOREACH(con, session_tree,
-				    &rlay->rl_sessions)
-					imsg_compose_event(iev,
-					    IMSG_CTL_SESSION,
-					    0, 0, -1, con, sizeof(*con));
-			imsg_compose_event(iev, IMSG_CTL_END,
-			    0, 0, -1, NULL, 0);
-			break;
-		case IMSG_CTL_LOG_VERBOSE:
-			memcpy(&verbose, imsg.data, sizeof(verbose));
-			log_verbose(verbose);
-			break;
-		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
 		}
-		imsg_free(&imsg);
+		proc_compose_imsg(env->sc_ps, p->p_id, -1, IMSG_CTL_END,
+		    -1, NULL, 0);
+		break;
+	default:
+		return (-1);
 	}
-	imsg_event_add(iev);
+
+	return (0);
 }
 
-void
-relay_dispatch_parent(int fd, short event, void * ptr)
+int
+relay_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
 	struct rsession		*con;
-	struct imsgev		*iev;
-	struct imsgbuf		*ibuf;
-	struct imsg		 imsg;
-	ssize_t			 n;
 	struct timeval		 tv;
 	objid_t			 id;
 
-	iev = ptr;
-	ibuf = &iev->ibuf;
-
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("relay_dispatch_parent: imsg_read error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
+	switch (imsg->hdr.type) {
+	case IMSG_BINDANY:
+		bcopy(imsg->data, &id, sizeof(id));
+		if ((con = session_find(env, id)) == NULL) {
+			log_debug("%s: session %d: expired",
+			    __func__, id);
+			break;
 		}
-	}
 
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("relay_dispatch_parent: msgbuf_write");
-	}
+		/* Will validate the result later */
+		con->se_bnds = imsg->fd;
 
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("relay_dispatch_parent: imsg_read error");
-		if (n == 0)
-			break;
-
-		switch (imsg.hdr.type) {
-		case IMSG_BINDANY:
-			bcopy(imsg.data, &id, sizeof(id));
-			if ((con = session_find(env, id)) == NULL) {
-				log_debug("%s: session %d: expired",
-				    __func__, id);
-				break;
-			}
-
-			/* Will validate the result later */
-			con->se_bnds = imsg.fd;
-
-			evtimer_del(&con->se_ev);
-			evtimer_set(&con->se_ev, relay_bindany, con);
-			bzero(&tv, sizeof(tv));
-			evtimer_add(&con->se_ev, &tv);
-			break;
-		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
-		}
-		imsg_free(&imsg);
+		evtimer_del(&con->se_ev);
+		evtimer_set(&con->se_ev, relay_bindany, con);
+		bzero(&tv, sizeof(tv));
+		evtimer_add(&con->se_ev, &tv);
+		break;
+	default:
+		return (-1);
 	}
-	imsg_event_add(iev);
+
+	return (0);
 }
 
 SSL_CTX *
blob - /dev/null
blob + 2bfb76a85beb75a49796af5891d0b49ebe88a227 (mode 644)
--- /dev/null
+++ proc.c
@@ -0,0 +1,568 @@
+/*	$OpenBSD: proc.c,v 1.1 2011/05/09 12:09:58 reyk Exp $	*/
+
+/*
+ * Copyright (c) 2010,2011 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <pwd.h>
+#include <event.h>
+
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+
+#include "relayd.h"
+
+void	 proc_setup(struct privsep *);
+int	 proc_ispeer(struct privsep_proc *, u_int, enum privsep_procid);
+void	 proc_shutdown(struct privsep_proc *);
+void	 proc_sig_handler(int, short, void *);
+void	 proc_range(struct privsep *, enum privsep_procid, int *, int *);
+
+int
+proc_ispeer(struct privsep_proc *p, u_int nproc, enum privsep_procid type)
+{
+	u_int	i;
+
+	for (i = 0; i < nproc; i++)
+		if (p[i].p_id == type)
+			return (1);
+	return (0);
+}
+
+void
+proc_init(struct privsep *ps, struct privsep_proc *p, u_int nproc)
+{
+	u_int	 i;
+
+	/*
+	 * Called from parent
+	 */
+	privsep_process = PROC_PARENT;
+	ps->ps_instances[PROC_PARENT] = 1;
+	ps->ps_title[PROC_PARENT] = "parent";
+	ps->ps_pid[PROC_PARENT] = getpid();
+
+	for (i = 0; i < nproc; i++) {
+		/* Default to 1 process instance */
+		if (ps->ps_instances[p[i].p_id] < 1)
+			ps->ps_instances[p[i].p_id] = 1;
+		ps->ps_title[p[i].p_id] = p[i].p_title;
+	}
+
+	proc_setup(ps);
+
+	/* Engage! */
+	for (i = 0; i < nproc; i++)
+		ps->ps_pid[p[i].p_id] = (*p[i].p_init)(ps, &p[i]);
+}
+
+void
+proc_kill(struct privsep *ps)
+{
+	pid_t		 pid;
+	u_int		 i;
+
+	if (privsep_process != PROC_PARENT)
+		return;
+
+	for (i = 0; i < PROC_MAX; i++) {
+		if (ps->ps_pid[i] == 0)
+			continue;
+		killpg(ps->ps_pid[i], SIGTERM);
+	}
+
+	do {
+		pid = waitpid(WAIT_ANY, NULL, 0);
+	} while (pid != -1 || (pid == -1 && errno == EINTR));
+}
+
+void
+proc_setup(struct privsep *ps)
+{
+	int	 i, j, n, count, sockpair[2];
+
+	for (i = 0; i < PROC_MAX; i++)
+		for (j = 0; j < PROC_MAX; j++) {
+			/*
+			 * find out how many instances of this peer there are.
+			 */
+			if (i >= j || ps->ps_instances[i] == 0||
+			   ps->ps_instances[j] == 0)
+				continue;
+
+			if (ps->ps_instances[i] > 1 &&
+			    ps->ps_instances[j] > 1)
+				fatalx("N:M peering not supported");
+
+			count = ps->ps_instances[i] * ps->ps_instances[j];
+
+			if ((ps->ps_pipes[i][j] =
+			    calloc(count, sizeof(int))) == NULL ||
+			    (ps->ps_pipes[j][i] =
+			    calloc(count, sizeof(int))) == NULL)
+				fatal(NULL);
+
+			for (n = 0; n < count; n++) {
+				if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
+				    sockpair) == -1)
+					fatal("socketpair");
+				ps->ps_pipes[i][j][n] = sockpair[0];
+				ps->ps_pipes[j][i][n] = sockpair[1];
+				socket_set_blockmode(
+				    ps->ps_pipes[i][j][n],
+				    BM_NONBLOCK);
+				socket_set_blockmode(
+				    ps->ps_pipes[j][i][n],
+				    BM_NONBLOCK);
+			}
+		}
+}
+
+void
+proc_config(struct privsep *ps, struct privsep_proc *p, u_int nproc)
+{
+	u_int	i, j, src, dst, count, n, instance;
+
+	src = privsep_process;
+
+	/*
+	 * close unused pipes
+	 */
+	for (i = 0; i < PROC_MAX; i++) {
+		for (j = 0; j < PROC_MAX; j++) {
+			if (i == j ||
+			    ps->ps_instances[i] == 0 ||
+			    ps->ps_instances[j] == 0)
+				continue;
+
+			count = ps->ps_instances[i] * ps->ps_instances[j];
+
+			for (n = 0; n < count; n++) {
+				instance = ps->ps_instances[i] > 1 ? n : 0;
+				if (i == src &&
+				    proc_ispeer(p, nproc, j) &&
+				    ps->ps_instance == instance)
+					continue;
+
+				close(ps->ps_pipes[i][j][n]);
+				ps->ps_pipes[i][j][n] = -1;
+			}
+		}
+	}
+
+	/*
+	 * listen on appropriate pipes
+	 */
+	for (i = 0; i < nproc; i++) {
+		dst = p[i].p_id;
+		p[i].p_ps = ps;
+		p[i].p_env = ps->ps_env;
+
+		if (src == dst)
+			fatal("proc_config: cannot peer with oneself");
+
+		count = ps->ps_instances[src] * ps->ps_instances[dst];
+		
+		if ((ps->ps_ievs[dst] = calloc(count,
+		    sizeof(struct imsgev))) == NULL)
+			fatal("proc_config");
+
+		for (n = 0; n < count; n++) {
+			if (ps->ps_pipes[src][dst][n] == -1)
+				continue;
+
+			imsg_init(&(ps->ps_ievs[dst][n].ibuf),
+			    ps->ps_pipes[src][dst][n]);
+			ps->ps_ievs[dst][n].handler = proc_dispatch;
+			ps->ps_ievs[dst][n].events = EV_READ;
+			ps->ps_ievs[dst][n].proc = &p[i];
+			ps->ps_ievs[dst][n].data = &ps->ps_ievs[dst][n];
+			p[i].p_instance = n;
+
+			event_set(&(ps->ps_ievs[dst][n].ev),
+			    ps->ps_ievs[dst][n].ibuf.fd,
+			    ps->ps_ievs[dst][n].events,
+			    ps->ps_ievs[dst][n].handler,
+			    ps->ps_ievs[dst][n].data);
+			event_add(&(ps->ps_ievs[dst][n].ev), NULL);
+		}
+	}
+}
+
+void
+proc_shutdown(struct privsep_proc *p)
+{
+	struct privsep	*ps = p->p_ps;
+
+	if (p->p_id == PROC_CONTROL && ps)
+		control_cleanup(&ps->ps_csock);
+
+	if (p->p_shutdown != NULL)
+		(*p->p_shutdown)();
+
+	log_info("%s exiting, pid %d", p->p_title, getpid());
+
+	_exit(0);
+}
+
+void
+proc_sig_handler(int sig, short event, void *arg)
+{
+	struct privsep_proc	*p = arg;
+
+	switch (sig) {
+	case SIGINT:
+	case SIGTERM:
+		proc_shutdown(p);
+		break;
+	case SIGCHLD:
+	case SIGHUP:
+	case SIGPIPE:
+		/* ignore */
+		break;
+	default:
+		fatalx("proc_sig_handler: unexpected signal");
+		/* NOTREACHED */
+	}
+}
+
+pid_t
+proc_run(struct privsep *ps, struct privsep_proc *p,
+    struct privsep_proc *procs, u_int nproc,
+    void (*init)(struct privsep *, struct privsep_proc *, void *), void *arg)
+{
+	pid_t		 pid;
+	struct passwd	*pw;
+	const char	*root;
+	u_int32_t	 seed[256];
+	u_int		 n;
+
+	switch (pid = fork()) {
+	case -1:
+		fatal("run_proc: cannot fork");
+	case 0:
+		/* Set the process group of the current process */
+		setpgrp(0, getpid());
+		break;
+	default:
+		return (pid);
+	}
+
+	pw = ps->ps_pw;
+
+	if (p->p_id == PROC_CONTROL) {
+		if (control_init(ps, &ps->ps_csock) == -1)
+			fatalx(p->p_title);
+	}
+
+	/* Change root directory */
+	if (p->p_chroot != NULL)
+		root = p->p_chroot;
+	else
+		root = pw->pw_dir;
+
+#ifndef DEBUG
+	if (chroot(root) == -1)
+		fatal("run_proc: chroot");
+	if (chdir("/") == -1)
+		fatal("run_proc: chdir(\"/\")");
+#else
+#warning disabling privilege revocation and chroot in DEBUG MODE
+	if (p->p_chroot != NULL) {
+		if (chroot(root) == -1)
+			fatal("run_proc: chroot");
+		if (chdir("/") == -1)
+			fatal("run_proc: chdir(\"/\")");
+	}
+#endif
+
+	privsep_process = p->p_id;
+
+	setproctitle("%s", p->p_title);
+
+#ifndef DEBUG
+	if (setgroups(1, &pw->pw_gid) ||
+	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+		fatal("run_proc: cannot drop privileges");
+#endif
+
+	/* Fork child handlers */
+	for (n = 1; n < ps->ps_instances[p->p_id]; n++) {
+		if (fork() == 0) {
+			ps->ps_instance = p->p_instance = n;
+			break;
+		}
+	}
+
+#ifdef DEBUG
+	log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
+	    ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
+#endif
+
+	event_init();
+
+	signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
+	signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
+	signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
+	signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
+	signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p);
+
+	signal_add(&ps->ps_evsigint, NULL);
+	signal_add(&ps->ps_evsigterm, NULL);
+	signal_add(&ps->ps_evsigchld, NULL);
+	signal_add(&ps->ps_evsighup, NULL);
+	signal_add(&ps->ps_evsigpipe, NULL);
+
+	proc_config(ps, procs, nproc);
+
+	arc4random_buf(seed, sizeof(seed));
+	RAND_seed(seed, sizeof(seed));
+
+	if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
+		TAILQ_INIT(&ctl_conns);
+		if (control_listen(&ps->ps_csock) == -1)
+			fatalx(p->p_title);
+	}
+
+	if (init != NULL)
+		init(ps, p, arg);
+
+	event_dispatch();
+
+	proc_shutdown(p);
+
+	return (0);
+}
+
+void
+proc_dispatch(int fd, short event, void *arg)
+{
+	struct imsgev		*iev = (struct imsgev *)arg;
+	struct privsep_proc	*p = iev->proc;
+	struct privsep		*ps = p->p_ps;
+	struct imsgbuf		*ibuf;
+	struct imsg		 imsg;
+	ssize_t			 n;
+	int			 verbose;
+	const char		*title;
+
+	title = ps->ps_title[privsep_process];
+	ibuf = &iev->ibuf;
+
+	if (event & EV_READ) {
+		if ((n = imsg_read(ibuf)) == -1)
+			fatal(title);
+		if (n == 0) {
+			/* this pipe is dead, so remove the event handler */
+			event_del(&iev->ev);
+			event_loopexit(NULL);
+			return;
+		}
+	}
+
+	if (event & EV_WRITE) {
+		if (msgbuf_write(&ibuf->w) == -1)
+			fatal(title);
+	}
+
+	for (;;) {
+		if ((n = imsg_get(ibuf, &imsg)) == -1)
+			fatal(title);
+		if (n == 0)
+			break;
+
+#ifdef DEBUG
+		log_debug("%s: %s %d got imsg %d from %s %d",
+		    __func__, title, ps->ps_instance + 1,
+		    imsg.hdr.type, p->p_title, p->p_instance);
+#endif
+
+		/*
+		 * Check the message with the program callback
+		 */
+		if ((p->p_cb)(fd, p, &imsg) == 0) {
+			/* Message was handled by the callback, continue */
+			imsg_free(&imsg);
+			continue;
+		}
+
+		/*
+		 * Generic message handling
+		 */
+		switch (imsg.hdr.type) {
+		case IMSG_CTL_VERBOSE:
+			IMSG_SIZE_CHECK(&imsg, &verbose);
+			memcpy(&verbose, imsg.data, sizeof(verbose));
+			log_verbose(verbose);
+			break;
+		default:
+			log_warnx("%s: %s %d got invalid imsg %d from %s %d",
+			    __func__, title, ps->ps_instance + 1,
+			    imsg.hdr.type, p->p_title, p->p_instance);
+			fatalx(title);
+		}
+		imsg_free(&imsg);
+	}
+	imsg_event_add(iev);
+}
+
+/*
+ * imsg helper functions
+ */
+
+void
+imsg_event_add(struct imsgev *iev)
+{
+	if (iev->handler == NULL) {
+		imsg_flush(&iev->ibuf);
+		return;
+	}
+
+	iev->events = EV_READ;
+	if (iev->ibuf.w.queued)
+		iev->events |= EV_WRITE;
+
+	event_del(&iev->ev);
+	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
+	event_add(&iev->ev, NULL);
+}
+
+int
+imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
+    pid_t pid, int fd, void *data, u_int16_t datalen)
+{
+	int	ret;
+
+	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
+	    pid, fd, data, datalen)) == -1)
+		return (ret);
+	imsg_event_add(iev);
+	return (ret);
+}
+
+int
+imsg_composev_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
+    pid_t pid, int fd, const struct iovec *iov, int iovcnt)
+{
+	int	ret;
+
+	if ((ret = imsg_composev(&iev->ibuf, type, peerid,
+	    pid, fd, iov, iovcnt)) == -1)
+		return (ret);
+	imsg_event_add(iev);
+	return (ret);
+}
+
+void
+proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
+{
+	if (*n == -1) {
+		/*
+		 * -1 means that the current process is
+		 * N:1 - one of many processes connected to a single peer,
+		 *       so find the right slot of the peer.
+		 * 1:N - a single process connected to many peers,
+		 *       so find the range of slots of all peers.
+		 */
+		if (ps->ps_instances[privsep_process] <=
+		    ps->ps_instances[id]) {
+			*n = 0;
+			*m = ps->ps_instances[id];
+			return;
+		}
+
+		*n = ps->ps_instance;
+		*m = ps->ps_instance + 1;
+	} else {
+		/* Use only a single slot of the specified peer process */
+		*m = *n + 1;
+	}
+}
+
+int
+proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
+    u_int16_t type, int fd, void *data, u_int16_t datalen)
+{
+	int	 m;
+
+	proc_range(ps, id, &n, &m);
+	for (; n < m; n++)
+		if (imsg_compose_event(&ps->ps_ievs[id][n],
+		    type, -1, 0, fd, data, datalen) == -1)
+			return (-1);
+	return (0);
+}
+
+int
+proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
+    u_int16_t type, int fd, const struct iovec *iov, int iovcnt)
+{
+	int	 m;
+
+	proc_range(ps, id, &n, &m);
+	for (; n < m; n++)
+		if (imsg_composev_event(&ps->ps_ievs[id][n],
+		    type, -1, 0, fd, iov, iovcnt) == -1)
+			return (-1);
+
+	return (0);
+}
+
+int
+proc_forward_imsg(struct privsep *ps, struct imsg *imsg,
+    enum privsep_procid id, int n)
+{
+	return (proc_compose_imsg(ps, id, n, imsg->hdr.type,
+	    imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg)));
+}
+
+void
+proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n)
+{
+	int	 m;
+
+	proc_range(ps, id, &n, &m);
+	for (; n < m; n++)
+		imsg_flush(&ps->ps_ievs[id][n].ibuf);
+}
+
+struct imsgbuf *
+proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
+{
+	int	 m;
+
+	proc_range(ps, id, &n, &m);
+	return (&ps->ps_ievs[id][n].ibuf);
+}
blob - 7c83d79515265f41d0e0c77ec1ea180083439a8c
blob + 3fe658a3f0bd6091e9e8be6cec998074c6ad8ff8
--- relay_udp.c
+++ relay_udp.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relay_udp.c,v 1.23 2011/05/05 12:01:44 reyk Exp $	*/
+/*	$OpenBSD: relay_udp.c,v 1.24 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -50,7 +50,6 @@
 extern volatile sig_atomic_t relay_sessions;
 extern objid_t relay_conid;
 extern int proc_id;
-extern struct imsgev *iev_pfe;
 extern int debug;
 
 struct relayd *env = NULL;
@@ -321,8 +320,8 @@ relay_udp_server(int fd, short sig, void *arg)
 		cnl->proto = IPPROTO_UDP;
 		bcopy(&con->se_in.ss, &cnl->src, sizeof(cnl->src));
 		bcopy(&rlay->rl_conf.ss, &cnl->dst, sizeof(cnl->dst));
-		imsg_compose_event(iev_pfe, IMSG_NATLOOK, 0, 0, -1, cnl,
-		    sizeof(*cnl));
+		proc_compose_imsg(env->sc_ps, PROC_PFE, -1,
+		    IMSG_NATLOOK, -1, cnl, sizeof(*cnl));
 
 		/* Schedule timeout */
 		evtimer_set(&con->se_ev, relay_natlook, con);
blob - 8194db2ce53b73401b45d9f43e614f30124ccf81
blob + f3a70f57f497e7743103bdba779d769df0319533
--- relayd.c
+++ relayd.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relayd.c,v 1.101 2011/05/05 12:01:44 reyk Exp $	*/
+/*	$OpenBSD: relayd.c,v 1.102 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
@@ -34,7 +34,6 @@
 #include <err.h>
 #include <errno.h>
 #include <event.h>
-#include <signal.h>
 #include <unistd.h>
 #include <ctype.h>
 #include <pwd.h>
@@ -47,39 +46,30 @@
 
 __dead void	 usage(void);
 
-void		 main_sig_handler(int, short, void *);
-void		 main_shutdown(struct relayd *);
-void		 main_dispatch_pfe(int, short, void *);
-void		 main_dispatch_hce(int, short, void *);
-void		 main_dispatch_relay(int, short, void *);
-int		 check_child(pid_t, const char *);
-int		 send_all(struct relayd *, enum imsg_type,
-		    void *, u_int16_t);
+void		 parent_sig_handler(int, short, void *);
+void		 parent_shutdown(struct relayd *);
+int		 parent_dispatch_pfe(int, struct privsep_proc *, struct imsg *);
+int		 parent_dispatch_hce(int, struct privsep_proc *, struct imsg *);
+int		 parent_dispatch_relay(int, struct privsep_proc *, struct imsg *);
 void		 reconfigure(void);
 void		 purge_tree(struct proto_tree *);
 int		 bindany(struct ctl_bindany *);
 
-int		 pipe_parent2pfe[2];
-int		 pipe_parent2hce[2];
-int		 pipe_pfe2hce[2];
-int		 pipe_parent2relay[RELAY_MAXPROC][2];
-int		 pipe_pfe2relay[RELAY_MAXPROC][2];
-
 struct relayd	*relayd_env;
 
-struct imsgev	*iev_pfe;
-struct imsgev	*iev_hce;
-struct imsgev	*iev_relay;
+static struct privsep_proc procs[] = {
+	{ "pfe",	PROC_PFE, parent_dispatch_pfe, pfe },
+	{ "hce",	PROC_HCE, parent_dispatch_hce, hce },
+	{ "relay",	PROC_RELAY, parent_dispatch_relay, relay }
+};
 
-pid_t		 pfe_pid = 0;
-pid_t		 hce_pid = 0;
-pid_t		 relay_pid = 0;
-
 void
-main_sig_handler(int sig, short event, void *arg)
+parent_sig_handler(int sig, short event, void *arg)
 {
-	struct relayd		*env = arg;
-	int			 die = 0;
+	struct privsep	*ps = arg;
+	int		 die = 0, status, fail, id;
+	pid_t		 pid;
+	char		*cause;
 
 	switch (sig) {
 	case SIGTERM:
@@ -87,20 +77,39 @@ main_sig_handler(int sig, short event, void *arg)
 		die = 1;
 		/* FALLTHROUGH */
 	case SIGCHLD:
-		if (check_child(pfe_pid, "pf update engine")) {
-			pfe_pid = 0;
-			die  = 1;
-		}
-		if (check_child(hce_pid, "host check engine")) {
-			hce_pid = 0;
-			die  = 1;
-		}
-		if (check_child(relay_pid, "socket relay engine")) {
-			relay_pid = 0;
-			die  = 1;
-		}
+		do {
+			pid = waitpid(WAIT_ANY, &status, WNOHANG);
+			if (pid <= 0)
+				continue;
+
+			fail = 0;
+			if (WIFSIGNALED(status)) {
+				fail = 1;
+				asprintf(&cause, "terminated; signal %d",
+				    WTERMSIG(status));
+			} else if (WIFEXITED(status)) {
+				if (WEXITSTATUS(status) != 0) {
+					fail = 1;
+					asprintf(&cause, "exited abnormally");
+				} else
+					asprintf(&cause, "exited okay");
+			} else
+				fatalx("unexpected cause of SIGCHLD");
+
+			die = 1;
+
+			for (id = 0; id < PROC_MAX; id++)
+				if (pid == ps->ps_pid[id]) {
+					log_warnx("lost child: %s %s",
+					    ps->ps_title[id], cause);
+					break;
+				}
+
+			free(cause);
+		} while (pid > 0 || (pid == -1 && errno == EINTR));
+
 		if (die)
-			main_shutdown(env);
+			parent_shutdown(ps->ps_env);
 		break;
 	case SIGHUP:
 		reconfigure();
@@ -131,8 +140,8 @@ main(int argc, char *argv[])
 	int			 debug;
 	u_int32_t		 opts;
 	struct relayd		*env;
+	struct privsep		*ps;
 	const char		*conffile;
-	struct imsgev		*iev;
 
 	opts = 0;
 	debug = 0;
@@ -170,9 +179,13 @@ main(int argc, char *argv[])
 	if (argc > 0)
 		usage();
 
-	if ((env = parse_config(conffile, opts)) == NULL)
+	if ((env = parse_config(conffile, opts)) == NULL ||
+	    (ps = calloc(1, sizeof(*ps))) == NULL)
 		exit(1);
+
 	relayd_env = env;
+	env->sc_ps = ps;
+	ps->ps_env = env;
 
 	if (env->sc_opts & RELAYD_OPT_NOACTION) {
 		fprintf(stderr, "configuration OK\n");
@@ -184,9 +197,12 @@ main(int argc, char *argv[])
 	if (geteuid())
 		errx(1, "need root privileges");
 
-	if (getpwnam(RELAYD_USER) == NULL)
+	if ((ps->ps_pw =  getpwnam(RELAYD_USER)) == NULL)
 		errx(1, "unknown user %s", RELAYD_USER);
 
+	/* Configure the control socket */
+	ps->ps_csock.cs_name = RELAYD_SOCKET;
+
 	if (!debug) {
 		log_init(debug);
 		if (daemon(1, 0) == -1)
@@ -195,181 +211,52 @@ main(int argc, char *argv[])
 
 	log_info("startup");
 
-	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
-	    pipe_parent2pfe) == -1)
-		fatal("socketpair");
-	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
-	    pipe_parent2hce) == -1)
-		fatal("socketpair");
-	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
-	    pipe_pfe2hce) == -1)
-		fatal("socketpair");
-	for (c = 0; c < env->sc_prefork_relay; c++) {
-		if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
-		    pipe_parent2relay[c]) == -1)
-			fatal("socketpair");
-		if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
-		    pipe_pfe2relay[c]) == -1)
-			fatal("socketpair");
-		session_socket_blockmode(pipe_pfe2relay[c][0], BM_NONBLOCK);
-		session_socket_blockmode(pipe_pfe2relay[c][1], BM_NONBLOCK);
-		session_socket_blockmode(pipe_parent2relay[c][0], BM_NONBLOCK);
-		session_socket_blockmode(pipe_parent2relay[c][1], BM_NONBLOCK);
-	}
+	ps->ps_instances[PROC_RELAY] = env->sc_prefork_relay;
+	proc_init(ps, procs, nitems(procs));
 
-	session_socket_blockmode(pipe_parent2pfe[0], BM_NONBLOCK);
-	session_socket_blockmode(pipe_parent2pfe[1], BM_NONBLOCK);
-	session_socket_blockmode(pipe_parent2hce[0], BM_NONBLOCK);
-	session_socket_blockmode(pipe_parent2hce[1], BM_NONBLOCK);
-	session_socket_blockmode(pipe_pfe2hce[0], BM_NONBLOCK);
-	session_socket_blockmode(pipe_pfe2hce[1], BM_NONBLOCK);
-
-	pfe_pid = pfe(env, pipe_parent2pfe, pipe_parent2hce,
-	    pipe_parent2relay, pipe_pfe2hce, pipe_pfe2relay);
-	hce_pid = hce(env, pipe_parent2pfe, pipe_parent2hce,
-	    pipe_parent2relay, pipe_pfe2hce, pipe_pfe2relay);
-	if (env->sc_prefork_relay > 0)
-		relay_pid = relay(env, pipe_parent2pfe, pipe_parent2hce,
-		    pipe_parent2relay, pipe_pfe2hce, pipe_pfe2relay);
-
 	setproctitle("parent");
 
 	event_init();
 
-	signal_set(&env->sc_evsigint, SIGINT, main_sig_handler, env);
-	signal_set(&env->sc_evsigterm, SIGTERM, main_sig_handler, env);
-	signal_set(&env->sc_evsigchld, SIGCHLD, main_sig_handler, env);
-	signal_set(&env->sc_evsighup, SIGHUP, main_sig_handler, env);
-	signal_set(&env->sc_evsigpipe, SIGPIPE, main_sig_handler, env);
+	signal_set(&ps->ps_evsigint, SIGINT, parent_sig_handler, ps);
+	signal_set(&ps->ps_evsigterm, SIGTERM, parent_sig_handler, ps);
+	signal_set(&ps->ps_evsigchld, SIGCHLD, parent_sig_handler, ps);
+	signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps);
+	signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps);
 
-	signal_add(&env->sc_evsigint, NULL);
-	signal_add(&env->sc_evsigterm, NULL);
-	signal_add(&env->sc_evsigchld, NULL);
-	signal_add(&env->sc_evsighup, NULL);
-	signal_add(&env->sc_evsigpipe, NULL);
+	signal_add(&ps->ps_evsigint, NULL);
+	signal_add(&ps->ps_evsigterm, NULL);
+	signal_add(&ps->ps_evsigchld, NULL);
+	signal_add(&ps->ps_evsighup, NULL);
+	signal_add(&ps->ps_evsigpipe, NULL);
 
-	close(pipe_parent2pfe[1]);
-	close(pipe_parent2hce[1]);
-	close(pipe_pfe2hce[0]);
-	close(pipe_pfe2hce[1]);
-	for (c = 0; c < env->sc_prefork_relay; c++) {
-		close(pipe_pfe2relay[c][0]);
-		close(pipe_pfe2relay[c][1]);
-		close(pipe_parent2relay[c][0]);
-	}
-
-	if ((iev_pfe = calloc(1, sizeof(struct imsgev))) == NULL ||
-	    (iev_hce = calloc(1, sizeof(struct imsgev))) == NULL)
-		fatal(NULL);
-
-	if (env->sc_prefork_relay > 0) {
-		if ((iev_relay = calloc(env->sc_prefork_relay,
-		    sizeof(struct imsgev))) == NULL)
-			fatal(NULL);
-	}
-
-	imsg_init(&iev_pfe->ibuf, pipe_parent2pfe[0]);
-	imsg_init(&iev_hce->ibuf, pipe_parent2hce[0]);
-	iev_pfe->handler = main_dispatch_pfe;
-	iev_pfe->data = env;
-	iev_hce->handler = main_dispatch_hce;
-	iev_hce->data = env;
-
-	for (c = 0; c < env->sc_prefork_relay; c++) {
-		iev = &iev_relay[c];
-		imsg_init(&iev->ibuf, pipe_parent2relay[c][1]);
-		iev->handler = main_dispatch_relay;
-		iev->events = EV_READ;
-		event_set(&iev->ev, iev->ibuf.fd, iev->events,
-		    iev->handler, iev);
-		event_add(&iev->ev, NULL);
-	}
-
-	iev_pfe->events = EV_READ;
-	event_set(&iev_pfe->ev, iev_pfe->ibuf.fd, iev_pfe->events,
-	    iev_pfe->handler, iev_pfe);
-	event_add(&iev_pfe->ev, NULL);
-
-	iev_hce->events = EV_READ;
-	event_set(&iev_hce->ev, iev_hce->ibuf.fd, iev_hce->events,
-	    iev_hce->handler, iev_hce);
-	event_add(&iev_hce->ev, NULL);
-
-	if (env->sc_flags & F_DEMOTE)
-		carp_demote_reset(env->sc_demote_group, 0);
-
+	proc_config(ps, procs, nitems(procs));
 	init_routes(env);
 
 	event_dispatch();
 
-	main_shutdown(env);
+	parent_shutdown(env);
 	/* NOTREACHED */
 	return (0);
 }
 
 void
-main_shutdown(struct relayd *env)
+parent_shutdown(struct relayd *env)
 {
-	pid_t	pid;
-
-	if (pfe_pid)
-		kill(pfe_pid, SIGTERM);
-	if (hce_pid)
-		kill(hce_pid, SIGTERM);
-	if (relay_pid)
-		kill(relay_pid, SIGTERM);
-
-	do {
-		if ((pid = wait(NULL)) == -1 &&
-		    errno != EINTR && errno != ECHILD)
-			fatal("wait");
-	} while (pid != -1 || (pid == -1 && errno == EINTR));
-
-	control_cleanup();
+	proc_kill(env->sc_ps);
+	control_cleanup(&env->sc_ps->ps_csock);
 	carp_demote_shutdown();
 	if (env->sc_flags & F_DEMOTE)
 		carp_demote_reset(env->sc_demote_group, 128);
-	log_info("terminating");
+
+	free(env->sc_ps);
+	free(env);
+
+	log_info("parent terminating, pid %d", getpid());
+
 	exit(0);
 }
 
-int
-check_child(pid_t pid, const char *pname)
-{
-	int	status;
-
-	if (waitpid(pid, &status, WNOHANG) > 0) {
-		if (WIFEXITED(status)) {
-			log_warnx("%s: lost child: %s exited", __func__, pname);
-			return (1);
-		}
-		if (WIFSIGNALED(status)) {
-			log_warnx("%s: lost child: %s terminated; "
-			    "signal %d", __func__, pname, WTERMSIG(status));
-			return (1);
-		}
-	}
-
-	return (0);
-}
-
-int
-send_all(struct relayd *env, enum imsg_type type, void *buf, u_int16_t len)
-{
-	int		 i;
-
-	if (imsg_compose_event(iev_pfe, type, 0, 0, -1, buf, len) == -1)
-		return (-1);
-	if (imsg_compose_event(iev_hce, type, 0, 0, -1, buf, len) == -1)
-		return (-1);
-	for (i = 0; i < env->sc_prefork_relay; i++) {
-		if (imsg_compose_event(&iev_relay[i], type, 0, 0, -1, buf, len)
-		    == -1)
-			return (-1);
-	}
-	return (0);
-}
-
 void
 merge_config(struct relayd *env, struct relayd *new_env)
 {
@@ -405,6 +292,7 @@ reconfigure(void)
 {
 	struct relayd		*env = relayd_env;
 	struct relayd		*new_env = NULL;
+	struct privsep		*ps = env->sc_ps;
 	struct rdr		*rdr;
 	struct address		*virt;
 	struct table            *table;
@@ -433,41 +321,40 @@ reconfigure(void)
 	/*
 	 * first reconfigure pfe
 	 */
-	imsg_compose_event(iev_pfe, IMSG_RECONF, 0, 0, -1, env, sizeof(*env));
+	proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF, -1, env, sizeof(*env));
 	TAILQ_FOREACH(table, env->sc_tables, entry) {
-		imsg_compose_event(iev_pfe, IMSG_RECONF_TABLE, 0, 0, -1,
+		proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_TABLE, -1,
 		    &table->conf, sizeof(table->conf));
 		TAILQ_FOREACH(host, &table->hosts, entry) {
-			imsg_compose_event(iev_pfe, IMSG_RECONF_HOST, 0, 0, -1,
-			    &host->conf, sizeof(host->conf));
+			proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_HOST,
+			    -1, &host->conf, sizeof(host->conf));
 		}
 	}
 	TAILQ_FOREACH(rdr, env->sc_rdrs, entry) {
-		imsg_compose_event(iev_pfe, IMSG_RECONF_RDR, 0, 0, -1,
+		proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_RDR, -1,
 		    &rdr->conf, sizeof(rdr->conf));
 		TAILQ_FOREACH(virt, &rdr->virts, entry)
-			imsg_compose_event(iev_pfe, IMSG_RECONF_VIRT, 0, 0, -1,
-				virt, sizeof(*virt));
+			proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_VIRT,
+			    -1, virt, sizeof(*virt));
 	}
-	imsg_compose_event(iev_pfe, IMSG_RECONF_END, 0, 0, -1, NULL, 0);
+	proc_compose_imsg(ps, PROC_PFE, -1, IMSG_RECONF_END, -1, NULL, 0);
 
 	/*
 	 * then reconfigure hce
 	 */
-	imsg_compose_event(iev_hce, IMSG_RECONF, 0, 0, -1, env, sizeof(*env));
+	proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF, -1, env, sizeof(*env));
 	TAILQ_FOREACH(table, env->sc_tables, entry) {
-		imsg_compose_event(iev_hce, IMSG_RECONF_TABLE, 0, 0, -1,
+		proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF_TABLE, -1,
 		    &table->conf, sizeof(table->conf));
 		if (table->sendbuf != NULL)
-			imsg_compose_event(iev_hce, IMSG_RECONF_SENDBUF,
-			    0, 0, -1, table->sendbuf,
-			    strlen(table->sendbuf) + 1);
+			proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF_SENDBUF,
+			    -1, table->sendbuf, strlen(table->sendbuf) + 1);
 		TAILQ_FOREACH(host, &table->hosts, entry) {
-			imsg_compose_event(iev_hce, IMSG_RECONF_HOST, 0, 0, -1,
-			    &host->conf, sizeof(host->conf));
+			proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF_HOST,
+			    -1, &host->conf, sizeof(host->conf));
 		}
 	}
-	imsg_compose_event(iev_hce, IMSG_RECONF_END, 0, 0, -1, NULL, 0);
+	proc_compose_imsg(ps, PROC_HCE, -1, IMSG_RECONF_END, -1, NULL, 0);
 }
 
 void
@@ -581,236 +468,95 @@ purge_table(struct tablelist *head, struct table *tabl
 	free(table);
 }
 
-void
-imsg_event_add(struct imsgev *iev)
-{
-	if (iev->handler == NULL) {
-		imsg_flush(&iev->ibuf);
-		return;
-	}
-
-	iev->events = EV_READ;
-	if (iev->ibuf.w.queued)
-		iev->events |= EV_WRITE;
-
-	event_del(&iev->ev);
-	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
-	event_add(&iev->ev, NULL);
-}
-
 int
-imsg_compose_event(struct imsgev *iev, u_int16_t type,
-    u_int32_t peerid, pid_t pid, int fd, void *data, u_int16_t datalen)
+parent_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	int	ret;
-
-	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
-	    pid, fd, data, datalen)) != -1)
-		imsg_event_add(iev);
-	return (ret);
-}
-
-void
-main_dispatch_pfe(int fd, short event, void *ptr)
-{
-	struct imsgev		*iev;
-	struct imsgbuf		*ibuf;
-	struct imsg		 imsg;
-	ssize_t			 n;
+	struct relayd		*env = p->p_env;
 	struct ctl_demote	 demote;
 	struct ctl_netroute	 crt;
-	struct relayd		*env;
-	int			 verbose;
 
-	iev = ptr;
-	ibuf = &iev->ibuf;
-	env = (struct relayd *)iev->data;
-
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("imsg_read_error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
-		}
+	switch (imsg->hdr.type) {
+	case IMSG_DEMOTE:
+		IMSG_SIZE_CHECK(imsg, &demote);
+		memcpy(&demote, imsg->data, sizeof(demote));
+		carp_demote_set(demote.group, demote.level);
+		break;
+	case IMSG_RTMSG:
+		IMSG_SIZE_CHECK(imsg, &crt);
+		memcpy(&crt, imsg->data, sizeof(crt));
+		pfe_route(env, &crt);
+		break;
+	case IMSG_CTL_RELOAD:
+		/*
+		 * so far we only get here if no L7 (relay) is done.
+		 */
+		reconfigure();
+		break;
+	default:
+		return (-1);
 	}
 
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("msgbuf_write");
-	}
-
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("main_dispatch_pfe: imsg_read error");
-		if (n == 0)
-			break;
-
-		switch (imsg.hdr.type) {
-		case IMSG_DEMOTE:
-			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
-			    sizeof(demote))
-				fatalx("main_dispatch_pfe: "
-				    "invalid size of demote request");
-			memcpy(&demote, imsg.data, sizeof(demote));
-			carp_demote_set(demote.group, demote.level);
-			break;
-		case IMSG_RTMSG:
-			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
-			    sizeof(crt))
-				fatalx("main_dispatch_pfe: "
-				    "invalid size of rtmsg request");
-			memcpy(&crt, imsg.data, sizeof(crt));
-			pfe_route(env, &crt);
-			break;
-		case IMSG_CTL_RELOAD:
-			/*
-			 * so far we only get here if no L7 (relay) is done.
-			 */
-			reconfigure();
-			break;
-		case IMSG_CTL_LOG_VERBOSE:
-			memcpy(&verbose, imsg.data, sizeof(verbose));
-			log_verbose(verbose);
-			break;
-		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
-		}
-		imsg_free(&imsg);
-	}
-	imsg_event_add(iev);
+	return (0);
 }
 
-void
-main_dispatch_hce(int fd, short event, void * ptr)
+int
+parent_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	struct imsgev		*iev;
-	struct imsgbuf		*ibuf;
-	struct imsg		 imsg;
-	ssize_t			 n;
+	struct relayd		*env = p->p_env;
+	struct privsep		*ps = env->sc_ps;
 	struct ctl_script	 scr;
-	struct relayd		*env;
 
-	env = relayd_env;
-	iev = ptr;
-	ibuf = &iev->ibuf;
-
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("imsg_read error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
-		}
+	switch (imsg->hdr.type) {
+	case IMSG_SCRIPT:
+		IMSG_SIZE_CHECK(imsg, &scr);
+		bcopy(imsg->data, &scr, sizeof(scr));
+		scr.retval = script_exec(env, &scr);
+		proc_compose_imsg(ps, PROC_HCE, -1, IMSG_SCRIPT,
+		    -1, &scr, sizeof(scr));
+		break;
+	case IMSG_SNMPSOCK:
+		(void)snmp_sendsock(env, p->p_id);
+		break;
+	default:
+		return (-1);
 	}
 
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("msgbuf_write");
-	}
-
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("main_dispatch_hce: imsg_read error");
-		if (n == 0)
-			break;
-
-		switch (imsg.hdr.type) {
-		case IMSG_SCRIPT:
-			if (imsg.hdr.len - IMSG_HEADER_SIZE !=
-			    sizeof(scr))
-				fatalx("main_dispatch_hce: "
-				    "invalid size of script request");
-			bcopy(imsg.data, &scr, sizeof(scr));
-			scr.retval = script_exec(env, &scr);
-			imsg_compose_event(iev_hce, IMSG_SCRIPT,
-			    0, 0, -1, &scr, sizeof(scr));
-			break;
-		case IMSG_SNMPSOCK:
-			(void)snmp_sendsock(iev);
-			break;
-		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
-		}
-		imsg_free(&imsg);
-	}
-	imsg_event_add(iev);
+	return (0);
 }
 
-void
-main_dispatch_relay(int fd, short event, void * ptr)
+int
+parent_dispatch_relay(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
-	struct relayd		*env = relayd_env;
-	struct imsgev		*iev;
-	struct imsgbuf		*ibuf;
-	struct imsg		 imsg;
-	ssize_t			 n;
+	struct relayd		*env = p->p_env;
+	struct privsep		*ps = env->sc_ps;
 	struct ctl_bindany	 bnd;
 	int			 s;
 
-	iev = ptr;
-	ibuf = &iev->ibuf;
-
-	if (event & EV_READ) {
-		if ((n = imsg_read(ibuf)) == -1)
-			fatal("imsg_read error");
-		if (n == 0) {
-			/* this pipe is dead, so remove the event handler */
-			event_del(&iev->ev);
-			event_loopexit(NULL);
-			return;
-		}
-	}
-
-	if (event & EV_WRITE) {
-		if (msgbuf_write(&ibuf->w) == -1)
-			fatal("msgbuf_write");
-	}
-
-	for (;;) {
-		if ((n = imsg_get(ibuf, &imsg)) == -1)
-			fatal("main_dispatch_relay: imsg_read error");
-		if (n == 0)
+	switch (imsg->hdr.type) {
+	case IMSG_BINDANY:
+		IMSG_SIZE_CHECK(imsg, &bnd);
+		bcopy(imsg->data, &bnd, sizeof(bnd));
+		if (bnd.bnd_proc > env->sc_prefork_relay)
+			fatalx("pfe_dispatch_relay: "
+			    "invalid relay proc");
+		switch (bnd.bnd_proto) {
+		case IPPROTO_TCP:
+		case IPPROTO_UDP:
 			break;
-
-		switch (imsg.hdr.type) {
-		case IMSG_BINDANY:
-			if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(bnd))
-				fatalx("invalid imsg header len");
-			bcopy(imsg.data, &bnd, sizeof(bnd));
-			if (bnd.bnd_proc > env->sc_prefork_relay)
-				fatalx("pfe_dispatch_relay: "
-				    "invalid relay proc");
-			switch (bnd.bnd_proto) {
-			case IPPROTO_TCP:
-			case IPPROTO_UDP:
-				break;
-			default:
-				fatalx("pfe_dispatch_relay: requested socket "
-				    "for invalid protocol");
-				/* NOTREACHED */
-			}
-			s = bindany(&bnd);
-			imsg_compose_event(&iev_relay[bnd.bnd_proc],
-			    IMSG_BINDANY,
-			    0, 0, s, &bnd.bnd_id, sizeof(bnd.bnd_id));
-			break;
 		default:
-			log_debug("%s: unexpected imsg %d", __func__,
-			    imsg.hdr.type);
-			break;
+			fatalx("pfe_dispatch_relay: requested socket "
+			    "for invalid protocol");
+			/* NOTREACHED */
 		}
-		imsg_free(&imsg);
+		s = bindany(&bnd);
+		proc_compose_imsg(ps, PROC_RELAY, bnd.bnd_proc,
+		    IMSG_BINDANY, s, &bnd.bnd_id, sizeof(bnd.bnd_id));
+		break;
+	default:
+		return (-1);
 	}
-	imsg_event_add(iev);
+
+	return (0);
 }
 
 struct host *
blob - b55119486323f57e1dfbc8379f5f095fbc7d19dd
blob + 330ad861cd45bb65cdef3bc41e0891384f525abb
--- relayd.h
+++ relayd.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: relayd.h,v 1.145 2011/05/05 10:20:24 phessler Exp $	*/
+/*	$OpenBSD: relayd.h,v 1.146 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -648,70 +648,16 @@ struct router {
 };
 TAILQ_HEAD(routerlist, router);
 
-enum {
-	PROC_MAIN,
-	PROC_PFE,
-	PROC_HCE,
-	PROC_RELAY
-} relayd_process;
 
-struct relayd {
-	u_int8_t		 sc_opts;
-	u_int32_t		 sc_flags;
-	const char		*sc_confpath;
-	struct pfdata		*sc_pf;
-	int			 sc_rtsock;
-	int			 sc_rtseq;
-	int			 sc_tablecount;
-	int			 sc_rdrcount;
-	int			 sc_protocount;
-	int			 sc_relaycount;
-	int			 sc_routercount;
-	int			 sc_routecount;
-	struct timeval		 sc_interval;
-	struct timeval		 sc_timeout;
-	struct table		 sc_empty_table;
-	struct protocol		 sc_proto_default;
-	struct event		 sc_ev;
-	struct tablelist	*sc_tables;
-	struct rdrlist		*sc_rdrs;
-	struct protolist	*sc_protos;
-	struct relaylist	*sc_relays;
-	struct routerlist	*sc_rts;
-	struct netroutelist	*sc_routes;
-	u_int16_t		 sc_prefork_relay;
-	char			 sc_demote_group[IFNAMSIZ];
-	u_int16_t		 sc_id;
-
-	struct event		 sc_statev;
-	struct timeval		 sc_statinterval;
-
-	int			 sc_snmp;
-	struct event		 sc_snmpto;
-	struct event		 sc_snmpev;
-
-	int			 sc_has_icmp;
-	int			 sc_has_icmp6;
-	struct ctl_icmp_event	 sc_icmp_send;
-	struct ctl_icmp_event	 sc_icmp_recv;
-	struct ctl_icmp_event	 sc_icmp6_send;
-	struct ctl_icmp_event	 sc_icmp6_recv;
-
-	/* Event and signal handlers */
-	struct event		 sc_evsigint;
-	struct event		 sc_evsigterm;
-	struct event		 sc_evsigchld;
-	struct event		 sc_evsighup;
-	struct event		 sc_evsigpipe;
-};
-
-#define RELAYD_OPT_VERBOSE		0x01
-#define RELAYD_OPT_NOACTION		0x04
-#define RELAYD_OPT_LOGUPDATE		0x08
-#define RELAYD_OPT_LOGNOTIFY		0x10
-#define RELAYD_OPT_LOGALL		0x18
-
 /* initially control.h */
+struct control_sock {
+	const char	*cs_name;
+	struct event	 cs_ev;
+	int		 cs_fd;
+	int		 cs_restricted;
+	void		*cs_env;
+};
+
 struct {
 	struct event	 ev;
 	int		 fd;
@@ -722,14 +668,22 @@ enum blockmodes {
 	BM_NONBLOCK
 };
 
+
 struct imsgev {
 	struct imsgbuf		 ibuf;
 	void			(*handler)(int, short, void *);
 	struct event		 ev;
+	struct privsep_proc	*proc;
 	void			*data;
 	short			 events;
 };
 
+#define IMSG_SIZE_CHECK(imsg, p) do {				\
+	if (IMSG_DATA_SIZE(imsg) < sizeof(*p))			\
+		fatalx("bad length imsg received");		\
+} while (0)
+#define IMSG_DATA_SIZE(imsg)	((imsg)->hdr.len - IMSG_HEADER_SIZE)
+
 struct ctl_conn {
 	TAILQ_ENTRY(ctl_conn)	 entry;
 	u_int8_t		 flags;
@@ -743,6 +697,7 @@ enum imsg_type {
 	IMSG_NONE,
 	IMSG_CTL_OK,		/* answer to relayctl requests */
 	IMSG_CTL_FAIL,
+	IMSG_CTL_VERBOSE,
 	IMSG_CTL_END,
 	IMSG_CTL_RDR,
 	IMSG_CTL_TABLE,
@@ -767,7 +722,6 @@ enum imsg_type {
 	IMSG_CTL_NOTIFY,
 	IMSG_CTL_RDR_STATS,
 	IMSG_CTL_RELAY_STATS,
-	IMSG_CTL_LOG_VERBOSE,
 	IMSG_RDR_ENABLE,	/* notifies from pfe to hce */
 	IMSG_RDR_DISABLE,
 	IMSG_TABLE_ENABLE,
@@ -798,16 +752,114 @@ enum imsg_type {
 	IMSG_RTMSG		/* from pfe to parent */
 };
 
+enum privsep_procid {
+	PROC_PARENT	= 0,
+	PROC_PFE,
+	PROC_HCE,
+	PROC_RELAY,
+	PROC_MAX
+} privsep_process;
+
+/* Attach the control socket to the following process */
+#define PROC_CONTROL	PROC_PFE
+
+struct privsep {
+	int				*ps_pipes[PROC_MAX][PROC_MAX];
+	struct imsgev			*ps_ievs[PROC_MAX];
+	const char			*ps_title[PROC_MAX];
+	pid_t				 ps_pid[PROC_MAX];
+
+	u_int				 ps_instances[PROC_MAX];
+	u_int				 ps_instance;
+
+	struct control_sock		 ps_csock;
+
+	/* Event and signal handlers */
+	struct event			 ps_evsigint;
+	struct event			 ps_evsigterm;
+	struct event			 ps_evsigchld;
+	struct event			 ps_evsighup;
+	struct event			 ps_evsigpipe;
+
+	struct passwd			*ps_pw;
+	struct relayd			*ps_env;
+};
+
+struct privsep_proc {
+	const char		*p_title;
+	enum privsep_procid	 p_id;
+	int			(*p_cb)(int, struct privsep_proc *,
+				    struct imsg *);
+	pid_t			(*p_init)(struct privsep *,
+				    struct privsep_proc *);
+	void			(*p_shutdown)(void);
+	u_int			 p_instance;
+	const char		*p_chroot;
+	struct privsep		*p_ps;
+	struct relayd		*p_env;
+};
+
+struct relayd {
+	u_int8_t		 sc_opts;
+	u_int32_t		 sc_flags;
+	const char		*sc_confpath;
+	struct pfdata		*sc_pf;
+	int			 sc_rtsock;
+	int			 sc_rtseq;
+	int			 sc_tablecount;
+	int			 sc_rdrcount;
+	int			 sc_protocount;
+	int			 sc_relaycount;
+	int			 sc_routercount;
+	int			 sc_routecount;
+	struct timeval		 sc_interval;
+	struct timeval		 sc_timeout;
+	struct table		 sc_empty_table;
+	struct protocol		 sc_proto_default;
+	struct event		 sc_ev;
+	struct tablelist	*sc_tables;
+	struct rdrlist		*sc_rdrs;
+	struct protolist	*sc_protos;
+	struct relaylist	*sc_relays;
+	struct routerlist	*sc_rts;
+	struct netroutelist	*sc_routes;
+	u_int16_t		 sc_prefork_relay;
+	char			 sc_demote_group[IFNAMSIZ];
+	u_int16_t		 sc_id;
+
+	struct event		 sc_statev;
+	struct timeval		 sc_statinterval;
+
+	int			 sc_snmp;
+	struct event		 sc_snmpto;
+	struct event		 sc_snmpev;
+
+	int			 sc_has_icmp;
+	int			 sc_has_icmp6;
+	struct ctl_icmp_event	 sc_icmp_send;
+	struct ctl_icmp_event	 sc_icmp_recv;
+	struct ctl_icmp_event	 sc_icmp6_send;
+	struct ctl_icmp_event	 sc_icmp6_recv;
+
+	struct privsep		*sc_ps;
+};
+
+#define RELAYD_OPT_VERBOSE		0x01
+#define RELAYD_OPT_NOACTION		0x04
+#define RELAYD_OPT_LOGUPDATE		0x08
+#define RELAYD_OPT_LOGNOTIFY		0x10
+#define RELAYD_OPT_LOGALL		0x18
+
 /* control.c */
-int	control_init(void);
-int	control_listen(struct relayd *, struct imsgev *, struct imsgev *);
-void    control_accept(int, short, void *);
-void    control_dispatch_imsg(int, short, void *);
-void	control_imsg_forward(struct imsg *);
-void    control_cleanup(void);
+int	 control_init(struct privsep *, struct control_sock *);
+int	 control_listen(struct control_sock *);
+void	 control_cleanup(struct control_sock *);
+void	 control_dispatch_imsg(int, short, void *);
+void	 control_imsg_forward(struct imsg *);
+struct ctl_conn	*
+	 control_connbyfd(int);
+void	 socket_set_blockmode(int, enum blockmodes);
 
-void    session_socket_blockmode(int, enum blockmodes);
-
 extern  struct ctl_connlist ctl_conns;
 
 /* parse.y */
@@ -826,8 +878,7 @@ const char *printb_flags(const u_int32_t, const char *
 
 
 /* pfe.c */
-pid_t	 pfe(struct relayd *, int [2], int [2], int [RELAY_MAXPROC][2],
-	    int [2], int [RELAY_MAXPROC][2]);
+pid_t	 pfe(struct privsep *, struct privsep_proc *);
 void	 show(struct ctl_conn *);
 void	 show_sessions(struct ctl_conn *);
 int	 enable_rdr(struct ctl_conn *, struct ctl_id *);
@@ -854,13 +905,11 @@ void	 sync_routes(struct relayd *, struct router *);
 int	 pfe_route(struct relayd *, struct ctl_netroute *);
 
 /* hce.c */
-pid_t	 hce(struct relayd *, int [2], int [2], int [RELAY_MAXPROC][2],
-	    int [2], int [RELAY_MAXPROC][2]);
+pid_t	 hce(struct privsep *, struct privsep_proc *);
 void	 hce_notify_done(struct host *, enum host_error);
 
 /* relay.c */
-pid_t	 relay(struct relayd *, int [2], int [2], int [RELAY_MAXPROC][2],
-	    int [2], int [RELAY_MAXPROC][2]);
+pid_t	 relay(struct privsep *, struct privsep_proc *);
 void	 relay_notify_done(struct host *, const char *);
 int	 relay_session_cmp(struct rsession *, struct rsession *);
 int	 relay_load_certfiles(struct relay *);
@@ -893,7 +942,7 @@ void	 check_icmp(struct relayd *, struct timeval *);
 void	 check_tcp(struct ctl_tcp_event *);
 
 /* check_script.c */
-void	 check_script(struct host *);
+void	 check_script(struct relayd *, struct host *);
 void	 script_done(struct relayd *, struct ctl_script *);
 int	 script_exec(struct relayd *, struct ctl_script *);
 
@@ -958,9 +1007,9 @@ void		 pn_unref(u_int16_t);
 void		 pn_ref(u_int16_t);
 
 /* snmp.c */
-void	 snmp_init(struct relayd *, struct imsgev *);
-int	 snmp_sendsock(struct imsgev *);
-void	 snmp_hosttrap(struct table *, struct host *);
+void	 snmp_init(struct relayd *, enum privsep_procid);
+int	 snmp_sendsock(struct relayd *, enum privsep_procid);
+void	 snmp_hosttrap(struct relayd *, struct table *, struct host *);
 
 /* shuffle.c */
 void		shuffle_init(struct shuffle *);
@@ -975,3 +1024,26 @@ void	log_info(const char *, ...);
 void	log_debug(const char *, ...);
 __dead void fatal(const char *);
 __dead void fatalx(const char *);
+
+/* proc.c */
+void	 proc_init(struct privsep *, struct privsep_proc *, u_int);
+void	 proc_kill(struct privsep *);
+void	 proc_config(struct privsep *, struct privsep_proc *, u_int);
+void	 proc_dispatch(int, short event, void *);
+pid_t	 proc_run(struct privsep *, struct privsep_proc *,
+	    struct privsep_proc *, u_int,
+	    void (*)(struct privsep *, struct privsep_proc *, void *), void *);
+int	 proc_compose_imsg(struct privsep *, enum privsep_procid, int,
+	    u_int16_t, int, void *, u_int16_t);
+int	 proc_composev_imsg(struct privsep *, enum privsep_procid, int,
+	    u_int16_t, int, const struct iovec *, int);
+int	 proc_forward_imsg(struct privsep *, struct imsg *,
+	    enum privsep_procid, int);
+void	 proc_flush_imsg(struct privsep *, enum privsep_procid, int);
+struct imsgbuf *
+	 proc_ibuf(struct privsep *, enum privsep_procid, int);
+void	 imsg_event_add(struct imsgev *);
+int	 imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t,
+	    pid_t, int, void *, u_int16_t);
+int	 imsg_composev_event(struct imsgev *, u_int16_t, u_int32_t,
+	    pid_t, int, const struct iovec *, int);
blob - e8a4ba42860dda19c63e3cb685507bc3f5011039
blob + f341d74bade61f7ee70a0cac160412171e5bb379
--- snmp.c
+++ snmp.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: snmp.c,v 1.9 2011/05/05 12:01:44 reyk Exp $	*/
+/*	$OpenBSD: snmp.c,v 1.10 2011/05/09 12:08:47 reyk Exp $	*/
 
 /*
  * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org>
@@ -46,19 +46,15 @@
 } while (0)
 
 static struct imsgev	*iev_snmp = NULL;
-static struct imsgev	*iev_main = NULL;
-static struct relayd	*env = NULL;
+enum privsep_procid	 snmp_procid;
 
 void	 snmp_sock(int, short, void *);
-int	 snmp_getsock(struct imsgev *);
+int	 snmp_getsock(struct relayd *, enum privsep_procid);
 int	 snmp_element(const char *, enum snmp_type, void *, int64_t);
 
 void
-snmp_init(struct relayd *x_env, struct imsgev *iev)
+snmp_init(struct relayd *env, enum privsep_procid id)
 {
-	env = x_env;
-	iev_main = iev;
-
 	if (event_initialized(&env->sc_snmpev))
 		event_del(&env->sc_snmpev);
 	if (event_initialized(&env->sc_snmpto))
@@ -68,16 +64,15 @@ snmp_init(struct relayd *x_env, struct imsgev *iev)
 		env->sc_snmp = -1;
 	}
 
-	if ((env->sc_flags & F_TRAP) == 0) {
-		iev_main = NULL;
+	if ((env->sc_flags & F_TRAP) == 0)
 		return;
-	}
 
-	snmp_sock(-1, -1, iev);
+	snmp_procid = id;
+	snmp_sock(-1, -1, env);
 }
 
 int
-snmp_sendsock(struct imsgev *iev)
+snmp_sendsock(struct relayd *env, enum privsep_procid id)
 {
 	struct imsgev		 tmpiev;
 	struct sockaddr_un	 sun;
@@ -97,34 +92,36 @@ snmp_sendsock(struct imsgev *iev)
 	imsg_init(&tmpiev.ibuf, s);
 	imsg_compose_event(&tmpiev, IMSG_SNMP_LOCK, 0, 0, -1, NULL, 0);
 
-	imsg_compose_event(iev, IMSG_SNMPSOCK, 0, 0, s, NULL, 0);
-	imsg_flush(&iev->ibuf);	/* need to send the socket now */
+	proc_compose_imsg(env->sc_ps, id, -1, IMSG_SNMPSOCK, s, NULL, 0);
+	proc_flush_imsg(env->sc_ps, id, -1); /* need to send the socket now */
 	close(s);
 	return (0);
 
  fail:
 	if (s != -1)
 		close(s);
-	imsg_compose_event(iev, IMSG_NONE, 0, 0, -1, NULL, 0);
+	proc_compose_imsg(env->sc_ps, id, -1, IMSG_NONE, -1, NULL, 0);
 	return (-1);
 }
 
 int
-snmp_getsock(struct imsgev *iev)
+snmp_getsock(struct relayd *env, enum privsep_procid id)
 {
 	struct imsg	 imsg;
+	struct imsgbuf	*ibuf;
 	int		 n, s = -1, done = 0;
 
-	imsg_compose_event(iev, IMSG_SNMPSOCK, 0, 0, -1, NULL, 0);
-	imsg_flush(&iev->ibuf);
+	ibuf = proc_ibuf(env->sc_ps, id, -1);
+	proc_compose_imsg(env->sc_ps, id, -1, IMSG_SNMPSOCK, -1, NULL, 0);
+	proc_flush_imsg(env->sc_ps, id, -1);
 
 	while (!done) {
-		if ((n = imsg_read(&iev->ibuf)) == -1)
-			fatal("snmp_getsock: failed to read imsg");
-		if (n == 0)
-			fatal("snmp_getsock: pipe closed");
+		do {
+			if ((n = imsg_read(ibuf)) == -1)
+				fatalx("snmp_getsock: imsg_read error");
+		} while (n == -2); /* handle non-blocking I/O */
 		while (!done) {
-			if ((n = imsg_get(&iev->ibuf, &imsg)) == -1)
+			if ((n = imsg_get(ibuf, &imsg)) == -1)
 				fatal("snmp_getsock: failed to get imsg");
 			if (n == 0)
 				break;
@@ -154,7 +151,8 @@ snmp_getsock(struct imsgev *iev)
 void
 snmp_sock(int fd, short event, void *arg)
 {
-	struct timeval	tv = SNMP_RECONNECT_TIMEOUT;
+	struct relayd	*env = arg;
+	struct timeval	 tv = SNMP_RECONNECT_TIMEOUT;
 
 	switch (event) {
 	case -1:
@@ -166,7 +164,7 @@ snmp_sock(int fd, short event, void *arg)
 		break;
 	}
 
-	if ((env->sc_snmp = snmp_getsock(iev_main)) == -1) {
+	if ((env->sc_snmp = snmp_getsock(env, snmp_procid)) == -1) {
 		DPRINTF("%s: failed to open snmp socket", __func__);
 		goto retry;
 	}
@@ -176,7 +174,7 @@ snmp_sock(int fd, short event, void *arg)
 	event_add(&env->sc_snmpev, NULL);
 	return;
  retry:
-	evtimer_set(&env->sc_snmpto, snmp_sock, env);
+	evtimer_set(&env->sc_snmpto, snmp_sock, arg);
 	evtimer_add(&env->sc_snmpto, &tv);
 }
 
@@ -248,7 +246,7 @@ snmp_element(const char *oid, enum snmp_type type, voi
  */
 
 void
-snmp_hosttrap(struct table *table, struct host *host)
+snmp_hosttrap(struct relayd *env, struct table *table, struct host *host)
 {
 	if (iev_snmp == NULL || env->sc_snmp == -1)
 		return;