xref: /openbsd-src/sbin/iked/proc.c (revision 0e59d0d19ca6a10a17663d531bcea1b99c1bfe09)
1*0e59d0d1Sclaudio /*	$OpenBSD: proc.c,v 1.51 2024/11/21 13:35:20 claudio Exp $	*/
245ae9d61Sreyk 
345ae9d61Sreyk /*
4a7dbf4aeStobhe  * Copyright (c) 2010 - 2016 Reyk Floeter <reyk@openbsd.org>
545ae9d61Sreyk  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
645ae9d61Sreyk  *
745ae9d61Sreyk  * Permission to use, copy, modify, and distribute this software for any
845ae9d61Sreyk  * purpose with or without fee is hereby granted, provided that the above
945ae9d61Sreyk  * copyright notice and this permission notice appear in all copies.
1045ae9d61Sreyk  *
1145ae9d61Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1245ae9d61Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1345ae9d61Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1445ae9d61Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1545ae9d61Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1645ae9d61Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1745ae9d61Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1845ae9d61Sreyk  */
1945ae9d61Sreyk 
20c205e972Sreyk #include <sys/types.h>
2145ae9d61Sreyk #include <sys/queue.h>
2245ae9d61Sreyk #include <sys/socket.h>
23bf556abcSreyk #include <sys/wait.h>
2445ae9d61Sreyk 
25a7dbf4aeStobhe #include <fcntl.h>
2645ae9d61Sreyk #include <stdio.h>
2745ae9d61Sreyk #include <stdlib.h>
2845ae9d61Sreyk #include <unistd.h>
2945ae9d61Sreyk #include <string.h>
3045ae9d61Sreyk #include <errno.h>
3145ae9d61Sreyk #include <signal.h>
32a7dbf4aeStobhe #include <paths.h>
3345ae9d61Sreyk #include <pwd.h>
3445ae9d61Sreyk #include <event.h>
35c205e972Sreyk #include <imsg.h>
3645ae9d61Sreyk 
3745ae9d61Sreyk #include "iked.h"
3845ae9d61Sreyk 
39f4602b7cSmortimer enum privsep_procid privsep_process;
40f4602b7cSmortimer 
41a7dbf4aeStobhe void	 proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int,
422269e292Stobhe 	    char **);
43a7dbf4aeStobhe void	 proc_setup(struct privsep *, struct privsep_proc *, unsigned int);
44a7dbf4aeStobhe void	 proc_open(struct privsep *, int, int);
45a7dbf4aeStobhe void	 proc_accept(struct privsep *, int, enum privsep_procid,
46a7dbf4aeStobhe 	    unsigned int);
47bf556abcSreyk void	 proc_close(struct privsep *);
48f2f2a684Sreyk void	 proc_shutdown(struct privsep_proc *);
4945ae9d61Sreyk void	 proc_sig_handler(int, short, void *);
50bf556abcSreyk void	 proc_range(struct privsep *, enum privsep_procid, int *, int *);
51ebfc3693Sreyk int	 proc_dispatch_null(int, struct privsep_proc *, struct imsg *);
5245ae9d61Sreyk 
53a7dbf4aeStobhe enum privsep_procid
54a7dbf4aeStobhe proc_getid(struct privsep_proc *procs, unsigned int nproc,
55a7dbf4aeStobhe     const char *proc_name)
56bf556abcSreyk {
57a7dbf4aeStobhe 	struct privsep_proc	*p;
58a7dbf4aeStobhe 	unsigned int		 proc;
59a7dbf4aeStobhe 
60a7dbf4aeStobhe 	for (proc = 0; proc < nproc; proc++) {
61a7dbf4aeStobhe 		p = &procs[proc];
62a7dbf4aeStobhe 		if (strcmp(p->p_title, proc_name))
63a7dbf4aeStobhe 			continue;
64a7dbf4aeStobhe 
65a7dbf4aeStobhe 		return (p->p_id);
66a7dbf4aeStobhe 	}
67a7dbf4aeStobhe 
68a7dbf4aeStobhe 	return (PROC_MAX);
69a7dbf4aeStobhe }
70a7dbf4aeStobhe 
71a7dbf4aeStobhe void
72a7dbf4aeStobhe proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
732269e292Stobhe     int argc, char **argv)
74a7dbf4aeStobhe {
75a7dbf4aeStobhe 	unsigned int		 proc, nargc, i, proc_i;
76a7dbf4aeStobhe 	char			**nargv;
77a7dbf4aeStobhe 	struct privsep_proc	*p;
78a7dbf4aeStobhe 	char			 num[32];
79a7dbf4aeStobhe 	int			 fd;
80a7dbf4aeStobhe 
81a7dbf4aeStobhe 	/* Prepare the new process argv. */
82a7dbf4aeStobhe 	nargv = calloc(argc + 5, sizeof(char *));
83a7dbf4aeStobhe 	if (nargv == NULL)
84a7dbf4aeStobhe 		fatal("%s: calloc", __func__);
85a7dbf4aeStobhe 
86a7dbf4aeStobhe 	/* Copy call argument first. */
87a7dbf4aeStobhe 	nargc = 0;
88a7dbf4aeStobhe 	nargv[nargc++] = argv[0];
89a7dbf4aeStobhe 
90a7dbf4aeStobhe 	/* Set process name argument and save the position. */
91a7dbf4aeStobhe 	nargv[nargc++] = "-P";
92a7dbf4aeStobhe 	proc_i = nargc;
93a7dbf4aeStobhe 	nargc++;
94a7dbf4aeStobhe 
95a7dbf4aeStobhe 	/* Point process instance arg to stack and copy the original args. */
96a7dbf4aeStobhe 	nargv[nargc++] = "-I";
97a7dbf4aeStobhe 	nargv[nargc++] = num;
98a7dbf4aeStobhe 	for (i = 1; i < (unsigned int) argc; i++)
99a7dbf4aeStobhe 		nargv[nargc++] = argv[i];
100a7dbf4aeStobhe 
101a7dbf4aeStobhe 	nargv[nargc] = NULL;
102a7dbf4aeStobhe 
103a7dbf4aeStobhe 	for (proc = 0; proc < nproc; proc++) {
104a7dbf4aeStobhe 		p = &procs[proc];
105a7dbf4aeStobhe 
106a7dbf4aeStobhe 		/* Update args with process title. */
107a7dbf4aeStobhe 		nargv[proc_i] = (char *)(uintptr_t)p->p_title;
108a7dbf4aeStobhe 
109a7dbf4aeStobhe 		/* Fire children processes. */
110a7dbf4aeStobhe 		for (i = 0; i < ps->ps_instances[p->p_id]; i++) {
111a7dbf4aeStobhe 			/* Update the process instance number. */
112a7dbf4aeStobhe 			snprintf(num, sizeof(num), "%u", i);
113a7dbf4aeStobhe 
114a7dbf4aeStobhe 			fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0];
115a7dbf4aeStobhe 			ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1;
116a7dbf4aeStobhe 
117a7dbf4aeStobhe 			switch (fork()) {
118a7dbf4aeStobhe 			case -1:
119a7dbf4aeStobhe 				fatal("%s: fork", __func__);
120a7dbf4aeStobhe 				break;
121a7dbf4aeStobhe 			case 0:
122a7dbf4aeStobhe 				/* Prepare parent socket. */
123a7dbf4aeStobhe 				if (fd != PROC_PARENT_SOCK_FILENO) {
124a7dbf4aeStobhe 					if (dup2(fd, PROC_PARENT_SOCK_FILENO)
125a7dbf4aeStobhe 					    == -1)
126a7dbf4aeStobhe 						fatal("dup2");
127a7dbf4aeStobhe 				} else if (fcntl(fd, F_SETFD, 0) == -1)
128a7dbf4aeStobhe 					fatal("fcntl");
129a7dbf4aeStobhe 
130a7dbf4aeStobhe 				execvp(argv[0], nargv);
131a7dbf4aeStobhe 				fatal("%s: execvp", __func__);
132a7dbf4aeStobhe 				break;
133a7dbf4aeStobhe 			default:
134a7dbf4aeStobhe 				/* Close child end. */
135a7dbf4aeStobhe 				close(fd);
136a7dbf4aeStobhe 				break;
137a7dbf4aeStobhe 			}
138a7dbf4aeStobhe 		}
139a7dbf4aeStobhe 	}
140a7dbf4aeStobhe 	free(nargv);
141a7dbf4aeStobhe }
142a7dbf4aeStobhe 
143a7dbf4aeStobhe void
1448e8f56e9Stobhe proc_connect(struct privsep *ps, void (*connected)(struct privsep *))
145a7dbf4aeStobhe {
146a7dbf4aeStobhe 	struct imsgev		*iev;
147a7dbf4aeStobhe 	unsigned int		 src, dst, inst;
148a7dbf4aeStobhe 
149a7dbf4aeStobhe 	/* Don't distribute any sockets if we are not really going to run. */
1508e8f56e9Stobhe 	if (ps->ps_noaction) {
1518e8f56e9Stobhe 		if (connected == NULL)
1528e8f56e9Stobhe 			fatalx("%s: missing callback", __func__);
1538e8f56e9Stobhe 		connected(ps);
154a7dbf4aeStobhe 		return;
1558e8f56e9Stobhe 	}
1568e8f56e9Stobhe 	ps->ps_connected = connected;
157a7dbf4aeStobhe 
158a7dbf4aeStobhe 	for (dst = 0; dst < PROC_MAX; dst++) {
159a7dbf4aeStobhe 		/* We don't communicate with ourselves. */
160a7dbf4aeStobhe 		if (dst == PROC_PARENT)
161a7dbf4aeStobhe 			continue;
162a7dbf4aeStobhe 
163a7dbf4aeStobhe 		for (inst = 0; inst < ps->ps_instances[dst]; inst++) {
164a7dbf4aeStobhe 			iev = &ps->ps_ievs[dst][inst];
165*0e59d0d1Sclaudio 			if (imsgbuf_init(&iev->ibuf,
166*0e59d0d1Sclaudio 			    ps->ps_pp->pp_pipes[dst][inst]) == -1)
167*0e59d0d1Sclaudio 				fatal("%s: imsgbuf_init", __func__);
168*0e59d0d1Sclaudio 			imsgbuf_allow_fdpass(&iev->ibuf);
169a7dbf4aeStobhe 			event_set(&iev->ev, iev->ibuf.fd, iev->events,
170a7dbf4aeStobhe 			    iev->handler, iev->data);
171a7dbf4aeStobhe 			event_add(&iev->ev, NULL);
172a7dbf4aeStobhe 		}
173a7dbf4aeStobhe 	}
174a7dbf4aeStobhe 
175a7dbf4aeStobhe 	/* Distribute the socketpair()s for everyone. */
176a7dbf4aeStobhe 	for (src = 0; src < PROC_MAX; src++)
177a7dbf4aeStobhe 		for (dst = src; dst < PROC_MAX; dst++) {
178a7dbf4aeStobhe 			/* Parent already distributed its fds. */
179a7dbf4aeStobhe 			if (src == PROC_PARENT || dst == PROC_PARENT)
180a7dbf4aeStobhe 				continue;
181a7dbf4aeStobhe 
182a7dbf4aeStobhe 			proc_open(ps, src, dst);
183a7dbf4aeStobhe 		}
1848e8f56e9Stobhe 
1858e8f56e9Stobhe 	/*
1868e8f56e9Stobhe 	 * Finally, send a ready message to everyone:
1878e8f56e9Stobhe 	 * When this message is processed by the receiver, it has
1888e8f56e9Stobhe 	 * already processed all IMSG_CTL_PROCFD messages and all
1898e8f56e9Stobhe 	 * pipes are ready.
1908e8f56e9Stobhe 	 */
1918e8f56e9Stobhe 	for (dst = 0; dst < PROC_MAX; dst++) {
1928e8f56e9Stobhe 		if (dst == PROC_PARENT)
1938e8f56e9Stobhe 			continue;
1948e8f56e9Stobhe 		for (inst = 0; inst < ps->ps_instances[dst]; inst++) {
1958e8f56e9Stobhe 			if (proc_compose_imsg(ps, dst, inst, IMSG_CTL_PROCREADY,
1968e8f56e9Stobhe 			    -1, -1, NULL, 0) == -1)
1978e8f56e9Stobhe 				fatal("%s: proc_compose_imsg", __func__);
1988e8f56e9Stobhe 			ps->ps_connecting++;
1998e8f56e9Stobhe #if DEBUG
2008e8f56e9Stobhe 			log_debug("%s: #%d %s %d", __func__,
2018e8f56e9Stobhe 			    ps->ps_connecting, ps->ps_title[dst], inst + 1);
2028e8f56e9Stobhe #endif
2038e8f56e9Stobhe 		}
2048e8f56e9Stobhe 	}
205a7dbf4aeStobhe }
206a7dbf4aeStobhe 
207a7dbf4aeStobhe void
208a7dbf4aeStobhe proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
209a7dbf4aeStobhe     int debug, int argc, char **argv, enum privsep_procid proc_id)
210a7dbf4aeStobhe {
211a7dbf4aeStobhe 	struct privsep_proc	*p = NULL;
212a7dbf4aeStobhe 	struct privsep_pipes	*pa, *pb;
213a7dbf4aeStobhe 	unsigned int		 proc;
214a7dbf4aeStobhe 	unsigned int		 dst;
215a7dbf4aeStobhe 	int			 fds[2];
216a7dbf4aeStobhe 
217a7dbf4aeStobhe 	/* Don't initiate anything if we are not really going to run. */
218a7dbf4aeStobhe 	if (ps->ps_noaction)
219a7dbf4aeStobhe 		return;
220a7dbf4aeStobhe 
221a7dbf4aeStobhe 	if (proc_id == PROC_PARENT) {
222a7dbf4aeStobhe 		privsep_process = PROC_PARENT;
2232a1b6222Stobhe 		proc_setup(ps, procs, nproc);
2242a1b6222Stobhe 
2251fee06b0Sgerhard 		if (!debug && daemon(0, 0) == -1)
2261fee06b0Sgerhard 			fatal("failed to daemonize");
227a7dbf4aeStobhe 
228a7dbf4aeStobhe 		/*
229a7dbf4aeStobhe 		 * Create the children sockets so we can use them
230a7dbf4aeStobhe 		 * to distribute the rest of the socketpair()s using
231a7dbf4aeStobhe 		 * proc_connect() later.
232a7dbf4aeStobhe 		 */
233a7dbf4aeStobhe 		for (dst = 0; dst < PROC_MAX; dst++) {
234a7dbf4aeStobhe 			/* Don't create socket for ourselves. */
235a7dbf4aeStobhe 			if (dst == PROC_PARENT)
236a7dbf4aeStobhe 				continue;
237a7dbf4aeStobhe 
238a7dbf4aeStobhe 			for (proc = 0; proc < ps->ps_instances[dst]; proc++) {
239a7dbf4aeStobhe 				pa = &ps->ps_pipes[PROC_PARENT][0];
240a7dbf4aeStobhe 				pb = &ps->ps_pipes[dst][proc];
241a7dbf4aeStobhe 				if (socketpair(AF_UNIX,
242a7dbf4aeStobhe 				    SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
243a7dbf4aeStobhe 				    PF_UNSPEC, fds) == -1)
244a7dbf4aeStobhe 					fatal("%s: socketpair", __func__);
245a7dbf4aeStobhe 
246a7dbf4aeStobhe 				pa->pp_pipes[dst][proc] = fds[0];
247a7dbf4aeStobhe 				pb->pp_pipes[PROC_PARENT][0] = fds[1];
248a7dbf4aeStobhe 			}
249a7dbf4aeStobhe 		}
250a7dbf4aeStobhe 
251a7dbf4aeStobhe 		/* Engage! */
2522269e292Stobhe 		proc_exec(ps, procs, nproc, argc, argv);
253a7dbf4aeStobhe 		return;
254a7dbf4aeStobhe 	}
255a7dbf4aeStobhe 
256a7dbf4aeStobhe 	/* Initialize a child */
257a7dbf4aeStobhe 	for (proc = 0; proc < nproc; proc++) {
258a7dbf4aeStobhe 		if (procs[proc].p_id != proc_id)
259a7dbf4aeStobhe 			continue;
260a7dbf4aeStobhe 		p = &procs[proc];
261a7dbf4aeStobhe 		break;
262a7dbf4aeStobhe 	}
263a7dbf4aeStobhe 	if (p == NULL || p->p_init == NULL)
264a7dbf4aeStobhe 		fatalx("%s: process %d missing process initialization",
265a7dbf4aeStobhe 		    __func__, proc_id);
266a7dbf4aeStobhe 
267a7dbf4aeStobhe 	p->p_init(ps, p);
268a7dbf4aeStobhe 
269a7dbf4aeStobhe 	fatalx("failed to initiate child process");
270a7dbf4aeStobhe }
271a7dbf4aeStobhe 
272a7dbf4aeStobhe void
273a7dbf4aeStobhe proc_accept(struct privsep *ps, int fd, enum privsep_procid dst,
274a7dbf4aeStobhe     unsigned int n)
275a7dbf4aeStobhe {
276a7dbf4aeStobhe 	struct privsep_pipes	*pp = ps->ps_pp;
277a7dbf4aeStobhe 	struct imsgev		*iev;
278a7dbf4aeStobhe 
279a7dbf4aeStobhe 	if (ps->ps_ievs[dst] == NULL) {
280a7dbf4aeStobhe #if DEBUG > 1
281a7dbf4aeStobhe 		log_debug("%s: %s src %d %d to dst %d %d not connected",
282a7dbf4aeStobhe 		    __func__, ps->ps_title[privsep_process],
283a7dbf4aeStobhe 		    privsep_process, ps->ps_instance + 1,
284a7dbf4aeStobhe 		    dst, n + 1);
285a7dbf4aeStobhe #endif
286a7dbf4aeStobhe 		close(fd);
287a7dbf4aeStobhe 		return;
288a7dbf4aeStobhe 	}
289a7dbf4aeStobhe 
290a7dbf4aeStobhe 	if (pp->pp_pipes[dst][n] != -1) {
291a7dbf4aeStobhe 		log_warnx("%s: duplicated descriptor", __func__);
292a7dbf4aeStobhe 		close(fd);
293a7dbf4aeStobhe 		return;
294a7dbf4aeStobhe 	} else
295a7dbf4aeStobhe 		pp->pp_pipes[dst][n] = fd;
296a7dbf4aeStobhe 
297a7dbf4aeStobhe 	iev = &ps->ps_ievs[dst][n];
298*0e59d0d1Sclaudio 	if (imsgbuf_init(&iev->ibuf, fd) == -1)
299*0e59d0d1Sclaudio 		fatal("%s: imsgbuf_init", __func__);
300*0e59d0d1Sclaudio 	imsgbuf_allow_fdpass(&iev->ibuf);
301a7dbf4aeStobhe 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
302a7dbf4aeStobhe 	event_add(&iev->ev, NULL);
303a7dbf4aeStobhe }
304a7dbf4aeStobhe 
305a7dbf4aeStobhe void
306a7dbf4aeStobhe proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
307a7dbf4aeStobhe {
308a7dbf4aeStobhe 	unsigned int		 i, j, src, dst, id;
309bf556abcSreyk 	struct privsep_pipes	*pp;
310bf556abcSreyk 
311a7dbf4aeStobhe 	/* Initialize parent title, ps_instances and procs. */
312a7dbf4aeStobhe 	ps->ps_title[PROC_PARENT] = "parent";
313a7dbf4aeStobhe 
314a7dbf4aeStobhe 	for (src = 0; src < PROC_MAX; src++)
315a7dbf4aeStobhe 		/* Default to 1 process instance */
316a7dbf4aeStobhe 		if (ps->ps_instances[src] < 1)
317a7dbf4aeStobhe 			ps->ps_instances[src] = 1;
318a7dbf4aeStobhe 
319a7dbf4aeStobhe 	for (src = 0; src < nproc; src++) {
320a7dbf4aeStobhe 		procs[src].p_ps = ps;
321a7dbf4aeStobhe 		if (procs[src].p_cb == NULL)
322a7dbf4aeStobhe 			procs[src].p_cb = proc_dispatch_null;
323a7dbf4aeStobhe 
324a7dbf4aeStobhe 		id = procs[src].p_id;
325a7dbf4aeStobhe 		ps->ps_title[id] = procs[src].p_title;
326a7dbf4aeStobhe 		if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id],
327a7dbf4aeStobhe 		    sizeof(struct imsgev))) == NULL)
328a7dbf4aeStobhe 			fatal("%s: calloc", __func__);
329a7dbf4aeStobhe 
330dd7efffeSclaudio 		/* With this set up, we are ready to call imsgbuf_init(). */
331a7dbf4aeStobhe 		for (i = 0; i < ps->ps_instances[id]; i++) {
332a7dbf4aeStobhe 			ps->ps_ievs[id][i].handler = proc_dispatch;
333a7dbf4aeStobhe 			ps->ps_ievs[id][i].events = EV_READ;
334a7dbf4aeStobhe 			ps->ps_ievs[id][i].proc = &procs[src];
335a7dbf4aeStobhe 			ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i];
336a7dbf4aeStobhe 		}
337a7dbf4aeStobhe 	}
338a7dbf4aeStobhe 
339fc7fd3e3Sreyk 	/*
340bf556abcSreyk 	 * Allocate pipes for all process instances (incl. parent)
341bf556abcSreyk 	 *
342bf556abcSreyk 	 * - ps->ps_pipes: N:M mapping
343bf556abcSreyk 	 * N source processes connected to M destination processes:
344bf556abcSreyk 	 * [src][instances][dst][instances], for example
345bf556abcSreyk 	 * [PROC_RELAY][3][PROC_CA][3]
346bf556abcSreyk 	 *
347bf556abcSreyk 	 * - ps->ps_pp: per-process 1:M part of ps->ps_pipes
348bf556abcSreyk 	 * Each process instance has a destination array of socketpair fds:
349bf556abcSreyk 	 * [dst][instances], for example
350bf556abcSreyk 	 * [PROC_PARENT][0]
351bf556abcSreyk 	 */
352bf556abcSreyk 	for (src = 0; src < PROC_MAX; src++) {
353bf556abcSreyk 		/* Allocate destination array for each process */
354a7dbf4aeStobhe 		if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src],
355bf556abcSreyk 		    sizeof(struct privsep_pipes))) == NULL)
356a7dbf4aeStobhe 			fatal("%s: calloc", __func__);
357bf556abcSreyk 
358a7dbf4aeStobhe 		for (i = 0; i < ps->ps_instances[src]; i++) {
359bf556abcSreyk 			pp = &ps->ps_pipes[src][i];
360bf556abcSreyk 
361bf556abcSreyk 			for (dst = 0; dst < PROC_MAX; dst++) {
362bf556abcSreyk 				/* Allocate maximum fd integers */
363bf556abcSreyk 				if ((pp->pp_pipes[dst] =
364a7dbf4aeStobhe 				    calloc(ps->ps_instances[dst],
365bf556abcSreyk 				    sizeof(int))) == NULL)
366a7dbf4aeStobhe 					fatal("%s: calloc", __func__);
367bf556abcSreyk 
368bf556abcSreyk 				/* Mark fd as unused */
369a7dbf4aeStobhe 				for (j = 0; j < ps->ps_instances[dst]; j++)
370bf556abcSreyk 					pp->pp_pipes[dst][j] = -1;
371bf556abcSreyk 			}
372bf556abcSreyk 		}
373bf556abcSreyk 	}
374bf556abcSreyk 
375a7dbf4aeStobhe 	ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance];
37645ae9d61Sreyk }
37745ae9d61Sreyk 
37845ae9d61Sreyk void
379fc7fd3e3Sreyk proc_kill(struct privsep *ps)
38045ae9d61Sreyk {
381a7dbf4aeStobhe 	char		*cause;
382fc7fd3e3Sreyk 	pid_t		 pid;
383a7dbf4aeStobhe 	int		 len, status;
38445ae9d61Sreyk 
385f2f2a684Sreyk 	if (privsep_process != PROC_PARENT)
38645ae9d61Sreyk 		return;
38745ae9d61Sreyk 
388a7dbf4aeStobhe 	proc_close(ps);
389fc7fd3e3Sreyk 
390fc7fd3e3Sreyk 	do {
391a7dbf4aeStobhe 		pid = waitpid(WAIT_ANY, &status, 0);
392a7dbf4aeStobhe 		if (pid <= 0)
393a7dbf4aeStobhe 			continue;
394bf556abcSreyk 
395a7dbf4aeStobhe 		if (WIFSIGNALED(status)) {
396a7dbf4aeStobhe 			len = asprintf(&cause, "terminated; signal %d",
397a7dbf4aeStobhe 			    WTERMSIG(status));
398a7dbf4aeStobhe 		} else if (WIFEXITED(status)) {
399a7dbf4aeStobhe 			if (WEXITSTATUS(status) != 0)
400a7dbf4aeStobhe 				len = asprintf(&cause, "exited abnormally");
401a7dbf4aeStobhe 			else
402a7dbf4aeStobhe 				len = 0;
403a7dbf4aeStobhe 		} else
404a7dbf4aeStobhe 			len = -1;
405a7dbf4aeStobhe 
406a7dbf4aeStobhe 		if (len == 0) {
407a7dbf4aeStobhe 			/* child exited OK, don't print a warning message */
408a7dbf4aeStobhe 		} else if (len != -1) {
409a7dbf4aeStobhe 			log_warnx("lost child: pid %u %s", pid, cause);
410a7dbf4aeStobhe 			free(cause);
411a7dbf4aeStobhe 		} else
412a7dbf4aeStobhe 			log_warnx("lost child: pid %u", pid);
413a7dbf4aeStobhe 	} while (pid != -1 || (pid == -1 && errno == EINTR));
41445ae9d61Sreyk }
41545ae9d61Sreyk 
41645ae9d61Sreyk void
417a7dbf4aeStobhe proc_open(struct privsep *ps, int src, int dst)
41845ae9d61Sreyk {
419bf556abcSreyk 	struct privsep_pipes	*pa, *pb;
420a7dbf4aeStobhe 	struct privsep_fd	 pf;
421bf556abcSreyk 	int			 fds[2];
422a7dbf4aeStobhe 	unsigned int		 i, j;
42345ae9d61Sreyk 
424a7dbf4aeStobhe 	/* Exchange pipes between process. */
425bf556abcSreyk 	for (i = 0; i < ps->ps_instances[src]; i++) {
426a7dbf4aeStobhe 		for (j = 0; j < ps->ps_instances[dst]; j++) {
427a7dbf4aeStobhe 			/* Don't create sockets for ourself. */
428a7dbf4aeStobhe 			if (src == dst && i == j)
429a7dbf4aeStobhe 				continue;
430a7dbf4aeStobhe 
431bf556abcSreyk 			pa = &ps->ps_pipes[src][i];
432a7dbf4aeStobhe 			pb = &ps->ps_pipes[dst][j];
43352ab28dfSreyk 			if (socketpair(AF_UNIX,
434a7dbf4aeStobhe 			    SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
435bf556abcSreyk 			    PF_UNSPEC, fds) == -1)
436a7dbf4aeStobhe 				fatal("%s: socketpair", __func__);
437bf556abcSreyk 
438a7dbf4aeStobhe 			pa->pp_pipes[dst][j] = fds[0];
439bf556abcSreyk 			pb->pp_pipes[src][i] = fds[1];
44045ae9d61Sreyk 
441a7dbf4aeStobhe 			pf.pf_procid = src;
442a7dbf4aeStobhe 			pf.pf_instance = i;
443a7dbf4aeStobhe 			if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD,
444a7dbf4aeStobhe 			    -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1)
445a7dbf4aeStobhe 				fatal("%s: proc_compose_imsg", __func__);
446a7dbf4aeStobhe 
447a7dbf4aeStobhe 			pf.pf_procid = dst;
448a7dbf4aeStobhe 			pf.pf_instance = j;
449a7dbf4aeStobhe 			if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD,
450a7dbf4aeStobhe 			    -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1)
451a7dbf4aeStobhe 				fatal("%s: proc_compose_imsg", __func__);
452bf556abcSreyk 
453bf556abcSreyk 			/*
454a7dbf4aeStobhe 			 * We have to flush to send the descriptors and close
455a7dbf4aeStobhe 			 * them to avoid the fd ramp on startup.
456bf556abcSreyk 			 */
457a7dbf4aeStobhe 			if (proc_flush_imsg(ps, src, i) == -1 ||
458a7dbf4aeStobhe 			    proc_flush_imsg(ps, dst, j) == -1)
459dd7efffeSclaudio 				fatal("%s: proc_flush_imsg", __func__);
46045ae9d61Sreyk 		}
46145ae9d61Sreyk 	}
46245ae9d61Sreyk }
46345ae9d61Sreyk 
464bf556abcSreyk void
465bf556abcSreyk proc_close(struct privsep *ps)
466bf556abcSreyk {
467a7dbf4aeStobhe 	unsigned int		 dst, n;
468bf556abcSreyk 	struct privsep_pipes	*pp;
46945ae9d61Sreyk 
470bf556abcSreyk 	if (ps == NULL)
471bf556abcSreyk 		return;
472bf556abcSreyk 
473bf556abcSreyk 	pp = ps->ps_pp;
474bf556abcSreyk 
475bf556abcSreyk 	for (dst = 0; dst < PROC_MAX; dst++) {
476bf556abcSreyk 		if (ps->ps_ievs[dst] == NULL)
477bf556abcSreyk 			continue;
478bf556abcSreyk 
479bf556abcSreyk 		for (n = 0; n < ps->ps_instances[dst]; n++) {
480bf556abcSreyk 			if (pp->pp_pipes[dst][n] == -1)
481bf556abcSreyk 				continue;
482bf556abcSreyk 
483bf556abcSreyk 			/* Cancel the fd, close and invalidate the fd */
484bf556abcSreyk 			event_del(&(ps->ps_ievs[dst][n].ev));
485dd7efffeSclaudio 			imsgbuf_clear(&(ps->ps_ievs[dst][n].ibuf));
486bf556abcSreyk 			close(pp->pp_pipes[dst][n]);
487bf556abcSreyk 			pp->pp_pipes[dst][n] = -1;
488bf556abcSreyk 		}
489bf556abcSreyk 		free(ps->ps_ievs[dst]);
49045ae9d61Sreyk 	}
49145ae9d61Sreyk }
49245ae9d61Sreyk 
49345ae9d61Sreyk void
494f2f2a684Sreyk proc_shutdown(struct privsep_proc *p)
49545ae9d61Sreyk {
496701048fbSreyk 	struct privsep	*ps = p->p_ps;
49745ae9d61Sreyk 
498bf556abcSreyk 	if (p->p_shutdown != NULL)
499e8e9d77fStobhe 		(*p->p_shutdown)();
500bf556abcSreyk 
501bf556abcSreyk 	proc_close(ps);
502bf556abcSreyk 
503bf556abcSreyk 	log_info("%s exiting, pid %d", p->p_title, getpid());
504bf556abcSreyk 
505a7dbf4aeStobhe 	exit(0);
50645ae9d61Sreyk }
50745ae9d61Sreyk 
50845ae9d61Sreyk void
50945ae9d61Sreyk proc_sig_handler(int sig, short event, void *arg)
51045ae9d61Sreyk {
511fc7fd3e3Sreyk 	struct privsep_proc	*p = arg;
512fc7fd3e3Sreyk 
51345ae9d61Sreyk 	switch (sig) {
51445ae9d61Sreyk 	case SIGINT:
51545ae9d61Sreyk 	case SIGTERM:
516fc7fd3e3Sreyk 		proc_shutdown(p);
51745ae9d61Sreyk 		break;
51845ae9d61Sreyk 	case SIGCHLD:
51945ae9d61Sreyk 	case SIGHUP:
52045ae9d61Sreyk 	case SIGPIPE:
521d5fd2e4bSreyk 	case SIGUSR1:
52245ae9d61Sreyk 		/* ignore */
52345ae9d61Sreyk 		break;
52445ae9d61Sreyk 	default:
525a7dbf4aeStobhe 		fatalx("%s: unexpected signal", __func__);
52645ae9d61Sreyk 		/* NOTREACHED */
52745ae9d61Sreyk 	}
52845ae9d61Sreyk }
52945ae9d61Sreyk 
530a7dbf4aeStobhe void
531fc7fd3e3Sreyk proc_run(struct privsep *ps, struct privsep_proc *p,
532d09d3a7dSreyk     struct privsep_proc *procs, unsigned int nproc,
533ebfc3693Sreyk     void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg)
53445ae9d61Sreyk {
53545ae9d61Sreyk 	struct passwd		*pw;
53645ae9d61Sreyk 	const char		*root;
53745ae9d61Sreyk 
5380f12961aSreyk 	log_procinit(p->p_title);
5390f12961aSreyk 
540bf556abcSreyk 	if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
541701048fbSreyk 		if (control_init(ps, &ps->ps_csock) == -1)
542a7dbf4aeStobhe 			fatalx("%s: control_init", __func__);
54345ae9d61Sreyk 	}
54445ae9d61Sreyk 
545a7dbf4aeStobhe 	/* Use non-standard user */
546a7dbf4aeStobhe 	if (p->p_pw != NULL)
547a7dbf4aeStobhe 		pw = p->p_pw;
548a7dbf4aeStobhe 	else
549a7dbf4aeStobhe 		pw = ps->ps_pw;
550a7dbf4aeStobhe 
55145ae9d61Sreyk 	/* Change root directory */
552701048fbSreyk 	if (p->p_chroot != NULL)
553701048fbSreyk 		root = p->p_chroot;
55445ae9d61Sreyk 	else
55545ae9d61Sreyk 		root = pw->pw_dir;
55645ae9d61Sreyk 
55745ae9d61Sreyk 	if (chroot(root) == -1)
558a7dbf4aeStobhe 		fatal("%s: chroot", __func__);
55945ae9d61Sreyk 	if (chdir("/") == -1)
560a7dbf4aeStobhe 		fatal("%s: chdir(\"/\")", __func__);
56145ae9d61Sreyk 
562701048fbSreyk 	privsep_process = p->p_id;
563fc7fd3e3Sreyk 
564701048fbSreyk 	setproctitle("%s", p->p_title);
56545ae9d61Sreyk 
56645ae9d61Sreyk 	if (setgroups(1, &pw->pw_gid) ||
56745ae9d61Sreyk 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
56845ae9d61Sreyk 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
569a7dbf4aeStobhe 		fatal("%s: cannot drop privileges", __func__);
570bf556abcSreyk 
57145ae9d61Sreyk 	event_init();
57245ae9d61Sreyk 
573701048fbSreyk 	signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
574701048fbSreyk 	signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
575701048fbSreyk 	signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
576701048fbSreyk 	signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
577701048fbSreyk 	signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p);
578d5fd2e4bSreyk 	signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p);
57945ae9d61Sreyk 
580701048fbSreyk 	signal_add(&ps->ps_evsigint, NULL);
581701048fbSreyk 	signal_add(&ps->ps_evsigterm, NULL);
582701048fbSreyk 	signal_add(&ps->ps_evsigchld, NULL);
583701048fbSreyk 	signal_add(&ps->ps_evsighup, NULL);
584701048fbSreyk 	signal_add(&ps->ps_evsigpipe, NULL);
585d5fd2e4bSreyk 	signal_add(&ps->ps_evsigusr1, NULL);
58645ae9d61Sreyk 
587a7dbf4aeStobhe 	proc_setup(ps, procs, nproc);
588a7dbf4aeStobhe 	proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0);
589bf556abcSreyk 	if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
590701048fbSreyk 		if (control_listen(&ps->ps_csock) == -1)
591a7dbf4aeStobhe 			fatalx("%s: control_listen", __func__);
59245ae9d61Sreyk 	}
59345ae9d61Sreyk 
594a7dbf4aeStobhe #if DEBUG
595a7dbf4aeStobhe 	log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
596a7dbf4aeStobhe 	    ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
597a7dbf4aeStobhe #endif
598a7dbf4aeStobhe 
599ebfc3693Sreyk 	if (run != NULL)
600ebfc3693Sreyk 		run(ps, p, arg);
60145ae9d61Sreyk 
60245ae9d61Sreyk 	event_dispatch();
60345ae9d61Sreyk 
60445ae9d61Sreyk 	proc_shutdown(p);
60545ae9d61Sreyk }
60645ae9d61Sreyk 
60745ae9d61Sreyk void
608fc7fd3e3Sreyk proc_dispatch(int fd, short event, void *arg)
60945ae9d61Sreyk {
610bf556abcSreyk 	struct imsgev		*iev = arg;
611bf556abcSreyk 	struct privsep_proc	*p = iev->proc;
612701048fbSreyk 	struct privsep		*ps = p->p_ps;
61345ae9d61Sreyk 	struct imsgbuf		*ibuf;
61445ae9d61Sreyk 	struct imsg		 imsg;
61545ae9d61Sreyk 	ssize_t			 n;
61645ae9d61Sreyk 	int			 verbose;
617fc7fd3e3Sreyk 	const char		*title;
618a7dbf4aeStobhe 	struct privsep_fd	 pf;
61945ae9d61Sreyk 
620fc7fd3e3Sreyk 	title = ps->ps_title[privsep_process];
62145ae9d61Sreyk 	ibuf = &iev->ibuf;
62245ae9d61Sreyk 
62345ae9d61Sreyk 	if (event & EV_READ) {
624668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
625dd7efffeSclaudio 			fatal("%s: imsgbuf_read", __func__);
62645ae9d61Sreyk 		if (n == 0) {
62745ae9d61Sreyk 			/* this pipe is dead, so remove the event handler */
62845ae9d61Sreyk 			event_del(&iev->ev);
62945ae9d61Sreyk 			event_loopexit(NULL);
63045ae9d61Sreyk 			return;
63145ae9d61Sreyk 		}
63245ae9d61Sreyk 	}
63345ae9d61Sreyk 
63445ae9d61Sreyk 	if (event & EV_WRITE) {
635dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
636e3b6409cSclaudio 			if (errno == EPIPE) {	/* Connection closed. */
637a7dbf4aeStobhe 				event_del(&iev->ev);
638a7dbf4aeStobhe 				event_loopexit(NULL);
639a7dbf4aeStobhe 				return;
640e3b6409cSclaudio 			} else
641dd7efffeSclaudio 				fatal("imsgbuf_write");
642a7dbf4aeStobhe 		}
64345ae9d61Sreyk 	}
64445ae9d61Sreyk 
64545ae9d61Sreyk 	for (;;) {
64645ae9d61Sreyk 		if ((n = imsg_get(ibuf, &imsg)) == -1)
647a7dbf4aeStobhe 			fatal("%s: imsg_get", __func__);
64845ae9d61Sreyk 		if (n == 0)
64945ae9d61Sreyk 			break;
65045ae9d61Sreyk 
651bf556abcSreyk #if DEBUG > 1
65266094308Sreyk 		log_debug("%s: %s %d got imsg %d peerid %d from %s %d",
653bf556abcSreyk 		    __func__, title, ps->ps_instance + 1,
654a7dbf4aeStobhe 		    imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid);
655bf556abcSreyk #endif
656bf556abcSreyk 
65745ae9d61Sreyk 		/*
65845ae9d61Sreyk 		 * Check the message with the program callback
65945ae9d61Sreyk 		 */
660701048fbSreyk 		if ((p->p_cb)(fd, p, &imsg) == 0) {
66145ae9d61Sreyk 			/* Message was handled by the callback, continue */
66245ae9d61Sreyk 			imsg_free(&imsg);
66345ae9d61Sreyk 			continue;
66445ae9d61Sreyk 		}
66545ae9d61Sreyk 
66645ae9d61Sreyk 		/*
66745ae9d61Sreyk 		 * Generic message handling
66845ae9d61Sreyk 		 */
66945ae9d61Sreyk 		switch (imsg.hdr.type) {
67045ae9d61Sreyk 		case IMSG_CTL_VERBOSE:
67145ae9d61Sreyk 			IMSG_SIZE_CHECK(&imsg, &verbose);
67245ae9d61Sreyk 			memcpy(&verbose, imsg.data, sizeof(verbose));
673871fc12cSreyk 			log_setverbose(verbose);
67445ae9d61Sreyk 			break;
675a7dbf4aeStobhe 		case IMSG_CTL_PROCFD:
676a7dbf4aeStobhe 			IMSG_SIZE_CHECK(&imsg, &pf);
677a7dbf4aeStobhe 			memcpy(&pf, imsg.data, sizeof(pf));
678fecd42b7Sclaudio 			proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid,
679a7dbf4aeStobhe 			    pf.pf_instance);
680a7dbf4aeStobhe 			break;
6818e8f56e9Stobhe 		case IMSG_CTL_PROCREADY:
6828e8f56e9Stobhe #if DEBUG
6838e8f56e9Stobhe 			log_debug("%s: ready-%s: #%d %s %d -> %s %d", __func__,
6848e8f56e9Stobhe 			    p->p_id == PROC_PARENT ? "req" : "ack",
6858e8f56e9Stobhe 			    ps->ps_connecting, p->p_title, imsg.hdr.pid,
6868e8f56e9Stobhe 			    title, ps->ps_instance + 1);
6878e8f56e9Stobhe #endif
6888e8f56e9Stobhe 			if (p->p_id == PROC_PARENT) {
6898e8f56e9Stobhe 				/* ack that we are ready */
6908e8f56e9Stobhe 				if (proc_compose_imsg(ps, PROC_PARENT, 0,
6918e8f56e9Stobhe 				    IMSG_CTL_PROCREADY, -1, -1, NULL, 0) == -1)
6928e8f56e9Stobhe 					fatal("%s: proc_compose_imsg", __func__);
6938e8f56e9Stobhe 			} else {
6948e8f56e9Stobhe 				/* parent received ack */
6958e8f56e9Stobhe 				if (ps->ps_connecting == 0)
6968e8f56e9Stobhe 					fatalx("%s: wrong acks", __func__);
6978e8f56e9Stobhe 				if (ps->ps_instance != 0)
6988e8f56e9Stobhe 					fatalx("%s: wrong instance %d",
6998e8f56e9Stobhe 					    __func__, ps->ps_instance);
7008e8f56e9Stobhe 				if (ps->ps_connected == NULL)
7018e8f56e9Stobhe 					fatalx("%s: missing callback", __func__);
7028e8f56e9Stobhe 				if (--ps->ps_connecting == 0) {
7038e8f56e9Stobhe 					log_debug("%s: all connected", __func__);
7048e8f56e9Stobhe 					ps->ps_connected(ps);
7058e8f56e9Stobhe 				}
7068e8f56e9Stobhe 			}
7078e8f56e9Stobhe 			break;
70845ae9d61Sreyk 		default:
709a7dbf4aeStobhe 			fatalx("%s: %s %d got invalid imsg %d peerid %d "
71066094308Sreyk 			    "from %s %d",
711bf556abcSreyk 			    __func__, title, ps->ps_instance + 1,
71266094308Sreyk 			    imsg.hdr.type, imsg.hdr.peerid,
713a7dbf4aeStobhe 			    p->p_title, imsg.hdr.pid);
71445ae9d61Sreyk 		}
71545ae9d61Sreyk 		imsg_free(&imsg);
71645ae9d61Sreyk 	}
71745ae9d61Sreyk 	imsg_event_add(iev);
71845ae9d61Sreyk }
719fc7fd3e3Sreyk 
720ebfc3693Sreyk int
721ebfc3693Sreyk proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg)
722ebfc3693Sreyk {
723ebfc3693Sreyk 	return (-1);
724ebfc3693Sreyk }
725ebfc3693Sreyk 
726bf556abcSreyk /*
727bf556abcSreyk  * imsg helper functions
728bf556abcSreyk  */
729bf556abcSreyk 
730fc7fd3e3Sreyk void
731fc7fd3e3Sreyk imsg_event_add(struct imsgev *iev)
732fc7fd3e3Sreyk {
733fc7fd3e3Sreyk 	if (iev->handler == NULL) {
734dd7efffeSclaudio 		imsgbuf_flush(&iev->ibuf);
735fc7fd3e3Sreyk 		return;
736fc7fd3e3Sreyk 	}
737fc7fd3e3Sreyk 
738fc7fd3e3Sreyk 	iev->events = EV_READ;
73931be28caSclaudio 	if (imsgbuf_queuelen(&iev->ibuf) > 0)
740fc7fd3e3Sreyk 		iev->events |= EV_WRITE;
741fc7fd3e3Sreyk 
742fc7fd3e3Sreyk 	event_del(&iev->ev);
743fc7fd3e3Sreyk 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
744fc7fd3e3Sreyk 	event_add(&iev->ev, NULL);
745fc7fd3e3Sreyk }
746fc7fd3e3Sreyk 
747fc7fd3e3Sreyk int
748d09d3a7dSreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
749d09d3a7dSreyk     pid_t pid, int fd, void *data, uint16_t datalen)
750fc7fd3e3Sreyk {
751fc7fd3e3Sreyk 	int	ret;
752fc7fd3e3Sreyk 
753fc7fd3e3Sreyk 	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
754fc7fd3e3Sreyk 	    pid, fd, data, datalen)) == -1)
755fc7fd3e3Sreyk 		return (ret);
756fc7fd3e3Sreyk 	imsg_event_add(iev);
757fc7fd3e3Sreyk 	return (ret);
758fc7fd3e3Sreyk }
759fc7fd3e3Sreyk 
760fc7fd3e3Sreyk int
761d09d3a7dSreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
762fc7fd3e3Sreyk     pid_t pid, int fd, const struct iovec *iov, int iovcnt)
763fc7fd3e3Sreyk {
764fc7fd3e3Sreyk 	int	ret;
765fc7fd3e3Sreyk 
766fc7fd3e3Sreyk 	if ((ret = imsg_composev(&iev->ibuf, type, peerid,
767fc7fd3e3Sreyk 	    pid, fd, iov, iovcnt)) == -1)
768fc7fd3e3Sreyk 		return (ret);
769fc7fd3e3Sreyk 	imsg_event_add(iev);
770fc7fd3e3Sreyk 	return (ret);
771fc7fd3e3Sreyk }
772fc7fd3e3Sreyk 
773bf556abcSreyk void
774bf556abcSreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
775bf556abcSreyk {
776bf556abcSreyk 	if (*n == -1) {
777bf556abcSreyk 		/* Use a range of all target instances */
778bf556abcSreyk 		*n = 0;
779bf556abcSreyk 		*m = ps->ps_instances[id];
780bf556abcSreyk 	} else {
781bf556abcSreyk 		/* Use only a single slot of the specified peer process */
782bf556abcSreyk 		*m = *n + 1;
783bf556abcSreyk 	}
784bf556abcSreyk }
785bf556abcSreyk 
786fc7fd3e3Sreyk int
787bf556abcSreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
788c205e972Sreyk     uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen)
789fc7fd3e3Sreyk {
790bf556abcSreyk 	int	 m;
791bf556abcSreyk 
792bf556abcSreyk 	proc_range(ps, id, &n, &m);
793bf556abcSreyk 	for (; n < m; n++) {
794bf556abcSreyk 		if (imsg_compose_event(&ps->ps_ievs[id][n],
795a7dbf4aeStobhe 		    type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1)
796bf556abcSreyk 			return (-1);
797bf556abcSreyk 	}
798bf556abcSreyk 
799bf556abcSreyk 	return (0);
800fc7fd3e3Sreyk }
801fc7fd3e3Sreyk 
802fc7fd3e3Sreyk int
803c205e972Sreyk proc_compose(struct privsep *ps, enum privsep_procid id,
804c205e972Sreyk     uint16_t type, void *data, uint16_t datalen)
805c205e972Sreyk {
806c205e972Sreyk 	return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen));
807c205e972Sreyk }
808c205e972Sreyk 
809c205e972Sreyk int
810bf556abcSreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
811c205e972Sreyk     uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt)
812fc7fd3e3Sreyk {
813bf556abcSreyk 	int	 m;
814bf556abcSreyk 
815bf556abcSreyk 	proc_range(ps, id, &n, &m);
816bf556abcSreyk 	for (; n < m; n++)
817bf556abcSreyk 		if (imsg_composev_event(&ps->ps_ievs[id][n],
818a7dbf4aeStobhe 		    type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1)
819bf556abcSreyk 			return (-1);
820bf556abcSreyk 
821bf556abcSreyk 	return (0);
822fc7fd3e3Sreyk }
823fc7fd3e3Sreyk 
824fc7fd3e3Sreyk int
825c205e972Sreyk proc_composev(struct privsep *ps, enum privsep_procid id,
826c205e972Sreyk     uint16_t type, const struct iovec *iov, int iovcnt)
827c205e972Sreyk {
828c205e972Sreyk 	return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt));
829c205e972Sreyk }
830c205e972Sreyk 
831c205e972Sreyk int
832bf556abcSreyk proc_forward_imsg(struct privsep *ps, struct imsg *imsg,
833bf556abcSreyk     enum privsep_procid id, int n)
834fc7fd3e3Sreyk {
835bf556abcSreyk 	return (proc_compose_imsg(ps, id, n, imsg->hdr.type,
836fecd42b7Sclaudio 	    imsg->hdr.peerid, -1, imsg->data, IMSG_DATA_SIZE(imsg)));
837fc7fd3e3Sreyk }
838bf556abcSreyk 
839bf556abcSreyk struct imsgbuf *
840bf556abcSreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
841bf556abcSreyk {
842bf556abcSreyk 	int	 m;
843bf556abcSreyk 
844bf556abcSreyk 	proc_range(ps, id, &n, &m);
845bf556abcSreyk 	return (&ps->ps_ievs[id][n].ibuf);
846bf556abcSreyk }
847bf556abcSreyk 
848bf556abcSreyk struct imsgev *
849bf556abcSreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n)
850bf556abcSreyk {
851bf556abcSreyk 	int	 m;
852bf556abcSreyk 
853bf556abcSreyk 	proc_range(ps, id, &n, &m);
854bf556abcSreyk 	return (&ps->ps_ievs[id][n]);
855bf556abcSreyk }
856a7dbf4aeStobhe 
857a7dbf4aeStobhe /* This function should only be called with care as it breaks async I/O */
858a7dbf4aeStobhe int
859a7dbf4aeStobhe proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n)
860a7dbf4aeStobhe {
861a7dbf4aeStobhe 	struct imsgbuf	*ibuf;
862a7dbf4aeStobhe 	int		 m, ret = 0;
863a7dbf4aeStobhe 
864a7dbf4aeStobhe 	proc_range(ps, id, &n, &m);
865a7dbf4aeStobhe 	for (; n < m; n++) {
866a7dbf4aeStobhe 		if ((ibuf = proc_ibuf(ps, id, n)) == NULL)
867a7dbf4aeStobhe 			return (-1);
868dd7efffeSclaudio 		if ((ret = imsgbuf_flush(ibuf)) == -1)
869a7dbf4aeStobhe 			break;
870a7dbf4aeStobhe 		imsg_event_add(&ps->ps_ievs[id][n]);
871a7dbf4aeStobhe 	}
872a7dbf4aeStobhe 
873a7dbf4aeStobhe 	return (ret);
874a7dbf4aeStobhe }
875