]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libnv/msgio.c
Add two missing eventhandler.h headers
[FreeBSD/FreeBSD.git] / lib / libnv / msgio.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 The FreeBSD Foundation
5  * Copyright (c) 2013 Mariusz Zaborski <oshogbo@FreeBSD.org>
6  * All rights reserved.
7  *
8  * This software was developed by Pawel Jakub Dawidek under sponsorship from
9  * the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/socket.h>
38 #include <sys/select.h>
39
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdbool.h>
43 #include <stdint.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 #ifdef HAVE_PJDLOG
49 #include <pjdlog.h>
50 #endif
51
52 #include "common_impl.h"
53 #include "msgio.h"
54
55 #ifndef HAVE_PJDLOG
56 #include <assert.h>
57 #define PJDLOG_ASSERT(...)              assert(__VA_ARGS__)
58 #define PJDLOG_RASSERT(expr, ...)       assert(expr)
59 #define PJDLOG_ABORT(...)               abort()
60 #endif
61
62 #ifdef __linux__
63 /* Linux: arbitrary size, but must be lower than SCM_MAX_FD. */
64 #define PKG_MAX_SIZE    ((64U - 1) * CMSG_SPACE(sizeof(int)))
65 #else
66 #define PKG_MAX_SIZE    (MCLBYTES / CMSG_SPACE(sizeof(int)) - 1)
67 #endif
68
69 static int
70 msghdr_add_fd(struct cmsghdr *cmsg, int fd)
71 {
72
73         PJDLOG_ASSERT(fd >= 0);
74
75         cmsg->cmsg_level = SOL_SOCKET;
76         cmsg->cmsg_type = SCM_RIGHTS;
77         cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
78         bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
79
80         return (0);
81 }
82
83 static void
84 fd_wait(int fd, bool doread)
85 {
86         fd_set fds;
87
88         PJDLOG_ASSERT(fd >= 0);
89
90         FD_ZERO(&fds);
91         FD_SET(fd, &fds);
92         (void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds,
93             NULL, NULL);
94 }
95
96 static int
97 msg_recv(int sock, struct msghdr *msg)
98 {
99         int flags;
100
101         PJDLOG_ASSERT(sock >= 0);
102
103 #ifdef MSG_CMSG_CLOEXEC
104         flags = MSG_CMSG_CLOEXEC;
105 #else
106         flags = 0;
107 #endif
108
109         for (;;) {
110                 fd_wait(sock, true);
111                 if (recvmsg(sock, msg, flags) == -1) {
112                         if (errno == EINTR)
113                                 continue;
114                         return (-1);
115                 }
116                 break;
117         }
118
119         return (0);
120 }
121
122 static int
123 msg_send(int sock, const struct msghdr *msg)
124 {
125
126         PJDLOG_ASSERT(sock >= 0);
127
128         for (;;) {
129                 fd_wait(sock, false);
130                 if (sendmsg(sock, msg, 0) == -1) {
131                         if (errno == EINTR)
132                                 continue;
133                         return (-1);
134                 }
135                 break;
136         }
137
138         return (0);
139 }
140
141 #ifdef __FreeBSD__
142 int
143 cred_send(int sock)
144 {
145         unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
146         struct msghdr msg;
147         struct cmsghdr *cmsg;
148         struct iovec iov;
149         uint8_t dummy;
150
151         bzero(credbuf, sizeof(credbuf));
152         bzero(&msg, sizeof(msg));
153         bzero(&iov, sizeof(iov));
154
155         /*
156          * XXX: We send one byte along with the control message, because
157          *      setting msg_iov to NULL only works if this is the first
158          *      packet send over the socket. Once we send some data we
159          *      won't be able to send credentials anymore. This is most
160          *      likely a kernel bug.
161          */
162         dummy = 0;
163         iov.iov_base = &dummy;
164         iov.iov_len = sizeof(dummy);
165
166         msg.msg_iov = &iov;
167         msg.msg_iovlen = 1;
168         msg.msg_control = credbuf;
169         msg.msg_controllen = sizeof(credbuf);
170
171         cmsg = CMSG_FIRSTHDR(&msg);
172         cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
173         cmsg->cmsg_level = SOL_SOCKET;
174         cmsg->cmsg_type = SCM_CREDS;
175
176         if (msg_send(sock, &msg) == -1)
177                 return (-1);
178
179         return (0);
180 }
181
182 int
183 cred_recv(int sock, struct cmsgcred *cred)
184 {
185         unsigned char credbuf[CMSG_SPACE(sizeof(struct cmsgcred))];
186         struct msghdr msg;
187         struct cmsghdr *cmsg;
188         struct iovec iov;
189         uint8_t dummy;
190
191         bzero(credbuf, sizeof(credbuf));
192         bzero(&msg, sizeof(msg));
193         bzero(&iov, sizeof(iov));
194
195         iov.iov_base = &dummy;
196         iov.iov_len = sizeof(dummy);
197
198         msg.msg_iov = &iov;
199         msg.msg_iovlen = 1;
200         msg.msg_control = credbuf;
201         msg.msg_controllen = sizeof(credbuf);
202
203         if (msg_recv(sock, &msg) == -1)
204                 return (-1);
205
206         cmsg = CMSG_FIRSTHDR(&msg);
207         if (cmsg == NULL ||
208             cmsg->cmsg_len != CMSG_LEN(sizeof(struct cmsgcred)) ||
209             cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDS) {
210                 errno = EINVAL;
211                 return (-1);
212         }
213         bcopy(CMSG_DATA(cmsg), cred, sizeof(*cred));
214
215         return (0);
216 }
217 #endif
218
219 static int
220 fd_package_send(int sock, const int *fds, size_t nfds)
221 {
222         struct msghdr msg;
223         struct cmsghdr *cmsg;
224         struct iovec iov;
225         unsigned int i;
226         int serrno, ret;
227         uint8_t dummy;
228
229         PJDLOG_ASSERT(sock >= 0);
230         PJDLOG_ASSERT(fds != NULL);
231         PJDLOG_ASSERT(nfds > 0);
232
233         bzero(&msg, sizeof(msg));
234
235         /*
236          * XXX: Look into cred_send function for more details.
237          */
238         dummy = 0;
239         iov.iov_base = &dummy;
240         iov.iov_len = sizeof(dummy);
241
242         msg.msg_iov = &iov;
243         msg.msg_iovlen = 1;
244         msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
245         msg.msg_control = calloc(1, msg.msg_controllen);
246         if (msg.msg_control == NULL)
247                 return (-1);
248
249         ret = -1;
250
251         for (i = 0, cmsg = CMSG_FIRSTHDR(&msg); i < nfds && cmsg != NULL;
252             i++, cmsg = CMSG_NXTHDR(&msg, cmsg)) {
253                 if (msghdr_add_fd(cmsg, fds[i]) == -1)
254                         goto end;
255         }
256
257         if (msg_send(sock, &msg) == -1)
258                 goto end;
259
260         ret = 0;
261 end:
262         serrno = errno;
263         free(msg.msg_control);
264         errno = serrno;
265         return (ret);
266 }
267
268 static int
269 fd_package_recv(int sock, int *fds, size_t nfds)
270 {
271         struct msghdr msg;
272         struct cmsghdr *cmsg;
273         unsigned int i;
274         int serrno, ret;
275         struct iovec iov;
276         uint8_t dummy;
277
278         PJDLOG_ASSERT(sock >= 0);
279         PJDLOG_ASSERT(nfds > 0);
280         PJDLOG_ASSERT(fds != NULL);
281
282         bzero(&msg, sizeof(msg));
283         bzero(&iov, sizeof(iov));
284
285         /*
286          * XXX: Look into cred_send function for more details.
287          */
288         iov.iov_base = &dummy;
289         iov.iov_len = sizeof(dummy);
290
291         msg.msg_iov = &iov;
292         msg.msg_iovlen = 1;
293         msg.msg_controllen = nfds * CMSG_SPACE(sizeof(int));
294         msg.msg_control = calloc(1, msg.msg_controllen);
295         if (msg.msg_control == NULL)
296                 return (-1);
297
298         ret = -1;
299
300         if (msg_recv(sock, &msg) == -1)
301                 goto end;
302
303         i = 0;
304         cmsg = CMSG_FIRSTHDR(&msg);
305         while (cmsg && i < nfds) {
306                 unsigned int n;
307
308                 if (cmsg->cmsg_level != SOL_SOCKET ||
309                     cmsg->cmsg_type != SCM_RIGHTS) {
310                         errno = EINVAL;
311                         break;
312                 }
313                 n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
314                 if (i + n > nfds) {
315                         errno = EINVAL;
316                         break;
317                 }
318                 bcopy(CMSG_DATA(cmsg), fds + i, sizeof(int) * n);
319                 cmsg = CMSG_NXTHDR(&msg, cmsg);
320                 i += n;
321         }
322
323         if (cmsg != NULL || i < nfds) {
324                 unsigned int last;
325
326                 /*
327                  * We need to close all received descriptors, even if we have
328                  * different control message (eg. SCM_CREDS) in between.
329                  */
330                 last = i;
331                 for (i = 0; i < last; i++) {
332                         if (fds[i] >= 0) {
333                                 close(fds[i]);
334                         }
335                 }
336                 errno = EINVAL;
337                 goto end;
338         }
339
340 #ifndef MSG_CMSG_CLOEXEC
341         /*
342          * If the MSG_CMSG_CLOEXEC flag is not available we cannot set the
343          * close-on-exec flag atomically, but we still want to set it for
344          * consistency.
345          */
346         for (i = 0; i < nfds; i++) {
347                 (void) fcntl(fds[i], F_SETFD, FD_CLOEXEC);
348         }
349 #endif
350
351         ret = 0;
352 end:
353         serrno = errno;
354         free(msg.msg_control);
355         errno = serrno;
356         return (ret);
357 }
358
359 int
360 fd_recv(int sock, int *fds, size_t nfds)
361 {
362         unsigned int i, step, j;
363         int ret, serrno;
364
365         if (nfds == 0 || fds == NULL) {
366                 errno = EINVAL;
367                 return (-1);
368         }
369
370         ret = i = step = 0;
371         while (i < nfds) {
372                 if (PKG_MAX_SIZE < nfds - i)
373                         step = PKG_MAX_SIZE;
374                 else
375                         step = nfds - i;
376                 ret = fd_package_recv(sock, fds + i, step);
377                 if (ret != 0) {
378                         /* Close all received descriptors. */
379                         serrno = errno;
380                         for (j = 0; j < i; j++)
381                                 close(fds[j]);
382                         errno = serrno;
383                         break;
384                 }
385                 i += step;
386         }
387
388         return (ret);
389 }
390
391 int
392 fd_send(int sock, const int *fds, size_t nfds)
393 {
394         unsigned int i, step;
395         int ret;
396
397         if (nfds == 0 || fds == NULL) {
398                 errno = EINVAL;
399                 return (-1);
400         }
401
402         ret = i = step = 0;
403         while (i < nfds) {
404                 if (PKG_MAX_SIZE < nfds - i)
405                         step = PKG_MAX_SIZE;
406                 else
407                         step = nfds - i;
408                 ret = fd_package_send(sock, fds + i, step);
409                 if (ret != 0)
410                         break;
411                 i += step;
412         }
413
414         return (ret);
415 }
416
417 int
418 buf_send(int sock, void *buf, size_t size)
419 {
420         ssize_t done;
421         unsigned char *ptr;
422
423         PJDLOG_ASSERT(sock >= 0);
424         PJDLOG_ASSERT(size > 0);
425         PJDLOG_ASSERT(buf != NULL);
426
427         ptr = buf;
428         do {
429                 fd_wait(sock, false);
430                 done = send(sock, ptr, size, 0);
431                 if (done == -1) {
432                         if (errno == EINTR)
433                                 continue;
434                         return (-1);
435                 } else if (done == 0) {
436                         errno = ENOTCONN;
437                         return (-1);
438                 }
439                 size -= done;
440                 ptr += done;
441         } while (size > 0);
442
443         return (0);
444 }
445
446 int
447 buf_recv(int sock, void *buf, size_t size)
448 {
449         ssize_t done;
450         unsigned char *ptr;
451
452         PJDLOG_ASSERT(sock >= 0);
453         PJDLOG_ASSERT(buf != NULL);
454
455         ptr = buf;
456         while (size > 0) {
457                 fd_wait(sock, true);
458                 done = recv(sock, ptr, size, 0);
459                 if (done == -1) {
460                         if (errno == EINTR)
461                                 continue;
462                         return (-1);
463                 } else if (done == 0) {
464                         errno = ENOTCONN;
465                         return (-1);
466                 }
467                 size -= done;
468                 ptr += done;
469         }
470
471         return (0);
472 }