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