1*7be2c8aaSrzalamena /* $OpenBSD: proc.c,v 1.21 2016/10/28 09:07:08 rzalamena Exp $ */ 260996bedSreyk 360996bedSreyk /* 4*7be2c8aaSrzalamena * 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/types.h> 2160996bedSreyk #include <sys/queue.h> 2260996bedSreyk #include <sys/socket.h> 2360996bedSreyk #include <sys/wait.h> 2460996bedSreyk 25*7be2c8aaSrzalamena #include <fcntl.h> 2660996bedSreyk #include <stdio.h> 2760996bedSreyk #include <stdlib.h> 2860996bedSreyk #include <unistd.h> 2921032f8aSreyk #include <string.h> 3021032f8aSreyk #include <errno.h> 3121032f8aSreyk #include <signal.h> 3260996bedSreyk #include <pwd.h> 3321032f8aSreyk #include <event.h> 340f12961aSreyk #include <imsg.h> 3560996bedSreyk 3660996bedSreyk #include "snmpd.h" 3760996bedSreyk 38*7be2c8aaSrzalamena void proc_exec(struct privsep *, struct privsep_proc *, unsigned int, 39*7be2c8aaSrzalamena int, char **); 40*7be2c8aaSrzalamena void proc_setup(struct privsep *, struct privsep_proc *, unsigned int); 41*7be2c8aaSrzalamena void proc_open(struct privsep *, int, int); 42*7be2c8aaSrzalamena void proc_accept(struct privsep *, int, enum privsep_procid, 43*7be2c8aaSrzalamena unsigned int); 4421032f8aSreyk void proc_close(struct privsep *); 4532c142bfSreyk int proc_ispeer(struct privsep_proc *, unsigned int, enum privsep_procid); 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 5121032f8aSreyk int 5232c142bfSreyk proc_ispeer(struct privsep_proc *procs, unsigned int nproc, 5332c142bfSreyk enum privsep_procid type) 5460996bedSreyk { 5532c142bfSreyk unsigned int i; 5660996bedSreyk 5721032f8aSreyk for (i = 0; i < nproc; i++) 5821032f8aSreyk if (procs[i].p_id == type) 5921032f8aSreyk return (1); 6021032f8aSreyk return (0); 6121032f8aSreyk } 6221032f8aSreyk 63*7be2c8aaSrzalamena enum privsep_procid 64*7be2c8aaSrzalamena proc_getid(struct privsep_proc *procs, unsigned int nproc, 65*7be2c8aaSrzalamena const char *proc_name) 6621032f8aSreyk { 67*7be2c8aaSrzalamena struct privsep_proc *p; 68*7be2c8aaSrzalamena unsigned int proc; 69*7be2c8aaSrzalamena 70*7be2c8aaSrzalamena for (proc = 0; proc < nproc; proc++) { 71*7be2c8aaSrzalamena p = &procs[proc]; 72*7be2c8aaSrzalamena if (strcmp(p->p_title, proc_name)) 73*7be2c8aaSrzalamena continue; 74*7be2c8aaSrzalamena 75*7be2c8aaSrzalamena return (p->p_id); 76*7be2c8aaSrzalamena } 77*7be2c8aaSrzalamena 78*7be2c8aaSrzalamena return (PROC_MAX); 79*7be2c8aaSrzalamena } 80*7be2c8aaSrzalamena 81*7be2c8aaSrzalamena void 82*7be2c8aaSrzalamena proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 83*7be2c8aaSrzalamena int argc, char **argv) 84*7be2c8aaSrzalamena { 85*7be2c8aaSrzalamena unsigned int proc, nargc, i, proc_i; 86*7be2c8aaSrzalamena char **nargv; 87*7be2c8aaSrzalamena struct privsep_proc *p; 88*7be2c8aaSrzalamena char num[32]; 89*7be2c8aaSrzalamena int fd; 90*7be2c8aaSrzalamena 91*7be2c8aaSrzalamena /* Prepare the new process argv. */ 92*7be2c8aaSrzalamena nargv = calloc(argc + 5, sizeof(char *)); 93*7be2c8aaSrzalamena if (nargv == NULL) 94*7be2c8aaSrzalamena fatal("%s: calloc", __func__); 95*7be2c8aaSrzalamena 96*7be2c8aaSrzalamena /* Copy call argument first. */ 97*7be2c8aaSrzalamena nargc = 0; 98*7be2c8aaSrzalamena nargv[nargc++] = argv[0]; 99*7be2c8aaSrzalamena 100*7be2c8aaSrzalamena /* Set process name argument and save the position. */ 101*7be2c8aaSrzalamena nargv[nargc++] = "-P"; 102*7be2c8aaSrzalamena proc_i = nargc; 103*7be2c8aaSrzalamena nargc++; 104*7be2c8aaSrzalamena 105*7be2c8aaSrzalamena /* Point process instance arg to stack and copy the original args. */ 106*7be2c8aaSrzalamena nargv[nargc++] = "-I"; 107*7be2c8aaSrzalamena nargv[nargc++] = num; 108*7be2c8aaSrzalamena for (i = 1; i < (unsigned int) argc; i++) 109*7be2c8aaSrzalamena nargv[nargc++] = argv[i]; 110*7be2c8aaSrzalamena 111*7be2c8aaSrzalamena nargv[nargc] = NULL; 112*7be2c8aaSrzalamena 113*7be2c8aaSrzalamena for (proc = 0; proc < nproc; proc++) { 114*7be2c8aaSrzalamena p = &procs[proc]; 115*7be2c8aaSrzalamena 116*7be2c8aaSrzalamena /* Update args with process title. */ 117*7be2c8aaSrzalamena nargv[proc_i] = (char *)(uintptr_t)p->p_title; 118*7be2c8aaSrzalamena 119*7be2c8aaSrzalamena /* Fire children processes. */ 120*7be2c8aaSrzalamena for (i = 0; i < ps->ps_instances[p->p_id]; i++) { 121*7be2c8aaSrzalamena /* Update the process instance number. */ 122*7be2c8aaSrzalamena snprintf(num, sizeof(num), "%u", i); 123*7be2c8aaSrzalamena 124*7be2c8aaSrzalamena fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0]; 125*7be2c8aaSrzalamena ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1; 126*7be2c8aaSrzalamena 127*7be2c8aaSrzalamena switch (fork()) { 128*7be2c8aaSrzalamena case -1: 129*7be2c8aaSrzalamena fatal("%s: fork", __func__); 130*7be2c8aaSrzalamena break; 131*7be2c8aaSrzalamena case 0: 132*7be2c8aaSrzalamena /* First create a new session */ 133*7be2c8aaSrzalamena if (setsid() == -1) 134*7be2c8aaSrzalamena fatal("setsid"); 135*7be2c8aaSrzalamena 136*7be2c8aaSrzalamena /* Prepare parent socket. */ 137*7be2c8aaSrzalamena if (fd != PROC_PARENT_SOCK_FILENO) { 138*7be2c8aaSrzalamena if (dup2(fd, PROC_PARENT_SOCK_FILENO) 139*7be2c8aaSrzalamena == -1) 140*7be2c8aaSrzalamena fatal("dup2"); 141*7be2c8aaSrzalamena } else if (fcntl(fd, F_SETFD, 0) == -1) 142*7be2c8aaSrzalamena fatal("fcntl"); 143*7be2c8aaSrzalamena 144*7be2c8aaSrzalamena execvp(argv[0], nargv); 145*7be2c8aaSrzalamena fatal("%s: execvp", __func__); 146*7be2c8aaSrzalamena break; 147*7be2c8aaSrzalamena default: 148*7be2c8aaSrzalamena /* Close child end. */ 149*7be2c8aaSrzalamena close(fd); 150*7be2c8aaSrzalamena break; 151*7be2c8aaSrzalamena } 152*7be2c8aaSrzalamena } 153*7be2c8aaSrzalamena } 154*7be2c8aaSrzalamena free(nargv); 155*7be2c8aaSrzalamena } 156*7be2c8aaSrzalamena 157*7be2c8aaSrzalamena void 158*7be2c8aaSrzalamena proc_connect(struct privsep *ps) 159*7be2c8aaSrzalamena { 160*7be2c8aaSrzalamena struct imsgev *iev; 161*7be2c8aaSrzalamena unsigned int src, dst, inst; 162*7be2c8aaSrzalamena 163*7be2c8aaSrzalamena /* Don't distribute any sockets if we are not really going to run. */ 164*7be2c8aaSrzalamena if (ps->ps_noaction) 165*7be2c8aaSrzalamena return; 166*7be2c8aaSrzalamena 167*7be2c8aaSrzalamena for (dst = 0; dst < PROC_MAX; dst++) { 168*7be2c8aaSrzalamena /* We don't communicate with ourselves. */ 169*7be2c8aaSrzalamena if (dst == PROC_PARENT) 170*7be2c8aaSrzalamena continue; 171*7be2c8aaSrzalamena 172*7be2c8aaSrzalamena for (inst = 0; inst < ps->ps_instances[dst]; inst++) { 173*7be2c8aaSrzalamena iev = &ps->ps_ievs[dst][inst]; 174*7be2c8aaSrzalamena imsg_init(&iev->ibuf, ps->ps_pp->pp_pipes[dst][inst]); 175*7be2c8aaSrzalamena event_set(&iev->ev, iev->ibuf.fd, iev->events, 176*7be2c8aaSrzalamena iev->handler, iev->data); 177*7be2c8aaSrzalamena event_add(&iev->ev, NULL); 178*7be2c8aaSrzalamena } 179*7be2c8aaSrzalamena } 180*7be2c8aaSrzalamena 181*7be2c8aaSrzalamena /* Distribute the socketpair()s for everyone. */ 182*7be2c8aaSrzalamena for (src = 0; src < PROC_MAX; src++) 183*7be2c8aaSrzalamena for (dst = src; dst < PROC_MAX; dst++) { 184*7be2c8aaSrzalamena /* Parent already distributed its fds. */ 185*7be2c8aaSrzalamena if (src == PROC_PARENT || dst == PROC_PARENT) 186*7be2c8aaSrzalamena continue; 187*7be2c8aaSrzalamena 188*7be2c8aaSrzalamena proc_open(ps, src, dst); 189*7be2c8aaSrzalamena } 190*7be2c8aaSrzalamena } 191*7be2c8aaSrzalamena 192*7be2c8aaSrzalamena void 193*7be2c8aaSrzalamena proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 194*7be2c8aaSrzalamena int argc, char **argv, enum privsep_procid proc_id) 195*7be2c8aaSrzalamena { 196*7be2c8aaSrzalamena struct privsep_proc *p = NULL; 197*7be2c8aaSrzalamena struct privsep_pipes *pa, *pb; 198*7be2c8aaSrzalamena unsigned int proc; 199*7be2c8aaSrzalamena unsigned int dst; 200*7be2c8aaSrzalamena int fds[2]; 201*7be2c8aaSrzalamena 202*7be2c8aaSrzalamena /* Don't initiate anything if we are not really going to run. */ 203*7be2c8aaSrzalamena if (ps->ps_noaction) 204*7be2c8aaSrzalamena return; 205*7be2c8aaSrzalamena 206*7be2c8aaSrzalamena if (proc_id == PROC_PARENT) { 207*7be2c8aaSrzalamena privsep_process = PROC_PARENT; 208*7be2c8aaSrzalamena proc_setup(ps, procs, nproc); 209*7be2c8aaSrzalamena 210*7be2c8aaSrzalamena /* 211*7be2c8aaSrzalamena * Create the children sockets so we can use them 212*7be2c8aaSrzalamena * to distribute the rest of the socketpair()s using 213*7be2c8aaSrzalamena * proc_connect() later. 214*7be2c8aaSrzalamena */ 215*7be2c8aaSrzalamena for (dst = 0; dst < PROC_MAX; dst++) { 216*7be2c8aaSrzalamena /* Don't create socket for ourselves. */ 217*7be2c8aaSrzalamena if (dst == PROC_PARENT) 218*7be2c8aaSrzalamena continue; 219*7be2c8aaSrzalamena 220*7be2c8aaSrzalamena for (proc = 0; proc < ps->ps_instances[dst]; proc++) { 221*7be2c8aaSrzalamena pa = &ps->ps_pipes[PROC_PARENT][0]; 222*7be2c8aaSrzalamena pb = &ps->ps_pipes[dst][proc]; 223*7be2c8aaSrzalamena if (socketpair(AF_UNIX, 224*7be2c8aaSrzalamena SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 225*7be2c8aaSrzalamena PF_UNSPEC, fds) == -1) 226*7be2c8aaSrzalamena fatal("%s: socketpair", __func__); 227*7be2c8aaSrzalamena 228*7be2c8aaSrzalamena pa->pp_pipes[dst][proc] = fds[0]; 229*7be2c8aaSrzalamena pb->pp_pipes[PROC_PARENT][0] = fds[1]; 230*7be2c8aaSrzalamena } 231*7be2c8aaSrzalamena } 232*7be2c8aaSrzalamena 233*7be2c8aaSrzalamena /* Engage! */ 234*7be2c8aaSrzalamena proc_exec(ps, procs, nproc, argc, argv); 235*7be2c8aaSrzalamena return; 236*7be2c8aaSrzalamena } 237*7be2c8aaSrzalamena 238*7be2c8aaSrzalamena /* Initialize a child */ 239*7be2c8aaSrzalamena for (proc = 0; proc < nproc; proc++) { 240*7be2c8aaSrzalamena if (procs[proc].p_id != proc_id) 241*7be2c8aaSrzalamena continue; 242*7be2c8aaSrzalamena p = &procs[proc]; 243*7be2c8aaSrzalamena break; 244*7be2c8aaSrzalamena } 245*7be2c8aaSrzalamena if (p == NULL || p->p_init == NULL) 246*7be2c8aaSrzalamena fatalx("%s: process %d missing process initialization", 247*7be2c8aaSrzalamena __func__, proc_id); 248*7be2c8aaSrzalamena 249*7be2c8aaSrzalamena p->p_init(ps, p); 250*7be2c8aaSrzalamena 251*7be2c8aaSrzalamena fatalx("failed to initiate child process"); 252*7be2c8aaSrzalamena } 253*7be2c8aaSrzalamena 254*7be2c8aaSrzalamena void 255*7be2c8aaSrzalamena proc_accept(struct privsep *ps, int fd, enum privsep_procid dst, 256*7be2c8aaSrzalamena unsigned int n) 257*7be2c8aaSrzalamena { 258*7be2c8aaSrzalamena struct privsep_pipes *pp = ps->ps_pp; 259*7be2c8aaSrzalamena struct imsgev *iev; 260*7be2c8aaSrzalamena 261*7be2c8aaSrzalamena if (ps->ps_ievs[dst] == NULL) { 262*7be2c8aaSrzalamena #if DEBUG > 1 263*7be2c8aaSrzalamena log_debug("%s: %s src %d %d to dst %d %d not connected", 264*7be2c8aaSrzalamena __func__, ps->ps_title[privsep_process], 265*7be2c8aaSrzalamena privsep_process, ps->ps_instance + 1, 266*7be2c8aaSrzalamena dst, n + 1); 267*7be2c8aaSrzalamena #endif 268*7be2c8aaSrzalamena close(fd); 269*7be2c8aaSrzalamena return; 270*7be2c8aaSrzalamena } 271*7be2c8aaSrzalamena 272*7be2c8aaSrzalamena if (pp->pp_pipes[dst][n] != -1) { 273*7be2c8aaSrzalamena log_warnx("%s: duplicated descriptor", __func__); 274*7be2c8aaSrzalamena close(fd); 275*7be2c8aaSrzalamena return; 276*7be2c8aaSrzalamena } else 277*7be2c8aaSrzalamena pp->pp_pipes[dst][n] = fd; 278*7be2c8aaSrzalamena 279*7be2c8aaSrzalamena iev = &ps->ps_ievs[dst][n]; 280*7be2c8aaSrzalamena imsg_init(&iev->ibuf, fd); 281*7be2c8aaSrzalamena event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 282*7be2c8aaSrzalamena event_add(&iev->ev, NULL); 283*7be2c8aaSrzalamena } 284*7be2c8aaSrzalamena 285*7be2c8aaSrzalamena void 286*7be2c8aaSrzalamena proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc) 287*7be2c8aaSrzalamena { 288*7be2c8aaSrzalamena unsigned int i, j, src, dst, id; 28921032f8aSreyk struct privsep_pipes *pp; 29021032f8aSreyk 291*7be2c8aaSrzalamena /* Initialize parent title, ps_instances and procs. */ 292*7be2c8aaSrzalamena ps->ps_title[PROC_PARENT] = "parent"; 293*7be2c8aaSrzalamena 294*7be2c8aaSrzalamena for (src = 0; src < PROC_MAX; src++) 295*7be2c8aaSrzalamena /* Default to 1 process instance */ 296*7be2c8aaSrzalamena if (ps->ps_instances[src] < 1) 297*7be2c8aaSrzalamena ps->ps_instances[src] = 1; 298*7be2c8aaSrzalamena 299*7be2c8aaSrzalamena for (src = 0; src < nproc; src++) { 300*7be2c8aaSrzalamena procs[src].p_ps = ps; 301*7be2c8aaSrzalamena if (procs[src].p_cb == NULL) 302*7be2c8aaSrzalamena procs[src].p_cb = proc_dispatch_null; 303*7be2c8aaSrzalamena 304*7be2c8aaSrzalamena id = procs[src].p_id; 305*7be2c8aaSrzalamena ps->ps_title[id] = procs[src].p_title; 306*7be2c8aaSrzalamena if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id], 307*7be2c8aaSrzalamena sizeof(struct imsgev))) == NULL) 308*7be2c8aaSrzalamena fatal("%s: calloc", __func__); 309*7be2c8aaSrzalamena 310*7be2c8aaSrzalamena /* With this set up, we are ready to call imsg_init(). */ 311*7be2c8aaSrzalamena for (i = 0; i < ps->ps_instances[id]; i++) { 312*7be2c8aaSrzalamena ps->ps_ievs[id][i].handler = proc_dispatch; 313*7be2c8aaSrzalamena ps->ps_ievs[id][i].events = EV_READ; 314*7be2c8aaSrzalamena ps->ps_ievs[id][i].proc = &procs[src]; 315*7be2c8aaSrzalamena ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i]; 316*7be2c8aaSrzalamena } 317*7be2c8aaSrzalamena } 318*7be2c8aaSrzalamena 31960996bedSreyk /* 32021032f8aSreyk * Allocate pipes for all process instances (incl. parent) 32121032f8aSreyk * 32221032f8aSreyk * - ps->ps_pipes: N:M mapping 32321032f8aSreyk * N source processes connected to M destination processes: 32421032f8aSreyk * [src][instances][dst][instances], for example 32521032f8aSreyk * [PROC_RELAY][3][PROC_CA][3] 32621032f8aSreyk * 32721032f8aSreyk * - ps->ps_pp: per-process 1:M part of ps->ps_pipes 32821032f8aSreyk * Each process instance has a destination array of socketpair fds: 32921032f8aSreyk * [dst][instances], for example 33021032f8aSreyk * [PROC_PARENT][0] 33121032f8aSreyk */ 33221032f8aSreyk for (src = 0; src < PROC_MAX; src++) { 33321032f8aSreyk /* Allocate destination array for each process */ 334*7be2c8aaSrzalamena if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src], 33521032f8aSreyk sizeof(struct privsep_pipes))) == NULL) 336*7be2c8aaSrzalamena fatal("%s: calloc", __func__); 33721032f8aSreyk 338*7be2c8aaSrzalamena for (i = 0; i < ps->ps_instances[src]; i++) { 33921032f8aSreyk pp = &ps->ps_pipes[src][i]; 34021032f8aSreyk 34121032f8aSreyk for (dst = 0; dst < PROC_MAX; dst++) { 34221032f8aSreyk /* Allocate maximum fd integers */ 34321032f8aSreyk if ((pp->pp_pipes[dst] = 344*7be2c8aaSrzalamena calloc(ps->ps_instances[dst], 34521032f8aSreyk sizeof(int))) == NULL) 346*7be2c8aaSrzalamena fatal("%s: calloc", __func__); 34721032f8aSreyk 34821032f8aSreyk /* Mark fd as unused */ 349*7be2c8aaSrzalamena for (j = 0; j < ps->ps_instances[dst]; j++) 35021032f8aSreyk pp->pp_pipes[dst][j] = -1; 35121032f8aSreyk } 35221032f8aSreyk } 35321032f8aSreyk } 35421032f8aSreyk 355*7be2c8aaSrzalamena ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance]; 35660996bedSreyk } 35760996bedSreyk 35860996bedSreyk void 35960996bedSreyk proc_kill(struct privsep *ps) 36060996bedSreyk { 361*7be2c8aaSrzalamena char *cause; 36260996bedSreyk pid_t pid; 363*7be2c8aaSrzalamena int len, status; 36460996bedSreyk 36560996bedSreyk if (privsep_process != PROC_PARENT) 36660996bedSreyk return; 36760996bedSreyk 368*7be2c8aaSrzalamena proc_close(ps); 36960996bedSreyk 37060996bedSreyk do { 371*7be2c8aaSrzalamena pid = waitpid(WAIT_ANY, &status, 0); 372*7be2c8aaSrzalamena if (pid <= 0) 373*7be2c8aaSrzalamena continue; 37421032f8aSreyk 375*7be2c8aaSrzalamena if (WIFSIGNALED(status)) { 376*7be2c8aaSrzalamena len = asprintf(&cause, "terminated; signal %d", 377*7be2c8aaSrzalamena WTERMSIG(status)); 378*7be2c8aaSrzalamena } else if (WIFEXITED(status)) { 379*7be2c8aaSrzalamena if (WEXITSTATUS(status) != 0) 380*7be2c8aaSrzalamena len = asprintf(&cause, "exited abnormally"); 381*7be2c8aaSrzalamena else 382*7be2c8aaSrzalamena len = 0; 383*7be2c8aaSrzalamena } else 384*7be2c8aaSrzalamena len = -1; 385*7be2c8aaSrzalamena 386*7be2c8aaSrzalamena if (len == 0) { 387*7be2c8aaSrzalamena /* child exited OK, don't print a warning message */ 388*7be2c8aaSrzalamena } else if (len != -1) { 389*7be2c8aaSrzalamena log_warnx("lost child: pid %u %s", pid, cause); 390*7be2c8aaSrzalamena free(cause); 391*7be2c8aaSrzalamena } else 392*7be2c8aaSrzalamena log_warnx("lost child: pid %u", pid); 393*7be2c8aaSrzalamena } while (pid != -1 || (pid == -1 && errno == EINTR)); 39460996bedSreyk } 39560996bedSreyk 39660996bedSreyk void 397*7be2c8aaSrzalamena proc_open(struct privsep *ps, int src, int dst) 39860996bedSreyk { 39921032f8aSreyk struct privsep_pipes *pa, *pb; 400*7be2c8aaSrzalamena struct privsep_fd pf; 40121032f8aSreyk int fds[2]; 402*7be2c8aaSrzalamena unsigned int i, j; 40360996bedSreyk 404*7be2c8aaSrzalamena /* Exchange pipes between process. */ 40521032f8aSreyk for (i = 0; i < ps->ps_instances[src]; i++) { 406*7be2c8aaSrzalamena for (j = 0; j < ps->ps_instances[dst]; j++) { 407*7be2c8aaSrzalamena /* Don't create sockets for ourself. */ 408*7be2c8aaSrzalamena if (src == dst && i == j) 409*7be2c8aaSrzalamena continue; 410*7be2c8aaSrzalamena 41121032f8aSreyk pa = &ps->ps_pipes[src][i]; 412*7be2c8aaSrzalamena pb = &ps->ps_pipes[dst][j]; 4135f0d8540Sreyk if (socketpair(AF_UNIX, 414*7be2c8aaSrzalamena SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 41521032f8aSreyk PF_UNSPEC, fds) == -1) 416*7be2c8aaSrzalamena fatal("%s: socketpair", __func__); 41721032f8aSreyk 418*7be2c8aaSrzalamena pa->pp_pipes[dst][j] = fds[0]; 41921032f8aSreyk pb->pp_pipes[src][i] = fds[1]; 42060996bedSreyk 421*7be2c8aaSrzalamena pf.pf_procid = src; 422*7be2c8aaSrzalamena pf.pf_instance = i; 423*7be2c8aaSrzalamena if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD, 424*7be2c8aaSrzalamena -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1) 425*7be2c8aaSrzalamena fatal("%s: proc_compose_imsg", __func__); 426*7be2c8aaSrzalamena 427*7be2c8aaSrzalamena pf.pf_procid = dst; 428*7be2c8aaSrzalamena pf.pf_instance = j; 429*7be2c8aaSrzalamena if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD, 430*7be2c8aaSrzalamena -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1) 431*7be2c8aaSrzalamena fatal("%s: proc_compose_imsg", __func__); 43221032f8aSreyk 43321032f8aSreyk /* 434*7be2c8aaSrzalamena * We have to flush to send the descriptors and close 435*7be2c8aaSrzalamena * them to avoid the fd ramp on startup. 43621032f8aSreyk */ 437*7be2c8aaSrzalamena if (proc_flush_imsg(ps, src, i) == -1 || 438*7be2c8aaSrzalamena proc_flush_imsg(ps, dst, j) == -1) 439*7be2c8aaSrzalamena fatal("%s: imsg_flush", __func__); 44060996bedSreyk } 44160996bedSreyk } 44260996bedSreyk } 44360996bedSreyk 44421032f8aSreyk void 44521032f8aSreyk proc_close(struct privsep *ps) 44621032f8aSreyk { 44732c142bfSreyk unsigned int dst, n; 44821032f8aSreyk struct privsep_pipes *pp; 44960996bedSreyk 45021032f8aSreyk if (ps == NULL) 45121032f8aSreyk return; 45221032f8aSreyk 45321032f8aSreyk pp = ps->ps_pp; 45421032f8aSreyk 45521032f8aSreyk for (dst = 0; dst < PROC_MAX; dst++) { 45621032f8aSreyk if (ps->ps_ievs[dst] == NULL) 45721032f8aSreyk continue; 45821032f8aSreyk 45921032f8aSreyk for (n = 0; n < ps->ps_instances[dst]; n++) { 46021032f8aSreyk if (pp->pp_pipes[dst][n] == -1) 46121032f8aSreyk continue; 46221032f8aSreyk 46321032f8aSreyk /* Cancel the fd, close and invalidate the fd */ 46421032f8aSreyk event_del(&(ps->ps_ievs[dst][n].ev)); 46521032f8aSreyk imsg_clear(&(ps->ps_ievs[dst][n].ibuf)); 46621032f8aSreyk close(pp->pp_pipes[dst][n]); 46721032f8aSreyk pp->pp_pipes[dst][n] = -1; 46821032f8aSreyk } 46921032f8aSreyk free(ps->ps_ievs[dst]); 47060996bedSreyk } 47160996bedSreyk } 47260996bedSreyk 47360996bedSreyk void 47460996bedSreyk proc_shutdown(struct privsep_proc *p) 47560996bedSreyk { 47660996bedSreyk struct privsep *ps = p->p_ps; 47721032f8aSreyk 47821032f8aSreyk if (p->p_id == PROC_CONTROL && ps) 47921032f8aSreyk control_cleanup(&ps->ps_csock); 48060996bedSreyk 48160996bedSreyk if (p->p_shutdown != NULL) 48221032f8aSreyk (*p->p_shutdown)(); 48360996bedSreyk 48421032f8aSreyk proc_close(ps); 48560996bedSreyk 48621032f8aSreyk log_info("%s exiting, pid %d", p->p_title, getpid()); 48721032f8aSreyk 488*7be2c8aaSrzalamena exit(0); 48960996bedSreyk } 49060996bedSreyk 49160996bedSreyk void 49260996bedSreyk proc_sig_handler(int sig, short event, void *arg) 49360996bedSreyk { 49460996bedSreyk struct privsep_proc *p = arg; 49560996bedSreyk 49660996bedSreyk switch (sig) { 49760996bedSreyk case SIGINT: 49860996bedSreyk case SIGTERM: 49960996bedSreyk proc_shutdown(p); 50060996bedSreyk break; 50160996bedSreyk case SIGCHLD: 50260996bedSreyk case SIGHUP: 50360996bedSreyk case SIGPIPE: 5045f2be52bSreyk case SIGUSR1: 50560996bedSreyk /* ignore */ 50660996bedSreyk break; 50760996bedSreyk default: 50860996bedSreyk fatalx("proc_sig_handler: unexpected signal"); 50960996bedSreyk /* NOTREACHED */ 51060996bedSreyk } 51160996bedSreyk } 51260996bedSreyk 513*7be2c8aaSrzalamena void 51460996bedSreyk proc_run(struct privsep *ps, struct privsep_proc *p, 51532c142bfSreyk struct privsep_proc *procs, unsigned int nproc, 5163f75f466Sreyk void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg) 51760996bedSreyk { 51860996bedSreyk struct passwd *pw; 51960996bedSreyk const char *root; 5201dbe1dbcSblambert struct control_sock *rcs; 52160996bedSreyk 5220f12961aSreyk log_procinit(p->p_title); 5230f12961aSreyk 52421032f8aSreyk /* Set the process group of the current process */ 5252fc3edb7Smillert setpgid(0, 0); 52660996bedSreyk 52721032f8aSreyk if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { 52860996bedSreyk if (control_init(ps, &ps->ps_csock) == -1) 529*7be2c8aaSrzalamena fatalx("%s: control_init", __func__); 5301dbe1dbcSblambert TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) 5311dbe1dbcSblambert if (control_init(ps, rcs) == -1) 532*7be2c8aaSrzalamena fatalx("%s: control_init", __func__); 53360996bedSreyk } 53460996bedSreyk 535*7be2c8aaSrzalamena /* Use non-standard user */ 536*7be2c8aaSrzalamena if (p->p_pw != NULL) 537*7be2c8aaSrzalamena pw = p->p_pw; 538*7be2c8aaSrzalamena else 539*7be2c8aaSrzalamena pw = ps->ps_pw; 540*7be2c8aaSrzalamena 54160996bedSreyk /* Change root directory */ 54260996bedSreyk if (p->p_chroot != NULL) 54360996bedSreyk root = p->p_chroot; 54460996bedSreyk else 54560996bedSreyk root = pw->pw_dir; 54660996bedSreyk 54760996bedSreyk if (chroot(root) == -1) 54860996bedSreyk fatal("proc_run: chroot"); 54960996bedSreyk if (chdir("/") == -1) 55060996bedSreyk fatal("proc_run: chdir(\"/\")"); 55160996bedSreyk 55260996bedSreyk privsep_process = p->p_id; 55360996bedSreyk 55460996bedSreyk setproctitle("%s", p->p_title); 55560996bedSreyk 55660996bedSreyk if (setgroups(1, &pw->pw_gid) || 55760996bedSreyk setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 55860996bedSreyk setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 55960996bedSreyk fatal("proc_run: cannot drop privileges"); 56060996bedSreyk 56160996bedSreyk event_init(); 56260996bedSreyk 56360996bedSreyk signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p); 56460996bedSreyk signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p); 56560996bedSreyk signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p); 56660996bedSreyk signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p); 56760996bedSreyk signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p); 5685f2be52bSreyk signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p); 56960996bedSreyk 57060996bedSreyk signal_add(&ps->ps_evsigint, NULL); 57160996bedSreyk signal_add(&ps->ps_evsigterm, NULL); 57260996bedSreyk signal_add(&ps->ps_evsigchld, NULL); 57360996bedSreyk signal_add(&ps->ps_evsighup, NULL); 57460996bedSreyk signal_add(&ps->ps_evsigpipe, NULL); 5755f2be52bSreyk signal_add(&ps->ps_evsigusr1, NULL); 57660996bedSreyk 577*7be2c8aaSrzalamena proc_setup(ps, procs, nproc); 578*7be2c8aaSrzalamena proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0); 57921032f8aSreyk if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { 58060996bedSreyk TAILQ_INIT(&ctl_conns); 58160996bedSreyk if (control_listen(&ps->ps_csock) == -1) 582*7be2c8aaSrzalamena fatalx("%s: control_listen", __func__); 5831dbe1dbcSblambert TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) 5841dbe1dbcSblambert if (control_listen(rcs) == -1) 585*7be2c8aaSrzalamena fatalx("%s: control_listen", __func__); 58660996bedSreyk } 58760996bedSreyk 588*7be2c8aaSrzalamena DPRINTF("%s: %s %d/%d, pid %d", __func__, p->p_title, 589*7be2c8aaSrzalamena ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid()); 590*7be2c8aaSrzalamena 5913f75f466Sreyk if (run != NULL) 5923f75f466Sreyk run(ps, p, arg); 59360996bedSreyk 59460996bedSreyk event_dispatch(); 59560996bedSreyk 59660996bedSreyk proc_shutdown(p); 59760996bedSreyk } 59860996bedSreyk 59960996bedSreyk void 60060996bedSreyk proc_dispatch(int fd, short event, void *arg) 60160996bedSreyk { 60221032f8aSreyk struct imsgev *iev = arg; 60321032f8aSreyk struct privsep_proc *p = iev->proc; 60460996bedSreyk struct privsep *ps = p->p_ps; 60560996bedSreyk struct imsgbuf *ibuf; 60660996bedSreyk struct imsg imsg; 60760996bedSreyk ssize_t n; 60860996bedSreyk int verbose; 60960996bedSreyk const char *title; 610*7be2c8aaSrzalamena struct privsep_fd pf; 61160996bedSreyk 61260996bedSreyk title = ps->ps_title[privsep_process]; 61360996bedSreyk ibuf = &iev->ibuf; 61460996bedSreyk 61560996bedSreyk if (event & EV_READ) { 6162c684640Sclaudio if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) 617*7be2c8aaSrzalamena fatal("%s: imsg_read", __func__); 61860996bedSreyk if (n == 0) { 61960996bedSreyk /* this pipe is dead, so remove the event handler */ 62060996bedSreyk event_del(&iev->ev); 62160996bedSreyk event_loopexit(NULL); 62260996bedSreyk return; 62360996bedSreyk } 62460996bedSreyk } 62560996bedSreyk 62660996bedSreyk if (event & EV_WRITE) { 627*7be2c8aaSrzalamena if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) 628*7be2c8aaSrzalamena fatal("%s: msgbuf_write", __func__); 629*7be2c8aaSrzalamena if (n == 0) { 630*7be2c8aaSrzalamena /* this pipe is dead, so remove the event handler */ 631*7be2c8aaSrzalamena event_del(&iev->ev); 632*7be2c8aaSrzalamena event_loopexit(NULL); 633*7be2c8aaSrzalamena return; 634*7be2c8aaSrzalamena } 63560996bedSreyk } 63660996bedSreyk 63760996bedSreyk for (;;) { 63860996bedSreyk if ((n = imsg_get(ibuf, &imsg)) == -1) 639*7be2c8aaSrzalamena fatal("%s: imsg_get", __func__); 64060996bedSreyk if (n == 0) 64160996bedSreyk break; 64260996bedSreyk 64321032f8aSreyk #if DEBUG > 1 64466094308Sreyk log_debug("%s: %s %d got imsg %d peerid %d from %s %d", 64521032f8aSreyk __func__, title, ps->ps_instance + 1, 646*7be2c8aaSrzalamena imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid); 64721032f8aSreyk #endif 64821032f8aSreyk 64960996bedSreyk /* 65060996bedSreyk * Check the message with the program callback 65160996bedSreyk */ 65260996bedSreyk if ((p->p_cb)(fd, p, &imsg) == 0) { 65360996bedSreyk /* Message was handled by the callback, continue */ 65460996bedSreyk imsg_free(&imsg); 65560996bedSreyk continue; 65660996bedSreyk } 65760996bedSreyk 65860996bedSreyk /* 65960996bedSreyk * Generic message handling 66060996bedSreyk */ 66160996bedSreyk switch (imsg.hdr.type) { 66260996bedSreyk case IMSG_CTL_VERBOSE: 66360996bedSreyk IMSG_SIZE_CHECK(&imsg, &verbose); 66460996bedSreyk memcpy(&verbose, imsg.data, sizeof(verbose)); 66560996bedSreyk log_verbose(verbose); 66660996bedSreyk break; 667*7be2c8aaSrzalamena case IMSG_CTL_PROCFD: 668*7be2c8aaSrzalamena IMSG_SIZE_CHECK(&imsg, &pf); 669*7be2c8aaSrzalamena memcpy(&pf, imsg.data, sizeof(pf)); 670*7be2c8aaSrzalamena proc_accept(ps, imsg.fd, pf.pf_procid, 671*7be2c8aaSrzalamena pf.pf_instance); 672*7be2c8aaSrzalamena break; 67360996bedSreyk default: 674*7be2c8aaSrzalamena fatalx("%s: %s %d got invalid imsg %d peerid %d " 67566094308Sreyk "from %s %d", 67621032f8aSreyk __func__, title, ps->ps_instance + 1, 67766094308Sreyk imsg.hdr.type, imsg.hdr.peerid, 678*7be2c8aaSrzalamena p->p_title, imsg.hdr.pid); 67960996bedSreyk } 68060996bedSreyk imsg_free(&imsg); 68160996bedSreyk } 68260996bedSreyk imsg_event_add(iev); 68360996bedSreyk } 68460996bedSreyk 6853f75f466Sreyk int 6863f75f466Sreyk proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg) 6873f75f466Sreyk { 6883f75f466Sreyk return (-1); 6893f75f466Sreyk } 6903f75f466Sreyk 69121032f8aSreyk /* 69221032f8aSreyk * imsg helper functions 69321032f8aSreyk */ 69421032f8aSreyk 69560996bedSreyk void 69660996bedSreyk imsg_event_add(struct imsgev *iev) 69760996bedSreyk { 69860996bedSreyk if (iev->handler == NULL) { 69960996bedSreyk imsg_flush(&iev->ibuf); 70060996bedSreyk return; 70160996bedSreyk } 70260996bedSreyk 70360996bedSreyk iev->events = EV_READ; 70460996bedSreyk if (iev->ibuf.w.queued) 70560996bedSreyk iev->events |= EV_WRITE; 70660996bedSreyk 70760996bedSreyk event_del(&iev->ev); 70860996bedSreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 70960996bedSreyk event_add(&iev->ev, NULL); 71060996bedSreyk } 71160996bedSreyk 71260996bedSreyk int 71332c142bfSreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 71432c142bfSreyk pid_t pid, int fd, void *data, uint16_t datalen) 71560996bedSreyk { 71660996bedSreyk int ret; 71760996bedSreyk 71860996bedSreyk if ((ret = imsg_compose(&iev->ibuf, type, peerid, 71960996bedSreyk pid, fd, data, datalen)) == -1) 72060996bedSreyk return (ret); 72160996bedSreyk imsg_event_add(iev); 72260996bedSreyk return (ret); 72360996bedSreyk } 72460996bedSreyk 72560996bedSreyk int 72632c142bfSreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 72760996bedSreyk pid_t pid, int fd, const struct iovec *iov, int iovcnt) 72860996bedSreyk { 72960996bedSreyk int ret; 73060996bedSreyk 73160996bedSreyk if ((ret = imsg_composev(&iev->ibuf, type, peerid, 73260996bedSreyk pid, fd, iov, iovcnt)) == -1) 73360996bedSreyk return (ret); 73460996bedSreyk imsg_event_add(iev); 73560996bedSreyk return (ret); 73660996bedSreyk } 73760996bedSreyk 73821032f8aSreyk void 73921032f8aSreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m) 74060996bedSreyk { 74121032f8aSreyk if (*n == -1) { 74221032f8aSreyk /* Use a range of all target instances */ 74321032f8aSreyk *n = 0; 74421032f8aSreyk *m = ps->ps_instances[id]; 74521032f8aSreyk } else { 74621032f8aSreyk /* Use only a single slot of the specified peer process */ 74721032f8aSreyk *m = *n + 1; 74821032f8aSreyk } 74960996bedSreyk } 75060996bedSreyk 75160996bedSreyk int 75221032f8aSreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n, 7539ac7219fSreyk uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen) 75421032f8aSreyk { 75521032f8aSreyk int m; 75621032f8aSreyk 75721032f8aSreyk proc_range(ps, id, &n, &m); 75821032f8aSreyk for (; n < m; n++) { 75921032f8aSreyk if (imsg_compose_event(&ps->ps_ievs[id][n], 760*7be2c8aaSrzalamena type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1) 76121032f8aSreyk return (-1); 76221032f8aSreyk } 76321032f8aSreyk 76421032f8aSreyk return (0); 76521032f8aSreyk } 76621032f8aSreyk 76721032f8aSreyk int 7689ac7219fSreyk proc_compose(struct privsep *ps, enum privsep_procid id, 7699ac7219fSreyk uint16_t type, void *data, uint16_t datalen) 7709ac7219fSreyk { 7719ac7219fSreyk return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen)); 7729ac7219fSreyk } 7739ac7219fSreyk 7749ac7219fSreyk int 77521032f8aSreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n, 7769ac7219fSreyk uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt) 77760996bedSreyk { 77821032f8aSreyk int m; 77921032f8aSreyk 78021032f8aSreyk proc_range(ps, id, &n, &m); 78121032f8aSreyk for (; n < m; n++) 78221032f8aSreyk if (imsg_composev_event(&ps->ps_ievs[id][n], 783*7be2c8aaSrzalamena type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1) 78421032f8aSreyk return (-1); 78521032f8aSreyk 78621032f8aSreyk return (0); 78760996bedSreyk } 78860996bedSreyk 78960996bedSreyk int 7909ac7219fSreyk proc_composev(struct privsep *ps, enum privsep_procid id, 7919ac7219fSreyk uint16_t type, const struct iovec *iov, int iovcnt) 7929ac7219fSreyk { 7939ac7219fSreyk return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt)); 7949ac7219fSreyk } 7959ac7219fSreyk 7969ac7219fSreyk int 79760996bedSreyk proc_forward_imsg(struct privsep *ps, struct imsg *imsg, 79821032f8aSreyk enum privsep_procid id, int n) 79960996bedSreyk { 80021032f8aSreyk return (proc_compose_imsg(ps, id, n, imsg->hdr.type, 8019ac7219fSreyk imsg->hdr.peerid, imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg))); 80260996bedSreyk } 80321032f8aSreyk 80421032f8aSreyk struct imsgbuf * 80521032f8aSreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n) 80621032f8aSreyk { 80721032f8aSreyk int m; 80821032f8aSreyk 80921032f8aSreyk proc_range(ps, id, &n, &m); 81021032f8aSreyk return (&ps->ps_ievs[id][n].ibuf); 81121032f8aSreyk } 81221032f8aSreyk 81321032f8aSreyk struct imsgev * 81421032f8aSreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n) 81521032f8aSreyk { 81621032f8aSreyk int m; 81721032f8aSreyk 81821032f8aSreyk proc_range(ps, id, &n, &m); 81921032f8aSreyk return (&ps->ps_ievs[id][n]); 82021032f8aSreyk } 821*7be2c8aaSrzalamena 822*7be2c8aaSrzalamena /* This function should only be called with care as it breaks async I/O */ 823*7be2c8aaSrzalamena int 824*7be2c8aaSrzalamena proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n) 825*7be2c8aaSrzalamena { 826*7be2c8aaSrzalamena struct imsgbuf *ibuf; 827*7be2c8aaSrzalamena int m, ret = 0; 828*7be2c8aaSrzalamena 829*7be2c8aaSrzalamena proc_range(ps, id, &n, &m); 830*7be2c8aaSrzalamena for (; n < m; n++) { 831*7be2c8aaSrzalamena if ((ibuf = proc_ibuf(ps, id, n)) == NULL) 832*7be2c8aaSrzalamena return (-1); 833*7be2c8aaSrzalamena do { 834*7be2c8aaSrzalamena ret = imsg_flush(ibuf); 835*7be2c8aaSrzalamena } while (ret == -1 && errno == EAGAIN); 836*7be2c8aaSrzalamena if (ret == -1) 837*7be2c8aaSrzalamena break; 838*7be2c8aaSrzalamena imsg_event_add(&ps->ps_ievs[id][n]); 839*7be2c8aaSrzalamena } 840*7be2c8aaSrzalamena 841*7be2c8aaSrzalamena return (ret); 842*7be2c8aaSrzalamena } 843