xref: /src/lib/libopenbsd/imsg-buffer.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
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