xref: /openbsd/lib/libutil/imsg.c (revision a4ff2ce6)
1 /*	$OpenBSD: imsg.c,v 1.38 2024/11/29 04:35:13 tb Exp $	*/
2 
3 /*
4  * Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/queue.h>
22 #include <sys/socket.h>
23 #include <sys/uio.h>
24 
25 #include <errno.h>
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "imsg.h"
32 
33 #define IMSG_ALLOW_FDPASS	0x01
34 #define IMSG_FD_MARK		0x80000000U
35 
36 static struct ibuf	*imsg_parse_hdr(struct ibuf *, void *, int *);
37 
38 int
imsgbuf_init(struct imsgbuf * imsgbuf,int fd)39 imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
40 {
41 	imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
42 	    imsgbuf);
43 	if (imsgbuf->w == NULL)
44 		return (-1);
45 	imsgbuf->pid = getpid();
46 	imsgbuf->maxsize = MAX_IMSGSIZE;
47 	imsgbuf->fd = fd;
48 	imsgbuf->flags = 0;
49 	return (0);
50 }
51 
52 void
imsgbuf_allow_fdpass(struct imsgbuf * imsgbuf)53 imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
54 {
55 	imsgbuf->flags |= IMSG_ALLOW_FDPASS;
56 }
57 
58 int
imsgbuf_set_maxsize(struct imsgbuf * imsgbuf,uint32_t maxsize)59 imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t maxsize)
60 {
61 	if (maxsize < IMSG_HEADER_SIZE || maxsize & IMSG_FD_MARK) {
62 		errno = EINVAL;
63 		return (-1);
64 	}
65 	imsgbuf->maxsize = maxsize;
66 	return (0);
67 }
68 
69 int
imsgbuf_read(struct imsgbuf * imsgbuf)70 imsgbuf_read(struct imsgbuf *imsgbuf)
71 {
72 	if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
73 		return msgbuf_read(imsgbuf->fd, imsgbuf->w);
74 	else
75 		return ibuf_read(imsgbuf->fd, imsgbuf->w);
76 }
77 
78 int
imsgbuf_write(struct imsgbuf * imsgbuf)79 imsgbuf_write(struct imsgbuf *imsgbuf)
80 {
81 	if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
82 		return msgbuf_write(imsgbuf->fd, imsgbuf->w);
83 	else
84 		return ibuf_write(imsgbuf->fd, imsgbuf->w);
85 }
86 
87 int
imsgbuf_flush(struct imsgbuf * imsgbuf)88 imsgbuf_flush(struct imsgbuf *imsgbuf)
89 {
90 	while (imsgbuf_queuelen(imsgbuf) > 0) {
91 		if (imsgbuf_write(imsgbuf) == -1)
92 			return (-1);
93 	}
94 	return (0);
95 }
96 
97 void
imsgbuf_clear(struct imsgbuf * imsgbuf)98 imsgbuf_clear(struct imsgbuf *imsgbuf)
99 {
100 	msgbuf_free(imsgbuf->w);
101 	imsgbuf->w = NULL;
102 }
103 
104 uint32_t
imsgbuf_queuelen(struct imsgbuf * imsgbuf)105 imsgbuf_queuelen(struct imsgbuf *imsgbuf)
106 {
107 	return msgbuf_queuelen(imsgbuf->w);
108 }
109 
110 ssize_t
imsg_get(struct imsgbuf * imsgbuf,struct imsg * imsg)111 imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
112 {
113 	struct imsg		 m;
114 	struct ibuf		*buf;
115 
116 	if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
117 		return (0);
118 
119 	if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
120 		return (-1);
121 
122 	if (ibuf_size(buf))
123 		m.data = ibuf_data(buf);
124 	else
125 		m.data = NULL;
126 	m.buf = buf;
127 	m.hdr.len &= ~IMSG_FD_MARK;
128 
129 	*imsg = m;
130 	return (ibuf_size(buf) + IMSG_HEADER_SIZE);
131 }
132 
133 int
imsg_get_ibuf(struct imsg * imsg,struct ibuf * ibuf)134 imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
135 {
136 	if (ibuf_size(imsg->buf) == 0) {
137 		errno = EBADMSG;
138 		return (-1);
139 	}
140 	return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
141 }
142 
143 int
imsg_get_data(struct imsg * imsg,void * data,size_t len)144 imsg_get_data(struct imsg *imsg, void *data, size_t len)
145 {
146 	if (len == 0) {
147 		errno = EINVAL;
148 		return (-1);
149 	}
150 	if (ibuf_size(imsg->buf) != len) {
151 		errno = EBADMSG;
152 		return (-1);
153 	}
154 	return ibuf_get(imsg->buf, data, len);
155 }
156 
157 int
imsg_get_fd(struct imsg * imsg)158 imsg_get_fd(struct imsg *imsg)
159 {
160 	return ibuf_fd_get(imsg->buf);
161 }
162 
163 uint32_t
imsg_get_id(struct imsg * imsg)164 imsg_get_id(struct imsg *imsg)
165 {
166 	return (imsg->hdr.peerid);
167 }
168 
169 size_t
imsg_get_len(struct imsg * imsg)170 imsg_get_len(struct imsg *imsg)
171 {
172 	return ibuf_size(imsg->buf);
173 }
174 
175 pid_t
imsg_get_pid(struct imsg * imsg)176 imsg_get_pid(struct imsg *imsg)
177 {
178 	return (imsg->hdr.pid);
179 }
180 
181 uint32_t
imsg_get_type(struct imsg * imsg)182 imsg_get_type(struct imsg *imsg)
183 {
184 	return (imsg->hdr.type);
185 }
186 
187 int
imsg_compose(struct imsgbuf * imsgbuf,uint32_t type,uint32_t id,pid_t pid,int fd,const void * data,size_t datalen)188 imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
189     int fd, const void *data, size_t datalen)
190 {
191 	struct ibuf	*wbuf;
192 
193 	if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
194 		return (-1);
195 
196 	if (imsg_add(wbuf, data, datalen) == -1)
197 		return (-1);
198 
199 	ibuf_fd_set(wbuf, fd);
200 	imsg_close(imsgbuf, wbuf);
201 
202 	return (1);
203 }
204 
205 int
imsg_composev(struct imsgbuf * imsgbuf,uint32_t type,uint32_t id,pid_t pid,int fd,const struct iovec * iov,int iovcnt)206 imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
207     int fd, const struct iovec *iov, int iovcnt)
208 {
209 	struct ibuf	*wbuf;
210 	int		 i;
211 	size_t		 datalen = 0;
212 
213 	for (i = 0; i < iovcnt; i++)
214 		datalen += iov[i].iov_len;
215 
216 	if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
217 		return (-1);
218 
219 	for (i = 0; i < iovcnt; i++)
220 		if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
221 			return (-1);
222 
223 	ibuf_fd_set(wbuf, fd);
224 	imsg_close(imsgbuf, wbuf);
225 
226 	return (1);
227 }
228 
229 /*
230  * Enqueue imsg with payload from ibuf buf. fd passing is not possible
231  * with this function.
232  */
233 int
imsg_compose_ibuf(struct imsgbuf * imsgbuf,uint32_t type,uint32_t id,pid_t pid,struct ibuf * buf)234 imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
235     pid_t pid, struct ibuf *buf)
236 {
237 	struct ibuf	*hdrbuf = NULL;
238 	struct imsg_hdr	 hdr;
239 	int save_errno;
240 
241 	if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) {
242 		errno = ERANGE;
243 		goto fail;
244 	}
245 
246 	hdr.type = type;
247 	hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
248 	hdr.peerid = id;
249 	if ((hdr.pid = pid) == 0)
250 		hdr.pid = imsgbuf->pid;
251 
252 	if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
253 		goto fail;
254 	if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
255 		goto fail;
256 
257 	ibuf_close(imsgbuf->w, hdrbuf);
258 	ibuf_close(imsgbuf->w, buf);
259 	return (1);
260 
261  fail:
262 	save_errno = errno;
263 	ibuf_free(buf);
264 	ibuf_free(hdrbuf);
265 	errno = save_errno;
266 	return (-1);
267 }
268 
269 /*
270  * Forward imsg to another channel. Any attached fd is closed.
271  */
272 int
imsg_forward(struct imsgbuf * imsgbuf,struct imsg * msg)273 imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
274 {
275 	struct ibuf	*wbuf;
276 	size_t		 len;
277 
278 	ibuf_rewind(msg->buf);
279 	ibuf_skip(msg->buf, sizeof(msg->hdr));
280 	len = ibuf_size(msg->buf);
281 
282 	if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
283 	    msg->hdr.pid, len)) == NULL)
284 		return (-1);
285 
286 	if (len != 0) {
287 		if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
288 			ibuf_free(wbuf);
289 			return (-1);
290 		}
291 	}
292 
293 	imsg_close(imsgbuf, wbuf);
294 	return (1);
295 }
296 
297 struct ibuf *
imsg_create(struct imsgbuf * imsgbuf,uint32_t type,uint32_t id,pid_t pid,size_t datalen)298 imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
299     size_t datalen)
300 {
301 	struct ibuf	*wbuf;
302 	struct imsg_hdr	 hdr;
303 
304 	datalen += IMSG_HEADER_SIZE;
305 	if (datalen > imsgbuf->maxsize) {
306 		errno = ERANGE;
307 		return (NULL);
308 	}
309 
310 	hdr.type = type;
311 	hdr.peerid = id;
312 	if ((hdr.pid = pid) == 0)
313 		hdr.pid = imsgbuf->pid;
314 	if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) {
315 		return (NULL);
316 	}
317 	if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
318 		return (NULL);
319 
320 	return (wbuf);
321 }
322 
323 int
imsg_add(struct ibuf * msg,const void * data,size_t datalen)324 imsg_add(struct ibuf *msg, const void *data, size_t datalen)
325 {
326 	if (datalen)
327 		if (ibuf_add(msg, data, datalen) == -1) {
328 			ibuf_free(msg);
329 			return (-1);
330 		}
331 	return (datalen);
332 }
333 
334 void
imsg_close(struct imsgbuf * imsgbuf,struct ibuf * msg)335 imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
336 {
337 	uint32_t len;
338 
339 	len = ibuf_size(msg);
340 	if (ibuf_fd_avail(msg))
341 		len |= IMSG_FD_MARK;
342 	(void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
343 	ibuf_close(imsgbuf->w, msg);
344 }
345 
346 void
imsg_free(struct imsg * imsg)347 imsg_free(struct imsg *imsg)
348 {
349 	ibuf_free(imsg->buf);
350 }
351 
352 static struct ibuf *
imsg_parse_hdr(struct ibuf * buf,void * arg,int * fd)353 imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd)
354 {
355 	struct imsgbuf *imsgbuf = arg;
356 	struct imsg_hdr hdr;
357 	struct ibuf *b;
358 	uint32_t len;
359 
360 	if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
361 		return (NULL);
362 
363 	len = hdr.len & ~IMSG_FD_MARK;
364 
365 	if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) {
366 		errno = ERANGE;
367 		return (NULL);
368 	}
369 	if ((b = ibuf_open(len)) == NULL)
370 		return (NULL);
371 	if (hdr.len & IMSG_FD_MARK) {
372 		ibuf_fd_set(b, *fd);
373 		*fd = -1;
374 	}
375 
376 	return b;
377 }
378