]>
Commit | Line | Data |
---|---|---|
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 |
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 *); | |
8429abe0 | 16 | |
996c9314 | 17 | struct 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 | 33 | struct 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 | 49 | static 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 | 68 | int 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 | 79 | void *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 | 92 | void *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 | 101 | size_t ibuf_size(struct ibuf *buf) |
8429abe0 RW |
102 | { |
103 | return (buf->wpos); | |
104 | } | |
105 | ||
996c9314 | 106 | size_t ibuf_left(struct ibuf *buf) |
8429abe0 RW |
107 | { |
108 | return (buf->max - buf->wpos); | |
109 | } | |
110 | ||
996c9314 | 111 | void ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) |
8429abe0 RW |
112 | { |
113 | ibuf_enqueue(msgbuf, buf); | |
114 | } | |
115 | ||
996c9314 | 116 | int 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 | ||
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; | |
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 | 151 | void ibuf_free(struct ibuf *buf) |
8429abe0 RW |
152 | { |
153 | if (buf == NULL) | |
154 | return; | |
155 | free(buf->buf); | |
156 | free(buf); | |
157 | } | |
158 | ||
996c9314 | 159 | void msgbuf_init(struct msgbuf *msgbuf) |
8429abe0 RW |
160 | { |
161 | msgbuf->queued = 0; | |
162 | msgbuf->fd = -1; | |
163 | TAILQ_INIT(&msgbuf->bufs); | |
164 | } | |
165 | ||
996c9314 | 166 | void 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 | 185 | void 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 | 193 | int 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 | ||
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; | |
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 | 260 | static 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 | 266 | static 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 | } |