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