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
proc_getpid(struct imsgproc * p)69 proc_getpid(struct imsgproc *p)
70 {
71 return p->pid;
72 }
73
74 int
proc_gettype(struct imsgproc * p)75 proc_gettype(struct imsgproc *p)
76 {
77 return p->type;
78 }
79
80 int
proc_getinstance(struct imsgproc * p)81 proc_getinstance(struct imsgproc *p)
82 {
83 return p->instance;
84 }
85
86 const char *
proc_gettitle(struct imsgproc * p)87 proc_gettitle(struct imsgproc *p)
88 {
89 return p->title;
90 }
91
92 struct imsgproc *
proc_bypid(pid_t pid)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 *
proc_exec(int type,char ** argv)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 *
proc_attach(int type,int fd)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
proc_settitle(struct imsgproc * p,const char * title)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
proc_setpid(struct imsgproc * p,pid_t pid)176 proc_setpid(struct imsgproc *p, pid_t pid)
177 {
178 p->pid = pid;
179 }
180
181 void
proc_setcallback(struct imsgproc * p,void (* cb)(struct imsgproc *,struct imsg *,void *),void * arg)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
proc_enable(struct imsgproc * p)190 proc_enable(struct imsgproc *p)
191 {
192 proc_event_add(p);
193 }
194
195 void
proc_free(struct imsgproc * p)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 *
proc_new(int type)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
proc_setsock(struct imsgproc * p,int sock)236 proc_setsock(struct imsgproc *p, int sock)
237 {
238 p->imsgbuf.fd = sock;
239 }
240
241 static void
proc_event_add(struct imsgproc * p)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
proc_callback(struct imsgproc * p,struct imsg * imsg)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
proc_dispatch(int fd,short event,void * arg)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
m_compose(struct imsgproc * p,uint32_t type,uint32_t peerid,pid_t pid,int fd,const void * data,size_t len)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
m_create(struct imsgproc * p,uint32_t type,uint32_t peerid,pid_t pid,int fd)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
m_close(struct imsgproc * p)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
m_add(struct imsgproc * p,const void * data,size_t len)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
m_add_int(struct imsgproc * p,int v)380 m_add_int(struct imsgproc *p, int v)
381 {
382 m_add(p, &v, sizeof(v));
383 };
384
385 void
m_add_u32(struct imsgproc * p,uint32_t v)386 m_add_u32(struct imsgproc *p, uint32_t v)
387 {
388 m_add(p, &v, sizeof(v));
389 };
390
391 void
m_add_u64(struct imsgproc * p,uint64_t v)392 m_add_u64(struct imsgproc *p, uint64_t v)
393 {
394 m_add(p, &v, sizeof(v));
395 }
396
397 void
m_add_size(struct imsgproc * p,size_t v)398 m_add_size(struct imsgproc *p, size_t v)
399 {
400 m_add(p, &v, sizeof(v));
401 }
402
403 void
m_add_time(struct imsgproc * p,time_t v)404 m_add_time(struct imsgproc *p, time_t v)
405 {
406 m_add(p, &v, sizeof(v));
407 }
408
409 void
m_add_string(struct imsgproc * p,const char * str)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
m_add_sockaddr(struct imsgproc * p,const struct sockaddr * sa)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
m_end(struct imsgproc * p)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
m_is_eom(struct imsgproc * p)436 m_is_eom(struct imsgproc *p)
437 {
438 return (p->m_in.pos == p->m_in.end);
439 }
440
441 void
m_get(struct imsgproc * p,void * dst,size_t sz)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
m_get_int(struct imsgproc * p,int * dst)454 m_get_int(struct imsgproc *p, int *dst)
455 {
456 m_get(p, dst, sizeof(*dst));
457 }
458
459 void
m_get_u32(struct imsgproc * p,uint32_t * dst)460 m_get_u32(struct imsgproc *p, uint32_t *dst)
461 {
462 m_get(p, dst, sizeof(*dst));
463 }
464
465 void
m_get_u64(struct imsgproc * p,uint64_t * dst)466 m_get_u64(struct imsgproc *p, uint64_t *dst)
467 {
468 m_get(p, dst, sizeof(*dst));
469 }
470
471 void
m_get_size(struct imsgproc * p,size_t * dst)472 m_get_size(struct imsgproc *p, size_t *dst)
473 {
474 m_get(p, dst, sizeof(*dst));
475 }
476
477 void
m_get_time(struct imsgproc * p,time_t * dst)478 m_get_time(struct imsgproc *p, time_t *dst)
479 {
480 m_get(p, dst, sizeof(*dst));
481 }
482
483 void
m_get_string(struct imsgproc * p,const char ** dst)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
m_get_sockaddr(struct imsgproc * p,struct sockaddr * dst)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