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