xref: /openbsd/usr.sbin/lpd/proc.c (revision 9d855d3d)
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