Blob


1 /* $OpenBSD: imsg-buffer.c,v 1.16 2023/06/19 17:19:50 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 #ifdef NOTYET
297 if (buf->fd != -1)
298 close(buf->fd);
299 #endif
300 freezero(buf->buf, buf->size);
301 free(buf);
304 int
305 ibuf_fd_avail(struct ibuf *buf)
307 return (buf->fd != -1);
310 int
311 ibuf_fd_get(struct ibuf *buf)
313 int fd;
315 fd = buf->fd;
316 #ifdef NOTYET
317 buf->fd = -1;
318 #endif
319 return (fd);
322 void
323 ibuf_fd_set(struct ibuf *buf, int fd)
325 if (buf->fd != -1)
326 close(buf->fd);
327 buf->fd = fd;
330 int
331 ibuf_write(struct msgbuf *msgbuf)
333 struct iovec iov[IOV_MAX];
334 struct ibuf *buf;
335 unsigned int i = 0;
336 ssize_t n;
338 memset(&iov, 0, sizeof(iov));
339 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
340 if (i >= IOV_MAX)
341 break;
342 iov[i].iov_base = buf->buf + buf->rpos;
343 iov[i].iov_len = buf->wpos - buf->rpos;
344 i++;
347 again:
348 if ((n = writev(msgbuf->fd, iov, i)) == -1) {
349 if (errno == EINTR)
350 goto again;
351 if (errno == ENOBUFS)
352 errno = EAGAIN;
353 return (-1);
356 if (n == 0) { /* connection closed */
357 errno = 0;
358 return (0);
361 msgbuf_drain(msgbuf, n);
363 return (1);
366 void
367 msgbuf_init(struct msgbuf *msgbuf)
369 msgbuf->queued = 0;
370 msgbuf->fd = -1;
371 TAILQ_INIT(&msgbuf->bufs);
374 static void
375 msgbuf_drain(struct msgbuf *msgbuf, size_t n)
377 struct ibuf *buf, *next;
379 for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
380 buf = next) {
381 next = TAILQ_NEXT(buf, entry);
382 if (n >= buf->wpos - buf->rpos) {
383 n -= buf->wpos - buf->rpos;
384 ibuf_dequeue(msgbuf, buf);
385 } else {
386 buf->rpos += n;
387 n = 0;
392 void
393 msgbuf_clear(struct msgbuf *msgbuf)
395 struct ibuf *buf;
397 while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
398 ibuf_dequeue(msgbuf, buf);
401 int
402 msgbuf_write(struct msgbuf *msgbuf)
404 struct iovec iov[IOV_MAX];
405 struct ibuf *buf, *buf0 = NULL;
406 unsigned int i = 0;
407 ssize_t n;
408 struct msghdr msg;
409 struct cmsghdr *cmsg;
410 union {
411 struct cmsghdr hdr;
412 char buf[CMSG_SPACE(sizeof(int))];
413 } cmsgbuf;
415 memset(&iov, 0, sizeof(iov));
416 memset(&msg, 0, sizeof(msg));
417 memset(&cmsgbuf, 0, sizeof(cmsgbuf));
418 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
419 if (i >= IOV_MAX)
420 break;
421 if (i > 0 && buf->fd != -1)
422 break;
423 iov[i].iov_base = buf->buf + buf->rpos;
424 iov[i].iov_len = buf->wpos - buf->rpos;
425 i++;
426 if (buf->fd != -1)
427 buf0 = buf;
430 msg.msg_iov = iov;
431 msg.msg_iovlen = i;
433 if (buf0 != NULL) {
434 msg.msg_control = (caddr_t)&cmsgbuf.buf;
435 msg.msg_controllen = sizeof(cmsgbuf.buf);
436 cmsg = CMSG_FIRSTHDR(&msg);
437 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
438 cmsg->cmsg_level = SOL_SOCKET;
439 cmsg->cmsg_type = SCM_RIGHTS;
440 *(int *)CMSG_DATA(cmsg) = buf0->fd;
443 again:
444 if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
445 if (errno == EINTR)
446 goto again;
447 if (errno == ENOBUFS)
448 errno = EAGAIN;
449 return (-1);
452 if (n == 0) { /* connection closed */
453 errno = 0;
454 return (0);
457 /*
458 * assumption: fd got sent if sendmsg sent anything
459 * this works because fds are passed one at a time
460 */
461 if (buf0 != NULL) {
462 close(buf0->fd);
463 buf0->fd = -1;
466 msgbuf_drain(msgbuf, n);
468 return (1);
471 static void
472 ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
474 TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
475 msgbuf->queued++;
478 static void
479 ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
481 TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
483 if (buf->fd != -1) {
484 close(buf->fd);
485 buf->fd = -1;
488 msgbuf->queued--;
489 ibuf_free(buf);