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