xref: /openbsd-src/usr.sbin/snmpd/proc.c (revision 6676295ff107abd6850f48ae428e19963f4b1dc5)
1*6676295fSclaudio /*	$OpenBSD: proc.c,v 1.39 2024/11/21 13:38:45 claudio Exp $	*/
260996bedSreyk 
360996bedSreyk /*
47be2c8aaSrzalamena  * Copyright (c) 2010 - 2016 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/socket.h>
2160996bedSreyk #include <sys/wait.h>
2260996bedSreyk 
237be2c8aaSrzalamena #include <fcntl.h>
2460996bedSreyk #include <stdio.h>
2560996bedSreyk #include <stdlib.h>
26a9292d2aSmartijn #include <stdint.h>
2760996bedSreyk #include <unistd.h>
2821032f8aSreyk #include <string.h>
2921032f8aSreyk #include <errno.h>
3021032f8aSreyk #include <signal.h>
319f020842Sbluhm #include <paths.h>
3260996bedSreyk #include <pwd.h>
3321032f8aSreyk #include <event.h>
340f12961aSreyk #include <imsg.h>
3560996bedSreyk 
36a9292d2aSmartijn #include "log.h"
3760996bedSreyk #include "snmpd.h"
3860996bedSreyk 
399f020842Sbluhm void	 proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int,
402269e292Stobhe 	    char **);
417be2c8aaSrzalamena void	 proc_setup(struct privsep *, struct privsep_proc *, unsigned int);
427be2c8aaSrzalamena void	 proc_open(struct privsep *, int, int);
437be2c8aaSrzalamena void	 proc_accept(struct privsep *, int, enum privsep_procid,
447be2c8aaSrzalamena 	    unsigned int);
4521032f8aSreyk void	 proc_close(struct privsep *);
4660996bedSreyk void	 proc_shutdown(struct privsep_proc *);
4760996bedSreyk void	 proc_sig_handler(int, short, void *);
4821032f8aSreyk void	 proc_range(struct privsep *, enum privsep_procid, int *, int *);
493f75f466Sreyk int	 proc_dispatch_null(int, struct privsep_proc *, struct imsg *);
5060996bedSreyk 
517be2c8aaSrzalamena enum privsep_procid
527be2c8aaSrzalamena proc_getid(struct privsep_proc *procs, unsigned int nproc,
537be2c8aaSrzalamena     const char *proc_name)
5421032f8aSreyk {
557be2c8aaSrzalamena 	struct privsep_proc	*p;
567be2c8aaSrzalamena 	unsigned int		 proc;
577be2c8aaSrzalamena 
587be2c8aaSrzalamena 	for (proc = 0; proc < nproc; proc++) {
597be2c8aaSrzalamena 		p = &procs[proc];
607be2c8aaSrzalamena 		if (strcmp(p->p_title, proc_name))
617be2c8aaSrzalamena 			continue;
627be2c8aaSrzalamena 
637be2c8aaSrzalamena 		return (p->p_id);
647be2c8aaSrzalamena 	}
657be2c8aaSrzalamena 
667be2c8aaSrzalamena 	return (PROC_MAX);
677be2c8aaSrzalamena }
687be2c8aaSrzalamena 
697be2c8aaSrzalamena void
707be2c8aaSrzalamena proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
712269e292Stobhe     int argc, char **argv)
727be2c8aaSrzalamena {
737be2c8aaSrzalamena 	unsigned int		 proc, nargc, i, proc_i;
747be2c8aaSrzalamena 	char			**nargv;
757be2c8aaSrzalamena 	struct privsep_proc	*p;
767be2c8aaSrzalamena 	char			 num[32];
777be2c8aaSrzalamena 	int			 fd;
787be2c8aaSrzalamena 
797be2c8aaSrzalamena 	/* Prepare the new process argv. */
807be2c8aaSrzalamena 	nargv = calloc(argc + 5, sizeof(char *));
817be2c8aaSrzalamena 	if (nargv == NULL)
827be2c8aaSrzalamena 		fatal("%s: calloc", __func__);
837be2c8aaSrzalamena 
847be2c8aaSrzalamena 	/* Copy call argument first. */
857be2c8aaSrzalamena 	nargc = 0;
867be2c8aaSrzalamena 	nargv[nargc++] = argv[0];
877be2c8aaSrzalamena 
887be2c8aaSrzalamena 	/* Set process name argument and save the position. */
897be2c8aaSrzalamena 	nargv[nargc++] = "-P";
907be2c8aaSrzalamena 	proc_i = nargc;
917be2c8aaSrzalamena 	nargc++;
927be2c8aaSrzalamena 
937be2c8aaSrzalamena 	/* Point process instance arg to stack and copy the original args. */
947be2c8aaSrzalamena 	nargv[nargc++] = "-I";
957be2c8aaSrzalamena 	nargv[nargc++] = num;
967be2c8aaSrzalamena 	for (i = 1; i < (unsigned int) argc; i++)
977be2c8aaSrzalamena 		nargv[nargc++] = argv[i];
987be2c8aaSrzalamena 
997be2c8aaSrzalamena 	nargv[nargc] = NULL;
1007be2c8aaSrzalamena 
1017be2c8aaSrzalamena 	for (proc = 0; proc < nproc; proc++) {
1027be2c8aaSrzalamena 		p = &procs[proc];
1037be2c8aaSrzalamena 
1047be2c8aaSrzalamena 		/* Update args with process title. */
1057be2c8aaSrzalamena 		nargv[proc_i] = (char *)(uintptr_t)p->p_title;
1067be2c8aaSrzalamena 
1077be2c8aaSrzalamena 		/* Fire children processes. */
1087be2c8aaSrzalamena 		for (i = 0; i < ps->ps_instances[p->p_id]; i++) {
1097be2c8aaSrzalamena 			/* Update the process instance number. */
1107be2c8aaSrzalamena 			snprintf(num, sizeof(num), "%u", i);
1117be2c8aaSrzalamena 
1127be2c8aaSrzalamena 			fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0];
1137be2c8aaSrzalamena 			ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1;
1147be2c8aaSrzalamena 
1157be2c8aaSrzalamena 			switch (fork()) {
1167be2c8aaSrzalamena 			case -1:
1177be2c8aaSrzalamena 				fatal("%s: fork", __func__);
1187be2c8aaSrzalamena 				break;
1197be2c8aaSrzalamena 			case 0:
1207be2c8aaSrzalamena 				/* Prepare parent socket. */
1217be2c8aaSrzalamena 				if (fd != PROC_PARENT_SOCK_FILENO) {
1227be2c8aaSrzalamena 					if (dup2(fd, PROC_PARENT_SOCK_FILENO)
1237be2c8aaSrzalamena 					    == -1)
1247be2c8aaSrzalamena 						fatal("dup2");
1257be2c8aaSrzalamena 				} else if (fcntl(fd, F_SETFD, 0) == -1)
1267be2c8aaSrzalamena 					fatal("fcntl");
1277be2c8aaSrzalamena 
1287be2c8aaSrzalamena 				execvp(argv[0], nargv);
1297be2c8aaSrzalamena 				fatal("%s: execvp", __func__);
1307be2c8aaSrzalamena 				break;
1317be2c8aaSrzalamena 			default:
1327be2c8aaSrzalamena 				/* Close child end. */
1337be2c8aaSrzalamena 				close(fd);
1347be2c8aaSrzalamena 				break;
1357be2c8aaSrzalamena 			}
1367be2c8aaSrzalamena 		}
1377be2c8aaSrzalamena 	}
1387be2c8aaSrzalamena 	free(nargv);
1397be2c8aaSrzalamena }
1407be2c8aaSrzalamena 
1417be2c8aaSrzalamena void
1427be2c8aaSrzalamena proc_connect(struct privsep *ps)
1437be2c8aaSrzalamena {
1447be2c8aaSrzalamena 	struct imsgev		*iev;
1457be2c8aaSrzalamena 	unsigned int		 src, dst, inst;
1467be2c8aaSrzalamena 
1477be2c8aaSrzalamena 	/* Don't distribute any sockets if we are not really going to run. */
1487be2c8aaSrzalamena 	if (ps->ps_noaction)
1497be2c8aaSrzalamena 		return;
1507be2c8aaSrzalamena 
1517be2c8aaSrzalamena 	for (dst = 0; dst < PROC_MAX; dst++) {
1527be2c8aaSrzalamena 		/* We don't communicate with ourselves. */
1537be2c8aaSrzalamena 		if (dst == PROC_PARENT)
1547be2c8aaSrzalamena 			continue;
1557be2c8aaSrzalamena 
1567be2c8aaSrzalamena 		for (inst = 0; inst < ps->ps_instances[dst]; inst++) {
1577be2c8aaSrzalamena 			iev = &ps->ps_ievs[dst][inst];
158*6676295fSclaudio 			if (imsgbuf_init(&iev->ibuf,
159*6676295fSclaudio 			    ps->ps_pp->pp_pipes[dst][inst]) == -1)
160*6676295fSclaudio 				fatal("imsgbuf_init");
161*6676295fSclaudio 			imsgbuf_allow_fdpass(&iev->ibuf);
1627be2c8aaSrzalamena 			event_set(&iev->ev, iev->ibuf.fd, iev->events,
1637be2c8aaSrzalamena 			    iev->handler, iev->data);
1647be2c8aaSrzalamena 			event_add(&iev->ev, NULL);
1657be2c8aaSrzalamena 		}
1667be2c8aaSrzalamena 	}
1677be2c8aaSrzalamena 
1687be2c8aaSrzalamena 	/* Distribute the socketpair()s for everyone. */
1697be2c8aaSrzalamena 	for (src = 0; src < PROC_MAX; src++)
1707be2c8aaSrzalamena 		for (dst = src; dst < PROC_MAX; dst++) {
1717be2c8aaSrzalamena 			/* Parent already distributed its fds. */
1727be2c8aaSrzalamena 			if (src == PROC_PARENT || dst == PROC_PARENT)
1737be2c8aaSrzalamena 				continue;
1747be2c8aaSrzalamena 
1757be2c8aaSrzalamena 			proc_open(ps, src, dst);
1767be2c8aaSrzalamena 		}
1777be2c8aaSrzalamena }
1787be2c8aaSrzalamena 
1797be2c8aaSrzalamena void
1807be2c8aaSrzalamena proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
1819f020842Sbluhm     int debug, int argc, char **argv, enum privsep_procid proc_id)
1827be2c8aaSrzalamena {
1837be2c8aaSrzalamena 	struct privsep_proc	*p = NULL;
1847be2c8aaSrzalamena 	struct privsep_pipes	*pa, *pb;
1857be2c8aaSrzalamena 	unsigned int		 proc;
1867be2c8aaSrzalamena 	unsigned int		 dst;
1877be2c8aaSrzalamena 	int			 fds[2];
1887be2c8aaSrzalamena 
1897be2c8aaSrzalamena 	/* Don't initiate anything if we are not really going to run. */
1907be2c8aaSrzalamena 	if (ps->ps_noaction)
1917be2c8aaSrzalamena 		return;
1927be2c8aaSrzalamena 
1937be2c8aaSrzalamena 	if (proc_id == PROC_PARENT) {
1947be2c8aaSrzalamena 		privsep_process = PROC_PARENT;
1957be2c8aaSrzalamena 		proc_setup(ps, procs, nproc);
1967be2c8aaSrzalamena 
19728fef5efStobhe 		if (!debug && daemon(0, 0) == -1)
19828fef5efStobhe 			fatal("failed to daemonize");
19928fef5efStobhe 
2007be2c8aaSrzalamena 		/*
2017be2c8aaSrzalamena 		 * Create the children sockets so we can use them
2027be2c8aaSrzalamena 		 * to distribute the rest of the socketpair()s using
2037be2c8aaSrzalamena 		 * proc_connect() later.
2047be2c8aaSrzalamena 		 */
2057be2c8aaSrzalamena 		for (dst = 0; dst < PROC_MAX; dst++) {
2067be2c8aaSrzalamena 			/* Don't create socket for ourselves. */
2077be2c8aaSrzalamena 			if (dst == PROC_PARENT)
2087be2c8aaSrzalamena 				continue;
2097be2c8aaSrzalamena 
2107be2c8aaSrzalamena 			for (proc = 0; proc < ps->ps_instances[dst]; proc++) {
2117be2c8aaSrzalamena 				pa = &ps->ps_pipes[PROC_PARENT][0];
2127be2c8aaSrzalamena 				pb = &ps->ps_pipes[dst][proc];
2137be2c8aaSrzalamena 				if (socketpair(AF_UNIX,
2147be2c8aaSrzalamena 				    SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
2157be2c8aaSrzalamena 				    PF_UNSPEC, fds) == -1)
2167be2c8aaSrzalamena 					fatal("%s: socketpair", __func__);
2177be2c8aaSrzalamena 
2187be2c8aaSrzalamena 				pa->pp_pipes[dst][proc] = fds[0];
2197be2c8aaSrzalamena 				pb->pp_pipes[PROC_PARENT][0] = fds[1];
2207be2c8aaSrzalamena 			}
2217be2c8aaSrzalamena 		}
2227be2c8aaSrzalamena 
2237be2c8aaSrzalamena 		/* Engage! */
2242269e292Stobhe 		proc_exec(ps, procs, nproc, argc, argv);
2257be2c8aaSrzalamena 		return;
2267be2c8aaSrzalamena 	}
2277be2c8aaSrzalamena 
2287be2c8aaSrzalamena 	/* Initialize a child */
2297be2c8aaSrzalamena 	for (proc = 0; proc < nproc; proc++) {
2307be2c8aaSrzalamena 		if (procs[proc].p_id != proc_id)
2317be2c8aaSrzalamena 			continue;
2327be2c8aaSrzalamena 		p = &procs[proc];
2337be2c8aaSrzalamena 		break;
2347be2c8aaSrzalamena 	}
2357be2c8aaSrzalamena 	if (p == NULL || p->p_init == NULL)
2367be2c8aaSrzalamena 		fatalx("%s: process %d missing process initialization",
2377be2c8aaSrzalamena 		    __func__, proc_id);
2387be2c8aaSrzalamena 
2397be2c8aaSrzalamena 	p->p_init(ps, p);
2407be2c8aaSrzalamena 
2417be2c8aaSrzalamena 	fatalx("failed to initiate child process");
2427be2c8aaSrzalamena }
2437be2c8aaSrzalamena 
2447be2c8aaSrzalamena void
2457be2c8aaSrzalamena proc_accept(struct privsep *ps, int fd, enum privsep_procid dst,
2467be2c8aaSrzalamena     unsigned int n)
2477be2c8aaSrzalamena {
2487be2c8aaSrzalamena 	struct privsep_pipes	*pp = ps->ps_pp;
2497be2c8aaSrzalamena 	struct imsgev		*iev;
2507be2c8aaSrzalamena 
2517be2c8aaSrzalamena 	if (ps->ps_ievs[dst] == NULL) {
2527be2c8aaSrzalamena #if DEBUG > 1
2537be2c8aaSrzalamena 		log_debug("%s: %s src %d %d to dst %d %d not connected",
2547be2c8aaSrzalamena 		    __func__, ps->ps_title[privsep_process],
2557be2c8aaSrzalamena 		    privsep_process, ps->ps_instance + 1,
2567be2c8aaSrzalamena 		    dst, n + 1);
2577be2c8aaSrzalamena #endif
2587be2c8aaSrzalamena 		close(fd);
2597be2c8aaSrzalamena 		return;
2607be2c8aaSrzalamena 	}
2617be2c8aaSrzalamena 
2627be2c8aaSrzalamena 	if (pp->pp_pipes[dst][n] != -1) {
2637be2c8aaSrzalamena 		log_warnx("%s: duplicated descriptor", __func__);
2647be2c8aaSrzalamena 		close(fd);
2657be2c8aaSrzalamena 		return;
2667be2c8aaSrzalamena 	} else
2677be2c8aaSrzalamena 		pp->pp_pipes[dst][n] = fd;
2687be2c8aaSrzalamena 
2697be2c8aaSrzalamena 	iev = &ps->ps_ievs[dst][n];
270*6676295fSclaudio 	if (imsgbuf_init(&iev->ibuf, fd) == -1)
271*6676295fSclaudio 		fatal("imsgbuf_init");
272*6676295fSclaudio 	imsgbuf_allow_fdpass(&iev->ibuf);
2737be2c8aaSrzalamena 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
2747be2c8aaSrzalamena 	event_add(&iev->ev, NULL);
2757be2c8aaSrzalamena }
2767be2c8aaSrzalamena 
2777be2c8aaSrzalamena void
2787be2c8aaSrzalamena proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
2797be2c8aaSrzalamena {
2807be2c8aaSrzalamena 	unsigned int		 i, j, src, dst, id;
28121032f8aSreyk 	struct privsep_pipes	*pp;
28221032f8aSreyk 
2837be2c8aaSrzalamena 	/* Initialize parent title, ps_instances and procs. */
2847be2c8aaSrzalamena 	ps->ps_title[PROC_PARENT] = "parent";
2857be2c8aaSrzalamena 
2867be2c8aaSrzalamena 	for (src = 0; src < PROC_MAX; src++)
2877be2c8aaSrzalamena 		/* Default to 1 process instance */
2887be2c8aaSrzalamena 		if (ps->ps_instances[src] < 1)
2897be2c8aaSrzalamena 			ps->ps_instances[src] = 1;
2907be2c8aaSrzalamena 
2917be2c8aaSrzalamena 	for (src = 0; src < nproc; src++) {
2927be2c8aaSrzalamena 		procs[src].p_ps = ps;
2937be2c8aaSrzalamena 		if (procs[src].p_cb == NULL)
2947be2c8aaSrzalamena 			procs[src].p_cb = proc_dispatch_null;
2957be2c8aaSrzalamena 
2967be2c8aaSrzalamena 		id = procs[src].p_id;
2977be2c8aaSrzalamena 		ps->ps_title[id] = procs[src].p_title;
2987be2c8aaSrzalamena 		if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id],
2997be2c8aaSrzalamena 		    sizeof(struct imsgev))) == NULL)
3007be2c8aaSrzalamena 			fatal("%s: calloc", __func__);
3017be2c8aaSrzalamena 
302dd7efffeSclaudio 		/* With this set up, we are ready to call imsgbuf_init(). */
3037be2c8aaSrzalamena 		for (i = 0; i < ps->ps_instances[id]; i++) {
3047be2c8aaSrzalamena 			ps->ps_ievs[id][i].handler = proc_dispatch;
3057be2c8aaSrzalamena 			ps->ps_ievs[id][i].events = EV_READ;
3067be2c8aaSrzalamena 			ps->ps_ievs[id][i].proc = &procs[src];
3077be2c8aaSrzalamena 			ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i];
3087be2c8aaSrzalamena 		}
3097be2c8aaSrzalamena 	}
3107be2c8aaSrzalamena 
31160996bedSreyk 	/*
31221032f8aSreyk 	 * Allocate pipes for all process instances (incl. parent)
31321032f8aSreyk 	 *
31421032f8aSreyk 	 * - ps->ps_pipes: N:M mapping
31521032f8aSreyk 	 * N source processes connected to M destination processes:
31621032f8aSreyk 	 * [src][instances][dst][instances], for example
31721032f8aSreyk 	 * [PROC_RELAY][3][PROC_CA][3]
31821032f8aSreyk 	 *
31921032f8aSreyk 	 * - ps->ps_pp: per-process 1:M part of ps->ps_pipes
32021032f8aSreyk 	 * Each process instance has a destination array of socketpair fds:
32121032f8aSreyk 	 * [dst][instances], for example
32221032f8aSreyk 	 * [PROC_PARENT][0]
32321032f8aSreyk 	 */
32421032f8aSreyk 	for (src = 0; src < PROC_MAX; src++) {
32521032f8aSreyk 		/* Allocate destination array for each process */
3267be2c8aaSrzalamena 		if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src],
32721032f8aSreyk 		    sizeof(struct privsep_pipes))) == NULL)
3287be2c8aaSrzalamena 			fatal("%s: calloc", __func__);
32921032f8aSreyk 
3307be2c8aaSrzalamena 		for (i = 0; i < ps->ps_instances[src]; i++) {
33121032f8aSreyk 			pp = &ps->ps_pipes[src][i];
33221032f8aSreyk 
33321032f8aSreyk 			for (dst = 0; dst < PROC_MAX; dst++) {
33421032f8aSreyk 				/* Allocate maximum fd integers */
33521032f8aSreyk 				if ((pp->pp_pipes[dst] =
3367be2c8aaSrzalamena 				    calloc(ps->ps_instances[dst],
33721032f8aSreyk 				    sizeof(int))) == NULL)
3387be2c8aaSrzalamena 					fatal("%s: calloc", __func__);
33921032f8aSreyk 
34021032f8aSreyk 				/* Mark fd as unused */
3417be2c8aaSrzalamena 				for (j = 0; j < ps->ps_instances[dst]; j++)
34221032f8aSreyk 					pp->pp_pipes[dst][j] = -1;
34321032f8aSreyk 			}
34421032f8aSreyk 		}
34521032f8aSreyk 	}
34621032f8aSreyk 
3477be2c8aaSrzalamena 	ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance];
34860996bedSreyk }
34960996bedSreyk 
35060996bedSreyk void
35160996bedSreyk proc_kill(struct privsep *ps)
35260996bedSreyk {
3537be2c8aaSrzalamena 	char		*cause;
35460996bedSreyk 	pid_t		 pid;
3557be2c8aaSrzalamena 	int		 len, status;
35660996bedSreyk 
35760996bedSreyk 	if (privsep_process != PROC_PARENT)
35860996bedSreyk 		return;
35960996bedSreyk 
3607be2c8aaSrzalamena 	proc_close(ps);
36160996bedSreyk 
36260996bedSreyk 	do {
3637be2c8aaSrzalamena 		pid = waitpid(WAIT_ANY, &status, 0);
3647be2c8aaSrzalamena 		if (pid <= 0)
3657be2c8aaSrzalamena 			continue;
36621032f8aSreyk 
3677be2c8aaSrzalamena 		if (WIFSIGNALED(status)) {
3687be2c8aaSrzalamena 			len = asprintf(&cause, "terminated; signal %d",
3697be2c8aaSrzalamena 			    WTERMSIG(status));
3707be2c8aaSrzalamena 		} else if (WIFEXITED(status)) {
3717be2c8aaSrzalamena 			if (WEXITSTATUS(status) != 0)
3727be2c8aaSrzalamena 				len = asprintf(&cause, "exited abnormally");
3737be2c8aaSrzalamena 			else
3747be2c8aaSrzalamena 				len = 0;
3757be2c8aaSrzalamena 		} else
3767be2c8aaSrzalamena 			len = -1;
3777be2c8aaSrzalamena 
3787be2c8aaSrzalamena 		if (len == 0) {
3797be2c8aaSrzalamena 			/* child exited OK, don't print a warning message */
3807be2c8aaSrzalamena 		} else if (len != -1) {
3817be2c8aaSrzalamena 			log_warnx("lost child: pid %u %s", pid, cause);
3827be2c8aaSrzalamena 			free(cause);
3837be2c8aaSrzalamena 		} else
3847be2c8aaSrzalamena 			log_warnx("lost child: pid %u", pid);
3857be2c8aaSrzalamena 	} while (pid != -1 || (pid == -1 && errno == EINTR));
38660996bedSreyk }
38760996bedSreyk 
38860996bedSreyk void
3897be2c8aaSrzalamena proc_open(struct privsep *ps, int src, int dst)
39060996bedSreyk {
39121032f8aSreyk 	struct privsep_pipes	*pa, *pb;
3927be2c8aaSrzalamena 	struct privsep_fd	 pf;
39321032f8aSreyk 	int			 fds[2];
3947be2c8aaSrzalamena 	unsigned int		 i, j;
39560996bedSreyk 
3967be2c8aaSrzalamena 	/* Exchange pipes between process. */
39721032f8aSreyk 	for (i = 0; i < ps->ps_instances[src]; i++) {
3987be2c8aaSrzalamena 		for (j = 0; j < ps->ps_instances[dst]; j++) {
3997be2c8aaSrzalamena 			/* Don't create sockets for ourself. */
4007be2c8aaSrzalamena 			if (src == dst && i == j)
4017be2c8aaSrzalamena 				continue;
4027be2c8aaSrzalamena 
40321032f8aSreyk 			pa = &ps->ps_pipes[src][i];
4047be2c8aaSrzalamena 			pb = &ps->ps_pipes[dst][j];
4055f0d8540Sreyk 			if (socketpair(AF_UNIX,
4067be2c8aaSrzalamena 			    SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
40721032f8aSreyk 			    PF_UNSPEC, fds) == -1)
4087be2c8aaSrzalamena 				fatal("%s: socketpair", __func__);
40921032f8aSreyk 
4107be2c8aaSrzalamena 			pa->pp_pipes[dst][j] = fds[0];
41121032f8aSreyk 			pb->pp_pipes[src][i] = fds[1];
41260996bedSreyk 
4137be2c8aaSrzalamena 			pf.pf_procid = src;
4147be2c8aaSrzalamena 			pf.pf_instance = i;
4157be2c8aaSrzalamena 			if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD,
4167be2c8aaSrzalamena 			    -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1)
4177be2c8aaSrzalamena 				fatal("%s: proc_compose_imsg", __func__);
4187be2c8aaSrzalamena 
4197be2c8aaSrzalamena 			pf.pf_procid = dst;
4207be2c8aaSrzalamena 			pf.pf_instance = j;
4217be2c8aaSrzalamena 			if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD,
4227be2c8aaSrzalamena 			    -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1)
4237be2c8aaSrzalamena 				fatal("%s: proc_compose_imsg", __func__);
42421032f8aSreyk 
42521032f8aSreyk 			/*
4267be2c8aaSrzalamena 			 * We have to flush to send the descriptors and close
4277be2c8aaSrzalamena 			 * them to avoid the fd ramp on startup.
42821032f8aSreyk 			 */
4297be2c8aaSrzalamena 			if (proc_flush_imsg(ps, src, i) == -1 ||
4307be2c8aaSrzalamena 			    proc_flush_imsg(ps, dst, j) == -1)
431dd7efffeSclaudio 				fatal("%s: proc_flush_imsg", __func__);
43260996bedSreyk 		}
43360996bedSreyk 	}
43460996bedSreyk }
43560996bedSreyk 
43621032f8aSreyk void
43721032f8aSreyk proc_close(struct privsep *ps)
43821032f8aSreyk {
43932c142bfSreyk 	unsigned int		 dst, n;
44021032f8aSreyk 	struct privsep_pipes	*pp;
44160996bedSreyk 
44221032f8aSreyk 	if (ps == NULL)
44321032f8aSreyk 		return;
44421032f8aSreyk 
44521032f8aSreyk 	pp = ps->ps_pp;
44621032f8aSreyk 
44721032f8aSreyk 	for (dst = 0; dst < PROC_MAX; dst++) {
44821032f8aSreyk 		if (ps->ps_ievs[dst] == NULL)
44921032f8aSreyk 			continue;
45021032f8aSreyk 
45121032f8aSreyk 		for (n = 0; n < ps->ps_instances[dst]; n++) {
45221032f8aSreyk 			if (pp->pp_pipes[dst][n] == -1)
45321032f8aSreyk 				continue;
45421032f8aSreyk 
45521032f8aSreyk 			/* Cancel the fd, close and invalidate the fd */
45621032f8aSreyk 			event_del(&(ps->ps_ievs[dst][n].ev));
457dd7efffeSclaudio 			imsgbuf_clear(&(ps->ps_ievs[dst][n].ibuf));
45821032f8aSreyk 			close(pp->pp_pipes[dst][n]);
45921032f8aSreyk 			pp->pp_pipes[dst][n] = -1;
46021032f8aSreyk 		}
46121032f8aSreyk 		free(ps->ps_ievs[dst]);
46260996bedSreyk 	}
46360996bedSreyk }
46460996bedSreyk 
46560996bedSreyk void
46660996bedSreyk proc_shutdown(struct privsep_proc *p)
46760996bedSreyk {
46860996bedSreyk 	struct privsep	*ps = p->p_ps;
46921032f8aSreyk 
47060996bedSreyk 	if (p->p_shutdown != NULL)
47121032f8aSreyk 		(*p->p_shutdown)();
47260996bedSreyk 
47321032f8aSreyk 	proc_close(ps);
47460996bedSreyk 
47521032f8aSreyk 	log_info("%s exiting, pid %d", p->p_title, getpid());
47621032f8aSreyk 
4777be2c8aaSrzalamena 	exit(0);
47860996bedSreyk }
47960996bedSreyk 
48060996bedSreyk void
48160996bedSreyk proc_sig_handler(int sig, short event, void *arg)
48260996bedSreyk {
48360996bedSreyk 	struct privsep_proc	*p = arg;
48460996bedSreyk 
48560996bedSreyk 	switch (sig) {
48660996bedSreyk 	case SIGINT:
48760996bedSreyk 	case SIGTERM:
48860996bedSreyk 		proc_shutdown(p);
48960996bedSreyk 		break;
49060996bedSreyk 	case SIGCHLD:
49160996bedSreyk 	case SIGHUP:
49260996bedSreyk 	case SIGPIPE:
4935f2be52bSreyk 	case SIGUSR1:
49460996bedSreyk 		/* ignore */
49560996bedSreyk 		break;
49660996bedSreyk 	default:
497566d2cadSbenno 		fatalx("%s: unexpected signal", __func__);
49860996bedSreyk 		/* NOTREACHED */
49960996bedSreyk 	}
50060996bedSreyk }
50160996bedSreyk 
5027be2c8aaSrzalamena void
50360996bedSreyk proc_run(struct privsep *ps, struct privsep_proc *p,
50432c142bfSreyk     struct privsep_proc *procs, unsigned int nproc,
5053f75f466Sreyk     void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg)
50660996bedSreyk {
50760996bedSreyk 	struct passwd		*pw;
50860996bedSreyk 	const char		*root;
50960996bedSreyk 
5100f12961aSreyk 	log_procinit(p->p_title);
5110f12961aSreyk 
5127be2c8aaSrzalamena 	/* Use non-standard user */
5137be2c8aaSrzalamena 	if (p->p_pw != NULL)
5147be2c8aaSrzalamena 		pw = p->p_pw;
5157be2c8aaSrzalamena 	else
5167be2c8aaSrzalamena 		pw = ps->ps_pw;
5177be2c8aaSrzalamena 
51860996bedSreyk 	/* Change root directory */
51960996bedSreyk 	if (p->p_chroot != NULL)
52060996bedSreyk 		root = p->p_chroot;
52160996bedSreyk 	else
52260996bedSreyk 		root = pw->pw_dir;
52360996bedSreyk 
52460996bedSreyk 	if (chroot(root) == -1)
525566d2cadSbenno 		fatal("%s: chroot", __func__);
52660996bedSreyk 	if (chdir("/") == -1)
527566d2cadSbenno 		fatal("%s: chdir(\"/\")", __func__);
52860996bedSreyk 
52960996bedSreyk 	privsep_process = p->p_id;
53060996bedSreyk 
53160996bedSreyk 	setproctitle("%s", p->p_title);
53260996bedSreyk 
53360996bedSreyk 	if (setgroups(1, &pw->pw_gid) ||
53460996bedSreyk 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
53560996bedSreyk 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
536566d2cadSbenno 		fatal("%s: cannot drop privileges", __func__);
53760996bedSreyk 
53860996bedSreyk 	event_init();
53960996bedSreyk 
54060996bedSreyk 	signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
54160996bedSreyk 	signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
54260996bedSreyk 	signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
54360996bedSreyk 	signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
54460996bedSreyk 	signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p);
5455f2be52bSreyk 	signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p);
54660996bedSreyk 
54760996bedSreyk 	signal_add(&ps->ps_evsigint, NULL);
54860996bedSreyk 	signal_add(&ps->ps_evsigterm, NULL);
54960996bedSreyk 	signal_add(&ps->ps_evsigchld, NULL);
55060996bedSreyk 	signal_add(&ps->ps_evsighup, NULL);
55160996bedSreyk 	signal_add(&ps->ps_evsigpipe, NULL);
5525f2be52bSreyk 	signal_add(&ps->ps_evsigusr1, NULL);
55360996bedSreyk 
5547be2c8aaSrzalamena 	proc_setup(ps, procs, nproc);
5557be2c8aaSrzalamena 	proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0);
5567be2c8aaSrzalamena 	DPRINTF("%s: %s %d/%d, pid %d", __func__, p->p_title,
5577be2c8aaSrzalamena 	    ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
5587be2c8aaSrzalamena 
5593f75f466Sreyk 	if (run != NULL)
5603f75f466Sreyk 		run(ps, p, arg);
56160996bedSreyk 
56260996bedSreyk 	event_dispatch();
56360996bedSreyk 
56460996bedSreyk 	proc_shutdown(p);
56560996bedSreyk }
56660996bedSreyk 
56760996bedSreyk void
56860996bedSreyk proc_dispatch(int fd, short event, void *arg)
56960996bedSreyk {
57021032f8aSreyk 	struct imsgev		*iev = arg;
57121032f8aSreyk 	struct privsep_proc	*p = iev->proc;
57260996bedSreyk 	struct privsep		*ps = p->p_ps;
57360996bedSreyk 	struct imsgbuf		*ibuf;
57460996bedSreyk 	struct imsg		 imsg;
57560996bedSreyk 	ssize_t			 n;
57660996bedSreyk 	int			 verbose;
57760996bedSreyk 	const char		*title;
5787be2c8aaSrzalamena 	struct privsep_fd	 pf;
57960996bedSreyk 
58060996bedSreyk 	title = ps->ps_title[privsep_process];
58160996bedSreyk 	ibuf = &iev->ibuf;
58260996bedSreyk 
58360996bedSreyk 	if (event & EV_READ) {
584668e5ba9Sclaudio 		if ((n = imsgbuf_read(ibuf)) == -1)
585dd7efffeSclaudio 			fatal("%s: imsgbuf_read", __func__);
58660996bedSreyk 		if (n == 0) {
58760996bedSreyk 			/* this pipe is dead, so remove the event handler */
58860996bedSreyk 			event_del(&iev->ev);
58960996bedSreyk 			event_loopexit(NULL);
59060996bedSreyk 			return;
59160996bedSreyk 		}
59260996bedSreyk 	}
59360996bedSreyk 
59460996bedSreyk 	if (event & EV_WRITE) {
595dd7efffeSclaudio 		if (imsgbuf_write(ibuf) == -1) {
596c1aa9554Sclaudio 			if (errno == EPIPE) {
597c1aa9554Sclaudio 				/* this pipe is dead, remove the handler */
5987be2c8aaSrzalamena 				event_del(&iev->ev);
5997be2c8aaSrzalamena 				event_loopexit(NULL);
6007be2c8aaSrzalamena 				return;
6017be2c8aaSrzalamena 			}
602dd7efffeSclaudio 			fatal("%s: imsgbuf_write", __func__);
603c1aa9554Sclaudio 		}
60460996bedSreyk 	}
60560996bedSreyk 
60660996bedSreyk 	for (;;) {
60760996bedSreyk 		if ((n = imsg_get(ibuf, &imsg)) == -1)
6087be2c8aaSrzalamena 			fatal("%s: imsg_get", __func__);
60960996bedSreyk 		if (n == 0)
61060996bedSreyk 			break;
61160996bedSreyk 
61221032f8aSreyk #if DEBUG > 1
61366094308Sreyk 		log_debug("%s: %s %d got imsg %d peerid %d from %s %d",
61421032f8aSreyk 		    __func__, title, ps->ps_instance + 1,
6157be2c8aaSrzalamena 		    imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid);
61621032f8aSreyk #endif
61721032f8aSreyk 
61860996bedSreyk 		/*
61960996bedSreyk 		 * Check the message with the program callback
62060996bedSreyk 		 */
62160996bedSreyk 		if ((p->p_cb)(fd, p, &imsg) == 0) {
62260996bedSreyk 			/* Message was handled by the callback, continue */
62360996bedSreyk 			imsg_free(&imsg);
62460996bedSreyk 			continue;
62560996bedSreyk 		}
62660996bedSreyk 
62760996bedSreyk 		/*
62860996bedSreyk 		 * Generic message handling
62960996bedSreyk 		 */
63060996bedSreyk 		switch (imsg.hdr.type) {
63160996bedSreyk 		case IMSG_CTL_VERBOSE:
63260996bedSreyk 			IMSG_SIZE_CHECK(&imsg, &verbose);
63360996bedSreyk 			memcpy(&verbose, imsg.data, sizeof(verbose));
634871fc12cSreyk 			log_setverbose(verbose);
63560996bedSreyk 			break;
6367be2c8aaSrzalamena 		case IMSG_CTL_PROCFD:
6377be2c8aaSrzalamena 			IMSG_SIZE_CHECK(&imsg, &pf);
6387be2c8aaSrzalamena 			memcpy(&pf, imsg.data, sizeof(pf));
639e60163e5Sclaudio 			proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid,
6407be2c8aaSrzalamena 			    pf.pf_instance);
6417be2c8aaSrzalamena 			break;
64260996bedSreyk 		default:
6437be2c8aaSrzalamena 			fatalx("%s: %s %d got invalid imsg %d peerid %d "
64466094308Sreyk 			    "from %s %d",
64521032f8aSreyk 			    __func__, title, ps->ps_instance + 1,
64666094308Sreyk 			    imsg.hdr.type, imsg.hdr.peerid,
6477be2c8aaSrzalamena 			    p->p_title, imsg.hdr.pid);
64860996bedSreyk 		}
64960996bedSreyk 		imsg_free(&imsg);
65060996bedSreyk 	}
65160996bedSreyk 	imsg_event_add(iev);
65260996bedSreyk }
65360996bedSreyk 
6543f75f466Sreyk int
6553f75f466Sreyk proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg)
6563f75f466Sreyk {
6573f75f466Sreyk 	return (-1);
6583f75f466Sreyk }
6593f75f466Sreyk 
66021032f8aSreyk /*
66121032f8aSreyk  * imsg helper functions
66221032f8aSreyk  */
66321032f8aSreyk 
66460996bedSreyk void
66560996bedSreyk imsg_event_add(struct imsgev *iev)
66660996bedSreyk {
66760996bedSreyk 	if (iev->handler == NULL) {
668dd7efffeSclaudio 		imsgbuf_flush(&iev->ibuf);
66960996bedSreyk 		return;
67060996bedSreyk 	}
67160996bedSreyk 
67260996bedSreyk 	iev->events = EV_READ;
67331be28caSclaudio 	if (imsgbuf_queuelen(&iev->ibuf) > 0)
67460996bedSreyk 		iev->events |= EV_WRITE;
67560996bedSreyk 
67660996bedSreyk 	event_del(&iev->ev);
67760996bedSreyk 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
67860996bedSreyk 	event_add(&iev->ev, NULL);
67960996bedSreyk }
68060996bedSreyk 
68160996bedSreyk int
68232c142bfSreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
68332c142bfSreyk     pid_t pid, int fd, void *data, uint16_t datalen)
68460996bedSreyk {
68560996bedSreyk 	int	ret;
68660996bedSreyk 
68760996bedSreyk 	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
68860996bedSreyk 	    pid, fd, data, datalen)) == -1)
68960996bedSreyk 		return (ret);
69060996bedSreyk 	imsg_event_add(iev);
69160996bedSreyk 	return (ret);
69260996bedSreyk }
69360996bedSreyk 
69460996bedSreyk int
69532c142bfSreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
69660996bedSreyk     pid_t pid, int fd, const struct iovec *iov, int iovcnt)
69760996bedSreyk {
69860996bedSreyk 	int	ret;
69960996bedSreyk 
70060996bedSreyk 	if ((ret = imsg_composev(&iev->ibuf, type, peerid,
70160996bedSreyk 	    pid, fd, iov, iovcnt)) == -1)
70260996bedSreyk 		return (ret);
70360996bedSreyk 	imsg_event_add(iev);
70460996bedSreyk 	return (ret);
70560996bedSreyk }
70660996bedSreyk 
70721032f8aSreyk void
70821032f8aSreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
70960996bedSreyk {
71021032f8aSreyk 	if (*n == -1) {
71121032f8aSreyk 		/* Use a range of all target instances */
71221032f8aSreyk 		*n = 0;
71321032f8aSreyk 		*m = ps->ps_instances[id];
71421032f8aSreyk 	} else {
71521032f8aSreyk 		/* Use only a single slot of the specified peer process */
71621032f8aSreyk 		*m = *n + 1;
71721032f8aSreyk 	}
71860996bedSreyk }
71960996bedSreyk 
72060996bedSreyk int
72121032f8aSreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
7229ac7219fSreyk     uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen)
72321032f8aSreyk {
72421032f8aSreyk 	int	 m;
72521032f8aSreyk 
72621032f8aSreyk 	proc_range(ps, id, &n, &m);
72721032f8aSreyk 	for (; n < m; n++) {
72821032f8aSreyk 		if (imsg_compose_event(&ps->ps_ievs[id][n],
7297be2c8aaSrzalamena 		    type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1)
73021032f8aSreyk 			return (-1);
73121032f8aSreyk 	}
73221032f8aSreyk 
73321032f8aSreyk 	return (0);
73421032f8aSreyk }
73521032f8aSreyk 
73621032f8aSreyk int
7379ac7219fSreyk proc_compose(struct privsep *ps, enum privsep_procid id,
7389ac7219fSreyk     uint16_t type, void *data, uint16_t datalen)
7399ac7219fSreyk {
7409ac7219fSreyk 	return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen));
7419ac7219fSreyk }
7429ac7219fSreyk 
7439ac7219fSreyk int
74421032f8aSreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
7459ac7219fSreyk     uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt)
74660996bedSreyk {
74721032f8aSreyk 	int	 m;
74821032f8aSreyk 
74921032f8aSreyk 	proc_range(ps, id, &n, &m);
75021032f8aSreyk 	for (; n < m; n++)
75121032f8aSreyk 		if (imsg_composev_event(&ps->ps_ievs[id][n],
7527be2c8aaSrzalamena 		    type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1)
75321032f8aSreyk 			return (-1);
75421032f8aSreyk 
75521032f8aSreyk 	return (0);
75660996bedSreyk }
75760996bedSreyk 
75860996bedSreyk int
7599ac7219fSreyk proc_composev(struct privsep *ps, enum privsep_procid id,
7609ac7219fSreyk     uint16_t type, const struct iovec *iov, int iovcnt)
7619ac7219fSreyk {
7629ac7219fSreyk 	return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt));
7639ac7219fSreyk }
7649ac7219fSreyk 
76521032f8aSreyk struct imsgbuf *
76621032f8aSreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
76721032f8aSreyk {
76821032f8aSreyk 	int	 m;
76921032f8aSreyk 
77021032f8aSreyk 	proc_range(ps, id, &n, &m);
77121032f8aSreyk 	return (&ps->ps_ievs[id][n].ibuf);
77221032f8aSreyk }
77321032f8aSreyk 
77421032f8aSreyk struct imsgev *
77521032f8aSreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n)
77621032f8aSreyk {
77721032f8aSreyk 	int	 m;
77821032f8aSreyk 
77921032f8aSreyk 	proc_range(ps, id, &n, &m);
78021032f8aSreyk 	return (&ps->ps_ievs[id][n]);
78121032f8aSreyk }
7827be2c8aaSrzalamena 
7837be2c8aaSrzalamena /* This function should only be called with care as it breaks async I/O */
7847be2c8aaSrzalamena int
7857be2c8aaSrzalamena proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n)
7867be2c8aaSrzalamena {
7877be2c8aaSrzalamena 	struct imsgbuf	*ibuf;
7887be2c8aaSrzalamena 	int		 m, ret = 0;
7897be2c8aaSrzalamena 
7907be2c8aaSrzalamena 	proc_range(ps, id, &n, &m);
7917be2c8aaSrzalamena 	for (; n < m; n++) {
7927be2c8aaSrzalamena 		if ((ibuf = proc_ibuf(ps, id, n)) == NULL)
7937be2c8aaSrzalamena 			return (-1);
794dd7efffeSclaudio 		if ((ret = imsgbuf_flush(ibuf)) == -1)
7957be2c8aaSrzalamena 			break;
7967be2c8aaSrzalamena 		imsg_event_add(&ps->ps_ievs[id][n]);
7977be2c8aaSrzalamena 	}
7987be2c8aaSrzalamena 
7997be2c8aaSrzalamena 	return (ret);
8007be2c8aaSrzalamena }
801