xref: /openbsd-src/lib/libutil/imsg.c (revision a4ff2ce6405b8ec1b573e8e9e86963cee013d385)
1*a4ff2ce6Stb /*	$OpenBSD: imsg.c,v 1.38 2024/11/29 04:35:13 tb Exp $	*/
2dfaf6462Snicm 
3dfaf6462Snicm /*
44658a150Sclaudio  * Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
5dfaf6462Snicm  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6dfaf6462Snicm  *
7dfaf6462Snicm  * Permission to use, copy, modify, and distribute this software for any
8dfaf6462Snicm  * purpose with or without fee is hereby granted, provided that the above
9dfaf6462Snicm  * copyright notice and this permission notice appear in all copies.
10dfaf6462Snicm  *
11dfaf6462Snicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12dfaf6462Snicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13dfaf6462Snicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14dfaf6462Snicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15dfaf6462Snicm  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16dfaf6462Snicm  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17dfaf6462Snicm  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18dfaf6462Snicm  */
19dfaf6462Snicm 
20429b3653Sderaadt #include <sys/types.h>
21dfaf6462Snicm #include <sys/queue.h>
22dfaf6462Snicm #include <sys/socket.h>
23dfaf6462Snicm #include <sys/uio.h>
24dfaf6462Snicm 
25dfaf6462Snicm #include <errno.h>
26ff59764dSclaudio #include <stddef.h>
27dfaf6462Snicm #include <stdlib.h>
28dfaf6462Snicm #include <string.h>
29dfaf6462Snicm #include <unistd.h>
30dfaf6462Snicm 
31dfaf6462Snicm #include "imsg.h"
32dfaf6462Snicm 
3304d83f12Sclaudio #define IMSG_ALLOW_FDPASS	0x01
34ff59764dSclaudio #define IMSG_FD_MARK		0x80000000U
354658a150Sclaudio 
36ff59764dSclaudio static struct ibuf	*imsg_parse_hdr(struct ibuf *, void *, int *);
37dfaf6462Snicm 
3804d83f12Sclaudio int
39d5c9e083Sclaudio imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
40dfaf6462Snicm {
4104d83f12Sclaudio 	imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
4204d83f12Sclaudio 	    imsgbuf);
4304d83f12Sclaudio 	if (imsgbuf->w == NULL)
4404d83f12Sclaudio 		return (-1);
4541d3eadeSclaudio 	imsgbuf->pid = getpid();
4604d83f12Sclaudio 	imsgbuf->maxsize = MAX_IMSGSIZE;
4704d83f12Sclaudio 	imsgbuf->fd = fd;
4804d83f12Sclaudio 	imsgbuf->flags = 0;
4904d83f12Sclaudio 	return (0);
5004d83f12Sclaudio }
5104d83f12Sclaudio 
5204d83f12Sclaudio void
5304d83f12Sclaudio imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
5404d83f12Sclaudio {
5504d83f12Sclaudio 	imsgbuf->flags |= IMSG_ALLOW_FDPASS;
5604d83f12Sclaudio }
5704d83f12Sclaudio 
58ff59764dSclaudio int
5904d83f12Sclaudio imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t maxsize)
6004d83f12Sclaudio {
61ff59764dSclaudio 	if (maxsize < IMSG_HEADER_SIZE || maxsize & IMSG_FD_MARK) {
62ff59764dSclaudio 		errno = EINVAL;
63ff59764dSclaudio 		return (-1);
64ff59764dSclaudio 	}
6504d83f12Sclaudio 	imsgbuf->maxsize = maxsize;
66ff59764dSclaudio 	return (0);
67dfaf6462Snicm }
68dfaf6462Snicm 
69c362c6deSclaudio int
70d5c9e083Sclaudio imsgbuf_read(struct imsgbuf *imsgbuf)
71dfaf6462Snicm {
7204d83f12Sclaudio 	if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
7304d83f12Sclaudio 		return msgbuf_read(imsgbuf->fd, imsgbuf->w);
7404d83f12Sclaudio 	else
7504d83f12Sclaudio 		return ibuf_read(imsgbuf->fd, imsgbuf->w);
76dfaf6462Snicm }
77dfaf6462Snicm 
7844c57f07Sclaudio int
79d5c9e083Sclaudio imsgbuf_write(struct imsgbuf *imsgbuf)
8044c57f07Sclaudio {
8104d83f12Sclaudio 	if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
82156c3c53Sclaudio 		return msgbuf_write(imsgbuf->fd, imsgbuf->w);
8304d83f12Sclaudio 	else
8404d83f12Sclaudio 		return ibuf_write(imsgbuf->fd, imsgbuf->w);
8544c57f07Sclaudio }
8644c57f07Sclaudio 
87d5c9e083Sclaudio int
88d5c9e083Sclaudio imsgbuf_flush(struct imsgbuf *imsgbuf)
89d5c9e083Sclaudio {
90156c3c53Sclaudio 	while (imsgbuf_queuelen(imsgbuf) > 0) {
91d5c9e083Sclaudio 		if (imsgbuf_write(imsgbuf) == -1)
92d5c9e083Sclaudio 			return (-1);
93156c3c53Sclaudio 	}
94d5c9e083Sclaudio 	return (0);
95d5c9e083Sclaudio }
96d5c9e083Sclaudio 
97d5c9e083Sclaudio void
98d5c9e083Sclaudio imsgbuf_clear(struct imsgbuf *imsgbuf)
99d5c9e083Sclaudio {
100156c3c53Sclaudio 	msgbuf_free(imsgbuf->w);
101156c3c53Sclaudio 	imsgbuf->w = NULL;
102d5c9e083Sclaudio }
103d5c9e083Sclaudio 
104a17071b2Sclaudio uint32_t
105a17071b2Sclaudio imsgbuf_queuelen(struct imsgbuf *imsgbuf)
106a17071b2Sclaudio {
107156c3c53Sclaudio 	return msgbuf_queuelen(imsgbuf->w);
108a17071b2Sclaudio }
109a17071b2Sclaudio 
110dfaf6462Snicm ssize_t
11141d3eadeSclaudio imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
112dfaf6462Snicm {
1134658a150Sclaudio 	struct imsg		 m;
11404d83f12Sclaudio 	struct ibuf		*buf;
115dfaf6462Snicm 
11604d83f12Sclaudio 	if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
117dfaf6462Snicm 		return (0);
118dfaf6462Snicm 
11904d83f12Sclaudio 	if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
120dfaf6462Snicm 		return (-1);
1214658a150Sclaudio 
12204d83f12Sclaudio 	if (ibuf_size(buf))
12304d83f12Sclaudio 		m.data = ibuf_data(buf);
12404d83f12Sclaudio 	else
1254658a150Sclaudio 		m.data = NULL;
12604d83f12Sclaudio 	m.buf = buf;
127ff59764dSclaudio 	m.hdr.len &= ~IMSG_FD_MARK;
128dfaf6462Snicm 
1294658a150Sclaudio 	*imsg = m;
13004d83f12Sclaudio 	return (ibuf_size(buf) + IMSG_HEADER_SIZE);
131dfaf6462Snicm }
132dfaf6462Snicm 
133dfaf6462Snicm int
1344658a150Sclaudio imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
1354658a150Sclaudio {
13604d83f12Sclaudio 	if (ibuf_size(imsg->buf) == 0) {
1374658a150Sclaudio 		errno = EBADMSG;
1384658a150Sclaudio 		return (-1);
1394658a150Sclaudio 	}
1404658a150Sclaudio 	return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
1414658a150Sclaudio }
1424658a150Sclaudio 
1434658a150Sclaudio int
1444658a150Sclaudio imsg_get_data(struct imsg *imsg, void *data, size_t len)
1454658a150Sclaudio {
1464658a150Sclaudio 	if (len == 0) {
1474658a150Sclaudio 		errno = EINVAL;
1484658a150Sclaudio 		return (-1);
1494658a150Sclaudio 	}
15004d83f12Sclaudio 	if (ibuf_size(imsg->buf) != len) {
1514658a150Sclaudio 		errno = EBADMSG;
1524658a150Sclaudio 		return (-1);
1534658a150Sclaudio 	}
1544658a150Sclaudio 	return ibuf_get(imsg->buf, data, len);
1554658a150Sclaudio }
1564658a150Sclaudio 
1574658a150Sclaudio int
1584658a150Sclaudio imsg_get_fd(struct imsg *imsg)
1594658a150Sclaudio {
1608d7d8ae7Sclaudio 	return ibuf_fd_get(imsg->buf);
1614658a150Sclaudio }
1624658a150Sclaudio 
1634658a150Sclaudio uint32_t
1644658a150Sclaudio imsg_get_id(struct imsg *imsg)
1654658a150Sclaudio {
1664658a150Sclaudio 	return (imsg->hdr.peerid);
1674658a150Sclaudio }
1684658a150Sclaudio 
1694658a150Sclaudio size_t
1704658a150Sclaudio imsg_get_len(struct imsg *imsg)
1714658a150Sclaudio {
1724658a150Sclaudio 	return ibuf_size(imsg->buf);
1734658a150Sclaudio }
1744658a150Sclaudio 
1754658a150Sclaudio pid_t
1764658a150Sclaudio imsg_get_pid(struct imsg *imsg)
1774658a150Sclaudio {
1784658a150Sclaudio 	return (imsg->hdr.pid);
1794658a150Sclaudio }
1804658a150Sclaudio 
1814658a150Sclaudio uint32_t
1824658a150Sclaudio imsg_get_type(struct imsg *imsg)
1834658a150Sclaudio {
1844658a150Sclaudio 	return (imsg->hdr.type);
1854658a150Sclaudio }
1864658a150Sclaudio 
1874658a150Sclaudio int
188c1465023Sclaudio imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
1894658a150Sclaudio     int fd, const void *data, size_t datalen)
190dfaf6462Snicm {
191dfaf6462Snicm 	struct ibuf	*wbuf;
192dfaf6462Snicm 
193c1465023Sclaudio 	if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
194dfaf6462Snicm 		return (-1);
195dfaf6462Snicm 
196dfaf6462Snicm 	if (imsg_add(wbuf, data, datalen) == -1)
197dfaf6462Snicm 		return (-1);
198dfaf6462Snicm 
19919778535Sclaudio 	ibuf_fd_set(wbuf, fd);
20041d3eadeSclaudio 	imsg_close(imsgbuf, wbuf);
201dfaf6462Snicm 
202dfaf6462Snicm 	return (1);
203dfaf6462Snicm }
204dfaf6462Snicm 
205dfaf6462Snicm int
206c1465023Sclaudio imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
207c1465023Sclaudio     int fd, const struct iovec *iov, int iovcnt)
208dfaf6462Snicm {
209dfaf6462Snicm 	struct ibuf	*wbuf;
2104658a150Sclaudio 	int		 i;
2114658a150Sclaudio 	size_t		 datalen = 0;
212dfaf6462Snicm 
213dfaf6462Snicm 	for (i = 0; i < iovcnt; i++)
214dfaf6462Snicm 		datalen += iov[i].iov_len;
215dfaf6462Snicm 
216c1465023Sclaudio 	if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
217dfaf6462Snicm 		return (-1);
218dfaf6462Snicm 
219dfaf6462Snicm 	for (i = 0; i < iovcnt; i++)
220dfaf6462Snicm 		if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
221dfaf6462Snicm 			return (-1);
222dfaf6462Snicm 
22319778535Sclaudio 	ibuf_fd_set(wbuf, fd);
22441d3eadeSclaudio 	imsg_close(imsgbuf, wbuf);
225dfaf6462Snicm 
226dfaf6462Snicm 	return (1);
227dfaf6462Snicm }
228dfaf6462Snicm 
2294658a150Sclaudio /*
2304658a150Sclaudio  * Enqueue imsg with payload from ibuf buf. fd passing is not possible
2314658a150Sclaudio  * with this function.
2324658a150Sclaudio  */
23319778535Sclaudio int
234c1465023Sclaudio imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
23519778535Sclaudio     pid_t pid, struct ibuf *buf)
23619778535Sclaudio {
2374658a150Sclaudio 	struct ibuf	*hdrbuf = NULL;
23819778535Sclaudio 	struct imsg_hdr	 hdr;
23919778535Sclaudio 	int save_errno;
24019778535Sclaudio 
24104d83f12Sclaudio 	if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) {
24219778535Sclaudio 		errno = ERANGE;
24319778535Sclaudio 		goto fail;
24419778535Sclaudio 	}
24519778535Sclaudio 
24619778535Sclaudio 	hdr.type = type;
24719778535Sclaudio 	hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
248c1465023Sclaudio 	hdr.peerid = id;
24919778535Sclaudio 	if ((hdr.pid = pid) == 0)
25041d3eadeSclaudio 		hdr.pid = imsgbuf->pid;
25119778535Sclaudio 
2524658a150Sclaudio 	if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
25319778535Sclaudio 		goto fail;
2544658a150Sclaudio 	if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
25519778535Sclaudio 		goto fail;
25619778535Sclaudio 
257156c3c53Sclaudio 	ibuf_close(imsgbuf->w, hdrbuf);
258156c3c53Sclaudio 	ibuf_close(imsgbuf->w, buf);
25919778535Sclaudio 	return (1);
26019778535Sclaudio 
26119778535Sclaudio  fail:
26219778535Sclaudio 	save_errno = errno;
26319778535Sclaudio 	ibuf_free(buf);
2644658a150Sclaudio 	ibuf_free(hdrbuf);
26519778535Sclaudio 	errno = save_errno;
26619778535Sclaudio 	return (-1);
26719778535Sclaudio }
26819778535Sclaudio 
2694658a150Sclaudio /*
2704658a150Sclaudio  * Forward imsg to another channel. Any attached fd is closed.
2714658a150Sclaudio  */
2724658a150Sclaudio int
2734658a150Sclaudio imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
2744658a150Sclaudio {
2754658a150Sclaudio 	struct ibuf	*wbuf;
276ff59764dSclaudio 	size_t		 len;
2774658a150Sclaudio 
2784658a150Sclaudio 	ibuf_rewind(msg->buf);
27904d83f12Sclaudio 	ibuf_skip(msg->buf, sizeof(msg->hdr));
2804658a150Sclaudio 	len = ibuf_size(msg->buf);
2814658a150Sclaudio 
2824658a150Sclaudio 	if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
2834658a150Sclaudio 	    msg->hdr.pid, len)) == NULL)
2844658a150Sclaudio 		return (-1);
2854658a150Sclaudio 
286ff59764dSclaudio 	if (len != 0) {
2873b84e804Sclaudio 		if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
2884658a150Sclaudio 			ibuf_free(wbuf);
2894658a150Sclaudio 			return (-1);
2904658a150Sclaudio 		}
2914658a150Sclaudio 	}
2924658a150Sclaudio 
2934658a150Sclaudio 	imsg_close(imsgbuf, wbuf);
2944658a150Sclaudio 	return (1);
2954658a150Sclaudio }
2964658a150Sclaudio 
297dfaf6462Snicm struct ibuf *
298c1465023Sclaudio imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
2994658a150Sclaudio     size_t datalen)
300dfaf6462Snicm {
301dfaf6462Snicm 	struct ibuf	*wbuf;
302dfaf6462Snicm 	struct imsg_hdr	 hdr;
303dfaf6462Snicm 
304dfaf6462Snicm 	datalen += IMSG_HEADER_SIZE;
30504d83f12Sclaudio 	if (datalen > imsgbuf->maxsize) {
306dfaf6462Snicm 		errno = ERANGE;
307dfaf6462Snicm 		return (NULL);
308dfaf6462Snicm 	}
309dfaf6462Snicm 
310dfaf6462Snicm 	hdr.type = type;
311c1465023Sclaudio 	hdr.peerid = id;
312dfaf6462Snicm 	if ((hdr.pid = pid) == 0)
31341d3eadeSclaudio 		hdr.pid = imsgbuf->pid;
31404d83f12Sclaudio 	if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) {
315dfaf6462Snicm 		return (NULL);
316dfaf6462Snicm 	}
317dfaf6462Snicm 	if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
318dfaf6462Snicm 		return (NULL);
319dfaf6462Snicm 
320dfaf6462Snicm 	return (wbuf);
321dfaf6462Snicm }
322dfaf6462Snicm 
323dfaf6462Snicm int
3244658a150Sclaudio imsg_add(struct ibuf *msg, const void *data, size_t datalen)
325dfaf6462Snicm {
326dfaf6462Snicm 	if (datalen)
327dfaf6462Snicm 		if (ibuf_add(msg, data, datalen) == -1) {
328dfaf6462Snicm 			ibuf_free(msg);
329dfaf6462Snicm 			return (-1);
330dfaf6462Snicm 		}
331dfaf6462Snicm 	return (datalen);
332dfaf6462Snicm }
333dfaf6462Snicm 
334dfaf6462Snicm void
33541d3eadeSclaudio imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
336dfaf6462Snicm {
337ff59764dSclaudio 	uint32_t len;
338dfaf6462Snicm 
339ff59764dSclaudio 	len = ibuf_size(msg);
340ff59764dSclaudio 	if (ibuf_fd_avail(msg))
341ff59764dSclaudio 		len |= IMSG_FD_MARK;
342ff59764dSclaudio 	(void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
343156c3c53Sclaudio 	ibuf_close(imsgbuf->w, msg);
344dfaf6462Snicm }
345dfaf6462Snicm 
346dfaf6462Snicm void
347dfaf6462Snicm imsg_free(struct imsg *imsg)
348dfaf6462Snicm {
3494658a150Sclaudio 	ibuf_free(imsg->buf);
350dfaf6462Snicm }
351dfaf6462Snicm 
352ff59764dSclaudio static struct ibuf *
353ff59764dSclaudio imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd)
354dfaf6462Snicm {
35504d83f12Sclaudio 	struct imsgbuf *imsgbuf = arg;
35604d83f12Sclaudio 	struct imsg_hdr hdr;
357ff59764dSclaudio 	struct ibuf *b;
358ff59764dSclaudio 	uint32_t len;
359dfaf6462Snicm 
36004d83f12Sclaudio 	if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
361ff59764dSclaudio 		return (NULL);
362ff59764dSclaudio 
363ff59764dSclaudio 	len = hdr.len & ~IMSG_FD_MARK;
364ff59764dSclaudio 
365ff59764dSclaudio 	if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) {
36604d83f12Sclaudio 		errno = ERANGE;
367ff59764dSclaudio 		return (NULL);
36804d83f12Sclaudio 	}
369ff59764dSclaudio 	if ((b = ibuf_open(len)) == NULL)
370ff59764dSclaudio 		return (NULL);
371ff59764dSclaudio 	if (hdr.len & IMSG_FD_MARK) {
372ff59764dSclaudio 		ibuf_fd_set(b, *fd);
373ff59764dSclaudio 		*fd = -1;
374ff59764dSclaudio 	}
375ff59764dSclaudio 
376ff59764dSclaudio 	return b;
377dfaf6462Snicm }
378