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