1*0a9d031fSclaudio /* $OpenBSD: proc.c,v 1.33 2024/11/21 13:39:34 claudio Exp $ */ 2af96af6cSreyk 3af96af6cSreyk /* 45921535cSreyk * Copyright (c) 2010 - 2016 Reyk Floeter <reyk@openbsd.org> 5af96af6cSreyk * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 6af96af6cSreyk * 7af96af6cSreyk * Permission to use, copy, modify, and distribute this software for any 8af96af6cSreyk * purpose with or without fee is hereby granted, provided that the above 9af96af6cSreyk * copyright notice and this permission notice appear in all copies. 10af96af6cSreyk * 11af96af6cSreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12af96af6cSreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13af96af6cSreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14af96af6cSreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15af96af6cSreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16af96af6cSreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17af96af6cSreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18af96af6cSreyk */ 19af96af6cSreyk 20af96af6cSreyk #include <sys/types.h> 21af96af6cSreyk #include <sys/queue.h> 22af96af6cSreyk #include <sys/socket.h> 23af96af6cSreyk #include <sys/wait.h> 24af96af6cSreyk 2568dc821bSrzalamena #include <fcntl.h> 26af96af6cSreyk #include <stdio.h> 27af96af6cSreyk #include <stdlib.h> 28af96af6cSreyk #include <unistd.h> 29af96af6cSreyk #include <string.h> 30af96af6cSreyk #include <errno.h> 31af96af6cSreyk #include <signal.h> 32af96af6cSreyk #include <pwd.h> 33af96af6cSreyk #include <event.h> 34af96af6cSreyk #include <imsg.h> 35af96af6cSreyk 36bcc679a1Sreyk #include "proc.h" 37af96af6cSreyk 38a9955862Sbluhm void proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int, 392269e292Stobhe char **); 40bcc679a1Sreyk void proc_setup(struct privsep *, struct privsep_proc *, unsigned int); 41bcc679a1Sreyk void proc_open(struct privsep *, int, int); 42bcc679a1Sreyk void proc_accept(struct privsep *, int, enum privsep_procid, 43bcc679a1Sreyk unsigned int); 44af96af6cSreyk void proc_close(struct privsep *); 45af96af6cSreyk void proc_shutdown(struct privsep_proc *); 46af96af6cSreyk void proc_sig_handler(int, short, void *); 47af96af6cSreyk void proc_range(struct privsep *, enum privsep_procid, int *, int *); 48af96af6cSreyk int proc_dispatch_null(int, struct privsep_proc *, struct imsg *); 49af96af6cSreyk 50bcc679a1Sreyk enum privsep_procid 51bcc679a1Sreyk proc_getid(struct privsep_proc *procs, unsigned int nproc, 52bcc679a1Sreyk const char *proc_name) 53af96af6cSreyk { 54bcc679a1Sreyk struct privsep_proc *p; 55bcc679a1Sreyk unsigned int proc; 56bcc679a1Sreyk 57bcc679a1Sreyk for (proc = 0; proc < nproc; proc++) { 58bcc679a1Sreyk p = &procs[proc]; 59bcc679a1Sreyk if (strcmp(p->p_title, proc_name)) 60bcc679a1Sreyk continue; 61bcc679a1Sreyk 62bcc679a1Sreyk return (p->p_id); 63bcc679a1Sreyk } 64bcc679a1Sreyk 65bcc679a1Sreyk return (PROC_MAX); 66bcc679a1Sreyk } 67bcc679a1Sreyk 68bcc679a1Sreyk void 69bcc679a1Sreyk proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 702269e292Stobhe int argc, char **argv) 71bcc679a1Sreyk { 72bcc679a1Sreyk unsigned int proc, nargc, i, proc_i; 73bcc679a1Sreyk char **nargv; 74bcc679a1Sreyk struct privsep_proc *p; 75bcc679a1Sreyk char num[32]; 76bcc679a1Sreyk int fd; 77bcc679a1Sreyk 78bcc679a1Sreyk /* Prepare the new process argv. */ 79bcc679a1Sreyk nargv = calloc(argc + 5, sizeof(char *)); 80bcc679a1Sreyk if (nargv == NULL) 81bcc679a1Sreyk fatal("%s: calloc", __func__); 82bcc679a1Sreyk 83bcc679a1Sreyk /* Copy call argument first. */ 84bcc679a1Sreyk nargc = 0; 85bcc679a1Sreyk nargv[nargc++] = argv[0]; 86bcc679a1Sreyk 87bcc679a1Sreyk /* Set process name argument and save the position. */ 88bcc679a1Sreyk nargv[nargc++] = "-P"; 89bcc679a1Sreyk proc_i = nargc; 90bcc679a1Sreyk nargc++; 91bcc679a1Sreyk 92bcc679a1Sreyk /* Point process instance arg to stack and copy the original args. */ 93bcc679a1Sreyk nargv[nargc++] = "-I"; 94bcc679a1Sreyk nargv[nargc++] = num; 95bcc679a1Sreyk for (i = 1; i < (unsigned int) argc; i++) 96bcc679a1Sreyk nargv[nargc++] = argv[i]; 97bcc679a1Sreyk 98bcc679a1Sreyk nargv[nargc] = NULL; 99bcc679a1Sreyk 100bcc679a1Sreyk for (proc = 0; proc < nproc; proc++) { 101bcc679a1Sreyk p = &procs[proc]; 102bcc679a1Sreyk 103bcc679a1Sreyk /* Update args with process title. */ 104bcc679a1Sreyk nargv[proc_i] = (char *)(uintptr_t)p->p_title; 105bcc679a1Sreyk 106bcc679a1Sreyk /* Fire children processes. */ 107bcc679a1Sreyk for (i = 0; i < ps->ps_instances[p->p_id]; i++) { 108bcc679a1Sreyk /* Update the process instance number. */ 109bcc679a1Sreyk snprintf(num, sizeof(num), "%u", i); 110bcc679a1Sreyk 111bcc679a1Sreyk fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0]; 112bcc679a1Sreyk ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1; 113bcc679a1Sreyk 114bcc679a1Sreyk switch (fork()) { 115bcc679a1Sreyk case -1: 116bcc679a1Sreyk fatal("%s: fork", __func__); 117bcc679a1Sreyk break; 118bcc679a1Sreyk case 0: 119bcc679a1Sreyk /* Prepare parent socket. */ 12068dc821bSrzalamena if (fd != PROC_PARENT_SOCK_FILENO) { 12168dc821bSrzalamena if (dup2(fd, PROC_PARENT_SOCK_FILENO) 12268dc821bSrzalamena == -1) 12368dc821bSrzalamena fatal("dup2"); 12468dc821bSrzalamena } else if (fcntl(fd, F_SETFD, 0) == -1) 12568dc821bSrzalamena fatal("fcntl"); 126bcc679a1Sreyk 127bcc679a1Sreyk execvp(argv[0], nargv); 128bcc679a1Sreyk fatal("%s: execvp", __func__); 129bcc679a1Sreyk break; 130bcc679a1Sreyk default: 131bcc679a1Sreyk /* Close child end. */ 132bcc679a1Sreyk close(fd); 133bcc679a1Sreyk break; 134bcc679a1Sreyk } 135bcc679a1Sreyk } 136bcc679a1Sreyk } 137bcc679a1Sreyk free(nargv); 138bcc679a1Sreyk } 139bcc679a1Sreyk 140bcc679a1Sreyk void 141bcc679a1Sreyk proc_connect(struct privsep *ps) 142bcc679a1Sreyk { 143bcc679a1Sreyk struct imsgev *iev; 14487e1f53bSrzalamena unsigned int src, dst, inst; 145bcc679a1Sreyk 14687e1f53bSrzalamena /* Don't distribute any sockets if we are not really going to run. */ 14787e1f53bSrzalamena if (ps->ps_noaction) 14887e1f53bSrzalamena return; 149bcc679a1Sreyk 15087e1f53bSrzalamena for (dst = 0; dst < PROC_MAX; dst++) { 15187e1f53bSrzalamena /* We don't communicate with ourselves. */ 15287e1f53bSrzalamena if (dst == PROC_PARENT) 153bcc679a1Sreyk continue; 154bcc679a1Sreyk 15587e1f53bSrzalamena for (inst = 0; inst < ps->ps_instances[dst]; inst++) { 15687e1f53bSrzalamena iev = &ps->ps_ievs[dst][inst]; 157*0a9d031fSclaudio if (imsgbuf_init(&iev->ibuf, 158*0a9d031fSclaudio ps->ps_pp->pp_pipes[dst][inst]) == -1) 159*0a9d031fSclaudio fatal("imsgbuf_init"); 160*0a9d031fSclaudio imsgbuf_allow_fdpass(&iev->ibuf); 161bcc679a1Sreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, 162bcc679a1Sreyk iev->handler, iev->data); 163bcc679a1Sreyk event_add(&iev->ev, NULL); 164bcc679a1Sreyk } 165bcc679a1Sreyk } 166bcc679a1Sreyk 16787e1f53bSrzalamena /* Distribute the socketpair()s for everyone. */ 16887e1f53bSrzalamena for (src = 0; src < PROC_MAX; src++) 16987e1f53bSrzalamena for (dst = src; dst < PROC_MAX; dst++) { 17087e1f53bSrzalamena /* Parent already distributed its fds. */ 17187e1f53bSrzalamena if (src == PROC_PARENT || dst == PROC_PARENT) 172bcc679a1Sreyk continue; 173bcc679a1Sreyk 17487e1f53bSrzalamena proc_open(ps, src, dst); 175bcc679a1Sreyk } 176bcc679a1Sreyk } 177bcc679a1Sreyk 178bcc679a1Sreyk void 179bcc679a1Sreyk proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 180a9955862Sbluhm int debug, int argc, char **argv, enum privsep_procid proc_id) 181bcc679a1Sreyk { 182bcc679a1Sreyk struct privsep_proc *p = NULL; 18387e1f53bSrzalamena struct privsep_pipes *pa, *pb; 184bcc679a1Sreyk unsigned int proc; 18587e1f53bSrzalamena unsigned int dst; 18687e1f53bSrzalamena int fds[2]; 18787e1f53bSrzalamena 18887e1f53bSrzalamena /* Don't initiate anything if we are not really going to run. */ 18987e1f53bSrzalamena if (ps->ps_noaction) 19087e1f53bSrzalamena return; 191bcc679a1Sreyk 192bcc679a1Sreyk if (proc_id == PROC_PARENT) { 193bcc679a1Sreyk privsep_process = PROC_PARENT; 194bcc679a1Sreyk proc_setup(ps, procs, nproc); 195bcc679a1Sreyk 196dac1ed25Stobhe if (!debug && daemon(0, 0) == -1) 197dac1ed25Stobhe fatal("failed to daemonize"); 198dac1ed25Stobhe 19987e1f53bSrzalamena /* 20087e1f53bSrzalamena * Create the children sockets so we can use them 20187e1f53bSrzalamena * to distribute the rest of the socketpair()s using 20287e1f53bSrzalamena * proc_connect() later. 20387e1f53bSrzalamena */ 20487e1f53bSrzalamena for (dst = 0; dst < PROC_MAX; dst++) { 20587e1f53bSrzalamena /* Don't create socket for ourselves. */ 20687e1f53bSrzalamena if (dst == PROC_PARENT) 20787e1f53bSrzalamena continue; 20887e1f53bSrzalamena 20987e1f53bSrzalamena for (proc = 0; proc < ps->ps_instances[dst]; proc++) { 21087e1f53bSrzalamena pa = &ps->ps_pipes[PROC_PARENT][0]; 21187e1f53bSrzalamena pb = &ps->ps_pipes[dst][proc]; 21287e1f53bSrzalamena if (socketpair(AF_UNIX, 21387e1f53bSrzalamena SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 21487e1f53bSrzalamena PF_UNSPEC, fds) == -1) 21587e1f53bSrzalamena fatal("%s: socketpair", __func__); 21687e1f53bSrzalamena 21787e1f53bSrzalamena pa->pp_pipes[dst][proc] = fds[0]; 21887e1f53bSrzalamena pb->pp_pipes[PROC_PARENT][0] = fds[1]; 21987e1f53bSrzalamena } 22087e1f53bSrzalamena } 221bcc679a1Sreyk 222bcc679a1Sreyk /* Engage! */ 2232269e292Stobhe proc_exec(ps, procs, nproc, argc, argv); 224bcc679a1Sreyk return; 225bcc679a1Sreyk } 226bcc679a1Sreyk 227bcc679a1Sreyk /* Initialize a child */ 228bcc679a1Sreyk for (proc = 0; proc < nproc; proc++) { 229bcc679a1Sreyk if (procs[proc].p_id != proc_id) 230bcc679a1Sreyk continue; 231bcc679a1Sreyk p = &procs[proc]; 232bcc679a1Sreyk break; 233bcc679a1Sreyk } 234bcc679a1Sreyk if (p == NULL || p->p_init == NULL) 235bcc679a1Sreyk fatalx("%s: process %d missing process initialization", 236bcc679a1Sreyk __func__, proc_id); 237bcc679a1Sreyk 238bcc679a1Sreyk p->p_init(ps, p); 239bcc679a1Sreyk 240bcc679a1Sreyk fatalx("failed to initiate child process"); 241bcc679a1Sreyk } 242bcc679a1Sreyk 243bcc679a1Sreyk void 244bcc679a1Sreyk proc_accept(struct privsep *ps, int fd, enum privsep_procid dst, 245bcc679a1Sreyk unsigned int n) 246bcc679a1Sreyk { 247bcc679a1Sreyk struct privsep_pipes *pp = ps->ps_pp; 248bcc679a1Sreyk struct imsgev *iev; 249bcc679a1Sreyk 250bcc679a1Sreyk if (ps->ps_ievs[dst] == NULL) { 251bcc679a1Sreyk #if DEBUG > 1 252bcc679a1Sreyk log_debug("%s: %s src %d %d to dst %d %d not connected", 253bcc679a1Sreyk __func__, ps->ps_title[privsep_process], 254bcc679a1Sreyk privsep_process, ps->ps_instance + 1, 255bcc679a1Sreyk dst, n + 1); 256bcc679a1Sreyk #endif 257bcc679a1Sreyk close(fd); 258bcc679a1Sreyk return; 259bcc679a1Sreyk } 260bcc679a1Sreyk 261bcc679a1Sreyk if (pp->pp_pipes[dst][n] != -1) { 262bcc679a1Sreyk log_warnx("%s: duplicated descriptor", __func__); 263bcc679a1Sreyk close(fd); 264bcc679a1Sreyk return; 265bcc679a1Sreyk } else 266bcc679a1Sreyk pp->pp_pipes[dst][n] = fd; 267bcc679a1Sreyk 268bcc679a1Sreyk iev = &ps->ps_ievs[dst][n]; 269*0a9d031fSclaudio if (imsgbuf_init(&iev->ibuf, fd) == -1) 270*0a9d031fSclaudio fatal("imsgbuf_init"); 271*0a9d031fSclaudio imsgbuf_allow_fdpass(&iev->ibuf); 272bcc679a1Sreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 273bcc679a1Sreyk event_add(&iev->ev, NULL); 274bcc679a1Sreyk } 275bcc679a1Sreyk 276bcc679a1Sreyk void 277bcc679a1Sreyk proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc) 278bcc679a1Sreyk { 279bcc679a1Sreyk unsigned int i, j, src, dst, id; 280bcc679a1Sreyk struct privsep_pipes *pp; 281bcc679a1Sreyk 282bcc679a1Sreyk /* Initialize parent title, ps_instances and procs. */ 28308d0da61Sdv ps->ps_title[PROC_PARENT] = "vmd"; 284bcc679a1Sreyk 285bcc679a1Sreyk for (src = 0; src < PROC_MAX; src++) 286bcc679a1Sreyk /* Default to 1 process instance */ 287bcc679a1Sreyk if (ps->ps_instances[src] < 1) 288bcc679a1Sreyk ps->ps_instances[src] = 1; 289bcc679a1Sreyk 290bcc679a1Sreyk for (src = 0; src < nproc; src++) { 291bcc679a1Sreyk procs[src].p_ps = ps; 292bcc679a1Sreyk if (procs[src].p_cb == NULL) 293bcc679a1Sreyk procs[src].p_cb = proc_dispatch_null; 294bcc679a1Sreyk 295bcc679a1Sreyk id = procs[src].p_id; 296bcc679a1Sreyk ps->ps_title[id] = procs[src].p_title; 297bcc679a1Sreyk if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id], 298bcc679a1Sreyk sizeof(struct imsgev))) == NULL) 29987e1f53bSrzalamena fatal("%s: calloc", __func__); 300bcc679a1Sreyk 301dd7efffeSclaudio /* With this set up, we are ready to call imsgbuf_init(). */ 302bcc679a1Sreyk for (i = 0; i < ps->ps_instances[id]; i++) { 303bcc679a1Sreyk ps->ps_ievs[id][i].handler = proc_dispatch; 304bcc679a1Sreyk ps->ps_ievs[id][i].events = EV_READ; 305bcc679a1Sreyk ps->ps_ievs[id][i].proc = &procs[src]; 306bcc679a1Sreyk ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i]; 307bcc679a1Sreyk } 308bcc679a1Sreyk } 309af96af6cSreyk 310af96af6cSreyk /* 311af96af6cSreyk * Allocate pipes for all process instances (incl. parent) 312af96af6cSreyk * 313af96af6cSreyk * - ps->ps_pipes: N:M mapping 314af96af6cSreyk * N source processes connected to M destination processes: 315af96af6cSreyk * [src][instances][dst][instances], for example 316af96af6cSreyk * [PROC_RELAY][3][PROC_CA][3] 317af96af6cSreyk * 318af96af6cSreyk * - ps->ps_pp: per-process 1:M part of ps->ps_pipes 319af96af6cSreyk * Each process instance has a destination array of socketpair fds: 320af96af6cSreyk * [dst][instances], for example 321af96af6cSreyk * [PROC_PARENT][0] 322af96af6cSreyk */ 323af96af6cSreyk for (src = 0; src < PROC_MAX; src++) { 324af96af6cSreyk /* Allocate destination array for each process */ 325bcc679a1Sreyk if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src], 326af96af6cSreyk sizeof(struct privsep_pipes))) == NULL) 327bcc679a1Sreyk fatal("%s: calloc", __func__); 328af96af6cSreyk 329bcc679a1Sreyk for (i = 0; i < ps->ps_instances[src]; i++) { 330af96af6cSreyk pp = &ps->ps_pipes[src][i]; 331af96af6cSreyk 332af96af6cSreyk for (dst = 0; dst < PROC_MAX; dst++) { 333af96af6cSreyk /* Allocate maximum fd integers */ 334af96af6cSreyk if ((pp->pp_pipes[dst] = 335bcc679a1Sreyk calloc(ps->ps_instances[dst], 336af96af6cSreyk sizeof(int))) == NULL) 337bcc679a1Sreyk fatal("%s: calloc", __func__); 338af96af6cSreyk 339af96af6cSreyk /* Mark fd as unused */ 340bcc679a1Sreyk for (j = 0; j < ps->ps_instances[dst]; j++) 341af96af6cSreyk pp->pp_pipes[dst][j] = -1; 342af96af6cSreyk } 343af96af6cSreyk } 344af96af6cSreyk } 345af96af6cSreyk 346bcc679a1Sreyk ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance]; 347af96af6cSreyk } 348af96af6cSreyk 349af96af6cSreyk void 350af96af6cSreyk proc_kill(struct privsep *ps) 351af96af6cSreyk { 352bcc679a1Sreyk char *cause; 353af96af6cSreyk pid_t pid; 354bcc679a1Sreyk int len, status; 355af96af6cSreyk 356af96af6cSreyk if (privsep_process != PROC_PARENT) 357af96af6cSreyk return; 358af96af6cSreyk 359bcc679a1Sreyk proc_close(ps); 360af96af6cSreyk 361af96af6cSreyk do { 362bcc679a1Sreyk pid = waitpid(WAIT_ANY, &status, 0); 363bcc679a1Sreyk if (pid <= 0) 364bcc679a1Sreyk continue; 365af96af6cSreyk 366bcc679a1Sreyk if (WIFSIGNALED(status)) { 367bcc679a1Sreyk len = asprintf(&cause, "terminated; signal %d", 368bcc679a1Sreyk WTERMSIG(status)); 369bcc679a1Sreyk } else if (WIFEXITED(status)) { 370bcc679a1Sreyk if (WEXITSTATUS(status) != 0) 371bcc679a1Sreyk len = asprintf(&cause, "exited abnormally"); 372bcc679a1Sreyk else 373bcc679a1Sreyk len = 0; 374bcc679a1Sreyk } else 375bcc679a1Sreyk len = -1; 376bcc679a1Sreyk 377bcc679a1Sreyk if (len == 0) { 378bcc679a1Sreyk /* child exited OK, don't print a warning message */ 379bcc679a1Sreyk } else if (len != -1) { 380bcc679a1Sreyk log_warnx("lost child: pid %u %s", pid, cause); 381bcc679a1Sreyk free(cause); 382bcc679a1Sreyk } else 383bcc679a1Sreyk log_warnx("lost child: pid %u", pid); 384bcc679a1Sreyk } while (pid != -1 || (pid == -1 && errno == EINTR)); 385af96af6cSreyk } 386af96af6cSreyk 387af96af6cSreyk void 388bcc679a1Sreyk proc_open(struct privsep *ps, int src, int dst) 389af96af6cSreyk { 390af96af6cSreyk struct privsep_pipes *pa, *pb; 39187e1f53bSrzalamena struct privsep_fd pf; 392af96af6cSreyk int fds[2]; 393bcc679a1Sreyk unsigned int i, j; 394af96af6cSreyk 39587e1f53bSrzalamena /* Exchange pipes between process. */ 396af96af6cSreyk for (i = 0; i < ps->ps_instances[src]; i++) { 397bcc679a1Sreyk for (j = 0; j < ps->ps_instances[dst]; j++) { 398bcc679a1Sreyk /* Don't create sockets for ourself. */ 399bcc679a1Sreyk if (src == dst && i == j) 400bcc679a1Sreyk continue; 401af96af6cSreyk 402bcc679a1Sreyk pa = &ps->ps_pipes[src][i]; 403bcc679a1Sreyk pb = &ps->ps_pipes[dst][j]; 404af96af6cSreyk if (socketpair(AF_UNIX, 405bcc679a1Sreyk SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 406af96af6cSreyk PF_UNSPEC, fds) == -1) 40787e1f53bSrzalamena fatal("%s: socketpair", __func__); 408af96af6cSreyk 409bcc679a1Sreyk pa->pp_pipes[dst][j] = fds[0]; 410af96af6cSreyk pb->pp_pipes[src][i] = fds[1]; 41187e1f53bSrzalamena 41287e1f53bSrzalamena pf.pf_procid = src; 41387e1f53bSrzalamena pf.pf_instance = i; 41487e1f53bSrzalamena if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD, 41587e1f53bSrzalamena -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1) 41687e1f53bSrzalamena fatal("%s: proc_compose_imsg", __func__); 41787e1f53bSrzalamena 41887e1f53bSrzalamena pf.pf_procid = dst; 41987e1f53bSrzalamena pf.pf_instance = j; 42087e1f53bSrzalamena if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD, 42187e1f53bSrzalamena -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1) 42287e1f53bSrzalamena fatal("%s: proc_compose_imsg", __func__); 42387e1f53bSrzalamena 42487e1f53bSrzalamena /* 42587e1f53bSrzalamena * We have to flush to send the descriptors and close 42687e1f53bSrzalamena * them to avoid the fd ramp on startup. 42787e1f53bSrzalamena */ 428d2491206Srzalamena if (proc_flush_imsg(ps, src, i) == -1 || 429d2491206Srzalamena proc_flush_imsg(ps, dst, j) == -1) 430dd7efffeSclaudio fatal("%s: proc_flush_imsg", __func__); 431af96af6cSreyk } 432af96af6cSreyk } 433af96af6cSreyk } 434af96af6cSreyk 435af96af6cSreyk void 436af96af6cSreyk proc_close(struct privsep *ps) 437af96af6cSreyk { 438af96af6cSreyk unsigned int dst, n; 439af96af6cSreyk struct privsep_pipes *pp; 440af96af6cSreyk 441af96af6cSreyk if (ps == NULL) 442af96af6cSreyk return; 443af96af6cSreyk 444af96af6cSreyk pp = ps->ps_pp; 445af96af6cSreyk 446af96af6cSreyk for (dst = 0; dst < PROC_MAX; dst++) { 447af96af6cSreyk if (ps->ps_ievs[dst] == NULL) 448af96af6cSreyk continue; 449af96af6cSreyk 450af96af6cSreyk for (n = 0; n < ps->ps_instances[dst]; n++) { 451af96af6cSreyk if (pp->pp_pipes[dst][n] == -1) 452af96af6cSreyk continue; 453af96af6cSreyk 454af96af6cSreyk /* Cancel the fd, close and invalidate the fd */ 455af96af6cSreyk event_del(&(ps->ps_ievs[dst][n].ev)); 456dd7efffeSclaudio imsgbuf_clear(&(ps->ps_ievs[dst][n].ibuf)); 457af96af6cSreyk close(pp->pp_pipes[dst][n]); 458af96af6cSreyk pp->pp_pipes[dst][n] = -1; 459af96af6cSreyk } 460af96af6cSreyk free(ps->ps_ievs[dst]); 461af96af6cSreyk } 462af96af6cSreyk } 463af96af6cSreyk 464af96af6cSreyk void 465af96af6cSreyk proc_shutdown(struct privsep_proc *p) 466af96af6cSreyk { 467af96af6cSreyk struct privsep *ps = p->p_ps; 468af96af6cSreyk 469af96af6cSreyk if (p->p_shutdown != NULL) 470af96af6cSreyk (*p->p_shutdown)(); 471af96af6cSreyk 472af96af6cSreyk proc_close(ps); 473af96af6cSreyk 474af96af6cSreyk log_info("%s exiting, pid %d", p->p_title, getpid()); 475af96af6cSreyk 476bcc679a1Sreyk exit(0); 477af96af6cSreyk } 478af96af6cSreyk 479af96af6cSreyk void 480af96af6cSreyk proc_sig_handler(int sig, short event, void *arg) 481af96af6cSreyk { 482af96af6cSreyk struct privsep_proc *p = arg; 483af96af6cSreyk 484af96af6cSreyk switch (sig) { 485af96af6cSreyk case SIGINT: 486af96af6cSreyk case SIGTERM: 487af96af6cSreyk proc_shutdown(p); 488af96af6cSreyk break; 489af96af6cSreyk case SIGCHLD: 490af96af6cSreyk case SIGHUP: 491af96af6cSreyk case SIGPIPE: 492af96af6cSreyk case SIGUSR1: 493af96af6cSreyk /* ignore */ 494af96af6cSreyk break; 495af96af6cSreyk default: 496566d2cadSbenno fatalx("%s: unexpected signal", __func__); 497af96af6cSreyk /* NOTREACHED */ 498af96af6cSreyk } 499af96af6cSreyk } 500af96af6cSreyk 501bcc679a1Sreyk void 502af96af6cSreyk proc_run(struct privsep *ps, struct privsep_proc *p, 503af96af6cSreyk struct privsep_proc *procs, unsigned int nproc, 504af96af6cSreyk void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg) 505af96af6cSreyk { 5065921535cSreyk struct passwd *pw; 507af96af6cSreyk const char *root; 508af96af6cSreyk struct control_sock *rcs; 509af96af6cSreyk 51008d0da61Sdv log_procinit("%s", p->p_title); 511af96af6cSreyk 512af96af6cSreyk if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { 513af96af6cSreyk if (control_init(ps, &ps->ps_csock) == -1) 51487e1f53bSrzalamena fatalx("%s: control_init", __func__); 515af96af6cSreyk TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) 516af96af6cSreyk if (control_init(ps, rcs) == -1) 51787e1f53bSrzalamena fatalx("%s: control_init", __func__); 518af96af6cSreyk } 519af96af6cSreyk 5205921535cSreyk /* Use non-standard user */ 5215921535cSreyk if (p->p_pw != NULL) 5225921535cSreyk pw = p->p_pw; 5235921535cSreyk else 5245921535cSreyk pw = ps->ps_pw; 5255921535cSreyk 526af96af6cSreyk /* Change root directory */ 527af96af6cSreyk if (p->p_chroot != NULL) 528af96af6cSreyk root = p->p_chroot; 529af96af6cSreyk else 530af96af6cSreyk root = pw->pw_dir; 531af96af6cSreyk 532af96af6cSreyk if (chroot(root) == -1) 533566d2cadSbenno fatal("%s: chroot", __func__); 534af96af6cSreyk if (chdir("/") == -1) 535566d2cadSbenno fatal("%s: chdir(\"/\")", __func__); 536af96af6cSreyk 537af96af6cSreyk privsep_process = p->p_id; 538af96af6cSreyk 539af96af6cSreyk setproctitle("%s", p->p_title); 540af96af6cSreyk 541af96af6cSreyk if (setgroups(1, &pw->pw_gid) || 542af96af6cSreyk setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 543af96af6cSreyk setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 544566d2cadSbenno fatal("%s: cannot drop privileges", __func__); 545af96af6cSreyk 546af96af6cSreyk event_init(); 547af96af6cSreyk 548af96af6cSreyk signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p); 549af96af6cSreyk signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p); 550af96af6cSreyk signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p); 551af96af6cSreyk signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p); 552af96af6cSreyk signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p); 553af96af6cSreyk signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p); 554af96af6cSreyk 555af96af6cSreyk signal_add(&ps->ps_evsigint, NULL); 556af96af6cSreyk signal_add(&ps->ps_evsigterm, NULL); 557af96af6cSreyk signal_add(&ps->ps_evsigchld, NULL); 558af96af6cSreyk signal_add(&ps->ps_evsighup, NULL); 559af96af6cSreyk signal_add(&ps->ps_evsigpipe, NULL); 560af96af6cSreyk signal_add(&ps->ps_evsigusr1, NULL); 561af96af6cSreyk 562bcc679a1Sreyk proc_setup(ps, procs, nproc); 563bcc679a1Sreyk proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0); 564af96af6cSreyk if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { 565af96af6cSreyk if (control_listen(&ps->ps_csock) == -1) 56687e1f53bSrzalamena fatalx("%s: control_listen", __func__); 567af96af6cSreyk TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) 568af96af6cSreyk if (control_listen(rcs) == -1) 56987e1f53bSrzalamena fatalx("%s: control_listen", __func__); 570af96af6cSreyk } 571af96af6cSreyk 572bcc679a1Sreyk DPRINTF("%s: %s %d/%d, pid %d", __func__, p->p_title, 573bcc679a1Sreyk ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid()); 574bcc679a1Sreyk 575af96af6cSreyk if (run != NULL) 576af96af6cSreyk run(ps, p, arg); 577af96af6cSreyk 578af96af6cSreyk event_dispatch(); 579af96af6cSreyk 580af96af6cSreyk proc_shutdown(p); 581af96af6cSreyk } 582af96af6cSreyk 583af96af6cSreyk void 584af96af6cSreyk proc_dispatch(int fd, short event, void *arg) 585af96af6cSreyk { 586af96af6cSreyk struct imsgev *iev = arg; 587af96af6cSreyk struct privsep_proc *p = iev->proc; 588af96af6cSreyk struct privsep *ps = p->p_ps; 589af96af6cSreyk struct imsgbuf *ibuf; 590af96af6cSreyk struct imsg imsg; 591af96af6cSreyk ssize_t n; 592af96af6cSreyk int verbose; 593af96af6cSreyk const char *title; 594bcc679a1Sreyk struct privsep_fd pf; 595af96af6cSreyk 596af96af6cSreyk title = ps->ps_title[privsep_process]; 597af96af6cSreyk ibuf = &iev->ibuf; 598af96af6cSreyk 599af96af6cSreyk if (event & EV_READ) { 600d12ef5f3Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 601dd7efffeSclaudio fatal("%s: imsgbuf_read", __func__); 602af96af6cSreyk if (n == 0) { 603af96af6cSreyk /* this pipe is dead, so remove the event handler */ 604af96af6cSreyk event_del(&iev->ev); 605af96af6cSreyk event_loopexit(NULL); 606af96af6cSreyk return; 607af96af6cSreyk } 608af96af6cSreyk } 609af96af6cSreyk 610af96af6cSreyk if (event & EV_WRITE) { 611dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 612c1aa9554Sclaudio if (errno == EPIPE) { 613c1aa9554Sclaudio /* this pipe is dead, remove the handler */ 61487e1f53bSrzalamena event_del(&iev->ev); 61587e1f53bSrzalamena event_loopexit(NULL); 61687e1f53bSrzalamena return; 61787e1f53bSrzalamena } 618dd7efffeSclaudio fatal("%s: imsgbuf_write", __func__); 619c1aa9554Sclaudio } 620af96af6cSreyk } 621af96af6cSreyk 622af96af6cSreyk for (;;) { 623af96af6cSreyk if ((n = imsg_get(ibuf, &imsg)) == -1) 62487e1f53bSrzalamena fatal("%s: imsg_get", __func__); 625af96af6cSreyk if (n == 0) 626af96af6cSreyk break; 627af96af6cSreyk 628af96af6cSreyk #if DEBUG > 1 62966094308Sreyk log_debug("%s: %s %d got imsg %d peerid %d from %s %d", 630af96af6cSreyk __func__, title, ps->ps_instance + 1, 631bcc679a1Sreyk imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid); 632af96af6cSreyk #endif 633af96af6cSreyk 634af96af6cSreyk /* 635af96af6cSreyk * Check the message with the program callback 636af96af6cSreyk */ 637af96af6cSreyk if ((p->p_cb)(fd, p, &imsg) == 0) { 638af96af6cSreyk /* Message was handled by the callback, continue */ 639af96af6cSreyk imsg_free(&imsg); 640af96af6cSreyk continue; 641af96af6cSreyk } 642af96af6cSreyk 643af96af6cSreyk /* 644af96af6cSreyk * Generic message handling 645af96af6cSreyk */ 646af96af6cSreyk switch (imsg.hdr.type) { 647af96af6cSreyk case IMSG_CTL_VERBOSE: 648af96af6cSreyk IMSG_SIZE_CHECK(&imsg, &verbose); 649af96af6cSreyk memcpy(&verbose, imsg.data, sizeof(verbose)); 650871fc12cSreyk log_setverbose(verbose); 651af96af6cSreyk break; 652bcc679a1Sreyk case IMSG_CTL_PROCFD: 653bcc679a1Sreyk IMSG_SIZE_CHECK(&imsg, &pf); 654bcc679a1Sreyk memcpy(&pf, imsg.data, sizeof(pf)); 65553027660Sclaudio proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid, 656bcc679a1Sreyk pf.pf_instance); 657bcc679a1Sreyk break; 658af96af6cSreyk default: 65987e1f53bSrzalamena fatalx("%s: %s %d got invalid imsg %d peerid %d " 66066094308Sreyk "from %s %d", 661af96af6cSreyk __func__, title, ps->ps_instance + 1, 66266094308Sreyk imsg.hdr.type, imsg.hdr.peerid, 663bcc679a1Sreyk p->p_title, imsg.hdr.pid); 664af96af6cSreyk } 665af96af6cSreyk imsg_free(&imsg); 666af96af6cSreyk } 667af96af6cSreyk imsg_event_add(iev); 668af96af6cSreyk } 669af96af6cSreyk 670af96af6cSreyk int 671af96af6cSreyk proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg) 672af96af6cSreyk { 673af96af6cSreyk return (-1); 674af96af6cSreyk } 675af96af6cSreyk 676af96af6cSreyk /* 677af96af6cSreyk * imsg helper functions 678af96af6cSreyk */ 679af96af6cSreyk void 680af96af6cSreyk imsg_event_add(struct imsgev *iev) 681af96af6cSreyk { 682a246f7a0Sdv imsg_event_add2(iev, NULL); 683a246f7a0Sdv } 684a246f7a0Sdv 685a246f7a0Sdv void 686a246f7a0Sdv imsg_event_add2(struct imsgev *iev, struct event_base *ev_base) 687a246f7a0Sdv { 688af96af6cSreyk if (iev->handler == NULL) { 689dd7efffeSclaudio imsgbuf_flush(&iev->ibuf); 690af96af6cSreyk return; 691af96af6cSreyk } 692af96af6cSreyk 693af96af6cSreyk iev->events = EV_READ; 69431be28caSclaudio if (imsgbuf_queuelen(&iev->ibuf) > 0) 695af96af6cSreyk iev->events |= EV_WRITE; 696af96af6cSreyk 697af96af6cSreyk event_del(&iev->ev); 698af96af6cSreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 699a246f7a0Sdv if (ev_base != NULL) 700a246f7a0Sdv event_base_set(ev_base, &iev->ev); 701af96af6cSreyk event_add(&iev->ev, NULL); 702af96af6cSreyk } 703af96af6cSreyk 704af96af6cSreyk int 705af96af6cSreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 706af96af6cSreyk pid_t pid, int fd, void *data, uint16_t datalen) 707af96af6cSreyk { 708a246f7a0Sdv return imsg_compose_event2(iev, type, peerid, pid, fd, data, datalen, 709a246f7a0Sdv NULL); 710a246f7a0Sdv } 711a246f7a0Sdv 712a246f7a0Sdv int 713a246f7a0Sdv imsg_compose_event2(struct imsgev *iev, uint16_t type, uint32_t peerid, 714a246f7a0Sdv pid_t pid, int fd, void *data, uint16_t datalen, struct event_base *ev_base) 715a246f7a0Sdv { 716af96af6cSreyk int ret; 717af96af6cSreyk 718af96af6cSreyk if ((ret = imsg_compose(&iev->ibuf, type, peerid, 719af96af6cSreyk pid, fd, data, datalen)) == -1) 720af96af6cSreyk return (ret); 721a246f7a0Sdv imsg_event_add2(iev, ev_base); 722af96af6cSreyk return (ret); 723af96af6cSreyk } 724af96af6cSreyk 725af96af6cSreyk int 726af96af6cSreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 727af96af6cSreyk pid_t pid, int fd, const struct iovec *iov, int iovcnt) 728af96af6cSreyk { 729af96af6cSreyk int ret; 730af96af6cSreyk 731af96af6cSreyk if ((ret = imsg_composev(&iev->ibuf, type, peerid, 732af96af6cSreyk pid, fd, iov, iovcnt)) == -1) 733af96af6cSreyk return (ret); 734af96af6cSreyk imsg_event_add(iev); 735af96af6cSreyk return (ret); 736af96af6cSreyk } 737af96af6cSreyk 738af96af6cSreyk void 739af96af6cSreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m) 740af96af6cSreyk { 741af96af6cSreyk if (*n == -1) { 742af96af6cSreyk /* Use a range of all target instances */ 743af96af6cSreyk *n = 0; 744af96af6cSreyk *m = ps->ps_instances[id]; 745af96af6cSreyk } else { 746af96af6cSreyk /* Use only a single slot of the specified peer process */ 747af96af6cSreyk *m = *n + 1; 748af96af6cSreyk } 749af96af6cSreyk } 750af96af6cSreyk 751af96af6cSreyk int 752af96af6cSreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n, 7535c759b1bSreyk uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen) 754af96af6cSreyk { 755af96af6cSreyk int m; 756af96af6cSreyk 757af96af6cSreyk proc_range(ps, id, &n, &m); 758af96af6cSreyk for (; n < m; n++) { 759af96af6cSreyk if (imsg_compose_event(&ps->ps_ievs[id][n], 760bcc679a1Sreyk type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1) 761af96af6cSreyk return (-1); 762af96af6cSreyk } 763af96af6cSreyk 764af96af6cSreyk return (0); 765af96af6cSreyk } 766af96af6cSreyk 767af96af6cSreyk int 76841b2bea6Sreyk proc_compose(struct privsep *ps, enum privsep_procid id, 76941b2bea6Sreyk uint16_t type, void *data, uint16_t datalen) 77041b2bea6Sreyk { 77141b2bea6Sreyk return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen)); 77241b2bea6Sreyk } 77341b2bea6Sreyk 77441b2bea6Sreyk int 775af96af6cSreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n, 7765c759b1bSreyk uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt) 777af96af6cSreyk { 778af96af6cSreyk int m; 779af96af6cSreyk 780af96af6cSreyk proc_range(ps, id, &n, &m); 781af96af6cSreyk for (; n < m; n++) 782af96af6cSreyk if (imsg_composev_event(&ps->ps_ievs[id][n], 783bcc679a1Sreyk type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1) 784af96af6cSreyk return (-1); 785af96af6cSreyk 786af96af6cSreyk return (0); 787af96af6cSreyk } 788af96af6cSreyk 789af96af6cSreyk int 79041b2bea6Sreyk proc_composev(struct privsep *ps, enum privsep_procid id, 79141b2bea6Sreyk uint16_t type, const struct iovec *iov, int iovcnt) 79241b2bea6Sreyk { 79341b2bea6Sreyk return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt)); 79441b2bea6Sreyk } 79541b2bea6Sreyk 79641b2bea6Sreyk int 797af96af6cSreyk proc_forward_imsg(struct privsep *ps, struct imsg *imsg, 798af96af6cSreyk enum privsep_procid id, int n) 799af96af6cSreyk { 800af96af6cSreyk return (proc_compose_imsg(ps, id, n, imsg->hdr.type, 80153027660Sclaudio imsg->hdr.peerid, imsg_get_fd(imsg), imsg->data, 80253027660Sclaudio IMSG_DATA_SIZE(imsg))); 803af96af6cSreyk } 804af96af6cSreyk 805af96af6cSreyk struct imsgbuf * 806af96af6cSreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n) 807af96af6cSreyk { 808af96af6cSreyk int m; 809af96af6cSreyk 810af96af6cSreyk proc_range(ps, id, &n, &m); 811af96af6cSreyk return (&ps->ps_ievs[id][n].ibuf); 812af96af6cSreyk } 813af96af6cSreyk 814af96af6cSreyk struct imsgev * 815af96af6cSreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n) 816af96af6cSreyk { 817af96af6cSreyk int m; 818af96af6cSreyk 819af96af6cSreyk proc_range(ps, id, &n, &m); 820af96af6cSreyk return (&ps->ps_ievs[id][n]); 821af96af6cSreyk } 822d2491206Srzalamena 823d2491206Srzalamena /* This function should only be called with care as it breaks async I/O */ 824d2491206Srzalamena int 825d2491206Srzalamena proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n) 826d2491206Srzalamena { 827d2491206Srzalamena struct imsgbuf *ibuf; 828d2491206Srzalamena int m, ret = 0; 829d2491206Srzalamena 830d2491206Srzalamena proc_range(ps, id, &n, &m); 831d2491206Srzalamena for (; n < m; n++) { 832d2491206Srzalamena if ((ibuf = proc_ibuf(ps, id, n)) == NULL) 833d2491206Srzalamena return (-1); 834dd7efffeSclaudio if ((ret = imsgbuf_flush(ibuf)) == -1) 835d2491206Srzalamena break; 836d2491206Srzalamena imsg_event_add(&ps->ps_ievs[id][n]); 837d2491206Srzalamena } 838d2491206Srzalamena 839d2491206Srzalamena return (ret); 840d2491206Srzalamena } 841