1 /* $OpenBSD: proc.c,v 1.7 2024/11/21 13:34:51 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2017 Eric Faurot <eric@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/socket.h> 21 22 #include <errno.h> 23 #include <event.h> 24 #include <imsg.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "log.h" 30 #include "proc.h" 31 32 struct imsgproc { 33 TAILQ_ENTRY(imsgproc) tqe; 34 int type; 35 int instance; 36 char *title; 37 pid_t pid; 38 void *arg; 39 void (*cb)(struct imsgproc *, struct imsg *, void *); 40 struct imsgbuf imsgbuf; 41 short events; 42 struct event ev; 43 44 struct { 45 const uint8_t *pos; 46 const uint8_t *end; 47 } m_in; 48 49 struct m_out { 50 char *buf; 51 size_t alloc; 52 size_t pos; 53 uint32_t type; 54 uint32_t peerid; 55 pid_t pid; 56 int fd; 57 } m_out; 58 }; 59 60 static struct imsgproc *proc_new(int); 61 static void proc_setsock(struct imsgproc *, int); 62 static void proc_callback(struct imsgproc *, struct imsg *); 63 static void proc_dispatch(int, short, void *); 64 static void proc_event_add(struct imsgproc *); 65 66 static TAILQ_HEAD(, imsgproc) procs = TAILQ_HEAD_INITIALIZER(procs); 67 68 pid_t 69 proc_getpid(struct imsgproc *p) 70 { 71 return p->pid; 72 } 73 74 int 75 proc_gettype(struct imsgproc *p) 76 { 77 return p->type; 78 } 79 80 int 81 proc_getinstance(struct imsgproc *p) 82 { 83 return p->instance; 84 } 85 86 const char * 87 proc_gettitle(struct imsgproc *p) 88 { 89 return p->title; 90 } 91 92 struct imsgproc * 93 proc_bypid(pid_t pid) 94 { 95 struct imsgproc *p; 96 97 TAILQ_FOREACH(p, &procs, tqe) 98 if (pid == p->pid) 99 return p; 100 101 return NULL; 102 } 103 104 struct imsgproc * 105 proc_exec(int type, char **argv) 106 { 107 struct imsgproc *p; 108 int sp[2]; 109 pid_t pid; 110 111 p = proc_new(type); 112 if (p == NULL) { 113 log_warn("%s: proc_new", __func__); 114 return NULL; 115 } 116 117 if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, PF_UNSPEC, sp) == -1) { 118 log_warn("%s: socketpair", __func__); 119 proc_free(p); 120 return NULL; 121 } 122 123 switch (pid = fork()) { 124 case -1: 125 log_warn("%s: fork", __func__); 126 close(sp[0]); 127 close(sp[1]); 128 proc_free(p); 129 return NULL; 130 case 0: 131 break; 132 default: 133 close(sp[0]); 134 p->pid = pid; 135 proc_setsock(p, sp[1]); 136 return p; 137 } 138 139 if (dup2(sp[0], 3) == -1) 140 fatal("%s: dup2", __func__); 141 142 if (closefrom(4) == -1) 143 fatal("%s: closefrom", __func__); 144 145 execvp(argv[0], argv); 146 fatal("%s: execvp: %s", __func__, argv[0]); 147 } 148 149 struct imsgproc * 150 proc_attach(int type, int fd) 151 { 152 struct imsgproc *p; 153 154 p = proc_new(type); 155 if (p == NULL) 156 return NULL; 157 158 proc_setsock(p, fd); 159 return p; 160 } 161 162 void 163 proc_settitle(struct imsgproc *p, const char *title) 164 { 165 free(p->title); 166 if (title) { 167 p->title = strdup(title); 168 if (p->title == NULL) 169 log_warn("%s: strdup", __func__); 170 } 171 else 172 p->title = NULL; 173 } 174 175 void 176 proc_setpid(struct imsgproc *p, pid_t pid) 177 { 178 p->pid = pid; 179 } 180 181 void 182 proc_setcallback(struct imsgproc *p, 183 void(*cb)(struct imsgproc *, struct imsg *, void *), void *arg) 184 { 185 p->cb = cb; 186 p->arg = arg; 187 } 188 189 void 190 proc_enable(struct imsgproc *p) 191 { 192 proc_event_add(p); 193 } 194 195 void 196 proc_free(struct imsgproc *p) 197 { 198 if (p == NULL) 199 return; 200 201 TAILQ_REMOVE(&procs, p, tqe); 202 203 if (event_initialized(&p->ev)) 204 event_del(&p->ev); 205 close(p->imsgbuf.fd); 206 imsgbuf_clear(&p->imsgbuf); 207 free(p->title); 208 free(p); 209 } 210 211 static struct imsgproc * 212 proc_new(int type) 213 { 214 struct imsgproc *p; 215 216 p = calloc(1, sizeof(*p)); 217 if (p == NULL) 218 return NULL; 219 220 if (imsgbuf_init(&p->imsgbuf, -1) == -1) { 221 free(p); 222 return NULL; 223 } 224 imsgbuf_allow_fdpass(&p->imsgbuf); 225 226 p->type = type; 227 p->instance = -1; 228 p->pid = -1; 229 230 TAILQ_INSERT_TAIL(&procs, p, tqe); 231 232 return p; 233 } 234 235 static void 236 proc_setsock(struct imsgproc *p, int sock) 237 { 238 p->imsgbuf.fd = sock; 239 } 240 241 static void 242 proc_event_add(struct imsgproc *p) 243 { 244 short events; 245 246 events = EV_READ; 247 if (imsgbuf_queuelen(&p->imsgbuf) > 0) 248 events |= EV_WRITE; 249 250 if (p->events) 251 event_del(&p->ev); 252 253 p->events = events; 254 if (events) { 255 event_set(&p->ev, p->imsgbuf.fd, events, proc_dispatch, p); 256 event_add(&p->ev, NULL); 257 } 258 } 259 260 static void 261 proc_callback(struct imsgproc *p, struct imsg *imsg) 262 { 263 if (imsg != NULL) { 264 p->m_in.pos = imsg->data; 265 p->m_in.end = p->m_in.pos + (imsg->hdr.len - sizeof(imsg->hdr)); 266 } 267 else { 268 p->m_in.pos = NULL; 269 p->m_in.end = NULL; 270 } 271 272 p->cb(p, imsg, p->arg); 273 } 274 275 static void 276 proc_dispatch(int fd, short event, void *arg) 277 { 278 struct imsgproc *p = arg; 279 struct imsg imsg; 280 ssize_t n; 281 282 p->events = 0; 283 284 if (event & EV_READ) { 285 n = imsgbuf_read(&p->imsgbuf); 286 switch (n) { 287 case -1: 288 log_warn("%s: imsgbuf_read", __func__); 289 proc_callback(p, NULL); 290 return; 291 case 0: 292 /* This pipe is dead. */ 293 proc_callback(p, NULL); 294 return; 295 default: 296 break; 297 } 298 } 299 300 if (event & EV_WRITE) { 301 if (imsgbuf_write(&p->imsgbuf) == -1) { 302 if (errno != EPIPE) 303 log_warn("%s: imsgbuf_write", __func__); 304 proc_callback(p, NULL); 305 return; 306 } 307 } 308 309 for (;;) { 310 if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) { 311 log_warn("%s: imsg_get", __func__); 312 proc_callback(p, NULL); 313 return; 314 } 315 if (n == 0) 316 break; 317 318 proc_callback(p, &imsg); 319 imsg_free(&imsg); 320 } 321 322 proc_event_add(p); 323 } 324 325 void 326 m_compose(struct imsgproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd, 327 const void *data, size_t len) 328 { 329 if (imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len) == -1) 330 fatal("%s: imsg_compose", __func__); 331 332 proc_event_add(p); 333 } 334 335 void 336 m_create(struct imsgproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd) 337 { 338 p->m_out.pos = 0; 339 p->m_out.type = type; 340 p->m_out.peerid = peerid; 341 p->m_out.pid = pid; 342 p->m_out.fd = fd; 343 } 344 345 void 346 m_close(struct imsgproc *p) 347 { 348 if (imsg_compose(&p->imsgbuf, p->m_out.type, p->m_out.peerid, 349 p->m_out.pid, p->m_out.fd, p->m_out.buf, p->m_out.pos) == -1) 350 fatal("%s: imsg_compose", __func__); 351 352 proc_event_add(p); 353 } 354 355 void 356 m_add(struct imsgproc *p, const void *data, size_t len) 357 { 358 size_t alloc; 359 void *tmp; 360 361 if (p->m_out.pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) 362 fatalx("%s: message too large", __func__); 363 364 alloc = p->m_out.alloc ? p->m_out.alloc : 128; 365 while (p->m_out.pos + len > alloc) 366 alloc *= 2; 367 if (alloc != p->m_out.alloc) { 368 tmp = recallocarray(p->m_out.buf, p->m_out.alloc, alloc, 1); 369 if (tmp == NULL) 370 fatal("%s: reallocarray", __func__); 371 p->m_out.alloc = alloc; 372 p->m_out.buf = tmp; 373 } 374 375 memmove(p->m_out.buf + p->m_out.pos, data, len); 376 p->m_out.pos += len; 377 } 378 379 void 380 m_add_int(struct imsgproc *p, int v) 381 { 382 m_add(p, &v, sizeof(v)); 383 }; 384 385 void 386 m_add_u32(struct imsgproc *p, uint32_t v) 387 { 388 m_add(p, &v, sizeof(v)); 389 }; 390 391 void 392 m_add_u64(struct imsgproc *p, uint64_t v) 393 { 394 m_add(p, &v, sizeof(v)); 395 } 396 397 void 398 m_add_size(struct imsgproc *p, size_t v) 399 { 400 m_add(p, &v, sizeof(v)); 401 } 402 403 void 404 m_add_time(struct imsgproc *p, time_t v) 405 { 406 m_add(p, &v, sizeof(v)); 407 } 408 409 void 410 m_add_string(struct imsgproc *p, const char *str) 411 { 412 if (str) { 413 m_add(p, "s", 1); 414 m_add(p, str, strlen(str) + 1); 415 } 416 else 417 m_add(p, "\0", 1); 418 } 419 420 void 421 m_add_sockaddr(struct imsgproc *p, const struct sockaddr *sa) 422 { 423 m_add_size(p, sa->sa_len); 424 m_add(p, sa, sa->sa_len); 425 } 426 427 void 428 m_end(struct imsgproc *p) 429 { 430 if (p->m_in.pos != p->m_in.end) 431 fatal("%s: %zi bytes left", __func__, 432 p->m_in.end - p->m_in.pos); 433 } 434 435 int 436 m_is_eom(struct imsgproc *p) 437 { 438 return (p->m_in.pos == p->m_in.end); 439 } 440 441 void 442 m_get(struct imsgproc *p, void *dst, size_t sz) 443 { 444 if (sz > MAX_IMSGSIZE || 445 p->m_in.end - p->m_in.pos < (ssize_t)sz ) 446 fatalx("%s: %zu bytes requested, %zi left", __func__, sz, 447 p->m_in.end - p->m_in.pos); 448 449 memmove(dst, p->m_in.pos, sz); 450 p->m_in.pos += sz; 451 } 452 453 void 454 m_get_int(struct imsgproc *p, int *dst) 455 { 456 m_get(p, dst, sizeof(*dst)); 457 } 458 459 void 460 m_get_u32(struct imsgproc *p, uint32_t *dst) 461 { 462 m_get(p, dst, sizeof(*dst)); 463 } 464 465 void 466 m_get_u64(struct imsgproc *p, uint64_t *dst) 467 { 468 m_get(p, dst, sizeof(*dst)); 469 } 470 471 void 472 m_get_size(struct imsgproc *p, size_t *dst) 473 { 474 m_get(p, dst, sizeof(*dst)); 475 } 476 477 void 478 m_get_time(struct imsgproc *p, time_t *dst) 479 { 480 m_get(p, dst, sizeof(*dst)); 481 } 482 483 void 484 m_get_string(struct imsgproc *p, const char **dst) 485 { 486 char *end, c; 487 488 if (p->m_in.pos >= p->m_in.end) 489 fatalx("%s: no data left", __func__); 490 491 c = *p->m_in.pos++; 492 if (c == '\0') { 493 *dst = NULL; 494 return; 495 } 496 497 if (p->m_in.pos >= p->m_in.end) 498 fatalx("%s: no data left", __func__); 499 end = memchr(p->m_in.pos, 0, p->m_in.end - p->m_in.pos); 500 if (end == NULL) 501 fatalx("%s: unterminated string", __func__); 502 503 *dst = p->m_in.pos; 504 p->m_in.pos = end + 1; 505 } 506 507 void 508 m_get_sockaddr(struct imsgproc *p, struct sockaddr *dst) 509 { 510 size_t len; 511 512 m_get_size(p, &len); 513 m_get(p, dst, len); 514 } 515