Blob


1 /* $OpenBSD: imsg-buffer.c,v 1.17 2023/10/24 14:05:23 claudio Exp $ */
3 /*
4 * Copyright (c) 2003, 2004 Henning Brauer <henning@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 */
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/uio.h>
23 #include <limits.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
29 #include "got_compat.h"
30 #include "imsg.h"
32 static int ibuf_realloc(struct ibuf *, size_t);
33 static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
34 static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
35 static void msgbuf_drain(struct msgbuf *, size_t);
37 struct ibuf *
38 ibuf_open(size_t len)
39 {
40 struct ibuf *buf;
42 if (len == 0) {
43 errno = EINVAL;
44 return (NULL);
45 }
46 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
47 return (NULL);
48 if ((buf->buf = calloc(len, 1)) == NULL) {
49 free(buf);
50 return (NULL);
51 }
52 buf->size = buf->max = len;
53 buf->fd = -1;
55 return (buf);
56 }
58 struct ibuf *
59 ibuf_dynamic(size_t len, size_t max)
60 {
61 struct ibuf *buf;
63 if (max < len) {
64 errno = EINVAL;
65 return (NULL);
66 }
68 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
69 return (NULL);
70 if (len > 0) {
71 if ((buf->buf = calloc(len, 1)) == NULL) {
72 free(buf);
73 return (NULL);
74 }
75 }
76 buf->size = len;
77 buf->max = max;
78 buf->fd = -1;
80 return (buf);
81 }
83 static int
84 ibuf_realloc(struct ibuf *buf, size_t len)
85 {
86 unsigned char *b;
88 /* on static buffers max is eq size and so the following fails */
89 if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
90 errno = ERANGE;
91 return (-1);
92 }
94 b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
95 if (b == NULL)
96 return (-1);
97 buf->buf = b;
98 buf->size = buf->wpos + len;
100 return (0);
103 void *
104 ibuf_reserve(struct ibuf *buf, size_t len)
106 void *b;
108 if (len > SIZE_MAX - buf->wpos) {
109 errno = ERANGE;
110 return (NULL);
113 if (buf->wpos + len > buf->size)
114 if (ibuf_realloc(buf, len) == -1)
115 return (NULL);
117 b = buf->buf + buf->wpos;
118 buf->wpos += len;
119 memset(b, 0, len);
120 return (b);
123 int
124 ibuf_add(struct ibuf *buf, const void *data, size_t len)
126 void *b;
128 if ((b = ibuf_reserve(buf, len)) == NULL)
129 return (-1);
131 memcpy(b, data, len);
132 return (0);
135 int
136 ibuf_add_buf(struct ibuf *buf, const struct ibuf *from)
138 return ibuf_add(buf, from->buf, from->wpos);
141 int
142 ibuf_add_n8(struct ibuf *buf, uint64_t value)
144 uint8_t v;
146 if (value > UINT8_MAX) {
147 errno = EINVAL;
148 return (-1);
150 v = value;
151 return ibuf_add(buf, &v, sizeof(v));
154 int
155 ibuf_add_n16(struct ibuf *buf, uint64_t value)
157 uint16_t v;
159 if (value > UINT16_MAX) {
160 errno = EINVAL;
161 return (-1);
163 v = htobe16(value);
164 return ibuf_add(buf, &v, sizeof(v));
167 int
168 ibuf_add_n32(struct ibuf *buf, uint64_t value)
170 uint32_t v;
172 if (value > UINT32_MAX) {
173 errno = EINVAL;
174 return (-1);
176 v = htobe32(value);
177 return ibuf_add(buf, &v, sizeof(v));
180 int
181 ibuf_add_n64(struct ibuf *buf, uint64_t value)
183 value = htobe64(value);
184 return ibuf_add(buf, &value, sizeof(value));
187 int
188 ibuf_add_zero(struct ibuf *buf, size_t len)
190 void *b;
192 if ((b = ibuf_reserve(buf, len)) == NULL)
193 return (-1);
194 return (0);
197 void *
198 ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
200 /* only allowed to seek in already written parts */
201 if (len > SIZE_MAX - pos || pos + len > buf->wpos) {
202 errno = ERANGE;
203 return (NULL);
206 return (buf->buf + pos);
209 int
210 ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len)
212 void *b;
214 if ((b = ibuf_seek(buf, pos, len)) == NULL)
215 return (-1);
217 memcpy(b, data, len);
218 return (0);
221 int
222 ibuf_set_n8(struct ibuf *buf, size_t pos, uint64_t value)
224 uint8_t v;
226 if (value > UINT8_MAX) {
227 errno = EINVAL;
228 return (-1);
230 v = value;
231 return (ibuf_set(buf, pos, &v, sizeof(v)));
234 int
235 ibuf_set_n16(struct ibuf *buf, size_t pos, uint64_t value)
237 uint16_t v;
239 if (value > UINT16_MAX) {
240 errno = EINVAL;
241 return (-1);
243 v = htobe16(value);
244 return (ibuf_set(buf, pos, &v, sizeof(v)));
247 int
248 ibuf_set_n32(struct ibuf *buf, size_t pos, uint64_t value)
250 uint32_t v;
252 if (value > UINT32_MAX) {
253 errno = EINVAL;
254 return (-1);
256 v = htobe32(value);
257 return (ibuf_set(buf, pos, &v, sizeof(v)));
260 int
261 ibuf_set_n64(struct ibuf *buf, size_t pos, uint64_t value)
263 value = htobe64(value);
264 return (ibuf_set(buf, pos, &value, sizeof(value)));
267 void *
268 ibuf_data(struct ibuf *buf)
270 return (buf->buf);
273 size_t
274 ibuf_size(struct ibuf *buf)
276 return (buf->wpos);
279 size_t
280 ibuf_left(struct ibuf *buf)
282 return (buf->max - buf->wpos);
285 void
286 ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
288 ibuf_enqueue(msgbuf, buf);
291 void
292 ibuf_free(struct ibuf *buf)
294 if (buf == NULL)
295 return;
296 if (buf->fd != -1)
297 close(buf->fd);
298 freezero(buf->buf, buf->size);
299 free(buf);
302 int
303 ibuf_fd_avail(struct ibuf *buf)
305 return (buf->fd != -1);
308 int
309 ibuf_fd_get(struct ibuf *buf)
311 int fd;
313 fd = buf->fd;
314 buf->fd = -1;
315 return (fd);
318 void
319 ibuf_fd_set(struct ibuf *buf, int fd)
321 if (buf->fd != -1)
322 close(buf->fd);
323 buf->fd = fd;
326 int
327 ibuf_write(struct msgbuf *msgbuf)
329 struct iovec iov[IOV_MAX];
330 struct ibuf *buf;
331 unsigned int i = 0;
332 ssize_t n;
334 memset(&iov, 0, sizeof(iov));
335 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
336 if (i >= IOV_MAX)
337 break;
338 iov[i].iov_base = buf->buf + buf->rpos;
339 iov[i].iov_len = buf->wpos - buf->rpos;
340 i++;
343 again:
344 if ((n = writev(msgbuf->fd, iov, i)) == -1) {
345 if (errno == EINTR)
346 goto again;
347 if (errno == ENOBUFS)
348 errno = EAGAIN;
349 return (-1);
352 if (n == 0) { /* connection closed */
353 errno = 0;
354 return (0);
357 msgbuf_drain(msgbuf, n);
359 return (1);
362 void
363 msgbuf_init(struct msgbuf *msgbuf)
365 msgbuf->queued = 0;
366 msgbuf->fd = -1;
367 TAILQ_INIT(&msgbuf->bufs);
370 static void
371 msgbuf_drain(struct msgbuf *msgbuf, size_t n)
373 struct ibuf *buf, *next;
375 for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
376 buf = next) {
377 next = TAILQ_NEXT(buf, entry);
378 if (n >= buf->wpos - buf->rpos) {
379 n -= buf->wpos - buf->rpos;
380 ibuf_dequeue(msgbuf, buf);
381 } else {
382 buf->rpos += n;
383 n = 0;
388 void
389 msgbuf_clear(struct msgbuf *msgbuf)
391 struct ibuf *buf;
393 while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
394 ibuf_dequeue(msgbuf, buf);
397 int
398 msgbuf_write(struct msgbuf *msgbuf)
400 struct iovec iov[IOV_MAX];
401 struct ibuf *buf, *buf0 = NULL;
402 unsigned int i = 0;
403 ssize_t n;
404 struct msghdr msg;
405 struct cmsghdr *cmsg;
406 union {
407 struct cmsghdr hdr;
408 char buf[CMSG_SPACE(sizeof(int))];
409 } cmsgbuf;
411 memset(&iov, 0, sizeof(iov));
412 memset(&msg, 0, sizeof(msg));
413 memset(&cmsgbuf, 0, sizeof(cmsgbuf));
414 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
415 if (i >= IOV_MAX)
416 break;
417 if (i > 0 && buf->fd != -1)
418 break;
419 iov[i].iov_base = buf->buf + buf->rpos;
420 iov[i].iov_len = buf->wpos - buf->rpos;
421 i++;
422 if (buf->fd != -1)
423 buf0 = buf;
426 msg.msg_iov = iov;
427 msg.msg_iovlen = i;
429 if (buf0 != NULL) {
430 msg.msg_control = (caddr_t)&cmsgbuf.buf;
431 msg.msg_controllen = sizeof(cmsgbuf.buf);
432 cmsg = CMSG_FIRSTHDR(&msg);
433 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
434 cmsg->cmsg_level = SOL_SOCKET;
435 cmsg->cmsg_type = SCM_RIGHTS;
436 *(int *)CMSG_DATA(cmsg) = buf0->fd;
439 again:
440 if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
441 if (errno == EINTR)
442 goto again;
443 if (errno == ENOBUFS)
444 errno = EAGAIN;
445 return (-1);
448 if (n == 0) { /* connection closed */
449 errno = 0;
450 return (0);
453 /*
454 * assumption: fd got sent if sendmsg sent anything
455 * this works because fds are passed one at a time
456 */
457 if (buf0 != NULL) {
458 close(buf0->fd);
459 buf0->fd = -1;
462 msgbuf_drain(msgbuf, n);
464 return (1);
467 static void
468 ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
470 TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
471 msgbuf->queued++;
474 static void
475 ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
477 TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
479 msgbuf->queued--;
480 ibuf_free(buf);