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