]>
Commit | Line | Data |
---|---|---|
8429abe0 RW |
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 | ||
eac6e3f0 | 19 | #include <zebra.h> |
8429abe0 | 20 | |
97b5d752 | 21 | #include "memory.h" |
cd85bc2e | 22 | #include "queue.h" |
8429abe0 RW |
23 | #include "imsg.h" |
24 | ||
c17faa4b | 25 | static int imsg_fd_overhead = 0; |
8429abe0 | 26 | |
c17faa4b | 27 | static int imsg_get_fd(struct imsgbuf *); |
8429abe0 | 28 | |
eac6e3f0 RW |
29 | #ifndef __OpenBSD__ |
30 | /* | |
31 | * The original code calls getdtablecount() which is OpenBSD specific. Use | |
32 | * available_fds() from OpenSMTPD instead. | |
33 | */ | |
996c9314 | 34 | static int available_fds(unsigned int n) |
eac6e3f0 | 35 | { |
996c9314 LB |
36 | unsigned int i; |
37 | int ret, fds[256]; | |
eac6e3f0 | 38 | |
97b5d752 | 39 | if (n > (unsigned int)array_size(fds)) |
95f7965d | 40 | return 1; |
eac6e3f0 RW |
41 | |
42 | ret = 0; | |
43 | for (i = 0; i < n; i++) { | |
44 | fds[i] = -1; | |
45 | if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { | |
46 | if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) | |
47 | fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); | |
48 | if (fds[i] < 0) { | |
49 | ret = 1; | |
50 | break; | |
51 | } | |
52 | } | |
53 | } | |
54 | ||
55 | for (i = 0; i < n && fds[i] >= 0; i++) | |
56 | close(fds[i]); | |
57 | ||
58 | return (ret); | |
59 | } | |
60 | #endif | |
61 | ||
996c9314 | 62 | void imsg_init(struct imsgbuf *ibuf, int fd) |
8429abe0 RW |
63 | { |
64 | msgbuf_init(&ibuf->w); | |
65 | memset(&ibuf->r, 0, sizeof(ibuf->r)); | |
66 | ibuf->fd = fd; | |
67 | ibuf->w.fd = fd; | |
68 | ibuf->pid = getpid(); | |
69 | TAILQ_INIT(&ibuf->fds); | |
70 | } | |
71 | ||
996c9314 | 72 | ssize_t imsg_read(struct imsgbuf *ibuf) |
8429abe0 | 73 | { |
996c9314 LB |
74 | struct msghdr msg; |
75 | struct cmsghdr *cmsg; | |
8429abe0 RW |
76 | union { |
77 | struct cmsghdr hdr; | |
996c9314 | 78 | char buf[CMSG_SPACE(sizeof(int) * 1)]; |
8429abe0 | 79 | } cmsgbuf; |
996c9314 | 80 | struct iovec iov; |
a2b6e694 | 81 | ssize_t n; |
996c9314 LB |
82 | int fd; |
83 | struct imsg_fd *ifd; | |
8429abe0 RW |
84 | |
85 | memset(&msg, 0, sizeof(msg)); | |
86 | memset(&cmsgbuf, 0, sizeof(cmsgbuf)); | |
87 | ||
88 | iov.iov_base = ibuf->r.buf + ibuf->r.wpos; | |
89 | iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; | |
90 | msg.msg_iov = &iov; | |
91 | msg.msg_iovlen = 1; | |
92 | msg.msg_control = &cmsgbuf.buf; | |
93 | msg.msg_controllen = sizeof(cmsgbuf.buf); | |
94 | ||
95 | if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) | |
95f7965d | 96 | return -1; |
8429abe0 RW |
97 | |
98 | again: | |
eac6e3f0 | 99 | #ifdef __OpenBSD__ |
996c9314 LB |
100 | if (getdtablecount() + imsg_fd_overhead |
101 | + (int)((CMSG_SPACE(sizeof(int)) - CMSG_SPACE(0)) | |
102 | / sizeof(int)) | |
8429abe0 | 103 | >= getdtablesize()) { |
eac6e3f0 | 104 | #else |
996c9314 LB |
105 | if (available_fds(imsg_fd_overhead |
106 | + (CMSG_SPACE(sizeof(int)) - CMSG_SPACE(0)) | |
107 | / sizeof(int))) { | |
eac6e3f0 | 108 | #endif |
8429abe0 RW |
109 | errno = EAGAIN; |
110 | free(ifd); | |
95f7965d | 111 | return -1; |
8429abe0 RW |
112 | } |
113 | ||
a2b6e694 | 114 | n = recvmsg(ibuf->fd, &msg, 0); |
115 | if (n == -1) { | |
8429abe0 RW |
116 | if (errno == EINTR) |
117 | goto again; | |
118 | goto fail; | |
119 | } | |
120 | ||
121 | ibuf->r.wpos += n; | |
122 | ||
123 | for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; | |
996c9314 LB |
124 | cmsg = CMSG_NXTHDR(&msg, cmsg)) { |
125 | if (cmsg->cmsg_level == SOL_SOCKET | |
126 | && cmsg->cmsg_type == SCM_RIGHTS) { | |
8429abe0 RW |
127 | int i; |
128 | int j; | |
129 | ||
130 | /* | |
131 | * We only accept one file descriptor. Due to C | |
132 | * padding rules, our control buffer might contain | |
133 | * more than one fd, and we must close them. | |
134 | */ | |
996c9314 LB |
135 | j = ((char *)cmsg + cmsg->cmsg_len |
136 | - (char *)CMSG_DATA(cmsg)) | |
137 | / sizeof(int); | |
8429abe0 RW |
138 | for (i = 0; i < j; i++) { |
139 | fd = ((int *)CMSG_DATA(cmsg))[i]; | |
140 | if (ifd != NULL) { | |
141 | ifd->fd = fd; | |
142 | TAILQ_INSERT_TAIL(&ibuf->fds, ifd, | |
996c9314 | 143 | entry); |
8429abe0 RW |
144 | ifd = NULL; |
145 | } else | |
146 | close(fd); | |
147 | } | |
148 | } | |
149 | /* we do not handle other ctl data level */ | |
150 | } | |
151 | ||
152 | fail: | |
153 | free(ifd); | |
154 | return (n); | |
155 | } | |
156 | ||
996c9314 | 157 | ssize_t imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) |
8429abe0 | 158 | { |
996c9314 | 159 | size_t av, left, datalen; |
8429abe0 RW |
160 | |
161 | av = ibuf->r.wpos; | |
162 | ||
163 | if (IMSG_HEADER_SIZE > av) | |
95f7965d | 164 | return 0; |
8429abe0 RW |
165 | |
166 | memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); | |
996c9314 | 167 | if (imsg->hdr.len < IMSG_HEADER_SIZE || imsg->hdr.len > MAX_IMSGSIZE) { |
8429abe0 | 168 | errno = ERANGE; |
95f7965d | 169 | return -1; |
8429abe0 RW |
170 | } |
171 | if (imsg->hdr.len > av) | |
95f7965d | 172 | return 0; |
8429abe0 RW |
173 | datalen = imsg->hdr.len - IMSG_HEADER_SIZE; |
174 | ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; | |
175 | if (datalen == 0) | |
176 | imsg->data = NULL; | |
177 | else if ((imsg->data = malloc(datalen)) == NULL) | |
95f7965d | 178 | return -1; |
8429abe0 RW |
179 | |
180 | if (imsg->hdr.flags & IMSGF_HASFD) | |
181 | imsg->fd = imsg_get_fd(ibuf); | |
182 | else | |
183 | imsg->fd = -1; | |
184 | ||
dd3415b7 | 185 | if (imsg->data) |
996c9314 | 186 | memcpy(imsg->data, ibuf->r.rptr, datalen); |
8429abe0 RW |
187 | |
188 | if (imsg->hdr.len < av) { | |
189 | left = av - imsg->hdr.len; | |
190 | memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); | |
191 | ibuf->r.wpos = left; | |
192 | } else | |
193 | ibuf->r.wpos = 0; | |
194 | ||
195 | return (datalen + IMSG_HEADER_SIZE); | |
196 | } | |
197 | ||
d7c0a89a QY |
198 | int imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, |
199 | pid_t pid, int fd, const void *data, uint16_t datalen) | |
8429abe0 | 200 | { |
996c9314 | 201 | struct ibuf *wbuf; |
8429abe0 RW |
202 | |
203 | if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) | |
95f7965d | 204 | return -1; |
8429abe0 RW |
205 | |
206 | if (imsg_add(wbuf, data, datalen) == -1) | |
95f7965d | 207 | return -1; |
8429abe0 RW |
208 | |
209 | wbuf->fd = fd; | |
210 | ||
211 | imsg_close(ibuf, wbuf); | |
212 | ||
95f7965d | 213 | return 1; |
8429abe0 RW |
214 | } |
215 | ||
d7c0a89a | 216 | int imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, |
996c9314 | 217 | pid_t pid, int fd, const struct iovec *iov, int iovcnt) |
8429abe0 | 218 | { |
996c9314 LB |
219 | struct ibuf *wbuf; |
220 | int i, datalen = 0; | |
8429abe0 RW |
221 | |
222 | for (i = 0; i < iovcnt; i++) | |
223 | datalen += iov[i].iov_len; | |
224 | ||
225 | if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) | |
95f7965d | 226 | return -1; |
8429abe0 RW |
227 | |
228 | for (i = 0; i < iovcnt; i++) | |
229 | if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) | |
95f7965d | 230 | return -1; |
8429abe0 RW |
231 | |
232 | wbuf->fd = fd; | |
233 | ||
234 | imsg_close(ibuf, wbuf); | |
235 | ||
95f7965d | 236 | return 1; |
8429abe0 RW |
237 | } |
238 | ||
239 | /* ARGSUSED */ | |
d7c0a89a QY |
240 | struct ibuf *imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, |
241 | pid_t pid, uint16_t datalen) | |
8429abe0 | 242 | { |
996c9314 LB |
243 | struct ibuf *wbuf; |
244 | struct imsg_hdr hdr; | |
8429abe0 | 245 | |
c2fb17b5 QY |
246 | memset(&hdr, 0x00, IMSG_HEADER_SIZE); |
247 | ||
8429abe0 RW |
248 | datalen += IMSG_HEADER_SIZE; |
249 | if (datalen > MAX_IMSGSIZE) { | |
250 | errno = ERANGE; | |
95f7965d | 251 | return NULL; |
8429abe0 RW |
252 | } |
253 | ||
254 | hdr.type = type; | |
255 | hdr.flags = 0; | |
256 | hdr.peerid = peerid; | |
257 | if ((hdr.pid = pid) == 0) | |
258 | hdr.pid = ibuf->pid; | |
259 | if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { | |
95f7965d | 260 | return NULL; |
8429abe0 RW |
261 | } |
262 | if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) | |
95f7965d | 263 | return NULL; |
8429abe0 RW |
264 | |
265 | return (wbuf); | |
266 | } | |
267 | ||
d7c0a89a | 268 | int imsg_add(struct ibuf *msg, const void *data, uint16_t datalen) |
8429abe0 RW |
269 | { |
270 | if (datalen) | |
271 | if (ibuf_add(msg, data, datalen) == -1) { | |
272 | ibuf_free(msg); | |
95f7965d | 273 | return -1; |
8429abe0 RW |
274 | } |
275 | return (datalen); | |
276 | } | |
277 | ||
996c9314 | 278 | void imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) |
8429abe0 | 279 | { |
996c9314 | 280 | struct imsg_hdr *hdr; |
8429abe0 RW |
281 | |
282 | hdr = (struct imsg_hdr *)msg->buf; | |
283 | ||
284 | hdr->flags &= ~IMSGF_HASFD; | |
285 | if (msg->fd != -1) | |
286 | hdr->flags |= IMSGF_HASFD; | |
287 | ||
d7c0a89a | 288 | hdr->len = (uint16_t)msg->wpos; |
8429abe0 RW |
289 | |
290 | ibuf_close(&ibuf->w, msg); | |
291 | } | |
292 | ||
996c9314 | 293 | void imsg_free(struct imsg *imsg) |
8429abe0 RW |
294 | { |
295 | free(imsg->data); | |
296 | } | |
297 | ||
996c9314 | 298 | int imsg_get_fd(struct imsgbuf *ibuf) |
8429abe0 | 299 | { |
996c9314 LB |
300 | int fd; |
301 | struct imsg_fd *ifd; | |
8429abe0 | 302 | |
a43ad4fe | 303 | if ((ifd = TAILQ_POP_FIRST(&ibuf->fds, entry)) == NULL) |
95f7965d | 304 | return -1; |
8429abe0 RW |
305 | |
306 | fd = ifd->fd; | |
8429abe0 RW |
307 | free(ifd); |
308 | ||
309 | return (fd); | |
310 | } | |
311 | ||
996c9314 | 312 | int imsg_flush(struct imsgbuf *ibuf) |
8429abe0 RW |
313 | { |
314 | while (ibuf->w.queued) | |
315 | if (msgbuf_write(&ibuf->w) <= 0) | |
95f7965d DA |
316 | return -1; |
317 | return 0; | |
8429abe0 RW |
318 | } |
319 | ||
996c9314 | 320 | void imsg_clear(struct imsgbuf *ibuf) |
8429abe0 | 321 | { |
996c9314 | 322 | int fd; |
8429abe0 RW |
323 | |
324 | msgbuf_clear(&ibuf->w); | |
325 | while ((fd = imsg_get_fd(ibuf)) != -1) | |
326 | close(fd); | |
327 | } |