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