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