xref: /openbsd/usr.sbin/snmpd/proc.c (revision 5f0d8540)
1*5f0d8540Sreyk /*	$OpenBSD: proc.c,v 1.17 2015/11/23 19:31:52 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 
2560996bedSreyk #include <stdio.h>
2660996bedSreyk #include <stdlib.h>
2760996bedSreyk #include <unistd.h>
2821032f8aSreyk #include <string.h>
2921032f8aSreyk #include <errno.h>
3021032f8aSreyk #include <signal.h>
3160996bedSreyk #include <pwd.h>
3221032f8aSreyk #include <event.h>
330f12961aSreyk #include <imsg.h>
3460996bedSreyk 
3560996bedSreyk #include "snmpd.h"
3660996bedSreyk 
3721032f8aSreyk void	 proc_open(struct privsep *, struct privsep_proc *,
3821032f8aSreyk 	    struct privsep_proc *, size_t);
3921032f8aSreyk void	 proc_close(struct privsep *);
4032c142bfSreyk int	 proc_ispeer(struct privsep_proc *, unsigned int, enum privsep_procid);
4160996bedSreyk void	 proc_shutdown(struct privsep_proc *);
4260996bedSreyk void	 proc_sig_handler(int, short, void *);
4321032f8aSreyk void	 proc_range(struct privsep *, enum privsep_procid, int *, int *);
443f75f466Sreyk int	 proc_dispatch_null(int, struct privsep_proc *, struct imsg *);
4560996bedSreyk 
4621032f8aSreyk int
4732c142bfSreyk proc_ispeer(struct privsep_proc *procs, unsigned int nproc,
4832c142bfSreyk     enum privsep_procid type)
4960996bedSreyk {
5032c142bfSreyk 	unsigned int	i;
5160996bedSreyk 
5221032f8aSreyk 	for (i = 0; i < nproc; i++)
5321032f8aSreyk 		if (procs[i].p_id == type)
5421032f8aSreyk 			return (1);
5521032f8aSreyk 	return (0);
5621032f8aSreyk }
5721032f8aSreyk 
5821032f8aSreyk void
5932c142bfSreyk proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
6021032f8aSreyk {
6132c142bfSreyk 	unsigned int		 i, j, src, dst;
6221032f8aSreyk 	struct privsep_pipes	*pp;
6321032f8aSreyk 
6460996bedSreyk 	/*
6521032f8aSreyk 	 * Allocate pipes for all process instances (incl. parent)
6621032f8aSreyk 	 *
6721032f8aSreyk 	 * - ps->ps_pipes: N:M mapping
6821032f8aSreyk 	 * N source processes connected to M destination processes:
6921032f8aSreyk 	 * [src][instances][dst][instances], for example
7021032f8aSreyk 	 * [PROC_RELAY][3][PROC_CA][3]
7121032f8aSreyk 	 *
7221032f8aSreyk 	 * - ps->ps_pp: per-process 1:M part of ps->ps_pipes
7321032f8aSreyk 	 * Each process instance has a destination array of socketpair fds:
7421032f8aSreyk 	 * [dst][instances], for example
7521032f8aSreyk 	 * [PROC_PARENT][0]
7621032f8aSreyk 	 */
7721032f8aSreyk 	for (src = 0; src < PROC_MAX; src++) {
7821032f8aSreyk 		/* Allocate destination array for each process */
7921032f8aSreyk 		if ((ps->ps_pipes[src] = calloc(ps->ps_ninstances,
8021032f8aSreyk 		    sizeof(struct privsep_pipes))) == NULL)
8121032f8aSreyk 			fatal("proc_init: calloc");
8221032f8aSreyk 
8321032f8aSreyk 		for (i = 0; i < ps->ps_ninstances; i++) {
8421032f8aSreyk 			pp = &ps->ps_pipes[src][i];
8521032f8aSreyk 
8621032f8aSreyk 			for (dst = 0; dst < PROC_MAX; dst++) {
8721032f8aSreyk 				/* Allocate maximum fd integers */
8821032f8aSreyk 				if ((pp->pp_pipes[dst] =
8921032f8aSreyk 				    calloc(ps->ps_ninstances,
9021032f8aSreyk 				    sizeof(int))) == NULL)
9121032f8aSreyk 					fatal("proc_init: calloc");
9221032f8aSreyk 
9321032f8aSreyk 				/* Mark fd as unused */
9421032f8aSreyk 				for (j = 0; j < ps->ps_ninstances; j++)
9521032f8aSreyk 					pp->pp_pipes[dst][j] = -1;
9621032f8aSreyk 			}
9721032f8aSreyk 		}
9821032f8aSreyk 	}
9921032f8aSreyk 
10021032f8aSreyk 	/*
10121032f8aSreyk 	 * Setup and run the parent and its children
10260996bedSreyk 	 */
10360996bedSreyk 	privsep_process = PROC_PARENT;
10421032f8aSreyk 	ps->ps_instances[PROC_PARENT] = 1;
10560996bedSreyk 	ps->ps_title[PROC_PARENT] = "parent";
10660996bedSreyk 	ps->ps_pid[PROC_PARENT] = getpid();
10721032f8aSreyk 	ps->ps_pp = &ps->ps_pipes[privsep_process][0];
10860996bedSreyk 
10921032f8aSreyk 	for (i = 0; i < nproc; i++) {
11021032f8aSreyk 		/* Default to 1 process instance */
11121032f8aSreyk 		if (ps->ps_instances[procs[i].p_id] < 1)
11221032f8aSreyk 			ps->ps_instances[procs[i].p_id] = 1;
11321032f8aSreyk 		ps->ps_title[procs[i].p_id] = procs[i].p_title;
11421032f8aSreyk 	}
11521032f8aSreyk 
11621032f8aSreyk 	proc_open(ps, NULL, procs, nproc);
11760996bedSreyk 
11860996bedSreyk 	/* Engage! */
11921032f8aSreyk 	for (i = 0; i < nproc; i++)
12021032f8aSreyk 		ps->ps_pid[procs[i].p_id] = (*procs[i].p_init)(ps, &procs[i]);
12160996bedSreyk }
12260996bedSreyk 
12360996bedSreyk void
12460996bedSreyk proc_kill(struct privsep *ps)
12560996bedSreyk {
12660996bedSreyk 	pid_t		 pid;
12732c142bfSreyk 	unsigned int	 i;
12860996bedSreyk 
12960996bedSreyk 	if (privsep_process != PROC_PARENT)
13060996bedSreyk 		return;
13160996bedSreyk 
13260996bedSreyk 	for (i = 0; i < PROC_MAX; i++) {
13360996bedSreyk 		if (ps->ps_pid[i] == 0)
13460996bedSreyk 			continue;
13521032f8aSreyk 		killpg(ps->ps_pid[i], SIGTERM);
13660996bedSreyk 	}
13760996bedSreyk 
13860996bedSreyk 	do {
13960996bedSreyk 		pid = waitpid(WAIT_ANY, NULL, 0);
14060996bedSreyk 	} while (pid != -1 || (pid == -1 && errno == EINTR));
14121032f8aSreyk 
14221032f8aSreyk 	proc_close(ps);
14360996bedSreyk }
14460996bedSreyk 
14560996bedSreyk void
14621032f8aSreyk proc_open(struct privsep *ps, struct privsep_proc *p,
14721032f8aSreyk     struct privsep_proc *procs, size_t nproc)
14860996bedSreyk {
14921032f8aSreyk 	struct privsep_pipes	*pa, *pb;
15021032f8aSreyk 	int			 fds[2];
15132c142bfSreyk 	unsigned int		 i, j, src, proc;
15260996bedSreyk 
15321032f8aSreyk 	if (p == NULL)
15421032f8aSreyk 		src = privsep_process; /* parent */
15521032f8aSreyk 	else
15621032f8aSreyk 		src = p->p_id;
15721032f8aSreyk 
15821032f8aSreyk 	/*
15921032f8aSreyk 	 * Open socket pairs for our peers
16021032f8aSreyk 	 */
16121032f8aSreyk 	for (proc = 0; proc < nproc; proc++) {
16221032f8aSreyk 		procs[proc].p_ps = ps;
16321032f8aSreyk 		procs[proc].p_env = ps->ps_env;
1643f75f466Sreyk 		if (procs[proc].p_cb == NULL)
1653f75f466Sreyk 			procs[proc].p_cb = proc_dispatch_null;
16621032f8aSreyk 
16721032f8aSreyk 		for (i = 0; i < ps->ps_instances[src]; i++) {
16821032f8aSreyk 			for (j = 0; j < ps->ps_instances[procs[proc].p_id];
16921032f8aSreyk 			    j++) {
17021032f8aSreyk 				pa = &ps->ps_pipes[src][i];
17121032f8aSreyk 				pb = &ps->ps_pipes[procs[proc].p_id][j];
17221032f8aSreyk 
17321032f8aSreyk 				/* Check if fds are already set by peer */
17421032f8aSreyk 				if (pa->pp_pipes[procs[proc].p_id][j] != -1)
17521032f8aSreyk 					continue;
17621032f8aSreyk 
177*5f0d8540Sreyk 				if (socketpair(AF_UNIX,
178*5f0d8540Sreyk 				    SOCK_STREAM | SOCK_NONBLOCK,
17921032f8aSreyk 				    PF_UNSPEC, fds) == -1)
18021032f8aSreyk 					fatal("socketpair");
18121032f8aSreyk 
18221032f8aSreyk 				pa->pp_pipes[procs[proc].p_id][j] = fds[0];
18321032f8aSreyk 				pb->pp_pipes[src][i] = fds[1];
18421032f8aSreyk 			}
18521032f8aSreyk 		}
18660996bedSreyk 	}
18760996bedSreyk }
18860996bedSreyk 
18960996bedSreyk void
19021032f8aSreyk proc_listen(struct privsep *ps, struct privsep_proc *procs, size_t nproc)
19160996bedSreyk {
19232c142bfSreyk 	unsigned int		 i, dst, src, n, m;
19321032f8aSreyk 	struct privsep_pipes	*pp;
19421032f8aSreyk 
19521032f8aSreyk 	/*
19621032f8aSreyk 	 * Close unused pipes
19721032f8aSreyk 	 */
19821032f8aSreyk 	for (src = 0; src < PROC_MAX; src++) {
19921032f8aSreyk 		for (n = 0; n < ps->ps_instances[src]; n++) {
20021032f8aSreyk 			/* Ingore current process */
20132c142bfSreyk 			if (src == (unsigned int)privsep_process &&
20221032f8aSreyk 			    n == ps->ps_instance)
20321032f8aSreyk 				continue;
20421032f8aSreyk 
20521032f8aSreyk 			pp = &ps->ps_pipes[src][n];
20621032f8aSreyk 
20721032f8aSreyk 			for (dst = 0; dst < PROC_MAX; dst++) {
20821032f8aSreyk 				if (src == dst)
20921032f8aSreyk 					continue;
21021032f8aSreyk 				for (m = 0; m < ps->ps_instances[dst]; m++) {
21121032f8aSreyk 					if (pp->pp_pipes[dst][m] == -1)
21221032f8aSreyk 						continue;
21321032f8aSreyk 
21421032f8aSreyk 					/* Close and invalidate fd */
21521032f8aSreyk 					close(pp->pp_pipes[dst][m]);
21621032f8aSreyk 					pp->pp_pipes[dst][m] = -1;
21721032f8aSreyk 				}
21821032f8aSreyk 			}
21921032f8aSreyk 		}
22021032f8aSreyk 	}
22160996bedSreyk 
22260996bedSreyk 	src = privsep_process;
22321032f8aSreyk 	ps->ps_pp = pp = &ps->ps_pipes[src][ps->ps_instance];
22460996bedSreyk 
22560996bedSreyk 	/*
22621032f8aSreyk 	 * Listen on appropriate pipes
22760996bedSreyk 	 */
22821032f8aSreyk 	for (i = 0; i < nproc; i++) {
22921032f8aSreyk 		dst = procs[i].p_id;
23021032f8aSreyk 
23121032f8aSreyk 		if (src == dst)
23221032f8aSreyk 			fatal("proc_listen: cannot peer with oneself");
23321032f8aSreyk 
23421032f8aSreyk 		if ((ps->ps_ievs[dst] = calloc(ps->ps_instances[dst],
23521032f8aSreyk 		    sizeof(struct imsgev))) == NULL)
23621032f8aSreyk 			fatal("proc_open");
23721032f8aSreyk 
23821032f8aSreyk 		for (n = 0; n < ps->ps_instances[dst]; n++) {
23921032f8aSreyk 			if (pp->pp_pipes[dst][n] == -1)
24021032f8aSreyk 				continue;
24121032f8aSreyk 
24221032f8aSreyk 			imsg_init(&(ps->ps_ievs[dst][n].ibuf),
24321032f8aSreyk 			    pp->pp_pipes[dst][n]);
24421032f8aSreyk 			ps->ps_ievs[dst][n].handler = proc_dispatch;
24521032f8aSreyk 			ps->ps_ievs[dst][n].events = EV_READ;
24621032f8aSreyk 			ps->ps_ievs[dst][n].proc = &procs[i];
24721032f8aSreyk 			ps->ps_ievs[dst][n].data = &ps->ps_ievs[dst][n];
24821032f8aSreyk 			procs[i].p_instance = n;
24921032f8aSreyk 
25021032f8aSreyk 			event_set(&(ps->ps_ievs[dst][n].ev),
25121032f8aSreyk 			    ps->ps_ievs[dst][n].ibuf.fd,
25221032f8aSreyk 			    ps->ps_ievs[dst][n].events,
25321032f8aSreyk 			    ps->ps_ievs[dst][n].handler,
25421032f8aSreyk 			    ps->ps_ievs[dst][n].data);
25521032f8aSreyk 			event_add(&(ps->ps_ievs[dst][n].ev), NULL);
25660996bedSreyk 		}
25760996bedSreyk 	}
25860996bedSreyk }
25960996bedSreyk 
26021032f8aSreyk void
26121032f8aSreyk proc_close(struct privsep *ps)
26221032f8aSreyk {
26332c142bfSreyk 	unsigned int		 dst, n;
26421032f8aSreyk 	struct privsep_pipes	*pp;
26560996bedSreyk 
26621032f8aSreyk 	if (ps == NULL)
26721032f8aSreyk 		return;
26821032f8aSreyk 
26921032f8aSreyk 	pp = ps->ps_pp;
27021032f8aSreyk 
27121032f8aSreyk 	for (dst = 0; dst < PROC_MAX; dst++) {
27221032f8aSreyk 		if (ps->ps_ievs[dst] == NULL)
27321032f8aSreyk 			continue;
27421032f8aSreyk 
27521032f8aSreyk 		for (n = 0; n < ps->ps_instances[dst]; n++) {
27621032f8aSreyk 			if (pp->pp_pipes[dst][n] == -1)
27721032f8aSreyk 				continue;
27821032f8aSreyk 
27921032f8aSreyk 			/* Cancel the fd, close and invalidate the fd */
28021032f8aSreyk 			event_del(&(ps->ps_ievs[dst][n].ev));
28121032f8aSreyk 			imsg_clear(&(ps->ps_ievs[dst][n].ibuf));
28221032f8aSreyk 			close(pp->pp_pipes[dst][n]);
28321032f8aSreyk 			pp->pp_pipes[dst][n] = -1;
28421032f8aSreyk 		}
28521032f8aSreyk 		free(ps->ps_ievs[dst]);
28660996bedSreyk 	}
28760996bedSreyk }
28860996bedSreyk 
28960996bedSreyk void
29060996bedSreyk proc_shutdown(struct privsep_proc *p)
29160996bedSreyk {
29260996bedSreyk 	struct privsep	*ps = p->p_ps;
29321032f8aSreyk 
29421032f8aSreyk 	if (p->p_id == PROC_CONTROL && ps)
29521032f8aSreyk 		control_cleanup(&ps->ps_csock);
29660996bedSreyk 
29760996bedSreyk 	if (p->p_shutdown != NULL)
29821032f8aSreyk 		(*p->p_shutdown)();
29960996bedSreyk 
30021032f8aSreyk 	proc_close(ps);
30160996bedSreyk 
30221032f8aSreyk 	log_info("%s exiting, pid %d", p->p_title, getpid());
30321032f8aSreyk 
30460996bedSreyk 	_exit(0);
30560996bedSreyk }
30660996bedSreyk 
30760996bedSreyk void
30860996bedSreyk proc_sig_handler(int sig, short event, void *arg)
30960996bedSreyk {
31060996bedSreyk 	struct privsep_proc	*p = arg;
31160996bedSreyk 
31260996bedSreyk 	switch (sig) {
31360996bedSreyk 	case SIGINT:
31460996bedSreyk 	case SIGTERM:
31560996bedSreyk 		proc_shutdown(p);
31660996bedSreyk 		break;
31760996bedSreyk 	case SIGCHLD:
31860996bedSreyk 	case SIGHUP:
31960996bedSreyk 	case SIGPIPE:
3205f2be52bSreyk 	case SIGUSR1:
32160996bedSreyk 		/* ignore */
32260996bedSreyk 		break;
32360996bedSreyk 	default:
32460996bedSreyk 		fatalx("proc_sig_handler: unexpected signal");
32560996bedSreyk 		/* NOTREACHED */
32660996bedSreyk 	}
32760996bedSreyk }
32860996bedSreyk 
32960996bedSreyk pid_t
33060996bedSreyk proc_run(struct privsep *ps, struct privsep_proc *p,
33132c142bfSreyk     struct privsep_proc *procs, unsigned int nproc,
3323f75f466Sreyk     void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg)
33360996bedSreyk {
33460996bedSreyk 	pid_t			 pid;
33560996bedSreyk 	struct passwd		*pw;
33660996bedSreyk 	const char		*root;
3371dbe1dbcSblambert 	struct control_sock	*rcs;
33832c142bfSreyk 	unsigned int		 n;
33960996bedSreyk 
34021032f8aSreyk 	if (ps->ps_noaction)
34121032f8aSreyk 		return (0);
34221032f8aSreyk 
34321032f8aSreyk 	proc_open(ps, p, procs, nproc);
34421032f8aSreyk 
34521032f8aSreyk 	/* Fork child handlers */
34660996bedSreyk 	switch (pid = fork()) {
34760996bedSreyk 	case -1:
34860996bedSreyk 		fatal("proc_run: cannot fork");
34960996bedSreyk 	case 0:
3500f12961aSreyk 		log_procinit(p->p_title);
3510f12961aSreyk 
35221032f8aSreyk 		/* Set the process group of the current process */
3532fc3edb7Smillert 		setpgid(0, 0);
35460996bedSreyk 		break;
35560996bedSreyk 	default:
35660996bedSreyk 		return (pid);
35760996bedSreyk 	}
35860996bedSreyk 
35960996bedSreyk 	pw = ps->ps_pw;
36060996bedSreyk 
36121032f8aSreyk 	if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
36260996bedSreyk 		if (control_init(ps, &ps->ps_csock) == -1)
3630f12961aSreyk 			fatalx(__func__);
3641dbe1dbcSblambert 		TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
3651dbe1dbcSblambert 			if (control_init(ps, rcs) == -1)
3660f12961aSreyk 				fatalx(__func__);
36760996bedSreyk 	}
36860996bedSreyk 
36960996bedSreyk 	/* Change root directory */
37060996bedSreyk 	if (p->p_chroot != NULL)
37160996bedSreyk 		root = p->p_chroot;
37260996bedSreyk 	else
37360996bedSreyk 		root = pw->pw_dir;
37460996bedSreyk 
37560996bedSreyk 	if (chroot(root) == -1)
37660996bedSreyk 		fatal("proc_run: chroot");
37760996bedSreyk 	if (chdir("/") == -1)
37860996bedSreyk 		fatal("proc_run: chdir(\"/\")");
37960996bedSreyk 
38060996bedSreyk 	privsep_process = p->p_id;
38160996bedSreyk 
38260996bedSreyk 	setproctitle("%s", p->p_title);
38360996bedSreyk 
38460996bedSreyk 	if (setgroups(1, &pw->pw_gid) ||
38560996bedSreyk 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
38660996bedSreyk 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
38760996bedSreyk 		fatal("proc_run: cannot drop privileges");
38860996bedSreyk 
38921032f8aSreyk 	/* Fork child handlers */
39021032f8aSreyk 	for (n = 1; n < ps->ps_instances[p->p_id]; n++) {
39121032f8aSreyk 		if (fork() == 0) {
39221032f8aSreyk 			ps->ps_instance = p->p_instance = n;
39321032f8aSreyk 			break;
39421032f8aSreyk 		}
39521032f8aSreyk 	}
39621032f8aSreyk 
39721032f8aSreyk #ifdef DEBUG
39821032f8aSreyk 	log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
39921032f8aSreyk 	    ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
40021032f8aSreyk #endif
40121032f8aSreyk 
40260996bedSreyk 	event_init();
40360996bedSreyk 
40460996bedSreyk 	signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
40560996bedSreyk 	signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
40660996bedSreyk 	signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
40760996bedSreyk 	signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
40860996bedSreyk 	signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p);
4095f2be52bSreyk 	signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p);
41060996bedSreyk 
41160996bedSreyk 	signal_add(&ps->ps_evsigint, NULL);
41260996bedSreyk 	signal_add(&ps->ps_evsigterm, NULL);
41360996bedSreyk 	signal_add(&ps->ps_evsigchld, NULL);
41460996bedSreyk 	signal_add(&ps->ps_evsighup, NULL);
41560996bedSreyk 	signal_add(&ps->ps_evsigpipe, NULL);
4165f2be52bSreyk 	signal_add(&ps->ps_evsigusr1, NULL);
41760996bedSreyk 
41821032f8aSreyk 	proc_listen(ps, procs, nproc);
41960996bedSreyk 
42021032f8aSreyk 	if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
42160996bedSreyk 		TAILQ_INIT(&ctl_conns);
42260996bedSreyk 		if (control_listen(&ps->ps_csock) == -1)
4230f12961aSreyk 			fatalx(__func__);
4241dbe1dbcSblambert 		TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
4251dbe1dbcSblambert 			if (control_listen(rcs) == -1)
4260f12961aSreyk 				fatalx(__func__);
42760996bedSreyk 	}
42860996bedSreyk 
4293f75f466Sreyk 	if (run != NULL)
4303f75f466Sreyk 		run(ps, p, arg);
43160996bedSreyk 
43260996bedSreyk 	event_dispatch();
43360996bedSreyk 
43460996bedSreyk 	proc_shutdown(p);
43560996bedSreyk 
43660996bedSreyk 	return (0);
43760996bedSreyk }
43860996bedSreyk 
43960996bedSreyk void
44060996bedSreyk proc_dispatch(int fd, short event, void *arg)
44160996bedSreyk {
44221032f8aSreyk 	struct imsgev		*iev = arg;
44321032f8aSreyk 	struct privsep_proc	*p = iev->proc;
44460996bedSreyk 	struct privsep		*ps = p->p_ps;
44560996bedSreyk 	struct imsgbuf		*ibuf;
44660996bedSreyk 	struct imsg		 imsg;
44760996bedSreyk 	ssize_t			 n;
44860996bedSreyk 	int			 verbose;
44960996bedSreyk 	const char		*title;
45060996bedSreyk 
45160996bedSreyk 	title = ps->ps_title[privsep_process];
45260996bedSreyk 	ibuf = &iev->ibuf;
45360996bedSreyk 
45460996bedSreyk 	if (event & EV_READ) {
45560996bedSreyk 		if ((n = imsg_read(ibuf)) == -1)
4560f12961aSreyk 			fatal(__func__);
45760996bedSreyk 		if (n == 0) {
45860996bedSreyk 			/* this pipe is dead, so remove the event handler */
45960996bedSreyk 			event_del(&iev->ev);
46060996bedSreyk 			event_loopexit(NULL);
46160996bedSreyk 			return;
46260996bedSreyk 		}
46360996bedSreyk 	}
46460996bedSreyk 
46560996bedSreyk 	if (event & EV_WRITE) {
4662b721f23Sreyk 		if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
4670f12961aSreyk 			fatal(__func__);
46860996bedSreyk 	}
46960996bedSreyk 
47060996bedSreyk 	for (;;) {
47160996bedSreyk 		if ((n = imsg_get(ibuf, &imsg)) == -1)
4720f12961aSreyk 			fatal(__func__);
47360996bedSreyk 		if (n == 0)
47460996bedSreyk 			break;
47560996bedSreyk 
47621032f8aSreyk #if DEBUG > 1
47721032f8aSreyk 		log_debug("%s: %s %d got imsg %d from %s %d",
47821032f8aSreyk 		    __func__, title, ps->ps_instance + 1,
47921032f8aSreyk 		    imsg.hdr.type, p->p_title, p->p_instance);
48021032f8aSreyk #endif
48121032f8aSreyk 
48260996bedSreyk 		/*
48360996bedSreyk 		 * Check the message with the program callback
48460996bedSreyk 		 */
48560996bedSreyk 		if ((p->p_cb)(fd, p, &imsg) == 0) {
48660996bedSreyk 			/* Message was handled by the callback, continue */
48760996bedSreyk 			imsg_free(&imsg);
48860996bedSreyk 			continue;
48960996bedSreyk 		}
49060996bedSreyk 
49160996bedSreyk 		/*
49260996bedSreyk 		 * Generic message handling
49360996bedSreyk 		 */
49460996bedSreyk 		switch (imsg.hdr.type) {
49560996bedSreyk 		case IMSG_CTL_VERBOSE:
49660996bedSreyk 			IMSG_SIZE_CHECK(&imsg, &verbose);
49760996bedSreyk 			memcpy(&verbose, imsg.data, sizeof(verbose));
49860996bedSreyk 			log_verbose(verbose);
49960996bedSreyk 			break;
50060996bedSreyk 		default:
50121032f8aSreyk 			log_warnx("%s: %s %d got invalid imsg %d from %s %d",
50221032f8aSreyk 			    __func__, title, ps->ps_instance + 1,
50321032f8aSreyk 			    imsg.hdr.type, p->p_title, p->p_instance);
5040f12961aSreyk 			fatalx(__func__);
50560996bedSreyk 		}
50660996bedSreyk 		imsg_free(&imsg);
50760996bedSreyk 	}
50860996bedSreyk 	imsg_event_add(iev);
50960996bedSreyk }
51060996bedSreyk 
5113f75f466Sreyk int
5123f75f466Sreyk proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg)
5133f75f466Sreyk {
5143f75f466Sreyk 	return (-1);
5153f75f466Sreyk }
5163f75f466Sreyk 
51721032f8aSreyk /*
51821032f8aSreyk  * imsg helper functions
51921032f8aSreyk  */
52021032f8aSreyk 
52160996bedSreyk void
52260996bedSreyk imsg_event_add(struct imsgev *iev)
52360996bedSreyk {
52460996bedSreyk 	if (iev->handler == NULL) {
52560996bedSreyk 		imsg_flush(&iev->ibuf);
52660996bedSreyk 		return;
52760996bedSreyk 	}
52860996bedSreyk 
52960996bedSreyk 	iev->events = EV_READ;
53060996bedSreyk 	if (iev->ibuf.w.queued)
53160996bedSreyk 		iev->events |= EV_WRITE;
53260996bedSreyk 
53360996bedSreyk 	event_del(&iev->ev);
53460996bedSreyk 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
53560996bedSreyk 	event_add(&iev->ev, NULL);
53660996bedSreyk }
53760996bedSreyk 
53860996bedSreyk int
53932c142bfSreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
54032c142bfSreyk     pid_t pid, int fd, void *data, uint16_t datalen)
54160996bedSreyk {
54260996bedSreyk 	int	ret;
54360996bedSreyk 
54460996bedSreyk 	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
54560996bedSreyk 	    pid, fd, data, datalen)) == -1)
54660996bedSreyk 		return (ret);
54760996bedSreyk 	imsg_event_add(iev);
54860996bedSreyk 	return (ret);
54960996bedSreyk }
55060996bedSreyk 
55160996bedSreyk int
55232c142bfSreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
55360996bedSreyk     pid_t pid, int fd, const struct iovec *iov, int iovcnt)
55460996bedSreyk {
55560996bedSreyk 	int	ret;
55660996bedSreyk 
55760996bedSreyk 	if ((ret = imsg_composev(&iev->ibuf, type, peerid,
55860996bedSreyk 	    pid, fd, iov, iovcnt)) == -1)
55960996bedSreyk 		return (ret);
56060996bedSreyk 	imsg_event_add(iev);
56160996bedSreyk 	return (ret);
56260996bedSreyk }
56360996bedSreyk 
56421032f8aSreyk void
56521032f8aSreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
56660996bedSreyk {
56721032f8aSreyk 	if (*n == -1) {
56821032f8aSreyk 		/* Use a range of all target instances */
56921032f8aSreyk 		*n = 0;
57021032f8aSreyk 		*m = ps->ps_instances[id];
57121032f8aSreyk 	} else {
57221032f8aSreyk 		/* Use only a single slot of the specified peer process */
57321032f8aSreyk 		*m = *n + 1;
57421032f8aSreyk 	}
57560996bedSreyk }
57660996bedSreyk 
57760996bedSreyk int
57821032f8aSreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
57932c142bfSreyk     uint16_t type, int fd, void *data, uint16_t datalen)
58021032f8aSreyk {
58121032f8aSreyk 	int	 m;
58221032f8aSreyk 
58321032f8aSreyk 	proc_range(ps, id, &n, &m);
58421032f8aSreyk 	for (; n < m; n++) {
58521032f8aSreyk 		if (imsg_compose_event(&ps->ps_ievs[id][n],
58621032f8aSreyk 		    type, -1, 0, fd, data, datalen) == -1)
58721032f8aSreyk 			return (-1);
58821032f8aSreyk 	}
58921032f8aSreyk 
59021032f8aSreyk 	return (0);
59121032f8aSreyk }
59221032f8aSreyk 
59321032f8aSreyk int
59421032f8aSreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
59532c142bfSreyk     uint16_t type, int fd, const struct iovec *iov, int iovcnt)
59660996bedSreyk {
59721032f8aSreyk 	int	 m;
59821032f8aSreyk 
59921032f8aSreyk 	proc_range(ps, id, &n, &m);
60021032f8aSreyk 	for (; n < m; n++)
60121032f8aSreyk 		if (imsg_composev_event(&ps->ps_ievs[id][n],
60221032f8aSreyk 		    type, -1, 0, fd, iov, iovcnt) == -1)
60321032f8aSreyk 			return (-1);
60421032f8aSreyk 
60521032f8aSreyk 	return (0);
60660996bedSreyk }
60760996bedSreyk 
60860996bedSreyk int
60960996bedSreyk proc_forward_imsg(struct privsep *ps, struct imsg *imsg,
61021032f8aSreyk     enum privsep_procid id, int n)
61160996bedSreyk {
61221032f8aSreyk 	return (proc_compose_imsg(ps, id, n, imsg->hdr.type,
61360996bedSreyk 	    imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg)));
61460996bedSreyk }
61521032f8aSreyk 
61621032f8aSreyk struct imsgbuf *
61721032f8aSreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
61821032f8aSreyk {
61921032f8aSreyk 	int	 m;
62021032f8aSreyk 
62121032f8aSreyk 	proc_range(ps, id, &n, &m);
62221032f8aSreyk 	return (&ps->ps_ievs[id][n].ibuf);
62321032f8aSreyk }
62421032f8aSreyk 
62521032f8aSreyk struct imsgev *
62621032f8aSreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n)
62721032f8aSreyk {
62821032f8aSreyk 	int	 m;
62921032f8aSreyk 
63021032f8aSreyk 	proc_range(ps, id, &n, &m);
63121032f8aSreyk 	return (&ps->ps_ievs[id][n]);
63221032f8aSreyk }
633