xref: /openbsd-src/usr.sbin/vmd/proc.c (revision 0a9d031fce78c0ebce0995b311938b1c87b1e208)
1 /*	$OpenBSD: proc.c,v 1.33 2024/11/21 13:39:34 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2010 - 2016 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/queue.h>
22 #include <sys/socket.h>
23 #include <sys/wait.h>
24 
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <signal.h>
32 #include <pwd.h>
33 #include <event.h>
34 #include <imsg.h>
35 
36 #include "proc.h"
37 
38 void	 proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int,
39 	    char **);
40 void	 proc_setup(struct privsep *, struct privsep_proc *, unsigned int);
41 void	 proc_open(struct privsep *, int, int);
42 void	 proc_accept(struct privsep *, int, enum privsep_procid,
43 	    unsigned int);
44 void	 proc_close(struct privsep *);
45 void	 proc_shutdown(struct privsep_proc *);
46 void	 proc_sig_handler(int, short, void *);
47 void	 proc_range(struct privsep *, enum privsep_procid, int *, int *);
48 int	 proc_dispatch_null(int, struct privsep_proc *, struct imsg *);
49 
50 enum privsep_procid
51 proc_getid(struct privsep_proc *procs, unsigned int nproc,
52     const char *proc_name)
53 {
54 	struct privsep_proc	*p;
55 	unsigned int		 proc;
56 
57 	for (proc = 0; proc < nproc; proc++) {
58 		p = &procs[proc];
59 		if (strcmp(p->p_title, proc_name))
60 			continue;
61 
62 		return (p->p_id);
63 	}
64 
65 	return (PROC_MAX);
66 }
67 
68 void
69 proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
70     int argc, char **argv)
71 {
72 	unsigned int		 proc, nargc, i, proc_i;
73 	char			**nargv;
74 	struct privsep_proc	*p;
75 	char			 num[32];
76 	int			 fd;
77 
78 	/* Prepare the new process argv. */
79 	nargv = calloc(argc + 5, sizeof(char *));
80 	if (nargv == NULL)
81 		fatal("%s: calloc", __func__);
82 
83 	/* Copy call argument first. */
84 	nargc = 0;
85 	nargv[nargc++] = argv[0];
86 
87 	/* Set process name argument and save the position. */
88 	nargv[nargc++] = "-P";
89 	proc_i = nargc;
90 	nargc++;
91 
92 	/* Point process instance arg to stack and copy the original args. */
93 	nargv[nargc++] = "-I";
94 	nargv[nargc++] = num;
95 	for (i = 1; i < (unsigned int) argc; i++)
96 		nargv[nargc++] = argv[i];
97 
98 	nargv[nargc] = NULL;
99 
100 	for (proc = 0; proc < nproc; proc++) {
101 		p = &procs[proc];
102 
103 		/* Update args with process title. */
104 		nargv[proc_i] = (char *)(uintptr_t)p->p_title;
105 
106 		/* Fire children processes. */
107 		for (i = 0; i < ps->ps_instances[p->p_id]; i++) {
108 			/* Update the process instance number. */
109 			snprintf(num, sizeof(num), "%u", i);
110 
111 			fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0];
112 			ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1;
113 
114 			switch (fork()) {
115 			case -1:
116 				fatal("%s: fork", __func__);
117 				break;
118 			case 0:
119 				/* Prepare parent socket. */
120 				if (fd != PROC_PARENT_SOCK_FILENO) {
121 					if (dup2(fd, PROC_PARENT_SOCK_FILENO)
122 					    == -1)
123 						fatal("dup2");
124 				} else if (fcntl(fd, F_SETFD, 0) == -1)
125 					fatal("fcntl");
126 
127 				execvp(argv[0], nargv);
128 				fatal("%s: execvp", __func__);
129 				break;
130 			default:
131 				/* Close child end. */
132 				close(fd);
133 				break;
134 			}
135 		}
136 	}
137 	free(nargv);
138 }
139 
140 void
141 proc_connect(struct privsep *ps)
142 {
143 	struct imsgev		*iev;
144 	unsigned int		 src, dst, inst;
145 
146 	/* Don't distribute any sockets if we are not really going to run. */
147 	if (ps->ps_noaction)
148 		return;
149 
150 	for (dst = 0; dst < PROC_MAX; dst++) {
151 		/* We don't communicate with ourselves. */
152 		if (dst == PROC_PARENT)
153 			continue;
154 
155 		for (inst = 0; inst < ps->ps_instances[dst]; inst++) {
156 			iev = &ps->ps_ievs[dst][inst];
157 			if (imsgbuf_init(&iev->ibuf,
158 			    ps->ps_pp->pp_pipes[dst][inst]) == -1)
159 				fatal("imsgbuf_init");
160 			imsgbuf_allow_fdpass(&iev->ibuf);
161 			event_set(&iev->ev, iev->ibuf.fd, iev->events,
162 			    iev->handler, iev->data);
163 			event_add(&iev->ev, NULL);
164 		}
165 	}
166 
167 	/* Distribute the socketpair()s for everyone. */
168 	for (src = 0; src < PROC_MAX; src++)
169 		for (dst = src; dst < PROC_MAX; dst++) {
170 			/* Parent already distributed its fds. */
171 			if (src == PROC_PARENT || dst == PROC_PARENT)
172 				continue;
173 
174 			proc_open(ps, src, dst);
175 		}
176 }
177 
178 void
179 proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
180     int debug, int argc, char **argv, enum privsep_procid proc_id)
181 {
182 	struct privsep_proc	*p = NULL;
183 	struct privsep_pipes	*pa, *pb;
184 	unsigned int		 proc;
185 	unsigned int		 dst;
186 	int			 fds[2];
187 
188 	/* Don't initiate anything if we are not really going to run. */
189 	if (ps->ps_noaction)
190 		return;
191 
192 	if (proc_id == PROC_PARENT) {
193 		privsep_process = PROC_PARENT;
194 		proc_setup(ps, procs, nproc);
195 
196 		if (!debug && daemon(0, 0) == -1)
197 			fatal("failed to daemonize");
198 
199 		/*
200 		 * Create the children sockets so we can use them
201 		 * to distribute the rest of the socketpair()s using
202 		 * proc_connect() later.
203 		 */
204 		for (dst = 0; dst < PROC_MAX; dst++) {
205 			/* Don't create socket for ourselves. */
206 			if (dst == PROC_PARENT)
207 				continue;
208 
209 			for (proc = 0; proc < ps->ps_instances[dst]; proc++) {
210 				pa = &ps->ps_pipes[PROC_PARENT][0];
211 				pb = &ps->ps_pipes[dst][proc];
212 				if (socketpair(AF_UNIX,
213 				    SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
214 				    PF_UNSPEC, fds) == -1)
215 					fatal("%s: socketpair", __func__);
216 
217 				pa->pp_pipes[dst][proc] = fds[0];
218 				pb->pp_pipes[PROC_PARENT][0] = fds[1];
219 			}
220 		}
221 
222 		/* Engage! */
223 		proc_exec(ps, procs, nproc, argc, argv);
224 		return;
225 	}
226 
227 	/* Initialize a child */
228 	for (proc = 0; proc < nproc; proc++) {
229 		if (procs[proc].p_id != proc_id)
230 			continue;
231 		p = &procs[proc];
232 		break;
233 	}
234 	if (p == NULL || p->p_init == NULL)
235 		fatalx("%s: process %d missing process initialization",
236 		    __func__, proc_id);
237 
238 	p->p_init(ps, p);
239 
240 	fatalx("failed to initiate child process");
241 }
242 
243 void
244 proc_accept(struct privsep *ps, int fd, enum privsep_procid dst,
245     unsigned int n)
246 {
247 	struct privsep_pipes	*pp = ps->ps_pp;
248 	struct imsgev		*iev;
249 
250 	if (ps->ps_ievs[dst] == NULL) {
251 #if DEBUG > 1
252 		log_debug("%s: %s src %d %d to dst %d %d not connected",
253 		    __func__, ps->ps_title[privsep_process],
254 		    privsep_process, ps->ps_instance + 1,
255 		    dst, n + 1);
256 #endif
257 		close(fd);
258 		return;
259 	}
260 
261 	if (pp->pp_pipes[dst][n] != -1) {
262 		log_warnx("%s: duplicated descriptor", __func__);
263 		close(fd);
264 		return;
265 	} else
266 		pp->pp_pipes[dst][n] = fd;
267 
268 	iev = &ps->ps_ievs[dst][n];
269 	if (imsgbuf_init(&iev->ibuf, fd) == -1)
270 		fatal("imsgbuf_init");
271 	imsgbuf_allow_fdpass(&iev->ibuf);
272 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
273 	event_add(&iev->ev, NULL);
274 }
275 
276 void
277 proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
278 {
279 	unsigned int		 i, j, src, dst, id;
280 	struct privsep_pipes	*pp;
281 
282 	/* Initialize parent title, ps_instances and procs. */
283 	ps->ps_title[PROC_PARENT] = "vmd";
284 
285 	for (src = 0; src < PROC_MAX; src++)
286 		/* Default to 1 process instance */
287 		if (ps->ps_instances[src] < 1)
288 			ps->ps_instances[src] = 1;
289 
290 	for (src = 0; src < nproc; src++) {
291 		procs[src].p_ps = ps;
292 		if (procs[src].p_cb == NULL)
293 			procs[src].p_cb = proc_dispatch_null;
294 
295 		id = procs[src].p_id;
296 		ps->ps_title[id] = procs[src].p_title;
297 		if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id],
298 		    sizeof(struct imsgev))) == NULL)
299 			fatal("%s: calloc", __func__);
300 
301 		/* With this set up, we are ready to call imsgbuf_init(). */
302 		for (i = 0; i < ps->ps_instances[id]; i++) {
303 			ps->ps_ievs[id][i].handler = proc_dispatch;
304 			ps->ps_ievs[id][i].events = EV_READ;
305 			ps->ps_ievs[id][i].proc = &procs[src];
306 			ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i];
307 		}
308 	}
309 
310 	/*
311 	 * Allocate pipes for all process instances (incl. parent)
312 	 *
313 	 * - ps->ps_pipes: N:M mapping
314 	 * N source processes connected to M destination processes:
315 	 * [src][instances][dst][instances], for example
316 	 * [PROC_RELAY][3][PROC_CA][3]
317 	 *
318 	 * - ps->ps_pp: per-process 1:M part of ps->ps_pipes
319 	 * Each process instance has a destination array of socketpair fds:
320 	 * [dst][instances], for example
321 	 * [PROC_PARENT][0]
322 	 */
323 	for (src = 0; src < PROC_MAX; src++) {
324 		/* Allocate destination array for each process */
325 		if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src],
326 		    sizeof(struct privsep_pipes))) == NULL)
327 			fatal("%s: calloc", __func__);
328 
329 		for (i = 0; i < ps->ps_instances[src]; i++) {
330 			pp = &ps->ps_pipes[src][i];
331 
332 			for (dst = 0; dst < PROC_MAX; dst++) {
333 				/* Allocate maximum fd integers */
334 				if ((pp->pp_pipes[dst] =
335 				    calloc(ps->ps_instances[dst],
336 				    sizeof(int))) == NULL)
337 					fatal("%s: calloc", __func__);
338 
339 				/* Mark fd as unused */
340 				for (j = 0; j < ps->ps_instances[dst]; j++)
341 					pp->pp_pipes[dst][j] = -1;
342 			}
343 		}
344 	}
345 
346 	ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance];
347 }
348 
349 void
350 proc_kill(struct privsep *ps)
351 {
352 	char		*cause;
353 	pid_t		 pid;
354 	int		 len, status;
355 
356 	if (privsep_process != PROC_PARENT)
357 		return;
358 
359 	proc_close(ps);
360 
361 	do {
362 		pid = waitpid(WAIT_ANY, &status, 0);
363 		if (pid <= 0)
364 			continue;
365 
366 		if (WIFSIGNALED(status)) {
367 			len = asprintf(&cause, "terminated; signal %d",
368 			    WTERMSIG(status));
369 		} else if (WIFEXITED(status)) {
370 			if (WEXITSTATUS(status) != 0)
371 				len = asprintf(&cause, "exited abnormally");
372 			else
373 				len = 0;
374 		} else
375 			len = -1;
376 
377 		if (len == 0) {
378 			/* child exited OK, don't print a warning message */
379 		} else if (len != -1) {
380 			log_warnx("lost child: pid %u %s", pid, cause);
381 			free(cause);
382 		} else
383 			log_warnx("lost child: pid %u", pid);
384 	} while (pid != -1 || (pid == -1 && errno == EINTR));
385 }
386 
387 void
388 proc_open(struct privsep *ps, int src, int dst)
389 {
390 	struct privsep_pipes	*pa, *pb;
391 	struct privsep_fd	 pf;
392 	int			 fds[2];
393 	unsigned int		 i, j;
394 
395 	/* Exchange pipes between process. */
396 	for (i = 0; i < ps->ps_instances[src]; i++) {
397 		for (j = 0; j < ps->ps_instances[dst]; j++) {
398 			/* Don't create sockets for ourself. */
399 			if (src == dst && i == j)
400 				continue;
401 
402 			pa = &ps->ps_pipes[src][i];
403 			pb = &ps->ps_pipes[dst][j];
404 			if (socketpair(AF_UNIX,
405 			    SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
406 			    PF_UNSPEC, fds) == -1)
407 				fatal("%s: socketpair", __func__);
408 
409 			pa->pp_pipes[dst][j] = fds[0];
410 			pb->pp_pipes[src][i] = fds[1];
411 
412 			pf.pf_procid = src;
413 			pf.pf_instance = i;
414 			if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD,
415 			    -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1)
416 				fatal("%s: proc_compose_imsg", __func__);
417 
418 			pf.pf_procid = dst;
419 			pf.pf_instance = j;
420 			if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD,
421 			    -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1)
422 				fatal("%s: proc_compose_imsg", __func__);
423 
424 			/*
425 			 * We have to flush to send the descriptors and close
426 			 * them to avoid the fd ramp on startup.
427 			 */
428 			if (proc_flush_imsg(ps, src, i) == -1 ||
429 			    proc_flush_imsg(ps, dst, j) == -1)
430 				fatal("%s: proc_flush_imsg", __func__);
431 		}
432 	}
433 }
434 
435 void
436 proc_close(struct privsep *ps)
437 {
438 	unsigned int		 dst, n;
439 	struct privsep_pipes	*pp;
440 
441 	if (ps == NULL)
442 		return;
443 
444 	pp = ps->ps_pp;
445 
446 	for (dst = 0; dst < PROC_MAX; dst++) {
447 		if (ps->ps_ievs[dst] == NULL)
448 			continue;
449 
450 		for (n = 0; n < ps->ps_instances[dst]; n++) {
451 			if (pp->pp_pipes[dst][n] == -1)
452 				continue;
453 
454 			/* Cancel the fd, close and invalidate the fd */
455 			event_del(&(ps->ps_ievs[dst][n].ev));
456 			imsgbuf_clear(&(ps->ps_ievs[dst][n].ibuf));
457 			close(pp->pp_pipes[dst][n]);
458 			pp->pp_pipes[dst][n] = -1;
459 		}
460 		free(ps->ps_ievs[dst]);
461 	}
462 }
463 
464 void
465 proc_shutdown(struct privsep_proc *p)
466 {
467 	struct privsep	*ps = p->p_ps;
468 
469 	if (p->p_shutdown != NULL)
470 		(*p->p_shutdown)();
471 
472 	proc_close(ps);
473 
474 	log_info("%s exiting, pid %d", p->p_title, getpid());
475 
476 	exit(0);
477 }
478 
479 void
480 proc_sig_handler(int sig, short event, void *arg)
481 {
482 	struct privsep_proc	*p = arg;
483 
484 	switch (sig) {
485 	case SIGINT:
486 	case SIGTERM:
487 		proc_shutdown(p);
488 		break;
489 	case SIGCHLD:
490 	case SIGHUP:
491 	case SIGPIPE:
492 	case SIGUSR1:
493 		/* ignore */
494 		break;
495 	default:
496 		fatalx("%s: unexpected signal", __func__);
497 		/* NOTREACHED */
498 	}
499 }
500 
501 void
502 proc_run(struct privsep *ps, struct privsep_proc *p,
503     struct privsep_proc *procs, unsigned int nproc,
504     void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg)
505 {
506 	struct passwd		*pw;
507 	const char		*root;
508 	struct control_sock	*rcs;
509 
510 	log_procinit("%s", p->p_title);
511 
512 	if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
513 		if (control_init(ps, &ps->ps_csock) == -1)
514 			fatalx("%s: control_init", __func__);
515 		TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
516 			if (control_init(ps, rcs) == -1)
517 				fatalx("%s: control_init", __func__);
518 	}
519 
520 	/* Use non-standard user */
521 	if (p->p_pw != NULL)
522 		pw = p->p_pw;
523 	else
524 		pw = ps->ps_pw;
525 
526 	/* Change root directory */
527 	if (p->p_chroot != NULL)
528 		root = p->p_chroot;
529 	else
530 		root = pw->pw_dir;
531 
532 	if (chroot(root) == -1)
533 		fatal("%s: chroot", __func__);
534 	if (chdir("/") == -1)
535 		fatal("%s: chdir(\"/\")", __func__);
536 
537 	privsep_process = p->p_id;
538 
539 	setproctitle("%s", p->p_title);
540 
541 	if (setgroups(1, &pw->pw_gid) ||
542 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
543 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
544 		fatal("%s: cannot drop privileges", __func__);
545 
546 	event_init();
547 
548 	signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
549 	signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
550 	signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
551 	signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
552 	signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p);
553 	signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p);
554 
555 	signal_add(&ps->ps_evsigint, NULL);
556 	signal_add(&ps->ps_evsigterm, NULL);
557 	signal_add(&ps->ps_evsigchld, NULL);
558 	signal_add(&ps->ps_evsighup, NULL);
559 	signal_add(&ps->ps_evsigpipe, NULL);
560 	signal_add(&ps->ps_evsigusr1, NULL);
561 
562 	proc_setup(ps, procs, nproc);
563 	proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0);
564 	if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
565 		if (control_listen(&ps->ps_csock) == -1)
566 			fatalx("%s: control_listen", __func__);
567 		TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry)
568 			if (control_listen(rcs) == -1)
569 				fatalx("%s: control_listen", __func__);
570 	}
571 
572 	DPRINTF("%s: %s %d/%d, pid %d", __func__, p->p_title,
573 	    ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
574 
575 	if (run != NULL)
576 		run(ps, p, arg);
577 
578 	event_dispatch();
579 
580 	proc_shutdown(p);
581 }
582 
583 void
584 proc_dispatch(int fd, short event, void *arg)
585 {
586 	struct imsgev		*iev = arg;
587 	struct privsep_proc	*p = iev->proc;
588 	struct privsep		*ps = p->p_ps;
589 	struct imsgbuf		*ibuf;
590 	struct imsg		 imsg;
591 	ssize_t			 n;
592 	int			 verbose;
593 	const char		*title;
594 	struct privsep_fd	 pf;
595 
596 	title = ps->ps_title[privsep_process];
597 	ibuf = &iev->ibuf;
598 
599 	if (event & EV_READ) {
600 		if ((n = imsgbuf_read(ibuf)) == -1)
601 			fatal("%s: imsgbuf_read", __func__);
602 		if (n == 0) {
603 			/* this pipe is dead, so remove the event handler */
604 			event_del(&iev->ev);
605 			event_loopexit(NULL);
606 			return;
607 		}
608 	}
609 
610 	if (event & EV_WRITE) {
611 		if (imsgbuf_write(ibuf) == -1) {
612 			if (errno == EPIPE) {
613 				/* this pipe is dead, remove the handler */
614 				event_del(&iev->ev);
615 				event_loopexit(NULL);
616 				return;
617 			}
618 			fatal("%s: imsgbuf_write", __func__);
619 		}
620 	}
621 
622 	for (;;) {
623 		if ((n = imsg_get(ibuf, &imsg)) == -1)
624 			fatal("%s: imsg_get", __func__);
625 		if (n == 0)
626 			break;
627 
628 #if DEBUG > 1
629 		log_debug("%s: %s %d got imsg %d peerid %d from %s %d",
630 		    __func__, title, ps->ps_instance + 1,
631 		    imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid);
632 #endif
633 
634 		/*
635 		 * Check the message with the program callback
636 		 */
637 		if ((p->p_cb)(fd, p, &imsg) == 0) {
638 			/* Message was handled by the callback, continue */
639 			imsg_free(&imsg);
640 			continue;
641 		}
642 
643 		/*
644 		 * Generic message handling
645 		 */
646 		switch (imsg.hdr.type) {
647 		case IMSG_CTL_VERBOSE:
648 			IMSG_SIZE_CHECK(&imsg, &verbose);
649 			memcpy(&verbose, imsg.data, sizeof(verbose));
650 			log_setverbose(verbose);
651 			break;
652 		case IMSG_CTL_PROCFD:
653 			IMSG_SIZE_CHECK(&imsg, &pf);
654 			memcpy(&pf, imsg.data, sizeof(pf));
655 			proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid,
656 			    pf.pf_instance);
657 			break;
658 		default:
659 			fatalx("%s: %s %d got invalid imsg %d peerid %d "
660 			    "from %s %d",
661 			    __func__, title, ps->ps_instance + 1,
662 			    imsg.hdr.type, imsg.hdr.peerid,
663 			    p->p_title, imsg.hdr.pid);
664 		}
665 		imsg_free(&imsg);
666 	}
667 	imsg_event_add(iev);
668 }
669 
670 int
671 proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg)
672 {
673 	return (-1);
674 }
675 
676 /*
677  * imsg helper functions
678  */
679 void
680 imsg_event_add(struct imsgev *iev)
681 {
682 	imsg_event_add2(iev, NULL);
683 }
684 
685 void
686 imsg_event_add2(struct imsgev *iev, struct event_base *ev_base)
687 {
688 	if (iev->handler == NULL) {
689 		imsgbuf_flush(&iev->ibuf);
690 		return;
691 	}
692 
693 	iev->events = EV_READ;
694 	if (imsgbuf_queuelen(&iev->ibuf) > 0)
695 		iev->events |= EV_WRITE;
696 
697 	event_del(&iev->ev);
698 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
699 	if (ev_base != NULL)
700 		event_base_set(ev_base, &iev->ev);
701 	event_add(&iev->ev, NULL);
702 }
703 
704 int
705 imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
706     pid_t pid, int fd, void *data, uint16_t datalen)
707 {
708 	return imsg_compose_event2(iev, type, peerid, pid, fd, data, datalen,
709 	    NULL);
710 }
711 
712 int
713 imsg_compose_event2(struct imsgev *iev, uint16_t type, uint32_t peerid,
714     pid_t pid, int fd, void *data, uint16_t datalen, struct event_base *ev_base)
715 {
716 	int	ret;
717 
718 	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
719 	    pid, fd, data, datalen)) == -1)
720 		return (ret);
721 	imsg_event_add2(iev, ev_base);
722 	return (ret);
723 }
724 
725 int
726 imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
727     pid_t pid, int fd, const struct iovec *iov, int iovcnt)
728 {
729 	int	ret;
730 
731 	if ((ret = imsg_composev(&iev->ibuf, type, peerid,
732 	    pid, fd, iov, iovcnt)) == -1)
733 		return (ret);
734 	imsg_event_add(iev);
735 	return (ret);
736 }
737 
738 void
739 proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
740 {
741 	if (*n == -1) {
742 		/* Use a range of all target instances */
743 		*n = 0;
744 		*m = ps->ps_instances[id];
745 	} else {
746 		/* Use only a single slot of the specified peer process */
747 		*m = *n + 1;
748 	}
749 }
750 
751 int
752 proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
753     uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen)
754 {
755 	int	 m;
756 
757 	proc_range(ps, id, &n, &m);
758 	for (; n < m; n++) {
759 		if (imsg_compose_event(&ps->ps_ievs[id][n],
760 		    type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1)
761 			return (-1);
762 	}
763 
764 	return (0);
765 }
766 
767 int
768 proc_compose(struct privsep *ps, enum privsep_procid id,
769     uint16_t type, void *data, uint16_t datalen)
770 {
771 	return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen));
772 }
773 
774 int
775 proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
776     uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt)
777 {
778 	int	 m;
779 
780 	proc_range(ps, id, &n, &m);
781 	for (; n < m; n++)
782 		if (imsg_composev_event(&ps->ps_ievs[id][n],
783 		    type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1)
784 			return (-1);
785 
786 	return (0);
787 }
788 
789 int
790 proc_composev(struct privsep *ps, enum privsep_procid id,
791     uint16_t type, const struct iovec *iov, int iovcnt)
792 {
793 	return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt));
794 }
795 
796 int
797 proc_forward_imsg(struct privsep *ps, struct imsg *imsg,
798     enum privsep_procid id, int n)
799 {
800 	return (proc_compose_imsg(ps, id, n, imsg->hdr.type,
801 	    imsg->hdr.peerid, imsg_get_fd(imsg), imsg->data,
802 	    IMSG_DATA_SIZE(imsg)));
803 }
804 
805 struct imsgbuf *
806 proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
807 {
808 	int	 m;
809 
810 	proc_range(ps, id, &n, &m);
811 	return (&ps->ps_ievs[id][n].ibuf);
812 }
813 
814 struct imsgev *
815 proc_iev(struct privsep *ps, enum privsep_procid id, int n)
816 {
817 	int	 m;
818 
819 	proc_range(ps, id, &n, &m);
820 	return (&ps->ps_ievs[id][n]);
821 }
822 
823 /* This function should only be called with care as it breaks async I/O */
824 int
825 proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n)
826 {
827 	struct imsgbuf	*ibuf;
828 	int		 m, ret = 0;
829 
830 	proc_range(ps, id, &n, &m);
831 	for (; n < m; n++) {
832 		if ((ibuf = proc_ibuf(ps, id, n)) == NULL)
833 			return (-1);
834 		if ((ret = imsgbuf_flush(ibuf)) == -1)
835 			break;
836 		imsg_event_add(&ps->ps_ievs[id][n]);
837 	}
838 
839 	return (ret);
840 }
841