1 /* $OpenBSD: proc.c,v 1.2 2019/04/04 19:25:46 eric 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 imsg_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 p->type = type; 221 p->instance = -1; 222 p->pid = -1; 223 imsg_init(&p->imsgbuf, -1); 224 225 TAILQ_INSERT_TAIL(&procs, p, tqe); 226 227 return p; 228 } 229 230 static void 231 proc_setsock(struct imsgproc *p, int sock) 232 { 233 p->imsgbuf.fd = sock; 234 p->imsgbuf.w.fd = sock; 235 } 236 237 static void 238 proc_event_add(struct imsgproc *p) 239 { 240 short events; 241 242 events = EV_READ; 243 if (p->imsgbuf.w.queued) 244 events |= EV_WRITE; 245 246 if (p->events) 247 event_del(&p->ev); 248 249 p->events = events; 250 if (events) { 251 event_set(&p->ev, p->imsgbuf.fd, events, proc_dispatch, p); 252 event_add(&p->ev, NULL); 253 } 254 } 255 256 static void 257 proc_callback(struct imsgproc *p, struct imsg *imsg) 258 { 259 if (imsg != NULL) { 260 p->m_in.pos = imsg->data; 261 p->m_in.end = p->m_in.pos + (imsg->hdr.len - sizeof(imsg->hdr)); 262 } 263 else { 264 p->m_in.pos = NULL; 265 p->m_in.end = NULL; 266 } 267 268 p->cb(p, imsg, p->arg); 269 } 270 271 static void 272 proc_dispatch(int fd, short event, void *arg) 273 { 274 struct imsgproc *p = arg; 275 struct imsg imsg; 276 ssize_t n; 277 278 p->events = 0; 279 280 if (event & EV_READ) { 281 n = imsg_read(&p->imsgbuf); 282 switch (n) { 283 case -1: 284 if (errno == EAGAIN) 285 break; 286 log_warn("%s: imsg_read", __func__); 287 proc_callback(p, NULL); 288 return; 289 case 0: 290 /* This pipe is dead. */ 291 proc_callback(p, NULL); 292 return; 293 default: 294 break; 295 } 296 } 297 298 if (event & EV_WRITE) { 299 n = msgbuf_write(&p->imsgbuf.w); 300 switch (n) { 301 case -1: 302 if (errno == EAGAIN) 303 break; 304 log_warn("%s: msgbuf_write", __func__); 305 proc_callback(p, NULL); 306 return; 307 case 0: 308 /* This pipe is dead. */ 309 proc_callback(p, NULL); 310 return; 311 default: 312 break; 313 } 314 } 315 316 for (;;) { 317 if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) { 318 log_warn("%s: imsg_get", __func__); 319 proc_callback(p, NULL); 320 return; 321 } 322 if (n == 0) 323 break; 324 325 proc_callback(p, &imsg); 326 imsg_free(&imsg); 327 } 328 329 proc_event_add(p); 330 } 331 332 void 333 m_compose(struct imsgproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd, 334 const void *data, size_t len) 335 { 336 if (imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len) == -1) 337 fatal("%s: imsg_compose", __func__); 338 339 proc_event_add(p); 340 } 341 342 void 343 m_create(struct imsgproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd) 344 { 345 p->m_out.pos = 0; 346 p->m_out.type = type; 347 p->m_out.peerid = peerid; 348 p->m_out.pid = pid; 349 p->m_out.fd = fd; 350 } 351 352 void 353 m_close(struct imsgproc *p) 354 { 355 if (imsg_compose(&p->imsgbuf, p->m_out.type, p->m_out.peerid, 356 p->m_out.pid, p->m_out.fd, p->m_out.buf, p->m_out.pos) == -1) 357 fatal("%s: imsg_compose", __func__); 358 359 proc_event_add(p); 360 } 361 362 void 363 m_add(struct imsgproc *p, const void *data, size_t len) 364 { 365 size_t alloc; 366 void *tmp; 367 368 if (p->m_out.pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) 369 fatalx("%s: message too large", __func__); 370 371 alloc = p->m_out.alloc ? p->m_out.alloc : 128; 372 while (p->m_out.pos + len > alloc) 373 alloc *= 2; 374 if (alloc != p->m_out.alloc) { 375 tmp = recallocarray(p->m_out.buf, p->m_out.alloc, alloc, 1); 376 if (tmp == NULL) 377 fatal("%s: reallocarray", __func__); 378 p->m_out.alloc = alloc; 379 p->m_out.buf = tmp; 380 } 381 382 memmove(p->m_out.buf + p->m_out.pos, data, len); 383 p->m_out.pos += len; 384 } 385 386 void 387 m_add_int(struct imsgproc *p, int v) 388 { 389 m_add(p, &v, sizeof(v)); 390 }; 391 392 void 393 m_add_u32(struct imsgproc *p, uint32_t v) 394 { 395 m_add(p, &v, sizeof(v)); 396 }; 397 398 void 399 m_add_u64(struct imsgproc *p, uint64_t v) 400 { 401 m_add(p, &v, sizeof(v)); 402 } 403 404 void 405 m_add_size(struct imsgproc *p, size_t v) 406 { 407 m_add(p, &v, sizeof(v)); 408 } 409 410 void 411 m_add_time(struct imsgproc *p, time_t v) 412 { 413 m_add(p, &v, sizeof(v)); 414 } 415 416 void 417 m_add_string(struct imsgproc *p, const char *str) 418 { 419 if (str) { 420 m_add(p, "s", 1); 421 m_add(p, str, strlen(str) + 1); 422 } 423 else 424 m_add(p, "\0", 1); 425 } 426 427 void 428 m_add_sockaddr(struct imsgproc *p, const struct sockaddr *sa) 429 { 430 m_add_size(p, sa->sa_len); 431 m_add(p, sa, sa->sa_len); 432 } 433 434 void 435 m_end(struct imsgproc *p) 436 { 437 if (p->m_in.pos != p->m_in.end) 438 fatal("%s: %zi bytes left", __func__, 439 p->m_in.end - p->m_in.pos); 440 } 441 442 int 443 m_is_eom(struct imsgproc *p) 444 { 445 return (p->m_in.pos == p->m_in.end); 446 } 447 448 void 449 m_get(struct imsgproc *p, void *dst, size_t sz) 450 { 451 if (sz > MAX_IMSGSIZE || 452 p->m_in.end - p->m_in.pos < (ssize_t)sz ) 453 fatalx("%s: %zu bytes requested, %zi left", __func__, sz, 454 p->m_in.end - p->m_in.pos); 455 456 memmove(dst, p->m_in.pos, sz); 457 p->m_in.pos += sz; 458 } 459 460 void 461 m_get_int(struct imsgproc *p, int *dst) 462 { 463 m_get(p, dst, sizeof(*dst)); 464 } 465 466 void 467 m_get_u32(struct imsgproc *p, uint32_t *dst) 468 { 469 m_get(p, dst, sizeof(*dst)); 470 } 471 472 void 473 m_get_u64(struct imsgproc *p, uint64_t *dst) 474 { 475 m_get(p, dst, sizeof(*dst)); 476 } 477 478 void 479 m_get_size(struct imsgproc *p, size_t *dst) 480 { 481 m_get(p, dst, sizeof(*dst)); 482 } 483 484 void 485 m_get_time(struct imsgproc *p, time_t *dst) 486 { 487 m_get(p, dst, sizeof(*dst)); 488 } 489 490 void 491 m_get_string(struct imsgproc *p, const char **dst) 492 { 493 char *end, c; 494 495 if (p->m_in.pos >= p->m_in.end) 496 fatalx("%s: no data left", __func__); 497 498 c = *p->m_in.pos++; 499 if (c == '\0') { 500 *dst = NULL; 501 return; 502 } 503 504 if (p->m_in.pos >= p->m_in.end) 505 fatalx("%s: no data left", __func__); 506 end = memchr(p->m_in.pos, 0, p->m_in.end - p->m_in.pos); 507 if (end == NULL) 508 fatalx("%s: unterminated string", __func__); 509 510 *dst = p->m_in.pos; 511 p->m_in.pos = end + 1; 512 } 513 514 void 515 m_get_sockaddr(struct imsgproc *p, struct sockaddr *dst) 516 { 517 size_t len; 518 519 m_get_size(p, &len); 520 m_get(p, dst, len); 521 } 522