xref: /freebsd/lib/libopenbsd/imsg-buffer.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
1*70623c80SCraig Rodrigues /*	$OpenBSD: imsg-buffer.c,v 1.7 2015/07/12 18:40:49 nicm Exp $	*/
2*70623c80SCraig Rodrigues 
3*70623c80SCraig Rodrigues /*
4*70623c80SCraig Rodrigues  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5*70623c80SCraig Rodrigues  *
6*70623c80SCraig Rodrigues  * Permission to use, copy, modify, and distribute this software for any
7*70623c80SCraig Rodrigues  * purpose with or without fee is hereby granted, provided that the above
8*70623c80SCraig Rodrigues  * copyright notice and this permission notice appear in all copies.
9*70623c80SCraig Rodrigues  *
10*70623c80SCraig Rodrigues  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*70623c80SCraig Rodrigues  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*70623c80SCraig Rodrigues  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*70623c80SCraig Rodrigues  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*70623c80SCraig Rodrigues  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*70623c80SCraig Rodrigues  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*70623c80SCraig Rodrigues  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*70623c80SCraig Rodrigues  */
18*70623c80SCraig Rodrigues 
19*70623c80SCraig Rodrigues #include <sys/types.h>
20*70623c80SCraig Rodrigues #include <sys/queue.h>
21*70623c80SCraig Rodrigues #include <sys/socket.h>
22*70623c80SCraig Rodrigues #include <sys/uio.h>
23*70623c80SCraig Rodrigues 
24*70623c80SCraig Rodrigues #include <limits.h>
25*70623c80SCraig Rodrigues #include <errno.h>
26*70623c80SCraig Rodrigues #include <stdlib.h>
27*70623c80SCraig Rodrigues #include <string.h>
28*70623c80SCraig Rodrigues #include <unistd.h>
29*70623c80SCraig Rodrigues 
30*70623c80SCraig Rodrigues #include "imsg.h"
31*70623c80SCraig Rodrigues 
32*70623c80SCraig Rodrigues int	ibuf_realloc(struct ibuf *, size_t);
33*70623c80SCraig Rodrigues void	ibuf_enqueue(struct msgbuf *, struct ibuf *);
34*70623c80SCraig Rodrigues void	ibuf_dequeue(struct msgbuf *, struct ibuf *);
35*70623c80SCraig Rodrigues 
36*70623c80SCraig Rodrigues struct ibuf *
ibuf_open(size_t len)37*70623c80SCraig Rodrigues ibuf_open(size_t len)
38*70623c80SCraig Rodrigues {
39*70623c80SCraig Rodrigues 	struct ibuf	*buf;
40*70623c80SCraig Rodrigues 
41*70623c80SCraig Rodrigues 	if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
42*70623c80SCraig Rodrigues 		return (NULL);
43*70623c80SCraig Rodrigues 	if ((buf->buf = malloc(len)) == NULL) {
44*70623c80SCraig Rodrigues 		free(buf);
45*70623c80SCraig Rodrigues 		return (NULL);
46*70623c80SCraig Rodrigues 	}
47*70623c80SCraig Rodrigues 	buf->size = buf->max = len;
48*70623c80SCraig Rodrigues 	buf->fd = -1;
49*70623c80SCraig Rodrigues 
50*70623c80SCraig Rodrigues 	return (buf);
51*70623c80SCraig Rodrigues }
52*70623c80SCraig Rodrigues 
53*70623c80SCraig Rodrigues struct ibuf *
ibuf_dynamic(size_t len,size_t max)54*70623c80SCraig Rodrigues ibuf_dynamic(size_t len, size_t max)
55*70623c80SCraig Rodrigues {
56*70623c80SCraig Rodrigues 	struct ibuf	*buf;
57*70623c80SCraig Rodrigues 
58*70623c80SCraig Rodrigues 	if (max < len)
59*70623c80SCraig Rodrigues 		return (NULL);
60*70623c80SCraig Rodrigues 
61*70623c80SCraig Rodrigues 	if ((buf = ibuf_open(len)) == NULL)
62*70623c80SCraig Rodrigues 		return (NULL);
63*70623c80SCraig Rodrigues 
64*70623c80SCraig Rodrigues 	if (max > 0)
65*70623c80SCraig Rodrigues 		buf->max = max;
66*70623c80SCraig Rodrigues 
67*70623c80SCraig Rodrigues 	return (buf);
68*70623c80SCraig Rodrigues }
69*70623c80SCraig Rodrigues 
70*70623c80SCraig Rodrigues int
ibuf_realloc(struct ibuf * buf,size_t len)71*70623c80SCraig Rodrigues ibuf_realloc(struct ibuf *buf, size_t len)
72*70623c80SCraig Rodrigues {
73*70623c80SCraig Rodrigues 	u_char	*b;
74*70623c80SCraig Rodrigues 
75*70623c80SCraig Rodrigues 	/* on static buffers max is eq size and so the following fails */
76*70623c80SCraig Rodrigues 	if (buf->wpos + len > buf->max) {
77*70623c80SCraig Rodrigues 		errno = ERANGE;
78*70623c80SCraig Rodrigues 		return (-1);
79*70623c80SCraig Rodrigues 	}
80*70623c80SCraig Rodrigues 
81*70623c80SCraig Rodrigues 	b = realloc(buf->buf, buf->wpos + len);
82*70623c80SCraig Rodrigues 	if (b == NULL)
83*70623c80SCraig Rodrigues 		return (-1);
84*70623c80SCraig Rodrigues 	buf->buf = b;
85*70623c80SCraig Rodrigues 	buf->size = buf->wpos + len;
86*70623c80SCraig Rodrigues 
87*70623c80SCraig Rodrigues 	return (0);
88*70623c80SCraig Rodrigues }
89*70623c80SCraig Rodrigues 
90*70623c80SCraig Rodrigues int
ibuf_add(struct ibuf * buf,const void * data,size_t len)91*70623c80SCraig Rodrigues ibuf_add(struct ibuf *buf, const void *data, size_t len)
92*70623c80SCraig Rodrigues {
93*70623c80SCraig Rodrigues 	if (buf->wpos + len > buf->size)
94*70623c80SCraig Rodrigues 		if (ibuf_realloc(buf, len) == -1)
95*70623c80SCraig Rodrigues 			return (-1);
96*70623c80SCraig Rodrigues 
97*70623c80SCraig Rodrigues 	memcpy(buf->buf + buf->wpos, data, len);
98*70623c80SCraig Rodrigues 	buf->wpos += len;
99*70623c80SCraig Rodrigues 	return (0);
100*70623c80SCraig Rodrigues }
101*70623c80SCraig Rodrigues 
102*70623c80SCraig Rodrigues void *
ibuf_reserve(struct ibuf * buf,size_t len)103*70623c80SCraig Rodrigues ibuf_reserve(struct ibuf *buf, size_t len)
104*70623c80SCraig Rodrigues {
105*70623c80SCraig Rodrigues 	void	*b;
106*70623c80SCraig Rodrigues 
107*70623c80SCraig Rodrigues 	if (buf->wpos + len > buf->size)
108*70623c80SCraig Rodrigues 		if (ibuf_realloc(buf, len) == -1)
109*70623c80SCraig Rodrigues 			return (NULL);
110*70623c80SCraig Rodrigues 
111*70623c80SCraig Rodrigues 	b = buf->buf + buf->wpos;
112*70623c80SCraig Rodrigues 	buf->wpos += len;
113*70623c80SCraig Rodrigues 	return (b);
114*70623c80SCraig Rodrigues }
115*70623c80SCraig Rodrigues 
116*70623c80SCraig Rodrigues void *
ibuf_seek(struct ibuf * buf,size_t pos,size_t len)117*70623c80SCraig Rodrigues ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
118*70623c80SCraig Rodrigues {
119*70623c80SCraig Rodrigues 	/* only allowed to seek in already written parts */
120*70623c80SCraig Rodrigues 	if (pos + len > buf->wpos)
121*70623c80SCraig Rodrigues 		return (NULL);
122*70623c80SCraig Rodrigues 
123*70623c80SCraig Rodrigues 	return (buf->buf + pos);
124*70623c80SCraig Rodrigues }
125*70623c80SCraig Rodrigues 
126*70623c80SCraig Rodrigues size_t
ibuf_size(struct ibuf * buf)127*70623c80SCraig Rodrigues ibuf_size(struct ibuf *buf)
128*70623c80SCraig Rodrigues {
129*70623c80SCraig Rodrigues 	return (buf->wpos);
130*70623c80SCraig Rodrigues }
131*70623c80SCraig Rodrigues 
132*70623c80SCraig Rodrigues size_t
ibuf_left(struct ibuf * buf)133*70623c80SCraig Rodrigues ibuf_left(struct ibuf *buf)
134*70623c80SCraig Rodrigues {
135*70623c80SCraig Rodrigues 	return (buf->max - buf->wpos);
136*70623c80SCraig Rodrigues }
137*70623c80SCraig Rodrigues 
138*70623c80SCraig Rodrigues void
ibuf_close(struct msgbuf * msgbuf,struct ibuf * buf)139*70623c80SCraig Rodrigues ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
140*70623c80SCraig Rodrigues {
141*70623c80SCraig Rodrigues 	ibuf_enqueue(msgbuf, buf);
142*70623c80SCraig Rodrigues }
143*70623c80SCraig Rodrigues 
144*70623c80SCraig Rodrigues int
ibuf_write(struct msgbuf * msgbuf)145*70623c80SCraig Rodrigues ibuf_write(struct msgbuf *msgbuf)
146*70623c80SCraig Rodrigues {
147*70623c80SCraig Rodrigues 	struct iovec	 iov[IOV_MAX];
148*70623c80SCraig Rodrigues 	struct ibuf	*buf;
149*70623c80SCraig Rodrigues 	unsigned int	 i = 0;
150*70623c80SCraig Rodrigues 	ssize_t	n;
151*70623c80SCraig Rodrigues 
152*70623c80SCraig Rodrigues 	memset(&iov, 0, sizeof(iov));
153*70623c80SCraig Rodrigues 	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
154*70623c80SCraig Rodrigues 		if (i >= IOV_MAX)
155*70623c80SCraig Rodrigues 			break;
156*70623c80SCraig Rodrigues 		iov[i].iov_base = buf->buf + buf->rpos;
157*70623c80SCraig Rodrigues 		iov[i].iov_len = buf->wpos - buf->rpos;
158*70623c80SCraig Rodrigues 		i++;
159*70623c80SCraig Rodrigues 	}
160*70623c80SCraig Rodrigues 
161*70623c80SCraig Rodrigues again:
162*70623c80SCraig Rodrigues 	if ((n = writev(msgbuf->fd, iov, i)) == -1) {
163*70623c80SCraig Rodrigues 		if (errno == EINTR)
164*70623c80SCraig Rodrigues 			goto again;
165*70623c80SCraig Rodrigues 		if (errno == ENOBUFS)
166*70623c80SCraig Rodrigues 			errno = EAGAIN;
167*70623c80SCraig Rodrigues 		return (-1);
168*70623c80SCraig Rodrigues 	}
169*70623c80SCraig Rodrigues 
170*70623c80SCraig Rodrigues 	if (n == 0) {			/* connection closed */
171*70623c80SCraig Rodrigues 		errno = 0;
172*70623c80SCraig Rodrigues 		return (0);
173*70623c80SCraig Rodrigues 	}
174*70623c80SCraig Rodrigues 
175*70623c80SCraig Rodrigues 	msgbuf_drain(msgbuf, n);
176*70623c80SCraig Rodrigues 
177*70623c80SCraig Rodrigues 	return (1);
178*70623c80SCraig Rodrigues }
179*70623c80SCraig Rodrigues 
180*70623c80SCraig Rodrigues void
ibuf_free(struct ibuf * buf)181*70623c80SCraig Rodrigues ibuf_free(struct ibuf *buf)
182*70623c80SCraig Rodrigues {
183*70623c80SCraig Rodrigues 	free(buf->buf);
184*70623c80SCraig Rodrigues 	free(buf);
185*70623c80SCraig Rodrigues }
186*70623c80SCraig Rodrigues 
187*70623c80SCraig Rodrigues void
msgbuf_init(struct msgbuf * msgbuf)188*70623c80SCraig Rodrigues msgbuf_init(struct msgbuf *msgbuf)
189*70623c80SCraig Rodrigues {
190*70623c80SCraig Rodrigues 	msgbuf->queued = 0;
191*70623c80SCraig Rodrigues 	msgbuf->fd = -1;
192*70623c80SCraig Rodrigues 	TAILQ_INIT(&msgbuf->bufs);
193*70623c80SCraig Rodrigues }
194*70623c80SCraig Rodrigues 
195*70623c80SCraig Rodrigues void
msgbuf_drain(struct msgbuf * msgbuf,size_t n)196*70623c80SCraig Rodrigues msgbuf_drain(struct msgbuf *msgbuf, size_t n)
197*70623c80SCraig Rodrigues {
198*70623c80SCraig Rodrigues 	struct ibuf	*buf, *next;
199*70623c80SCraig Rodrigues 
200*70623c80SCraig Rodrigues 	for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
201*70623c80SCraig Rodrigues 	    buf = next) {
202*70623c80SCraig Rodrigues 		next = TAILQ_NEXT(buf, entry);
203*70623c80SCraig Rodrigues 		if (buf->rpos + n >= buf->wpos) {
204*70623c80SCraig Rodrigues 			n -= buf->wpos - buf->rpos;
205*70623c80SCraig Rodrigues 			ibuf_dequeue(msgbuf, buf);
206*70623c80SCraig Rodrigues 		} else {
207*70623c80SCraig Rodrigues 			buf->rpos += n;
208*70623c80SCraig Rodrigues 			n = 0;
209*70623c80SCraig Rodrigues 		}
210*70623c80SCraig Rodrigues 	}
211*70623c80SCraig Rodrigues }
212*70623c80SCraig Rodrigues 
213*70623c80SCraig Rodrigues void
msgbuf_clear(struct msgbuf * msgbuf)214*70623c80SCraig Rodrigues msgbuf_clear(struct msgbuf *msgbuf)
215*70623c80SCraig Rodrigues {
216*70623c80SCraig Rodrigues 	struct ibuf	*buf;
217*70623c80SCraig Rodrigues 
218*70623c80SCraig Rodrigues 	while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
219*70623c80SCraig Rodrigues 		ibuf_dequeue(msgbuf, buf);
220*70623c80SCraig Rodrigues }
221*70623c80SCraig Rodrigues 
222*70623c80SCraig Rodrigues int
msgbuf_write(struct msgbuf * msgbuf)223*70623c80SCraig Rodrigues msgbuf_write(struct msgbuf *msgbuf)
224*70623c80SCraig Rodrigues {
225*70623c80SCraig Rodrigues 	struct iovec	 iov[IOV_MAX];
226*70623c80SCraig Rodrigues 	struct ibuf	*buf;
227*70623c80SCraig Rodrigues 	unsigned int	 i = 0;
228*70623c80SCraig Rodrigues 	ssize_t		 n;
229*70623c80SCraig Rodrigues 	struct msghdr	 msg;
230*70623c80SCraig Rodrigues 	struct cmsghdr	*cmsg;
231*70623c80SCraig Rodrigues 	union {
232*70623c80SCraig Rodrigues 		struct cmsghdr	hdr;
233*70623c80SCraig Rodrigues 		char		buf[CMSG_SPACE(sizeof(int))];
234*70623c80SCraig Rodrigues 	} cmsgbuf;
235*70623c80SCraig Rodrigues 
236*70623c80SCraig Rodrigues 	memset(&iov, 0, sizeof(iov));
237*70623c80SCraig Rodrigues 	memset(&msg, 0, sizeof(msg));
238*70623c80SCraig Rodrigues 	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
239*70623c80SCraig Rodrigues 	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
240*70623c80SCraig Rodrigues 		if (i >= IOV_MAX)
241*70623c80SCraig Rodrigues 			break;
242*70623c80SCraig Rodrigues 		iov[i].iov_base = buf->buf + buf->rpos;
243*70623c80SCraig Rodrigues 		iov[i].iov_len = buf->wpos - buf->rpos;
244*70623c80SCraig Rodrigues 		i++;
245*70623c80SCraig Rodrigues 		if (buf->fd != -1)
246*70623c80SCraig Rodrigues 			break;
247*70623c80SCraig Rodrigues 	}
248*70623c80SCraig Rodrigues 
249*70623c80SCraig Rodrigues 	msg.msg_iov = iov;
250*70623c80SCraig Rodrigues 	msg.msg_iovlen = i;
251*70623c80SCraig Rodrigues 
252*70623c80SCraig Rodrigues 	if (buf != NULL && buf->fd != -1) {
253*70623c80SCraig Rodrigues 		msg.msg_control = (caddr_t)&cmsgbuf.buf;
254*70623c80SCraig Rodrigues 		msg.msg_controllen = sizeof(cmsgbuf.buf);
255*70623c80SCraig Rodrigues 		cmsg = CMSG_FIRSTHDR(&msg);
256*70623c80SCraig Rodrigues 		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
257*70623c80SCraig Rodrigues 		cmsg->cmsg_level = SOL_SOCKET;
258*70623c80SCraig Rodrigues 		cmsg->cmsg_type = SCM_RIGHTS;
259*70623c80SCraig Rodrigues 		*(int *)CMSG_DATA(cmsg) = buf->fd;
260*70623c80SCraig Rodrigues 	}
261*70623c80SCraig Rodrigues 
262*70623c80SCraig Rodrigues again:
263*70623c80SCraig Rodrigues 	if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
264*70623c80SCraig Rodrigues 		if (errno == EINTR)
265*70623c80SCraig Rodrigues 			goto again;
266*70623c80SCraig Rodrigues 		if (errno == ENOBUFS)
267*70623c80SCraig Rodrigues 			errno = EAGAIN;
268*70623c80SCraig Rodrigues 		return (-1);
269*70623c80SCraig Rodrigues 	}
270*70623c80SCraig Rodrigues 
271*70623c80SCraig Rodrigues 	if (n == 0) {			/* connection closed */
272*70623c80SCraig Rodrigues 		errno = 0;
273*70623c80SCraig Rodrigues 		return (0);
274*70623c80SCraig Rodrigues 	}
275*70623c80SCraig Rodrigues 
276*70623c80SCraig Rodrigues 	/*
277*70623c80SCraig Rodrigues 	 * assumption: fd got sent if sendmsg sent anything
278*70623c80SCraig Rodrigues 	 * this works because fds are passed one at a time
279*70623c80SCraig Rodrigues 	 */
280*70623c80SCraig Rodrigues 	if (buf != NULL && buf->fd != -1) {
281*70623c80SCraig Rodrigues 		close(buf->fd);
282*70623c80SCraig Rodrigues 		buf->fd = -1;
283*70623c80SCraig Rodrigues 	}
284*70623c80SCraig Rodrigues 
285*70623c80SCraig Rodrigues 	msgbuf_drain(msgbuf, n);
286*70623c80SCraig Rodrigues 
287*70623c80SCraig Rodrigues 	return (1);
288*70623c80SCraig Rodrigues }
289*70623c80SCraig Rodrigues 
290*70623c80SCraig Rodrigues void
ibuf_enqueue(struct msgbuf * msgbuf,struct ibuf * buf)291*70623c80SCraig Rodrigues ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
292*70623c80SCraig Rodrigues {
293*70623c80SCraig Rodrigues 	TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
294*70623c80SCraig Rodrigues 	msgbuf->queued++;
295*70623c80SCraig Rodrigues }
296*70623c80SCraig Rodrigues 
297*70623c80SCraig Rodrigues void
ibuf_dequeue(struct msgbuf * msgbuf,struct ibuf * buf)298*70623c80SCraig Rodrigues ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
299*70623c80SCraig Rodrigues {
300*70623c80SCraig Rodrigues 	TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
301*70623c80SCraig Rodrigues 
302*70623c80SCraig Rodrigues 	if (buf->fd != -1)
303*70623c80SCraig Rodrigues 		close(buf->fd);
304*70623c80SCraig Rodrigues 
305*70623c80SCraig Rodrigues 	msgbuf->queued--;
306*70623c80SCraig Rodrigues 	ibuf_free(buf);
307*70623c80SCraig Rodrigues }
308