170623c80SCraig Rodrigues /* $OpenBSD: imsg-buffer.c,v 1.7 2015/07/12 18:40:49 nicm Exp $ */
270623c80SCraig Rodrigues
370623c80SCraig Rodrigues /*
470623c80SCraig Rodrigues * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
570623c80SCraig Rodrigues *
670623c80SCraig Rodrigues * Permission to use, copy, modify, and distribute this software for any
770623c80SCraig Rodrigues * purpose with or without fee is hereby granted, provided that the above
870623c80SCraig Rodrigues * copyright notice and this permission notice appear in all copies.
970623c80SCraig Rodrigues *
1070623c80SCraig Rodrigues * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1170623c80SCraig Rodrigues * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1270623c80SCraig Rodrigues * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1370623c80SCraig Rodrigues * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1470623c80SCraig Rodrigues * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1570623c80SCraig Rodrigues * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1670623c80SCraig Rodrigues * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1770623c80SCraig Rodrigues */
1870623c80SCraig Rodrigues
1970623c80SCraig Rodrigues #include <sys/types.h>
2070623c80SCraig Rodrigues #include <sys/queue.h>
2170623c80SCraig Rodrigues #include <sys/socket.h>
2270623c80SCraig Rodrigues #include <sys/uio.h>
2370623c80SCraig Rodrigues
2470623c80SCraig Rodrigues #include <limits.h>
2570623c80SCraig Rodrigues #include <errno.h>
2670623c80SCraig Rodrigues #include <stdlib.h>
2770623c80SCraig Rodrigues #include <string.h>
2870623c80SCraig Rodrigues #include <unistd.h>
2970623c80SCraig Rodrigues
3070623c80SCraig Rodrigues #include "imsg.h"
3170623c80SCraig Rodrigues
3270623c80SCraig Rodrigues int ibuf_realloc(struct ibuf *, size_t);
3370623c80SCraig Rodrigues void ibuf_enqueue(struct msgbuf *, struct ibuf *);
3470623c80SCraig Rodrigues void ibuf_dequeue(struct msgbuf *, struct ibuf *);
3570623c80SCraig Rodrigues
3670623c80SCraig Rodrigues struct ibuf *
ibuf_open(size_t len)3770623c80SCraig Rodrigues ibuf_open(size_t len)
3870623c80SCraig Rodrigues {
3970623c80SCraig Rodrigues struct ibuf *buf;
4070623c80SCraig Rodrigues
4170623c80SCraig Rodrigues if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
4270623c80SCraig Rodrigues return (NULL);
4370623c80SCraig Rodrigues if ((buf->buf = malloc(len)) == NULL) {
4470623c80SCraig Rodrigues free(buf);
4570623c80SCraig Rodrigues return (NULL);
4670623c80SCraig Rodrigues }
4770623c80SCraig Rodrigues buf->size = buf->max = len;
4870623c80SCraig Rodrigues buf->fd = -1;
4970623c80SCraig Rodrigues
5070623c80SCraig Rodrigues return (buf);
5170623c80SCraig Rodrigues }
5270623c80SCraig Rodrigues
5370623c80SCraig Rodrigues struct ibuf *
ibuf_dynamic(size_t len,size_t max)5470623c80SCraig Rodrigues ibuf_dynamic(size_t len, size_t max)
5570623c80SCraig Rodrigues {
5670623c80SCraig Rodrigues struct ibuf *buf;
5770623c80SCraig Rodrigues
5870623c80SCraig Rodrigues if (max < len)
5970623c80SCraig Rodrigues return (NULL);
6070623c80SCraig Rodrigues
6170623c80SCraig Rodrigues if ((buf = ibuf_open(len)) == NULL)
6270623c80SCraig Rodrigues return (NULL);
6370623c80SCraig Rodrigues
6470623c80SCraig Rodrigues if (max > 0)
6570623c80SCraig Rodrigues buf->max = max;
6670623c80SCraig Rodrigues
6770623c80SCraig Rodrigues return (buf);
6870623c80SCraig Rodrigues }
6970623c80SCraig Rodrigues
7070623c80SCraig Rodrigues int
ibuf_realloc(struct ibuf * buf,size_t len)7170623c80SCraig Rodrigues ibuf_realloc(struct ibuf *buf, size_t len)
7270623c80SCraig Rodrigues {
7370623c80SCraig Rodrigues u_char *b;
7470623c80SCraig Rodrigues
7570623c80SCraig Rodrigues /* on static buffers max is eq size and so the following fails */
7670623c80SCraig Rodrigues if (buf->wpos + len > buf->max) {
7770623c80SCraig Rodrigues errno = ERANGE;
7870623c80SCraig Rodrigues return (-1);
7970623c80SCraig Rodrigues }
8070623c80SCraig Rodrigues
8170623c80SCraig Rodrigues b = realloc(buf->buf, buf->wpos + len);
8270623c80SCraig Rodrigues if (b == NULL)
8370623c80SCraig Rodrigues return (-1);
8470623c80SCraig Rodrigues buf->buf = b;
8570623c80SCraig Rodrigues buf->size = buf->wpos + len;
8670623c80SCraig Rodrigues
8770623c80SCraig Rodrigues return (0);
8870623c80SCraig Rodrigues }
8970623c80SCraig Rodrigues
9070623c80SCraig Rodrigues int
ibuf_add(struct ibuf * buf,const void * data,size_t len)9170623c80SCraig Rodrigues ibuf_add(struct ibuf *buf, const void *data, size_t len)
9270623c80SCraig Rodrigues {
9370623c80SCraig Rodrigues if (buf->wpos + len > buf->size)
9470623c80SCraig Rodrigues if (ibuf_realloc(buf, len) == -1)
9570623c80SCraig Rodrigues return (-1);
9670623c80SCraig Rodrigues
9770623c80SCraig Rodrigues memcpy(buf->buf + buf->wpos, data, len);
9870623c80SCraig Rodrigues buf->wpos += len;
9970623c80SCraig Rodrigues return (0);
10070623c80SCraig Rodrigues }
10170623c80SCraig Rodrigues
10270623c80SCraig Rodrigues void *
ibuf_reserve(struct ibuf * buf,size_t len)10370623c80SCraig Rodrigues ibuf_reserve(struct ibuf *buf, size_t len)
10470623c80SCraig Rodrigues {
10570623c80SCraig Rodrigues void *b;
10670623c80SCraig Rodrigues
10770623c80SCraig Rodrigues if (buf->wpos + len > buf->size)
10870623c80SCraig Rodrigues if (ibuf_realloc(buf, len) == -1)
10970623c80SCraig Rodrigues return (NULL);
11070623c80SCraig Rodrigues
11170623c80SCraig Rodrigues b = buf->buf + buf->wpos;
11270623c80SCraig Rodrigues buf->wpos += len;
11370623c80SCraig Rodrigues return (b);
11470623c80SCraig Rodrigues }
11570623c80SCraig Rodrigues
11670623c80SCraig Rodrigues void *
ibuf_seek(struct ibuf * buf,size_t pos,size_t len)11770623c80SCraig Rodrigues ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
11870623c80SCraig Rodrigues {
11970623c80SCraig Rodrigues /* only allowed to seek in already written parts */
12070623c80SCraig Rodrigues if (pos + len > buf->wpos)
12170623c80SCraig Rodrigues return (NULL);
12270623c80SCraig Rodrigues
12370623c80SCraig Rodrigues return (buf->buf + pos);
12470623c80SCraig Rodrigues }
12570623c80SCraig Rodrigues
12670623c80SCraig Rodrigues size_t
ibuf_size(struct ibuf * buf)12770623c80SCraig Rodrigues ibuf_size(struct ibuf *buf)
12870623c80SCraig Rodrigues {
12970623c80SCraig Rodrigues return (buf->wpos);
13070623c80SCraig Rodrigues }
13170623c80SCraig Rodrigues
13270623c80SCraig Rodrigues size_t
ibuf_left(struct ibuf * buf)13370623c80SCraig Rodrigues ibuf_left(struct ibuf *buf)
13470623c80SCraig Rodrigues {
13570623c80SCraig Rodrigues return (buf->max - buf->wpos);
13670623c80SCraig Rodrigues }
13770623c80SCraig Rodrigues
13870623c80SCraig Rodrigues void
ibuf_close(struct msgbuf * msgbuf,struct ibuf * buf)13970623c80SCraig Rodrigues ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
14070623c80SCraig Rodrigues {
14170623c80SCraig Rodrigues ibuf_enqueue(msgbuf, buf);
14270623c80SCraig Rodrigues }
14370623c80SCraig Rodrigues
14470623c80SCraig Rodrigues int
ibuf_write(struct msgbuf * msgbuf)14570623c80SCraig Rodrigues ibuf_write(struct msgbuf *msgbuf)
14670623c80SCraig Rodrigues {
14770623c80SCraig Rodrigues struct iovec iov[IOV_MAX];
14870623c80SCraig Rodrigues struct ibuf *buf;
14970623c80SCraig Rodrigues unsigned int i = 0;
15070623c80SCraig Rodrigues ssize_t n;
15170623c80SCraig Rodrigues
15270623c80SCraig Rodrigues memset(&iov, 0, sizeof(iov));
15370623c80SCraig Rodrigues TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
15470623c80SCraig Rodrigues if (i >= IOV_MAX)
15570623c80SCraig Rodrigues break;
15670623c80SCraig Rodrigues iov[i].iov_base = buf->buf + buf->rpos;
15770623c80SCraig Rodrigues iov[i].iov_len = buf->wpos - buf->rpos;
15870623c80SCraig Rodrigues i++;
15970623c80SCraig Rodrigues }
16070623c80SCraig Rodrigues
16170623c80SCraig Rodrigues again:
16270623c80SCraig Rodrigues if ((n = writev(msgbuf->fd, iov, i)) == -1) {
16370623c80SCraig Rodrigues if (errno == EINTR)
16470623c80SCraig Rodrigues goto again;
16570623c80SCraig Rodrigues if (errno == ENOBUFS)
16670623c80SCraig Rodrigues errno = EAGAIN;
16770623c80SCraig Rodrigues return (-1);
16870623c80SCraig Rodrigues }
16970623c80SCraig Rodrigues
17070623c80SCraig Rodrigues if (n == 0) { /* connection closed */
17170623c80SCraig Rodrigues errno = 0;
17270623c80SCraig Rodrigues return (0);
17370623c80SCraig Rodrigues }
17470623c80SCraig Rodrigues
17570623c80SCraig Rodrigues msgbuf_drain(msgbuf, n);
17670623c80SCraig Rodrigues
17770623c80SCraig Rodrigues return (1);
17870623c80SCraig Rodrigues }
17970623c80SCraig Rodrigues
18070623c80SCraig Rodrigues void
ibuf_free(struct ibuf * buf)18170623c80SCraig Rodrigues ibuf_free(struct ibuf *buf)
18270623c80SCraig Rodrigues {
18370623c80SCraig Rodrigues free(buf->buf);
18470623c80SCraig Rodrigues free(buf);
18570623c80SCraig Rodrigues }
18670623c80SCraig Rodrigues
18770623c80SCraig Rodrigues void
msgbuf_init(struct msgbuf * msgbuf)18870623c80SCraig Rodrigues msgbuf_init(struct msgbuf *msgbuf)
18970623c80SCraig Rodrigues {
19070623c80SCraig Rodrigues msgbuf->queued = 0;
19170623c80SCraig Rodrigues msgbuf->fd = -1;
19270623c80SCraig Rodrigues TAILQ_INIT(&msgbuf->bufs);
19370623c80SCraig Rodrigues }
19470623c80SCraig Rodrigues
19570623c80SCraig Rodrigues void
msgbuf_drain(struct msgbuf * msgbuf,size_t n)19670623c80SCraig Rodrigues msgbuf_drain(struct msgbuf *msgbuf, size_t n)
19770623c80SCraig Rodrigues {
19870623c80SCraig Rodrigues struct ibuf *buf, *next;
19970623c80SCraig Rodrigues
20070623c80SCraig Rodrigues for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
20170623c80SCraig Rodrigues buf = next) {
20270623c80SCraig Rodrigues next = TAILQ_NEXT(buf, entry);
20370623c80SCraig Rodrigues if (buf->rpos + n >= buf->wpos) {
20470623c80SCraig Rodrigues n -= buf->wpos - buf->rpos;
20570623c80SCraig Rodrigues ibuf_dequeue(msgbuf, buf);
20670623c80SCraig Rodrigues } else {
20770623c80SCraig Rodrigues buf->rpos += n;
20870623c80SCraig Rodrigues n = 0;
20970623c80SCraig Rodrigues }
21070623c80SCraig Rodrigues }
21170623c80SCraig Rodrigues }
21270623c80SCraig Rodrigues
21370623c80SCraig Rodrigues void
msgbuf_clear(struct msgbuf * msgbuf)21470623c80SCraig Rodrigues msgbuf_clear(struct msgbuf *msgbuf)
21570623c80SCraig Rodrigues {
21670623c80SCraig Rodrigues struct ibuf *buf;
21770623c80SCraig Rodrigues
21870623c80SCraig Rodrigues while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
21970623c80SCraig Rodrigues ibuf_dequeue(msgbuf, buf);
22070623c80SCraig Rodrigues }
22170623c80SCraig Rodrigues
22270623c80SCraig Rodrigues int
msgbuf_write(struct msgbuf * msgbuf)22370623c80SCraig Rodrigues msgbuf_write(struct msgbuf *msgbuf)
22470623c80SCraig Rodrigues {
22570623c80SCraig Rodrigues struct iovec iov[IOV_MAX];
22670623c80SCraig Rodrigues struct ibuf *buf;
22770623c80SCraig Rodrigues unsigned int i = 0;
22870623c80SCraig Rodrigues ssize_t n;
22970623c80SCraig Rodrigues struct msghdr msg;
23070623c80SCraig Rodrigues struct cmsghdr *cmsg;
23170623c80SCraig Rodrigues union {
23270623c80SCraig Rodrigues struct cmsghdr hdr;
23370623c80SCraig Rodrigues char buf[CMSG_SPACE(sizeof(int))];
23470623c80SCraig Rodrigues } cmsgbuf;
23570623c80SCraig Rodrigues
23670623c80SCraig Rodrigues memset(&iov, 0, sizeof(iov));
23770623c80SCraig Rodrigues memset(&msg, 0, sizeof(msg));
23870623c80SCraig Rodrigues memset(&cmsgbuf, 0, sizeof(cmsgbuf));
23970623c80SCraig Rodrigues TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
24070623c80SCraig Rodrigues if (i >= IOV_MAX)
24170623c80SCraig Rodrigues break;
24270623c80SCraig Rodrigues iov[i].iov_base = buf->buf + buf->rpos;
24370623c80SCraig Rodrigues iov[i].iov_len = buf->wpos - buf->rpos;
24470623c80SCraig Rodrigues i++;
24570623c80SCraig Rodrigues if (buf->fd != -1)
24670623c80SCraig Rodrigues break;
24770623c80SCraig Rodrigues }
24870623c80SCraig Rodrigues
24970623c80SCraig Rodrigues msg.msg_iov = iov;
25070623c80SCraig Rodrigues msg.msg_iovlen = i;
25170623c80SCraig Rodrigues
25270623c80SCraig Rodrigues if (buf != NULL && buf->fd != -1) {
25370623c80SCraig Rodrigues msg.msg_control = (caddr_t)&cmsgbuf.buf;
25470623c80SCraig Rodrigues msg.msg_controllen = sizeof(cmsgbuf.buf);
25570623c80SCraig Rodrigues cmsg = CMSG_FIRSTHDR(&msg);
25670623c80SCraig Rodrigues cmsg->cmsg_len = CMSG_LEN(sizeof(int));
25770623c80SCraig Rodrigues cmsg->cmsg_level = SOL_SOCKET;
25870623c80SCraig Rodrigues cmsg->cmsg_type = SCM_RIGHTS;
25970623c80SCraig Rodrigues *(int *)CMSG_DATA(cmsg) = buf->fd;
26070623c80SCraig Rodrigues }
26170623c80SCraig Rodrigues
26270623c80SCraig Rodrigues again:
26370623c80SCraig Rodrigues if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
26470623c80SCraig Rodrigues if (errno == EINTR)
26570623c80SCraig Rodrigues goto again;
26670623c80SCraig Rodrigues if (errno == ENOBUFS)
26770623c80SCraig Rodrigues errno = EAGAIN;
26870623c80SCraig Rodrigues return (-1);
26970623c80SCraig Rodrigues }
27070623c80SCraig Rodrigues
27170623c80SCraig Rodrigues if (n == 0) { /* connection closed */
27270623c80SCraig Rodrigues errno = 0;
27370623c80SCraig Rodrigues return (0);
27470623c80SCraig Rodrigues }
27570623c80SCraig Rodrigues
27670623c80SCraig Rodrigues /*
27770623c80SCraig Rodrigues * assumption: fd got sent if sendmsg sent anything
27870623c80SCraig Rodrigues * this works because fds are passed one at a time
27970623c80SCraig Rodrigues */
28070623c80SCraig Rodrigues if (buf != NULL && buf->fd != -1) {
28170623c80SCraig Rodrigues close(buf->fd);
28270623c80SCraig Rodrigues buf->fd = -1;
28370623c80SCraig Rodrigues }
28470623c80SCraig Rodrigues
28570623c80SCraig Rodrigues msgbuf_drain(msgbuf, n);
28670623c80SCraig Rodrigues
28770623c80SCraig Rodrigues return (1);
28870623c80SCraig Rodrigues }
28970623c80SCraig Rodrigues
29070623c80SCraig Rodrigues void
ibuf_enqueue(struct msgbuf * msgbuf,struct ibuf * buf)29170623c80SCraig Rodrigues ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
29270623c80SCraig Rodrigues {
29370623c80SCraig Rodrigues TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
29470623c80SCraig Rodrigues msgbuf->queued++;
29570623c80SCraig Rodrigues }
29670623c80SCraig Rodrigues
29770623c80SCraig Rodrigues void
ibuf_dequeue(struct msgbuf * msgbuf,struct ibuf * buf)29870623c80SCraig Rodrigues ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
29970623c80SCraig Rodrigues {
30070623c80SCraig Rodrigues TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
30170623c80SCraig Rodrigues
30270623c80SCraig Rodrigues if (buf->fd != -1)
30370623c80SCraig Rodrigues close(buf->fd);
30470623c80SCraig Rodrigues
30570623c80SCraig Rodrigues msgbuf->queued--;
30670623c80SCraig Rodrigues ibuf_free(buf);
30770623c80SCraig Rodrigues }
308