1*af27b3ccSclaudio /* $OpenBSD: proc.c,v 1.7 2024/11/21 13:34:51 claudio Exp $ */ 23b188dabSeric 33b188dabSeric /* 43b188dabSeric * Copyright (c) 2017 Eric Faurot <eric@openbsd.org> 53b188dabSeric * 63b188dabSeric * Permission to use, copy, modify, and distribute this software for any 73b188dabSeric * purpose with or without fee is hereby granted, provided that the above 83b188dabSeric * copyright notice and this permission notice appear in all copies. 93b188dabSeric * 103b188dabSeric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 113b188dabSeric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 123b188dabSeric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 133b188dabSeric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 143b188dabSeric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 153b188dabSeric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 163b188dabSeric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 173b188dabSeric */ 183b188dabSeric 193b188dabSeric #include <sys/queue.h> 203b188dabSeric #include <sys/socket.h> 213b188dabSeric 223b188dabSeric #include <errno.h> 233b188dabSeric #include <event.h> 243b188dabSeric #include <imsg.h> 253b188dabSeric #include <stdlib.h> 263b188dabSeric #include <string.h> 273b188dabSeric #include <unistd.h> 283b188dabSeric 293b188dabSeric #include "log.h" 303b188dabSeric #include "proc.h" 313b188dabSeric 323b188dabSeric struct imsgproc { 333b188dabSeric TAILQ_ENTRY(imsgproc) tqe; 343b188dabSeric int type; 353b188dabSeric int instance; 363b188dabSeric char *title; 373b188dabSeric pid_t pid; 383b188dabSeric void *arg; 393b188dabSeric void (*cb)(struct imsgproc *, struct imsg *, void *); 403b188dabSeric struct imsgbuf imsgbuf; 413b188dabSeric short events; 423b188dabSeric struct event ev; 433b188dabSeric 443b188dabSeric struct { 453b188dabSeric const uint8_t *pos; 463b188dabSeric const uint8_t *end; 473b188dabSeric } m_in; 483b188dabSeric 493b188dabSeric struct m_out { 503b188dabSeric char *buf; 513b188dabSeric size_t alloc; 523b188dabSeric size_t pos; 533b188dabSeric uint32_t type; 543b188dabSeric uint32_t peerid; 553b188dabSeric pid_t pid; 563b188dabSeric int fd; 573b188dabSeric } m_out; 583b188dabSeric }; 593b188dabSeric 603b188dabSeric static struct imsgproc *proc_new(int); 613b188dabSeric static void proc_setsock(struct imsgproc *, int); 623b188dabSeric static void proc_callback(struct imsgproc *, struct imsg *); 633b188dabSeric static void proc_dispatch(int, short, void *); 643b188dabSeric static void proc_event_add(struct imsgproc *); 653b188dabSeric 663b188dabSeric static TAILQ_HEAD(, imsgproc) procs = TAILQ_HEAD_INITIALIZER(procs); 673b188dabSeric 683b188dabSeric pid_t 693b188dabSeric proc_getpid(struct imsgproc *p) 703b188dabSeric { 713b188dabSeric return p->pid; 723b188dabSeric } 733b188dabSeric 743b188dabSeric int 753b188dabSeric proc_gettype(struct imsgproc *p) 763b188dabSeric { 773b188dabSeric return p->type; 783b188dabSeric } 793b188dabSeric 803b188dabSeric int 813b188dabSeric proc_getinstance(struct imsgproc *p) 823b188dabSeric { 833b188dabSeric return p->instance; 843b188dabSeric } 853b188dabSeric 863b188dabSeric const char * 873b188dabSeric proc_gettitle(struct imsgproc *p) 883b188dabSeric { 893b188dabSeric return p->title; 903b188dabSeric } 913b188dabSeric 923b188dabSeric struct imsgproc * 933b188dabSeric proc_bypid(pid_t pid) 943b188dabSeric { 953b188dabSeric struct imsgproc *p; 963b188dabSeric 973b188dabSeric TAILQ_FOREACH(p, &procs, tqe) 983b188dabSeric if (pid == p->pid) 993b188dabSeric return p; 1003b188dabSeric 1013b188dabSeric return NULL; 1023b188dabSeric } 1033b188dabSeric 1043b188dabSeric struct imsgproc * 1053b188dabSeric proc_exec(int type, char **argv) 1063b188dabSeric { 1073b188dabSeric struct imsgproc *p; 1083b188dabSeric int sp[2]; 1093b188dabSeric pid_t pid; 1103b188dabSeric 1113b188dabSeric p = proc_new(type); 1123b188dabSeric if (p == NULL) { 1133b188dabSeric log_warn("%s: proc_new", __func__); 1143b188dabSeric return NULL; 1153b188dabSeric } 1163b188dabSeric 1173b188dabSeric if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, PF_UNSPEC, sp) == -1) { 1183b188dabSeric log_warn("%s: socketpair", __func__); 1193b188dabSeric proc_free(p); 1203b188dabSeric return NULL; 1213b188dabSeric } 1223b188dabSeric 1233b188dabSeric switch (pid = fork()) { 1243b188dabSeric case -1: 1253b188dabSeric log_warn("%s: fork", __func__); 1263b188dabSeric close(sp[0]); 1273b188dabSeric close(sp[1]); 1283b188dabSeric proc_free(p); 1293b188dabSeric return NULL; 1303b188dabSeric case 0: 1313b188dabSeric break; 1323b188dabSeric default: 1333b188dabSeric close(sp[0]); 1343b188dabSeric p->pid = pid; 1353b188dabSeric proc_setsock(p, sp[1]); 1363b188dabSeric return p; 1373b188dabSeric } 1383b188dabSeric 1393b188dabSeric if (dup2(sp[0], 3) == -1) 1403b188dabSeric fatal("%s: dup2", __func__); 1413b188dabSeric 1423b188dabSeric if (closefrom(4) == -1) 1433b188dabSeric fatal("%s: closefrom", __func__); 1443b188dabSeric 1453b188dabSeric execvp(argv[0], argv); 1463b188dabSeric fatal("%s: execvp: %s", __func__, argv[0]); 1473b188dabSeric } 1483b188dabSeric 1493b188dabSeric struct imsgproc * 1503b188dabSeric proc_attach(int type, int fd) 1513b188dabSeric { 1523b188dabSeric struct imsgproc *p; 1533b188dabSeric 1543b188dabSeric p = proc_new(type); 1553b188dabSeric if (p == NULL) 1563b188dabSeric return NULL; 1573b188dabSeric 1583b188dabSeric proc_setsock(p, fd); 1593b188dabSeric return p; 1603b188dabSeric } 1613b188dabSeric 1623b188dabSeric void 1633b188dabSeric proc_settitle(struct imsgproc *p, const char *title) 1643b188dabSeric { 1653b188dabSeric free(p->title); 1663b188dabSeric if (title) { 1673b188dabSeric p->title = strdup(title); 1683b188dabSeric if (p->title == NULL) 1693b188dabSeric log_warn("%s: strdup", __func__); 1703b188dabSeric } 1713b188dabSeric else 1723b188dabSeric p->title = NULL; 1733b188dabSeric } 1743b188dabSeric 1753b188dabSeric void 1763b188dabSeric proc_setpid(struct imsgproc *p, pid_t pid) 1773b188dabSeric { 1783b188dabSeric p->pid = pid; 1793b188dabSeric } 1803b188dabSeric 1813b188dabSeric void 1823b188dabSeric proc_setcallback(struct imsgproc *p, 1833b188dabSeric void(*cb)(struct imsgproc *, struct imsg *, void *), void *arg) 1843b188dabSeric { 1853b188dabSeric p->cb = cb; 1863b188dabSeric p->arg = arg; 1873b188dabSeric } 1883b188dabSeric 1893b188dabSeric void 1903b188dabSeric proc_enable(struct imsgproc *p) 1913b188dabSeric { 1923b188dabSeric proc_event_add(p); 1933b188dabSeric } 1943b188dabSeric 1953b188dabSeric void 1963b188dabSeric proc_free(struct imsgproc *p) 1973b188dabSeric { 1983b188dabSeric if (p == NULL) 1993b188dabSeric return; 2003b188dabSeric 2013b188dabSeric TAILQ_REMOVE(&procs, p, tqe); 2023b188dabSeric 2033b188dabSeric if (event_initialized(&p->ev)) 2043b188dabSeric event_del(&p->ev); 2053b188dabSeric close(p->imsgbuf.fd); 206dd7efffeSclaudio imsgbuf_clear(&p->imsgbuf); 2073b188dabSeric free(p->title); 2083b188dabSeric free(p); 2093b188dabSeric } 2103b188dabSeric 2113b188dabSeric static struct imsgproc * 2123b188dabSeric proc_new(int type) 2133b188dabSeric { 2143b188dabSeric struct imsgproc *p; 2153b188dabSeric 2163b188dabSeric p = calloc(1, sizeof(*p)); 2173b188dabSeric if (p == NULL) 2183b188dabSeric return NULL; 2193b188dabSeric 220*af27b3ccSclaudio if (imsgbuf_init(&p->imsgbuf, -1) == -1) { 221*af27b3ccSclaudio free(p); 222*af27b3ccSclaudio return NULL; 223*af27b3ccSclaudio } 224*af27b3ccSclaudio imsgbuf_allow_fdpass(&p->imsgbuf); 225*af27b3ccSclaudio 2263b188dabSeric p->type = type; 2273b188dabSeric p->instance = -1; 2283b188dabSeric p->pid = -1; 2293b188dabSeric 2303b188dabSeric TAILQ_INSERT_TAIL(&procs, p, tqe); 2313b188dabSeric 2323b188dabSeric return p; 2333b188dabSeric } 2343b188dabSeric 2353b188dabSeric static void 2363b188dabSeric proc_setsock(struct imsgproc *p, int sock) 2373b188dabSeric { 2383b188dabSeric p->imsgbuf.fd = sock; 2393b188dabSeric } 2403b188dabSeric 2413b188dabSeric static void 2423b188dabSeric proc_event_add(struct imsgproc *p) 2433b188dabSeric { 2443b188dabSeric short events; 2453b188dabSeric 2463b188dabSeric events = EV_READ; 247*af27b3ccSclaudio if (imsgbuf_queuelen(&p->imsgbuf) > 0) 2483b188dabSeric events |= EV_WRITE; 2493b188dabSeric 2503b188dabSeric if (p->events) 2513b188dabSeric event_del(&p->ev); 2523b188dabSeric 2533b188dabSeric p->events = events; 2543b188dabSeric if (events) { 2553b188dabSeric event_set(&p->ev, p->imsgbuf.fd, events, proc_dispatch, p); 2563b188dabSeric event_add(&p->ev, NULL); 2573b188dabSeric } 2583b188dabSeric } 2593b188dabSeric 2603b188dabSeric static void 2613b188dabSeric proc_callback(struct imsgproc *p, struct imsg *imsg) 2623b188dabSeric { 2633b188dabSeric if (imsg != NULL) { 2643b188dabSeric p->m_in.pos = imsg->data; 2653b188dabSeric p->m_in.end = p->m_in.pos + (imsg->hdr.len - sizeof(imsg->hdr)); 2663b188dabSeric } 2673b188dabSeric else { 2683b188dabSeric p->m_in.pos = NULL; 2693b188dabSeric p->m_in.end = NULL; 2703b188dabSeric } 2713b188dabSeric 2723b188dabSeric p->cb(p, imsg, p->arg); 2733b188dabSeric } 2743b188dabSeric 2753b188dabSeric static void 2763b188dabSeric proc_dispatch(int fd, short event, void *arg) 2773b188dabSeric { 2783b188dabSeric struct imsgproc *p = arg; 2793b188dabSeric struct imsg imsg; 2803b188dabSeric ssize_t n; 2813b188dabSeric 2823b188dabSeric p->events = 0; 2833b188dabSeric 2843b188dabSeric if (event & EV_READ) { 285dd7efffeSclaudio n = imsgbuf_read(&p->imsgbuf); 2863b188dabSeric switch (n) { 2873b188dabSeric case -1: 288dd7efffeSclaudio log_warn("%s: imsgbuf_read", __func__); 2893b188dabSeric proc_callback(p, NULL); 2903b188dabSeric return; 2913b188dabSeric case 0: 2923b188dabSeric /* This pipe is dead. */ 2933b188dabSeric proc_callback(p, NULL); 2943b188dabSeric return; 2953b188dabSeric default: 2963b188dabSeric break; 2973b188dabSeric } 2983b188dabSeric } 2993b188dabSeric 3003b188dabSeric if (event & EV_WRITE) { 301dd7efffeSclaudio if (imsgbuf_write(&p->imsgbuf) == -1) { 302c1aa9554Sclaudio if (errno != EPIPE) 303dd7efffeSclaudio log_warn("%s: imsgbuf_write", __func__); 3043b188dabSeric proc_callback(p, NULL); 3053b188dabSeric return; 3063b188dabSeric } 3073b188dabSeric } 3083b188dabSeric 3093b188dabSeric for (;;) { 3103b188dabSeric if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) { 3113b188dabSeric log_warn("%s: imsg_get", __func__); 3123b188dabSeric proc_callback(p, NULL); 3133b188dabSeric return; 3143b188dabSeric } 3153b188dabSeric if (n == 0) 3163b188dabSeric break; 3173b188dabSeric 3183b188dabSeric proc_callback(p, &imsg); 3193b188dabSeric imsg_free(&imsg); 3203b188dabSeric } 3213b188dabSeric 3223b188dabSeric proc_event_add(p); 3233b188dabSeric } 3243b188dabSeric 3253b188dabSeric void 3263b188dabSeric m_compose(struct imsgproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd, 3273b188dabSeric const void *data, size_t len) 3283b188dabSeric { 3293b188dabSeric if (imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len) == -1) 3303b188dabSeric fatal("%s: imsg_compose", __func__); 3313b188dabSeric 3323b188dabSeric proc_event_add(p); 3333b188dabSeric } 3343b188dabSeric 3353b188dabSeric void 3363b188dabSeric m_create(struct imsgproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd) 3373b188dabSeric { 3383b188dabSeric p->m_out.pos = 0; 3393b188dabSeric p->m_out.type = type; 3403b188dabSeric p->m_out.peerid = peerid; 3413b188dabSeric p->m_out.pid = pid; 3423b188dabSeric p->m_out.fd = fd; 3433b188dabSeric } 3443b188dabSeric 3453b188dabSeric void 3463b188dabSeric m_close(struct imsgproc *p) 3473b188dabSeric { 3483b188dabSeric if (imsg_compose(&p->imsgbuf, p->m_out.type, p->m_out.peerid, 3493b188dabSeric p->m_out.pid, p->m_out.fd, p->m_out.buf, p->m_out.pos) == -1) 3503b188dabSeric fatal("%s: imsg_compose", __func__); 3513b188dabSeric 3523b188dabSeric proc_event_add(p); 3533b188dabSeric } 3543b188dabSeric 3553b188dabSeric void 3563b188dabSeric m_add(struct imsgproc *p, const void *data, size_t len) 3573b188dabSeric { 3583b188dabSeric size_t alloc; 3593b188dabSeric void *tmp; 3603b188dabSeric 3613b188dabSeric if (p->m_out.pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) 3623b188dabSeric fatalx("%s: message too large", __func__); 3633b188dabSeric 3643b188dabSeric alloc = p->m_out.alloc ? p->m_out.alloc : 128; 3653b188dabSeric while (p->m_out.pos + len > alloc) 3663b188dabSeric alloc *= 2; 3673b188dabSeric if (alloc != p->m_out.alloc) { 3683b188dabSeric tmp = recallocarray(p->m_out.buf, p->m_out.alloc, alloc, 1); 3693b188dabSeric if (tmp == NULL) 3703b188dabSeric fatal("%s: reallocarray", __func__); 3713b188dabSeric p->m_out.alloc = alloc; 3723b188dabSeric p->m_out.buf = tmp; 3733b188dabSeric } 3743b188dabSeric 3753b188dabSeric memmove(p->m_out.buf + p->m_out.pos, data, len); 3763b188dabSeric p->m_out.pos += len; 3773b188dabSeric } 3783b188dabSeric 3793b188dabSeric void 3803b188dabSeric m_add_int(struct imsgproc *p, int v) 3813b188dabSeric { 3823b188dabSeric m_add(p, &v, sizeof(v)); 3833b188dabSeric }; 3843b188dabSeric 3853b188dabSeric void 3863b188dabSeric m_add_u32(struct imsgproc *p, uint32_t v) 3873b188dabSeric { 3883b188dabSeric m_add(p, &v, sizeof(v)); 3893b188dabSeric }; 3903b188dabSeric 3913b188dabSeric void 3923b188dabSeric m_add_u64(struct imsgproc *p, uint64_t v) 3933b188dabSeric { 3943b188dabSeric m_add(p, &v, sizeof(v)); 3953b188dabSeric } 3963b188dabSeric 3973b188dabSeric void 3983b188dabSeric m_add_size(struct imsgproc *p, size_t v) 3993b188dabSeric { 4003b188dabSeric m_add(p, &v, sizeof(v)); 4013b188dabSeric } 4023b188dabSeric 4033b188dabSeric void 4043b188dabSeric m_add_time(struct imsgproc *p, time_t v) 4053b188dabSeric { 4063b188dabSeric m_add(p, &v, sizeof(v)); 4073b188dabSeric } 4083b188dabSeric 4093b188dabSeric void 4103b188dabSeric m_add_string(struct imsgproc *p, const char *str) 4113b188dabSeric { 4129d855d3dSeric if (str) { 4139d855d3dSeric m_add(p, "s", 1); 4143b188dabSeric m_add(p, str, strlen(str) + 1); 4153b188dabSeric } 4169d855d3dSeric else 4179d855d3dSeric m_add(p, "\0", 1); 4189d855d3dSeric } 4193b188dabSeric 4203b188dabSeric void 4213b188dabSeric m_add_sockaddr(struct imsgproc *p, const struct sockaddr *sa) 4223b188dabSeric { 4233b188dabSeric m_add_size(p, sa->sa_len); 4243b188dabSeric m_add(p, sa, sa->sa_len); 4253b188dabSeric } 4263b188dabSeric 4273b188dabSeric void 4283b188dabSeric m_end(struct imsgproc *p) 4293b188dabSeric { 4303b188dabSeric if (p->m_in.pos != p->m_in.end) 4313b188dabSeric fatal("%s: %zi bytes left", __func__, 4323b188dabSeric p->m_in.end - p->m_in.pos); 4333b188dabSeric } 4343b188dabSeric 4353b188dabSeric int 4363b188dabSeric m_is_eom(struct imsgproc *p) 4373b188dabSeric { 4383b188dabSeric return (p->m_in.pos == p->m_in.end); 4393b188dabSeric } 4403b188dabSeric 4413b188dabSeric void 4423b188dabSeric m_get(struct imsgproc *p, void *dst, size_t sz) 4433b188dabSeric { 4443b188dabSeric if (sz > MAX_IMSGSIZE || 4453b188dabSeric p->m_in.end - p->m_in.pos < (ssize_t)sz ) 4463b188dabSeric fatalx("%s: %zu bytes requested, %zi left", __func__, sz, 4473b188dabSeric p->m_in.end - p->m_in.pos); 4483b188dabSeric 4493b188dabSeric memmove(dst, p->m_in.pos, sz); 4503b188dabSeric p->m_in.pos += sz; 4513b188dabSeric } 4523b188dabSeric 4533b188dabSeric void 4543b188dabSeric m_get_int(struct imsgproc *p, int *dst) 4553b188dabSeric { 4563b188dabSeric m_get(p, dst, sizeof(*dst)); 4573b188dabSeric } 4583b188dabSeric 4593b188dabSeric void 4603b188dabSeric m_get_u32(struct imsgproc *p, uint32_t *dst) 4613b188dabSeric { 4623b188dabSeric m_get(p, dst, sizeof(*dst)); 4633b188dabSeric } 4643b188dabSeric 4653b188dabSeric void 4663b188dabSeric m_get_u64(struct imsgproc *p, uint64_t *dst) 4673b188dabSeric { 4683b188dabSeric m_get(p, dst, sizeof(*dst)); 4693b188dabSeric } 4703b188dabSeric 4713b188dabSeric void 4723b188dabSeric m_get_size(struct imsgproc *p, size_t *dst) 4733b188dabSeric { 4743b188dabSeric m_get(p, dst, sizeof(*dst)); 4753b188dabSeric } 4763b188dabSeric 4773b188dabSeric void 4783b188dabSeric m_get_time(struct imsgproc *p, time_t *dst) 4793b188dabSeric { 4803b188dabSeric m_get(p, dst, sizeof(*dst)); 4813b188dabSeric } 4823b188dabSeric 4833b188dabSeric void 4843b188dabSeric m_get_string(struct imsgproc *p, const char **dst) 4853b188dabSeric { 4869d855d3dSeric char *end, c; 4873b188dabSeric 4883b188dabSeric if (p->m_in.pos >= p->m_in.end) 4893b188dabSeric fatalx("%s: no data left", __func__); 4903b188dabSeric 4919d855d3dSeric c = *p->m_in.pos++; 4929d855d3dSeric if (c == '\0') { 4939d855d3dSeric *dst = NULL; 4949d855d3dSeric return; 4959d855d3dSeric } 4969d855d3dSeric 4979d855d3dSeric if (p->m_in.pos >= p->m_in.end) 4989d855d3dSeric fatalx("%s: no data left", __func__); 4993b188dabSeric end = memchr(p->m_in.pos, 0, p->m_in.end - p->m_in.pos); 5003b188dabSeric if (end == NULL) 5013b188dabSeric fatalx("%s: unterminated string", __func__); 5023b188dabSeric 5033b188dabSeric *dst = p->m_in.pos; 5043b188dabSeric p->m_in.pos = end + 1; 5053b188dabSeric } 5063b188dabSeric 5073b188dabSeric void 5083b188dabSeric m_get_sockaddr(struct imsgproc *p, struct sockaddr *dst) 5093b188dabSeric { 5103b188dabSeric size_t len; 5113b188dabSeric 5123b188dabSeric m_get_size(p, &len); 5133b188dabSeric m_get(p, dst, len); 5143b188dabSeric } 515