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