xref: /openbsd/usr.sbin/snmpd/proc.c (revision 32c142bf)
1*32c142bfSreyk /*	$OpenBSD: proc.c,v 1.14 2015/10/14 14:51:57 reyk Exp $	*/
260996bedSreyk 
360996bedSreyk /*
421032f8aSreyk  * Copyright (c) 2010 - 2014 Reyk Floeter <reyk@openbsd.org>
560996bedSreyk  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
660996bedSreyk  *
760996bedSreyk  * Permission to use, copy, modify, and distribute this software for any
860996bedSreyk  * purpose with or without fee is hereby granted, provided that the above
960996bedSreyk  * copyright notice and this permission notice appear in all copies.
1060996bedSreyk  *
1160996bedSreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1260996bedSreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1360996bedSreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1460996bedSreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1560996bedSreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1660996bedSreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1760996bedSreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1860996bedSreyk  */
1960996bedSreyk 
2060996bedSreyk #include <sys/types.h>
2160996bedSreyk #include <sys/queue.h>
2260996bedSreyk #include <sys/socket.h>
2360996bedSreyk #include <sys/wait.h>
2460996bedSreyk #include <sys/tree.h>
2560996bedSreyk 
2660996bedSreyk #include <net/if.h>
2721032f8aSreyk #include <netinet/in.h>
2821032f8aSreyk #include <netinet/ip.h>
2921032f8aSreyk #include <arpa/inet.h>
3060996bedSreyk 
3160996bedSreyk #include <stdio.h>
3260996bedSreyk #include <stdlib.h>
3360996bedSreyk #include <unistd.h>
3421032f8aSreyk #include <string.h>
3521032f8aSreyk #include <errno.h>
3621032f8aSreyk #include <signal.h>
3760996bedSreyk #include <pwd.h>
3821032f8aSreyk #include <event.h>
3960996bedSreyk 
4060996bedSreyk #include "snmpd.h"
4160996bedSreyk 
4221032f8aSreyk void	 proc_open(struct privsep *, struct privsep_proc *,
4321032f8aSreyk 	    struct privsep_proc *, size_t);
4421032f8aSreyk void	 proc_close(struct privsep *);
45*32c142bfSreyk int	 proc_ispeer(struct privsep_proc *, unsigned int, enum privsep_procid);
4660996bedSreyk void	 proc_shutdown(struct privsep_proc *);
4760996bedSreyk void	 proc_sig_handler(int, short, void *);
4821032f8aSreyk void	 proc_range(struct privsep *, enum privsep_procid, int *, int *);
4960996bedSreyk 
5021032f8aSreyk int
51*32c142bfSreyk proc_ispeer(struct privsep_proc *procs, unsigned int nproc,
52*32c142bfSreyk     enum privsep_procid type)
5360996bedSreyk {
54*32c142bfSreyk 	unsigned int	i;
5560996bedSreyk 
5621032f8aSreyk 	for (i = 0; i < nproc; i++)
5721032f8aSreyk 		if (procs[i].p_id == type)
5821032f8aSreyk 			return (1);
5921032f8aSreyk 	return (0);
6021032f8aSreyk }
6121032f8aSreyk 
6221032f8aSreyk void
63*32c142bfSreyk proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
6421032f8aSreyk {
65*32c142bfSreyk 	unsigned int		 i, j, src, dst;
6621032f8aSreyk 	struct privsep_pipes	*pp;
6721032f8aSreyk 
6860996bedSreyk 	/*
6921032f8aSreyk 	 * Allocate pipes for all process instances (incl. parent)
7021032f8aSreyk 	 *
7121032f8aSreyk 	 * - ps->ps_pipes: N:M mapping
7221032f8aSreyk 	 * N source processes connected to M destination processes:
7321032f8aSreyk 	 * [src][instances][dst][instances], for example
7421032f8aSreyk 	 * [PROC_RELAY][3][PROC_CA][3]
7521032f8aSreyk 	 *
7621032f8aSreyk 	 * - ps->ps_pp: per-process 1:M part of ps->ps_pipes
7721032f8aSreyk 	 * Each process instance has a destination array of socketpair fds:
7821032f8aSreyk 	 * [dst][instances], for example
7921032f8aSreyk 	 * [PROC_PARENT][0]
8021032f8aSreyk 	 */
8121032f8aSreyk 	for (src = 0; src < PROC_MAX; src++) {
8221032f8aSreyk 		/* Allocate destination array for each process */
8321032f8aSreyk 		if ((ps->ps_pipes[src] = calloc(ps->ps_ninstances,
8421032f8aSreyk 		    sizeof(struct privsep_pipes))) == NULL)
8521032f8aSreyk 			fatal("proc_init: calloc");
8621032f8aSreyk 
8721032f8aSreyk 		for (i = 0; i < ps->ps_ninstances; i++) {
8821032f8aSreyk 			pp = &ps->ps_pipes[src][i];
8921032f8aSreyk 
9021032f8aSreyk 			for (dst = 0; dst < PROC_MAX; dst++) {
9121032f8aSreyk 				/* Allocate maximum fd integers */
9221032f8aSreyk 				if ((pp->pp_pipes[dst] =
9321032f8aSreyk 				    calloc(ps->ps_ninstances,
9421032f8aSreyk 				    sizeof(int))) == NULL)
9521032f8aSreyk 					fatal("proc_init: calloc");
9621032f8aSreyk 
9721032f8aSreyk 				/* Mark fd as unused */
9821032f8aSreyk 				for (j = 0; j < ps->ps_ninstances; j++)
9921032f8aSreyk 					pp->pp_pipes[dst][j] = -1;
10021032f8aSreyk 			}
10121032f8aSreyk 		}
10221032f8aSreyk 	}
10321032f8aSreyk 
10421032f8aSreyk 	/*
10521032f8aSreyk 	 * Setup and run the parent and its children
10660996bedSreyk 	 */
10760996bedSreyk 	privsep_process = PROC_PARENT;
10821032f8aSreyk 	ps->ps_instances[PROC_PARENT] = 1;
10960996bedSreyk 	ps->ps_title[PROC_PARENT] = "parent";
11060996bedSreyk 	ps->ps_pid[PROC_PARENT] = getpid();
11121032f8aSreyk 	ps->ps_pp = &ps->ps_pipes[privsep_process][0];
11260996bedSreyk 
11321032f8aSreyk 	for (i = 0; i < nproc; i++) {
11421032f8aSreyk 		/* Default to 1 process instance */
11521032f8aSreyk 		if (ps->ps_instances[procs[i].p_id] < 1)
11621032f8aSreyk 			ps->ps_instances[procs[i].p_id] = 1;
11721032f8aSreyk 		ps->ps_title[procs[i].p_id] = procs[i].p_title;
11821032f8aSreyk 	}
11921032f8aSreyk 
12021032f8aSreyk 	proc_open(ps, NULL, procs, nproc);
12160996bedSreyk 
12260996bedSreyk 	/* Engage! */
12321032f8aSreyk 	for (i = 0; i < nproc; i++)
12421032f8aSreyk 		ps->ps_pid[procs[i].p_id] = (*procs[i].p_init)(ps, &procs[i]);
12560996bedSreyk }
12660996bedSreyk 
12760996bedSreyk void
12860996bedSreyk proc_kill(struct privsep *ps)
12960996bedSreyk {
13060996bedSreyk 	pid_t		 pid;
131*32c142bfSreyk 	unsigned int	 i;
13260996bedSreyk 
13360996bedSreyk 	if (privsep_process != PROC_PARENT)
13460996bedSreyk 		return;
13560996bedSreyk 
13660996bedSreyk 	for (i = 0; i < PROC_MAX; i++) {
13760996bedSreyk 		if (ps->ps_pid[i] == 0)
13860996bedSreyk 			continue;
13921032f8aSreyk 		killpg(ps->ps_pid[i], SIGTERM);
14060996bedSreyk 	}
14160996bedSreyk 
14260996bedSreyk 	do {
14360996bedSreyk 		pid = waitpid(WAIT_ANY, NULL, 0);
14460996bedSreyk 	} while (pid != -1 || (pid == -1 && errno == EINTR));
14521032f8aSreyk 
14621032f8aSreyk 	proc_close(ps);
14760996bedSreyk }
14860996bedSreyk 
14960996bedSreyk void
15021032f8aSreyk proc_open(struct privsep *ps, struct privsep_proc *p,
15121032f8aSreyk     struct privsep_proc *procs, size_t nproc)
15260996bedSreyk {
15321032f8aSreyk 	struct privsep_pipes	*pa, *pb;
15421032f8aSreyk 	int			 fds[2];
155*32c142bfSreyk 	unsigned int		 i, j, src, proc;
15660996bedSreyk 
15721032f8aSreyk 	if (p == NULL)
15821032f8aSreyk 		src = privsep_process; /* parent */
15921032f8aSreyk 	else
16021032f8aSreyk 		src = p->p_id;
16121032f8aSreyk 
16221032f8aSreyk 	/*
16321032f8aSreyk 	 * Open socket pairs for our peers
16421032f8aSreyk 	 */
16521032f8aSreyk 	for (proc = 0; proc < nproc; proc++) {
16621032f8aSreyk 		procs[proc].p_ps = ps;
16721032f8aSreyk 		procs[proc].p_env = ps->ps_env;
16821032f8aSreyk 
16921032f8aSreyk 		for (i = 0; i < ps->ps_instances[src]; i++) {
17021032f8aSreyk 			for (j = 0; j < ps->ps_instances[procs[proc].p_id];
17121032f8aSreyk 			    j++) {
17221032f8aSreyk 				pa = &ps->ps_pipes[src][i];
17321032f8aSreyk 				pb = &ps->ps_pipes[procs[proc].p_id][j];
17421032f8aSreyk 
17521032f8aSreyk 				/* Check if fds are already set by peer */
17621032f8aSreyk 				if (pa->pp_pipes[procs[proc].p_id][j] != -1)
17721032f8aSreyk 					continue;
17821032f8aSreyk 
17921032f8aSreyk 				if (socketpair(AF_UNIX, SOCK_STREAM,
18021032f8aSreyk 				    PF_UNSPEC, fds) == -1)
18121032f8aSreyk 					fatal("socketpair");
18221032f8aSreyk 
18321032f8aSreyk 				socket_set_blockmode(fds[0], BM_NONBLOCK);
18421032f8aSreyk 				socket_set_blockmode(fds[1], BM_NONBLOCK);
18521032f8aSreyk 
18621032f8aSreyk 				pa->pp_pipes[procs[proc].p_id][j] = fds[0];
18721032f8aSreyk 				pb->pp_pipes[src][i] = fds[1];
18821032f8aSreyk 			}
18921032f8aSreyk 		}
19060996bedSreyk 	}
19160996bedSreyk }
19260996bedSreyk 
19360996bedSreyk void
19421032f8aSreyk proc_listen(struct privsep *ps, struct privsep_proc *procs, size_t nproc)
19560996bedSreyk {
196*32c142bfSreyk 	unsigned int		 i, dst, src, n, m;
19721032f8aSreyk 	struct privsep_pipes	*pp;
19821032f8aSreyk 
19921032f8aSreyk 	/*
20021032f8aSreyk 	 * Close unused pipes
20121032f8aSreyk 	 */
20221032f8aSreyk 	for (src = 0; src < PROC_MAX; src++) {
20321032f8aSreyk 		for (n = 0; n < ps->ps_instances[src]; n++) {
20421032f8aSreyk 			/* Ingore current process */
205*32c142bfSreyk 			if (src == (unsigned int)privsep_process &&
20621032f8aSreyk 			    n == ps->ps_instance)
20721032f8aSreyk 				continue;
20821032f8aSreyk 
20921032f8aSreyk 			pp = &ps->ps_pipes[src][n];
21021032f8aSreyk 
21121032f8aSreyk 			for (dst = 0; dst < PROC_MAX; dst++) {
21221032f8aSreyk 				if (src == dst)
21321032f8aSreyk 					continue;
21421032f8aSreyk 				for (m = 0; m < ps->ps_instances[dst]; m++) {
21521032f8aSreyk 					if (pp->pp_pipes[dst][m] == -1)
21621032f8aSreyk 						continue;
21721032f8aSreyk 
21821032f8aSreyk 					/* Close and invalidate fd */
21921032f8aSreyk 					close(pp->pp_pipes[dst][m]);
22021032f8aSreyk 					pp->pp_pipes[dst][m] = -1;
22121032f8aSreyk 				}
22221032f8aSreyk 			}
22321032f8aSreyk 		}
22421032f8aSreyk 	}
22560996bedSreyk 
22660996bedSreyk 	src = privsep_process;
22721032f8aSreyk 	ps->ps_pp = pp = &ps->ps_pipes[src][ps->ps_instance];
22860996bedSreyk 
22960996bedSreyk 	/*
23021032f8aSreyk 	 * Listen on appropriate pipes
23160996bedSreyk 	 */
23221032f8aSreyk 	for (i = 0; i < nproc; i++) {
23321032f8aSreyk 		dst = procs[i].p_id;
23421032f8aSreyk 
23521032f8aSreyk 		if (src == dst)
23621032f8aSreyk 			fatal("proc_listen: cannot peer with oneself");
23721032f8aSreyk 
23821032f8aSreyk 		if ((ps->ps_ievs[dst] = calloc(ps->ps_instances[dst],
23921032f8aSreyk 		    sizeof(struct imsgev))) == NULL)
24021032f8aSreyk 			fatal("proc_open");
24121032f8aSreyk 
24221032f8aSreyk 		for (n = 0; n < ps->ps_instances[dst]; n++) {
24321032f8aSreyk 			if (pp->pp_pipes[dst][n] == -1)
24421032f8aSreyk 				continue;
24521032f8aSreyk 
24621032f8aSreyk 			imsg_init(&(ps->ps_ievs[dst][n].ibuf),
24721032f8aSreyk 			    pp->pp_pipes[dst][n]);
24821032f8aSreyk 			ps->ps_ievs[dst][n].handler = proc_dispatch;
24921032f8aSreyk 			ps->ps_ievs[dst][n].events = EV_READ;
25021032f8aSreyk 			ps->ps_ievs[dst][n].proc = &procs[i];
25121032f8aSreyk 			ps->ps_ievs[dst][n].data = &ps->ps_ievs[dst][n];
25221032f8aSreyk 			procs[i].p_instance = n;
25321032f8aSreyk 
25421032f8aSreyk 			event_set(&(ps->ps_ievs[dst][n].ev),
25521032f8aSreyk 			    ps->ps_ievs[dst][n].ibuf.fd,
25621032f8aSreyk 			    ps->ps_ievs[dst][n].events,
25721032f8aSreyk 			    ps->ps_ievs[dst][n].handler,
25821032f8aSreyk 			    ps->ps_ievs[dst][n].data);
25921032f8aSreyk 			event_add(&(ps->ps_ievs[dst][n].ev), NULL);
26060996bedSreyk 		}
26160996bedSreyk 	}
26260996bedSreyk }
26360996bedSreyk 
26421032f8aSreyk void
26521032f8aSreyk proc_close(struct privsep *ps)
26621032f8aSreyk {
267*32c142bfSreyk 	unsigned int		 dst, n;
26821032f8aSreyk 	struct privsep_pipes	*pp;
26960996bedSreyk 
27021032f8aSreyk 	if (ps == NULL)
27121032f8aSreyk 		return;
27221032f8aSreyk 
27321032f8aSreyk 	pp = ps->ps_pp;
27421032f8aSreyk 
27521032f8aSreyk 	for (dst = 0; dst < PROC_MAX; dst++) {
27621032f8aSreyk 		if (ps->ps_ievs[dst] == NULL)
27721032f8aSreyk 			continue;
27821032f8aSreyk 
27921032f8aSreyk 		for (n = 0; n < ps->ps_instances[dst]; n++) {
28021032f8aSreyk 			if (pp->pp_pipes[dst][n] == -1)
28121032f8aSreyk 				continue;
28221032f8aSreyk 
28321032f8aSreyk 			/* Cancel the fd, close and invalidate the fd */
28421032f8aSreyk 			event_del(&(ps->ps_ievs[dst][n].ev));
28521032f8aSreyk 			imsg_clear(&(ps->ps_ievs[dst][n].ibuf));
28621032f8aSreyk 			close(pp->pp_pipes[dst][n]);
28721032f8aSreyk 			pp->pp_pipes[dst][n] = -1;
28821032f8aSreyk 		}
28921032f8aSreyk 		free(ps->ps_ievs[dst]);
29060996bedSreyk 	}
29160996bedSreyk }
29260996bedSreyk 
29360996bedSreyk void
29460996bedSreyk proc_shutdown(struct privsep_proc *p)
29560996bedSreyk {
29660996bedSreyk 	struct privsep	*ps = p->p_ps;
29721032f8aSreyk 
29821032f8aSreyk 	if (p->p_id == PROC_CONTROL && ps)
29921032f8aSreyk 		control_cleanup(&ps->ps_csock);
30060996bedSreyk 
30160996bedSreyk 	if (p->p_shutdown != NULL)
30221032f8aSreyk 		(*p->p_shutdown)();
30360996bedSreyk 
30421032f8aSreyk 	proc_close(ps);
30560996bedSreyk 
30621032f8aSreyk 	log_info("%s exiting, pid %d", p->p_title, getpid());
30721032f8aSreyk 
30860996bedSreyk 	_exit(0);
30960996bedSreyk }
31060996bedSreyk 
31160996bedSreyk void
31260996bedSreyk proc_sig_handler(int sig, short event, void *arg)
31360996bedSreyk {
31460996bedSreyk 	struct privsep_proc	*p = arg;
31560996bedSreyk 
31660996bedSreyk 	switch (sig) {
31760996bedSreyk 	case SIGINT:
31860996bedSreyk 	case SIGTERM:
31960996bedSreyk 		proc_shutdown(p);
32060996bedSreyk 		break;
32160996bedSreyk 	case SIGCHLD:
32260996bedSreyk 	case SIGHUP:
32360996bedSreyk 	case SIGPIPE:
3245f2be52bSreyk 	case SIGUSR1:
32560996bedSreyk 		/* ignore */
32660996bedSreyk 		break;
32760996bedSreyk 	default:
32860996bedSreyk 		fatalx("proc_sig_handler: unexpected signal");
32960996bedSreyk 		/* NOTREACHED */
33060996bedSreyk 	}
33160996bedSreyk }
33260996bedSreyk 
33360996bedSreyk pid_t
33460996bedSreyk proc_run(struct privsep *ps, struct privsep_proc *p,
335*32c142bfSreyk     struct privsep_proc *procs, unsigned int nproc,
33621032f8aSreyk     void (*init)(struct privsep *, struct privsep_proc *, void *), void *arg)
33760996bedSreyk {
33860996bedSreyk 	pid_t			 pid;
33960996bedSreyk 	struct passwd		*pw;
34060996bedSreyk 	const char		*root;
3411dbe1dbcSblambert 	struct control_sock	*rcs;
342*32c142bfSreyk 	unsigned int		 n;
34360996bedSreyk 
34421032f8aSreyk 	if (ps->ps_noaction)
34521032f8aSreyk 		return (0);
34621032f8aSreyk 
34721032f8aSreyk 	proc_open(ps, p, procs, nproc);
34821032f8aSreyk 
34921032f8aSreyk 	/* Fork child handlers */
35060996bedSreyk 	switch (pid = fork()) {
35160996bedSreyk 	case -1:
35260996bedSreyk 		fatal("proc_run: cannot fork");
35360996bedSreyk 	case 0:
35421032f8aSreyk 		/* Set the process group of the current process */
3552fc3edb7Smillert 		setpgid(0, 0);
35660996bedSreyk 		break;
35760996bedSreyk 	default:
35860996bedSreyk 		return (pid);
35960996bedSreyk 	}
36060996bedSreyk 
36160996bedSreyk 	pw = ps->ps_pw;
36260996bedSreyk 
36321032f8aSreyk 	if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
36460996bedSreyk 		if (control_init(ps, &ps->ps_csock) == -1)
36560996bedSreyk 			fatalx(p->p_title);
3661dbe1dbcSblambert 		TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
3671dbe1dbcSblambert 			if (control_init(ps, rcs) == -1)
3681dbe1dbcSblambert 				fatalx(p->p_title);
36960996bedSreyk 	}
37060996bedSreyk 
37160996bedSreyk 	/* Change root directory */
37260996bedSreyk 	if (p->p_chroot != NULL)
37360996bedSreyk 		root = p->p_chroot;
37460996bedSreyk 	else
37560996bedSreyk 		root = pw->pw_dir;
37660996bedSreyk 
37760996bedSreyk 	if (chroot(root) == -1)
37860996bedSreyk 		fatal("proc_run: chroot");
37960996bedSreyk 	if (chdir("/") == -1)
38060996bedSreyk 		fatal("proc_run: chdir(\"/\")");
38160996bedSreyk 
38260996bedSreyk 	privsep_process = p->p_id;
38360996bedSreyk 
38460996bedSreyk 	setproctitle("%s", p->p_title);
38560996bedSreyk 
38660996bedSreyk 	if (setgroups(1, &pw->pw_gid) ||
38760996bedSreyk 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
38860996bedSreyk 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
38960996bedSreyk 		fatal("proc_run: cannot drop privileges");
39060996bedSreyk 
39121032f8aSreyk 	/* Fork child handlers */
39221032f8aSreyk 	for (n = 1; n < ps->ps_instances[p->p_id]; n++) {
39321032f8aSreyk 		if (fork() == 0) {
39421032f8aSreyk 			ps->ps_instance = p->p_instance = n;
39521032f8aSreyk 			break;
39621032f8aSreyk 		}
39721032f8aSreyk 	}
39821032f8aSreyk 
39921032f8aSreyk #ifdef DEBUG
40021032f8aSreyk 	log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
40121032f8aSreyk 	    ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
40221032f8aSreyk #endif
40321032f8aSreyk 
40460996bedSreyk 	event_init();
40560996bedSreyk 
40660996bedSreyk 	signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
40760996bedSreyk 	signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
40860996bedSreyk 	signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
40960996bedSreyk 	signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
41060996bedSreyk 	signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p);
4115f2be52bSreyk 	signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p);
41260996bedSreyk 
41360996bedSreyk 	signal_add(&ps->ps_evsigint, NULL);
41460996bedSreyk 	signal_add(&ps->ps_evsigterm, NULL);
41560996bedSreyk 	signal_add(&ps->ps_evsigchld, NULL);
41660996bedSreyk 	signal_add(&ps->ps_evsighup, NULL);
41760996bedSreyk 	signal_add(&ps->ps_evsigpipe, NULL);
4185f2be52bSreyk 	signal_add(&ps->ps_evsigusr1, NULL);
41960996bedSreyk 
42021032f8aSreyk 	proc_listen(ps, procs, nproc);
42160996bedSreyk 
42221032f8aSreyk 	if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
42360996bedSreyk 		TAILQ_INIT(&ctl_conns);
42460996bedSreyk 		if (control_listen(&ps->ps_csock) == -1)
42560996bedSreyk 			fatalx(p->p_title);
4261dbe1dbcSblambert 		TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
4271dbe1dbcSblambert 			if (control_listen(rcs) == -1)
4281dbe1dbcSblambert 				fatalx(p->p_title);
42960996bedSreyk 	}
43060996bedSreyk 
43160996bedSreyk 	if (init != NULL)
43221032f8aSreyk 		init(ps, p, arg);
43360996bedSreyk 
43460996bedSreyk 	event_dispatch();
43560996bedSreyk 
43660996bedSreyk 	proc_shutdown(p);
43760996bedSreyk 
43860996bedSreyk 	return (0);
43960996bedSreyk }
44060996bedSreyk 
44160996bedSreyk void
44260996bedSreyk proc_dispatch(int fd, short event, void *arg)
44360996bedSreyk {
44421032f8aSreyk 	struct imsgev		*iev = arg;
44521032f8aSreyk 	struct privsep_proc	*p = iev->proc;
44660996bedSreyk 	struct privsep		*ps = p->p_ps;
44760996bedSreyk 	struct imsgbuf		*ibuf;
44860996bedSreyk 	struct imsg		 imsg;
44960996bedSreyk 	ssize_t			 n;
45060996bedSreyk 	int			 verbose;
45160996bedSreyk 	const char		*title;
45260996bedSreyk 
45360996bedSreyk 	title = ps->ps_title[privsep_process];
45460996bedSreyk 	ibuf = &iev->ibuf;
45560996bedSreyk 
45660996bedSreyk 	if (event & EV_READ) {
45760996bedSreyk 		if ((n = imsg_read(ibuf)) == -1)
45860996bedSreyk 			fatal(title);
45960996bedSreyk 		if (n == 0) {
46060996bedSreyk 			/* this pipe is dead, so remove the event handler */
46160996bedSreyk 			event_del(&iev->ev);
46260996bedSreyk 			event_loopexit(NULL);
46360996bedSreyk 			return;
46460996bedSreyk 		}
46560996bedSreyk 	}
46660996bedSreyk 
46760996bedSreyk 	if (event & EV_WRITE) {
4682b721f23Sreyk 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
46960996bedSreyk 			fatal(title);
47060996bedSreyk 	}
47160996bedSreyk 
47260996bedSreyk 	for (;;) {
47360996bedSreyk 		if ((n = imsg_get(ibuf, &imsg)) == -1)
47460996bedSreyk 			fatal(title);
47560996bedSreyk 		if (n == 0)
47660996bedSreyk 			break;
47760996bedSreyk 
47821032f8aSreyk #if DEBUG > 1
47921032f8aSreyk 		log_debug("%s: %s %d got imsg %d from %s %d",
48021032f8aSreyk 		    __func__, title, ps->ps_instance + 1,
48121032f8aSreyk 		    imsg.hdr.type, p->p_title, p->p_instance);
48221032f8aSreyk #endif
48321032f8aSreyk 
48460996bedSreyk 		/*
48560996bedSreyk 		 * Check the message with the program callback
48660996bedSreyk 		 */
48760996bedSreyk 		if ((p->p_cb)(fd, p, &imsg) == 0) {
48860996bedSreyk 			/* Message was handled by the callback, continue */
48960996bedSreyk 			imsg_free(&imsg);
49060996bedSreyk 			continue;
49160996bedSreyk 		}
49260996bedSreyk 
49360996bedSreyk 		/*
49460996bedSreyk 		 * Generic message handling
49560996bedSreyk 		 */
49660996bedSreyk 		switch (imsg.hdr.type) {
49760996bedSreyk 		case IMSG_CTL_VERBOSE:
49860996bedSreyk 			IMSG_SIZE_CHECK(&imsg, &verbose);
49960996bedSreyk 			memcpy(&verbose, imsg.data, sizeof(verbose));
50060996bedSreyk 			log_verbose(verbose);
50160996bedSreyk 			break;
50260996bedSreyk 		default:
50321032f8aSreyk 			log_warnx("%s: %s %d got invalid imsg %d from %s %d",
50421032f8aSreyk 			    __func__, title, ps->ps_instance + 1,
50521032f8aSreyk 			    imsg.hdr.type, p->p_title, p->p_instance);
50660996bedSreyk 			fatalx(title);
50760996bedSreyk 		}
50860996bedSreyk 		imsg_free(&imsg);
50960996bedSreyk 	}
51060996bedSreyk 	imsg_event_add(iev);
51160996bedSreyk }
51260996bedSreyk 
51321032f8aSreyk /*
51421032f8aSreyk  * imsg helper functions
51521032f8aSreyk  */
51621032f8aSreyk 
51760996bedSreyk void
51860996bedSreyk imsg_event_add(struct imsgev *iev)
51960996bedSreyk {
52060996bedSreyk 	if (iev->handler == NULL) {
52160996bedSreyk 		imsg_flush(&iev->ibuf);
52260996bedSreyk 		return;
52360996bedSreyk 	}
52460996bedSreyk 
52560996bedSreyk 	iev->events = EV_READ;
52660996bedSreyk 	if (iev->ibuf.w.queued)
52760996bedSreyk 		iev->events |= EV_WRITE;
52860996bedSreyk 
52960996bedSreyk 	event_del(&iev->ev);
53060996bedSreyk 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
53160996bedSreyk 	event_add(&iev->ev, NULL);
53260996bedSreyk }
53360996bedSreyk 
53460996bedSreyk int
535*32c142bfSreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
536*32c142bfSreyk     pid_t pid, int fd, void *data, uint16_t datalen)
53760996bedSreyk {
53860996bedSreyk 	int	ret;
53960996bedSreyk 
54060996bedSreyk 	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
54160996bedSreyk 	    pid, fd, data, datalen)) == -1)
54260996bedSreyk 		return (ret);
54360996bedSreyk 	imsg_event_add(iev);
54460996bedSreyk 	return (ret);
54560996bedSreyk }
54660996bedSreyk 
54760996bedSreyk int
548*32c142bfSreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
54960996bedSreyk     pid_t pid, int fd, const struct iovec *iov, int iovcnt)
55060996bedSreyk {
55160996bedSreyk 	int	ret;
55260996bedSreyk 
55360996bedSreyk 	if ((ret = imsg_composev(&iev->ibuf, type, peerid,
55460996bedSreyk 	    pid, fd, iov, iovcnt)) == -1)
55560996bedSreyk 		return (ret);
55660996bedSreyk 	imsg_event_add(iev);
55760996bedSreyk 	return (ret);
55860996bedSreyk }
55960996bedSreyk 
56021032f8aSreyk void
56121032f8aSreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
56260996bedSreyk {
56321032f8aSreyk 	if (*n == -1) {
56421032f8aSreyk 		/* Use a range of all target instances */
56521032f8aSreyk 		*n = 0;
56621032f8aSreyk 		*m = ps->ps_instances[id];
56721032f8aSreyk 	} else {
56821032f8aSreyk 		/* Use only a single slot of the specified peer process */
56921032f8aSreyk 		*m = *n + 1;
57021032f8aSreyk 	}
57160996bedSreyk }
57260996bedSreyk 
57360996bedSreyk int
57421032f8aSreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
575*32c142bfSreyk     uint16_t type, int fd, void *data, uint16_t datalen)
57621032f8aSreyk {
57721032f8aSreyk 	int	 m;
57821032f8aSreyk 
57921032f8aSreyk 	proc_range(ps, id, &n, &m);
58021032f8aSreyk 	for (; n < m; n++) {
58121032f8aSreyk 		if (imsg_compose_event(&ps->ps_ievs[id][n],
58221032f8aSreyk 		    type, -1, 0, fd, data, datalen) == -1)
58321032f8aSreyk 			return (-1);
58421032f8aSreyk 	}
58521032f8aSreyk 
58621032f8aSreyk 	return (0);
58721032f8aSreyk }
58821032f8aSreyk 
58921032f8aSreyk int
59021032f8aSreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
591*32c142bfSreyk     uint16_t type, int fd, const struct iovec *iov, int iovcnt)
59260996bedSreyk {
59321032f8aSreyk 	int	 m;
59421032f8aSreyk 
59521032f8aSreyk 	proc_range(ps, id, &n, &m);
59621032f8aSreyk 	for (; n < m; n++)
59721032f8aSreyk 		if (imsg_composev_event(&ps->ps_ievs[id][n],
59821032f8aSreyk 		    type, -1, 0, fd, iov, iovcnt) == -1)
59921032f8aSreyk 			return (-1);
60021032f8aSreyk 
60121032f8aSreyk 	return (0);
60260996bedSreyk }
60360996bedSreyk 
60460996bedSreyk int
60560996bedSreyk proc_forward_imsg(struct privsep *ps, struct imsg *imsg,
60621032f8aSreyk     enum privsep_procid id, int n)
60760996bedSreyk {
60821032f8aSreyk 	return (proc_compose_imsg(ps, id, n, imsg->hdr.type,
60960996bedSreyk 	    imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg)));
61060996bedSreyk }
61121032f8aSreyk 
61221032f8aSreyk struct imsgbuf *
61321032f8aSreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
61421032f8aSreyk {
61521032f8aSreyk 	int	 m;
61621032f8aSreyk 
61721032f8aSreyk 	proc_range(ps, id, &n, &m);
61821032f8aSreyk 	return (&ps->ps_ievs[id][n].ibuf);
61921032f8aSreyk }
62021032f8aSreyk 
62121032f8aSreyk struct imsgev *
62221032f8aSreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n)
62321032f8aSreyk {
62421032f8aSreyk 	int	 m;
62521032f8aSreyk 
62621032f8aSreyk 	proc_range(ps, id, &n, &m);
62721032f8aSreyk 	return (&ps->ps_ievs[id][n]);
62821032f8aSreyk }
629