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