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
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 imsg_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 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
proc_setsock(struct imsgproc * p,int sock)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
proc_event_add(struct imsgproc * p)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
proc_callback(struct imsgproc * p,struct imsg * imsg)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
proc_dispatch(int fd,short event,void * arg)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
m_compose(struct imsgproc * p,uint32_t type,uint32_t peerid,pid_t pid,int fd,const void * data,size_t len)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
m_create(struct imsgproc * p,uint32_t type,uint32_t peerid,pid_t pid,int fd)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
m_close(struct imsgproc * p)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
m_add(struct imsgproc * p,const void * data,size_t len)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
m_add_int(struct imsgproc * p,int v)387 m_add_int(struct imsgproc *p, int v)
388 {
389 m_add(p, &v, sizeof(v));
390 };
391
392 void
m_add_u32(struct imsgproc * p,uint32_t v)393 m_add_u32(struct imsgproc *p, uint32_t v)
394 {
395 m_add(p, &v, sizeof(v));
396 };
397
398 void
m_add_u64(struct imsgproc * p,uint64_t v)399 m_add_u64(struct imsgproc *p, uint64_t v)
400 {
401 m_add(p, &v, sizeof(v));
402 }
403
404 void
m_add_size(struct imsgproc * p,size_t v)405 m_add_size(struct imsgproc *p, size_t v)
406 {
407 m_add(p, &v, sizeof(v));
408 }
409
410 void
m_add_time(struct imsgproc * p,time_t v)411 m_add_time(struct imsgproc *p, time_t v)
412 {
413 m_add(p, &v, sizeof(v));
414 }
415
416 void
m_add_string(struct imsgproc * p,const char * str)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
m_add_sockaddr(struct imsgproc * p,const struct sockaddr * sa)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
m_end(struct imsgproc * p)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
m_is_eom(struct imsgproc * p)443 m_is_eom(struct imsgproc *p)
444 {
445 return (p->m_in.pos == p->m_in.end);
446 }
447
448 void
m_get(struct imsgproc * p,void * dst,size_t sz)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
m_get_int(struct imsgproc * p,int * dst)461 m_get_int(struct imsgproc *p, int *dst)
462 {
463 m_get(p, dst, sizeof(*dst));
464 }
465
466 void
m_get_u32(struct imsgproc * p,uint32_t * dst)467 m_get_u32(struct imsgproc *p, uint32_t *dst)
468 {
469 m_get(p, dst, sizeof(*dst));
470 }
471
472 void
m_get_u64(struct imsgproc * p,uint64_t * dst)473 m_get_u64(struct imsgproc *p, uint64_t *dst)
474 {
475 m_get(p, dst, sizeof(*dst));
476 }
477
478 void
m_get_size(struct imsgproc * p,size_t * dst)479 m_get_size(struct imsgproc *p, size_t *dst)
480 {
481 m_get(p, dst, sizeof(*dst));
482 }
483
484 void
m_get_time(struct imsgproc * p,time_t * dst)485 m_get_time(struct imsgproc *p, time_t *dst)
486 {
487 m_get(p, dst, sizeof(*dst));
488 }
489
490 void
m_get_string(struct imsgproc * p,const char ** dst)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
m_get_sockaddr(struct imsgproc * p,struct sockaddr * dst)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