1 /* $OpenBSD: imsg-buffer.c,v 1.17 2023/10/24 14:05:23 claudio Exp $ */
4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
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.
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.
19 #include <sys/types.h>
20 #include <sys/socket.h>
29 #include "got_compat.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);
46 if ((buf
= calloc(1, sizeof(struct ibuf
))) == NULL
)
48 if ((buf
->buf
= calloc(len
, 1)) == NULL
) {
52 buf
->size
= buf
->max
= len
;
59 ibuf_dynamic(size_t len
, size_t max
)
68 if ((buf
= calloc(1, sizeof(struct ibuf
))) == NULL
)
71 if ((buf
->buf
= calloc(len
, 1)) == NULL
) {
84 ibuf_realloc(struct ibuf
*buf
, size_t len
)
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
) {
94 b
= recallocarray(buf
->buf
, buf
->size
, buf
->wpos
+ len
, 1);
98 buf
->size
= buf
->wpos
+ len
;
104 ibuf_reserve(struct ibuf
*buf
, size_t len
)
108 if (len
> SIZE_MAX
- buf
->wpos
) {
113 if (buf
->wpos
+ len
> buf
->size
)
114 if (ibuf_realloc(buf
, len
) == -1)
117 b
= buf
->buf
+ buf
->wpos
;
124 ibuf_add(struct ibuf
*buf
, const void *data
, size_t len
)
128 if ((b
= ibuf_reserve(buf
, len
)) == NULL
)
131 memcpy(b
, data
, len
);
136 ibuf_add_buf(struct ibuf
*buf
, const struct ibuf
*from
)
138 return ibuf_add(buf
, from
->buf
, from
->wpos
);
142 ibuf_add_n8(struct ibuf
*buf
, uint64_t value
)
146 if (value
> UINT8_MAX
) {
151 return ibuf_add(buf
, &v
, sizeof(v
));
155 ibuf_add_n16(struct ibuf
*buf
, uint64_t value
)
159 if (value
> UINT16_MAX
) {
164 return ibuf_add(buf
, &v
, sizeof(v
));
168 ibuf_add_n32(struct ibuf
*buf
, uint64_t value
)
172 if (value
> UINT32_MAX
) {
177 return ibuf_add(buf
, &v
, sizeof(v
));
181 ibuf_add_n64(struct ibuf
*buf
, uint64_t value
)
183 value
= htobe64(value
);
184 return ibuf_add(buf
, &value
, sizeof(value
));
188 ibuf_add_zero(struct ibuf
*buf
, size_t len
)
192 if ((b
= ibuf_reserve(buf
, len
)) == NULL
)
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
) {
206 return (buf
->buf
+ pos
);
210 ibuf_set(struct ibuf
*buf
, size_t pos
, const void *data
, size_t len
)
214 if ((b
= ibuf_seek(buf
, pos
, len
)) == NULL
)
217 memcpy(b
, data
, len
);
222 ibuf_set_n8(struct ibuf
*buf
, size_t pos
, uint64_t value
)
226 if (value
> UINT8_MAX
) {
231 return (ibuf_set(buf
, pos
, &v
, sizeof(v
)));
235 ibuf_set_n16(struct ibuf
*buf
, size_t pos
, uint64_t value
)
239 if (value
> UINT16_MAX
) {
244 return (ibuf_set(buf
, pos
, &v
, sizeof(v
)));
248 ibuf_set_n32(struct ibuf
*buf
, size_t pos
, uint64_t value
)
252 if (value
> UINT32_MAX
) {
257 return (ibuf_set(buf
, pos
, &v
, sizeof(v
)));
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
)));
268 ibuf_data(struct ibuf
*buf
)
274 ibuf_size(struct ibuf
*buf
)
280 ibuf_left(struct ibuf
*buf
)
282 return (buf
->max
- buf
->wpos
);
286 ibuf_close(struct msgbuf
*msgbuf
, struct ibuf
*buf
)
288 ibuf_enqueue(msgbuf
, buf
);
292 ibuf_free(struct ibuf
*buf
)
298 freezero(buf
->buf
, buf
->size
);
303 ibuf_fd_avail(struct ibuf
*buf
)
305 return (buf
->fd
!= -1);
309 ibuf_fd_get(struct ibuf
*buf
)
319 ibuf_fd_set(struct ibuf
*buf
, int fd
)
327 ibuf_write(struct msgbuf
*msgbuf
)
329 struct iovec iov
[IOV_MAX
];
334 memset(&iov
, 0, sizeof(iov
));
335 TAILQ_FOREACH(buf
, &msgbuf
->bufs
, entry
) {
338 iov
[i
].iov_base
= buf
->buf
+ buf
->rpos
;
339 iov
[i
].iov_len
= buf
->wpos
- buf
->rpos
;
344 if ((n
= writev(msgbuf
->fd
, iov
, i
)) == -1) {
347 if (errno
== ENOBUFS
)
352 if (n
== 0) { /* connection closed */
357 msgbuf_drain(msgbuf
, n
);
363 msgbuf_init(struct msgbuf
*msgbuf
)
367 TAILQ_INIT(&msgbuf
->bufs
);
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;
377 next
= TAILQ_NEXT(buf
, entry
);
378 if (n
>= buf
->wpos
- buf
->rpos
) {
379 n
-= buf
->wpos
- buf
->rpos
;
380 ibuf_dequeue(msgbuf
, buf
);
389 msgbuf_clear(struct msgbuf
*msgbuf
)
393 while ((buf
= TAILQ_FIRST(&msgbuf
->bufs
)) != NULL
)
394 ibuf_dequeue(msgbuf
, buf
);
398 msgbuf_write(struct msgbuf
*msgbuf
)
400 struct iovec iov
[IOV_MAX
];
401 struct ibuf
*buf
, *buf0
= NULL
;
405 struct cmsghdr
*cmsg
;
408 char buf
[CMSG_SPACE(sizeof(int))];
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
) {
417 if (i
> 0 && buf
->fd
!= -1)
419 iov
[i
].iov_base
= buf
->buf
+ buf
->rpos
;
420 iov
[i
].iov_len
= buf
->wpos
- buf
->rpos
;
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
;
440 if ((n
= sendmsg(msgbuf
->fd
, &msg
, 0)) == -1) {
443 if (errno
== ENOBUFS
)
448 if (n
== 0) { /* connection closed */
454 * assumption: fd got sent if sendmsg sent anything
455 * this works because fds are passed one at a time
462 msgbuf_drain(msgbuf
, n
);
468 ibuf_enqueue(struct msgbuf
*msgbuf
, struct ibuf
*buf
)
470 TAILQ_INSERT_TAIL(&msgbuf
->bufs
, buf
, entry
);
475 ibuf_dequeue(struct msgbuf
*msgbuf
, struct ibuf
*buf
)
477 TAILQ_REMOVE(&msgbuf
->bufs
, buf
, entry
);