]> git.proxmox.com Git - mirror_frr.git/blame - lib/imsg-buffer.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / lib / imsg-buffer.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: ISC
8429abe0
RW
2/* $OpenBSD$ */
3
4/*
5 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8429abe0
RW
6 */
7
eac6e3f0 8#include <zebra.h>
8429abe0 9
cd85bc2e 10#include "queue.h"
8429abe0
RW
11#include "imsg.h"
12
a43ad4fe
DL
13static int ibuf_realloc(struct ibuf *, size_t);
14static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
15static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
8429abe0 16
996c9314 17struct ibuf *ibuf_open(size_t len)
8429abe0 18{
996c9314 19 struct ibuf *buf;
8429abe0
RW
20
21 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
95f7965d 22 return NULL;
8429abe0
RW
23 if ((buf->buf = malloc(len)) == NULL) {
24 free(buf);
95f7965d 25 return NULL;
8429abe0
RW
26 }
27 buf->size = buf->max = len;
28 buf->fd = -1;
29
30 return (buf);
31}
32
996c9314 33struct ibuf *ibuf_dynamic(size_t len, size_t max)
8429abe0 34{
996c9314 35 struct ibuf *buf;
8429abe0
RW
36
37 if (max < len)
95f7965d 38 return NULL;
8429abe0
RW
39
40 if ((buf = ibuf_open(len)) == NULL)
95f7965d 41 return NULL;
8429abe0
RW
42
43 if (max > 0)
44 buf->max = max;
45
46 return (buf);
47}
48
a43ad4fe 49static int ibuf_realloc(struct ibuf *buf, size_t len)
8429abe0 50{
d7c0a89a 51 uint8_t *b;
8429abe0
RW
52
53 /* on static buffers max is eq size and so the following fails */
54 if (buf->wpos + len > buf->max) {
55 errno = ERANGE;
95f7965d 56 return -1;
8429abe0
RW
57 }
58
59 b = realloc(buf->buf, buf->wpos + len);
60 if (b == NULL)
95f7965d 61 return -1;
8429abe0
RW
62 buf->buf = b;
63 buf->size = buf->wpos + len;
64
95f7965d 65 return 0;
8429abe0
RW
66}
67
996c9314 68int ibuf_add(struct ibuf *buf, const void *data, size_t len)
8429abe0
RW
69{
70 if (buf->wpos + len > buf->size)
71 if (ibuf_realloc(buf, len) == -1)
95f7965d 72 return -1;
8429abe0
RW
73
74 memcpy(buf->buf + buf->wpos, data, len);
75 buf->wpos += len;
95f7965d 76 return 0;
8429abe0
RW
77}
78
996c9314 79void *ibuf_reserve(struct ibuf *buf, size_t len)
8429abe0 80{
996c9314 81 void *b;
8429abe0
RW
82
83 if (buf->wpos + len > buf->size)
84 if (ibuf_realloc(buf, len) == -1)
95f7965d 85 return NULL;
8429abe0
RW
86
87 b = buf->buf + buf->wpos;
88 buf->wpos += len;
89 return (b);
90}
91
996c9314 92void *ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
8429abe0
RW
93{
94 /* only allowed to seek in already written parts */
95 if (pos + len > buf->wpos)
95f7965d 96 return NULL;
8429abe0
RW
97
98 return (buf->buf + pos);
99}
100
996c9314 101size_t ibuf_size(struct ibuf *buf)
8429abe0
RW
102{
103 return (buf->wpos);
104}
105
996c9314 106size_t ibuf_left(struct ibuf *buf)
8429abe0
RW
107{
108 return (buf->max - buf->wpos);
109}
110
996c9314 111void ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
8429abe0
RW
112{
113 ibuf_enqueue(msgbuf, buf);
114}
115
996c9314 116int ibuf_write(struct msgbuf *msgbuf)
8429abe0 117{
996c9314
LB
118 struct iovec iov[IOV_MAX];
119 struct ibuf *buf;
120 unsigned int i = 0;
121 ssize_t n;
8429abe0
RW
122
123 memset(&iov, 0, sizeof(iov));
996c9314 124 TAILQ_FOREACH (buf, &msgbuf->bufs, entry) {
8429abe0
RW
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
132again:
133 if ((n = writev(msgbuf->fd, iov, i)) == -1) {
134 if (errno == EINTR)
135 goto again;
136 if (errno == ENOBUFS)
137 errno = EAGAIN;
95f7965d 138 return -1;
8429abe0
RW
139 }
140
996c9314 141 if (n == 0) { /* connection closed */
8429abe0 142 errno = 0;
95f7965d 143 return 0;
8429abe0
RW
144 }
145
146 msgbuf_drain(msgbuf, n);
147
95f7965d 148 return 1;
8429abe0
RW
149}
150
996c9314 151void ibuf_free(struct ibuf *buf)
8429abe0
RW
152{
153 if (buf == NULL)
154 return;
155 free(buf->buf);
156 free(buf);
157}
158
996c9314 159void msgbuf_init(struct msgbuf *msgbuf)
8429abe0
RW
160{
161 msgbuf->queued = 0;
162 msgbuf->fd = -1;
163 TAILQ_INIT(&msgbuf->bufs);
164}
165
996c9314 166void msgbuf_drain(struct msgbuf *msgbuf, size_t n)
8429abe0 167{
996c9314 168 struct ibuf *buf, *next;
8429abe0
RW
169
170 for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
996c9314 171 buf = next) {
8429abe0
RW
172 next = TAILQ_NEXT(buf, entry);
173 if (buf->rpos + n >= buf->wpos) {
174 n -= buf->wpos - buf->rpos;
a43ad4fe
DL
175
176 TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
8429abe0
RW
177 ibuf_dequeue(msgbuf, buf);
178 } else {
179 buf->rpos += n;
180 n = 0;
181 }
182 }
183}
184
996c9314 185void msgbuf_clear(struct msgbuf *msgbuf)
8429abe0 186{
996c9314 187 struct ibuf *buf;
8429abe0 188
a43ad4fe 189 while ((buf = TAILQ_POP_FIRST(&msgbuf->bufs, entry)) != NULL)
8429abe0
RW
190 ibuf_dequeue(msgbuf, buf);
191}
192
996c9314 193int msgbuf_write(struct msgbuf *msgbuf)
8429abe0 194{
996c9314
LB
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;
8429abe0 201 union {
996c9314
LB
202 struct cmsghdr hdr;
203 char buf[CMSG_SPACE(sizeof(int))];
8429abe0
RW
204 } cmsgbuf;
205
206 memset(&iov, 0, sizeof(iov));
207 memset(&msg, 0, sizeof(msg));
208 memset(&cmsgbuf, 0, sizeof(cmsgbuf));
996c9314 209 TAILQ_FOREACH (buf, &msgbuf->bufs, entry) {
8429abe0
RW
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;
eac6e3f0 229 memcpy(CMSG_DATA(cmsg), &buf->fd, sizeof(int));
8429abe0
RW
230 }
231
232again:
233 if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
234 if (errno == EINTR)
235 goto again;
236 if (errno == ENOBUFS)
237 errno = EAGAIN;
95f7965d 238 return -1;
8429abe0
RW
239 }
240
996c9314 241 if (n == 0) { /* connection closed */
8429abe0 242 errno = 0;
95f7965d 243 return 0;
8429abe0
RW
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
95f7965d 257 return 1;
8429abe0
RW
258}
259
a43ad4fe 260static void ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
8429abe0
RW
261{
262 TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
263 msgbuf->queued++;
264}
265
a43ad4fe 266static void ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
8429abe0 267{
a43ad4fe 268 /* TAILQ_REMOVE done by caller */
8429abe0
RW
269 if (buf->fd != -1)
270 close(buf->fd);
271
272 msgbuf->queued--;
273 ibuf_free(buf);
274}