xref: /unit/src/nxt_socketpair.c (revision 1)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru 
100Sigor@sysoev.ru /*
110Sigor@sysoev.ru  * SOCK_SEQPACKET protocol is supported for AF_UNIX in Solaris 8 X/Open
120Sigor@sysoev.ru  * sockets, Linux 2.6.4, FreeBSD 9.0, NetBSD 6.0, and OpenBSD 5.0.
130Sigor@sysoev.ru  */
140Sigor@sysoev.ru 
150Sigor@sysoev.ru /* SOCK_SEQPACKET is disabled to test SOCK_DGRAM on all platforms. */
160Sigor@sysoev.ru #if (0 || NXT_HAVE_AF_UNIX_SOCK_SEQPACKET)
170Sigor@sysoev.ru #define NXT_UNIX_SOCKET  SOCK_SEQPACKET
180Sigor@sysoev.ru #else
190Sigor@sysoev.ru #define NXT_UNIX_SOCKET  SOCK_DGRAM
200Sigor@sysoev.ru #endif
210Sigor@sysoev.ru 
220Sigor@sysoev.ru 
230Sigor@sysoev.ru static ssize_t nxt_sendmsg(nxt_socket_t s, nxt_fd_t fd, nxt_iobuf_t *iob,
240Sigor@sysoev.ru    nxt_uint_t niob);
250Sigor@sysoev.ru static ssize_t nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob,
260Sigor@sysoev.ru    nxt_uint_t niob);
270Sigor@sysoev.ru 
280Sigor@sysoev.ru 
290Sigor@sysoev.ru nxt_int_t
300Sigor@sysoev.ru nxt_socketpair_create(nxt_socket_t *pair)
310Sigor@sysoev.ru {
320Sigor@sysoev.ru     if (nxt_slow_path(socketpair(AF_UNIX, NXT_UNIX_SOCKET, 0, pair) != 0)) {
330Sigor@sysoev.ru         nxt_thread_log_alert("socketpair() failed %E", nxt_errno);
340Sigor@sysoev.ru         return NXT_ERROR;
350Sigor@sysoev.ru     }
360Sigor@sysoev.ru 
370Sigor@sysoev.ru     nxt_thread_log_debug("socketpair(): %d:%d", pair[0], pair[1]);
380Sigor@sysoev.ru 
390Sigor@sysoev.ru     if (nxt_slow_path(nxt_socket_nonblocking(pair[0]) != NXT_OK)) {
400Sigor@sysoev.ru         goto fail;
410Sigor@sysoev.ru     }
420Sigor@sysoev.ru 
430Sigor@sysoev.ru     if (nxt_slow_path(fcntl(pair[0], F_SETFD, FD_CLOEXEC) == -1)) {
440Sigor@sysoev.ru         goto fail;
450Sigor@sysoev.ru     }
460Sigor@sysoev.ru 
470Sigor@sysoev.ru     if (nxt_slow_path(nxt_socket_nonblocking(pair[1]) != NXT_OK)) {
480Sigor@sysoev.ru         goto fail;
490Sigor@sysoev.ru     }
500Sigor@sysoev.ru 
510Sigor@sysoev.ru     if (nxt_slow_path(fcntl(pair[1], F_SETFD, FD_CLOEXEC) == -1)) {
520Sigor@sysoev.ru         goto fail;
530Sigor@sysoev.ru     }
540Sigor@sysoev.ru 
550Sigor@sysoev.ru     return NXT_OK;
560Sigor@sysoev.ru 
570Sigor@sysoev.ru fail:
580Sigor@sysoev.ru 
590Sigor@sysoev.ru     nxt_socketpair_close(pair);
600Sigor@sysoev.ru 
610Sigor@sysoev.ru     return NXT_ERROR;
620Sigor@sysoev.ru }
630Sigor@sysoev.ru 
640Sigor@sysoev.ru 
650Sigor@sysoev.ru void
660Sigor@sysoev.ru nxt_socketpair_close(nxt_socket_t *pair)
670Sigor@sysoev.ru {
680Sigor@sysoev.ru     nxt_socket_close(pair[0]);
690Sigor@sysoev.ru     nxt_socket_close(pair[1]);
700Sigor@sysoev.ru }
710Sigor@sysoev.ru 
720Sigor@sysoev.ru 
730Sigor@sysoev.ru ssize_t
740Sigor@sysoev.ru nxt_socketpair_send(nxt_event_fd_t *ev, nxt_fd_t fd, nxt_iobuf_t *iob,
750Sigor@sysoev.ru     nxt_uint_t niob)
760Sigor@sysoev.ru {
770Sigor@sysoev.ru     ssize_t    n;
780Sigor@sysoev.ru     nxt_err_t  err;
790Sigor@sysoev.ru 
800Sigor@sysoev.ru     for ( ;; ) {
810Sigor@sysoev.ru         n = nxt_sendmsg(ev->fd, fd, iob, niob);
820Sigor@sysoev.ru 
830Sigor@sysoev.ru         err = (n == -1) ? nxt_socket_errno : 0;
840Sigor@sysoev.ru 
85*1Sigor@sysoev.ru         nxt_debug(ev->task, "sendmsg(%d, %FD, %ui): %z", ev->fd, fd, niob, n);
860Sigor@sysoev.ru 
870Sigor@sysoev.ru         if (n > 0) {
880Sigor@sysoev.ru             return n;
890Sigor@sysoev.ru         }
900Sigor@sysoev.ru 
910Sigor@sysoev.ru         /* n == -1 */
920Sigor@sysoev.ru 
930Sigor@sysoev.ru         switch (err) {
940Sigor@sysoev.ru 
950Sigor@sysoev.ru         case NXT_EAGAIN:
96*1Sigor@sysoev.ru             nxt_debug(ev->task, "sendmsg(%d) not ready", ev->fd);
970Sigor@sysoev.ru             ev->write_ready = 0;
98*1Sigor@sysoev.ru 
990Sigor@sysoev.ru             return NXT_AGAIN;
1000Sigor@sysoev.ru 
1010Sigor@sysoev.ru         case NXT_EINTR:
102*1Sigor@sysoev.ru             nxt_debug(ev->task, "sendmsg(%d) interrupted", ev->fd);
1030Sigor@sysoev.ru             continue;
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru         default:
106*1Sigor@sysoev.ru             nxt_log(ev->task, NXT_LOG_CRIT, "sendmsg(%d, %FD, %ui) failed %E",
107*1Sigor@sysoev.ru                     ev->fd, fd, niob, err);
108*1Sigor@sysoev.ru 
1090Sigor@sysoev.ru             return NXT_ERROR;
1100Sigor@sysoev.ru         }
1110Sigor@sysoev.ru     }
1120Sigor@sysoev.ru }
1130Sigor@sysoev.ru 
1140Sigor@sysoev.ru 
1150Sigor@sysoev.ru ssize_t
1160Sigor@sysoev.ru nxt_socketpair_recv(nxt_event_fd_t *ev, nxt_fd_t *fd, nxt_iobuf_t *iob,
1170Sigor@sysoev.ru     nxt_uint_t niob)
1180Sigor@sysoev.ru {
1190Sigor@sysoev.ru     ssize_t    n;
1200Sigor@sysoev.ru     nxt_err_t  err;
1210Sigor@sysoev.ru 
1220Sigor@sysoev.ru     for ( ;; ) {
1230Sigor@sysoev.ru         n = nxt_recvmsg(ev->fd, fd, iob, niob);
1240Sigor@sysoev.ru 
1250Sigor@sysoev.ru         err = (n == -1) ? nxt_socket_errno : 0;
1260Sigor@sysoev.ru 
127*1Sigor@sysoev.ru         nxt_debug(ev->task, "recvmsg(%d, %FD, %ui): %z", ev->fd, *fd, niob, n);
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru         if (n > 0) {
1300Sigor@sysoev.ru             return n;
1310Sigor@sysoev.ru         }
1320Sigor@sysoev.ru 
1330Sigor@sysoev.ru         if (n == 0) {
1340Sigor@sysoev.ru             ev->closed = 1;
1350Sigor@sysoev.ru             ev->read_ready = 0;
136*1Sigor@sysoev.ru 
1370Sigor@sysoev.ru             return n;
1380Sigor@sysoev.ru         }
1390Sigor@sysoev.ru 
1400Sigor@sysoev.ru         /* n == -1 */
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru         switch (err) {
1430Sigor@sysoev.ru 
1440Sigor@sysoev.ru         case NXT_EAGAIN:
145*1Sigor@sysoev.ru             nxt_debug(ev->task, "recvmsg(%d) not ready", ev->fd);
1460Sigor@sysoev.ru             ev->read_ready = 0;
147*1Sigor@sysoev.ru 
1480Sigor@sysoev.ru             return NXT_AGAIN;
1490Sigor@sysoev.ru 
1500Sigor@sysoev.ru         case NXT_EINTR:
151*1Sigor@sysoev.ru             nxt_debug(ev->task, "recvmsg(%d) interrupted", ev->fd);
1520Sigor@sysoev.ru             continue;
1530Sigor@sysoev.ru 
1540Sigor@sysoev.ru         default:
155*1Sigor@sysoev.ru             nxt_log(ev->task, NXT_LOG_CRIT, "recvmsg(%d, %p, %ui) failed %E",
156*1Sigor@sysoev.ru                     ev->fd, fd, niob, err);
157*1Sigor@sysoev.ru 
1580Sigor@sysoev.ru             return NXT_ERROR;
1590Sigor@sysoev.ru         }
1600Sigor@sysoev.ru     }
1610Sigor@sysoev.ru }
1620Sigor@sysoev.ru 
1630Sigor@sysoev.ru 
1640Sigor@sysoev.ru #if (NXT_HAVE_MSGHDR_MSG_CONTROL)
1650Sigor@sysoev.ru 
1660Sigor@sysoev.ru /*
1670Sigor@sysoev.ru  * Linux, FreeBSD, Solaris X/Open sockets,
1680Sigor@sysoev.ru  * MacOSX, NetBSD, AIX, HP-UX X/Open sockets.
1690Sigor@sysoev.ru  */
1700Sigor@sysoev.ru 
1710Sigor@sysoev.ru static ssize_t
1720Sigor@sysoev.ru nxt_sendmsg(nxt_socket_t s, nxt_fd_t fd, nxt_iobuf_t *iob, nxt_uint_t niob)
1730Sigor@sysoev.ru {
1740Sigor@sysoev.ru     struct msghdr       msg;
1750Sigor@sysoev.ru     union {
1760Sigor@sysoev.ru         struct cmsghdr  cm;
1770Sigor@sysoev.ru         char            space[CMSG_SPACE(sizeof(int))];
1780Sigor@sysoev.ru     } cmsg;
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru     msg.msg_name = NULL;
1810Sigor@sysoev.ru     msg.msg_namelen = 0;
1820Sigor@sysoev.ru     msg.msg_iov = iob;
1830Sigor@sysoev.ru     msg.msg_iovlen = niob;
1840Sigor@sysoev.ru     /* Flags are cleared just to suppress valgrind warning. */
1850Sigor@sysoev.ru     msg.msg_flags = 0;
1860Sigor@sysoev.ru 
1870Sigor@sysoev.ru     if (fd != -1) {
1880Sigor@sysoev.ru         msg.msg_control = (caddr_t) &cmsg;
1890Sigor@sysoev.ru         msg.msg_controllen = sizeof(cmsg);
1900Sigor@sysoev.ru 
1910Sigor@sysoev.ru         cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
1920Sigor@sysoev.ru         cmsg.cm.cmsg_level = SOL_SOCKET;
1930Sigor@sysoev.ru         cmsg.cm.cmsg_type = SCM_RIGHTS;
1940Sigor@sysoev.ru 
1950Sigor@sysoev.ru         /*
1960Sigor@sysoev.ru          * nxt_memcpy() is used instead of simple
1970Sigor@sysoev.ru          *   *(int *) CMSG_DATA(&cmsg.cm) = fd;
1980Sigor@sysoev.ru          * because GCC 4.4 with -O2/3/s optimization may issue a warning:
1990Sigor@sysoev.ru          *   dereferencing type-punned pointer will break strict-aliasing rules
2000Sigor@sysoev.ru          *
2010Sigor@sysoev.ru          * Fortunately, GCC with -O1 compiles this nxt_memcpy()
2020Sigor@sysoev.ru          * in the same simple assignment as in the code above.
2030Sigor@sysoev.ru          */
2040Sigor@sysoev.ru         nxt_memcpy(CMSG_DATA(&cmsg.cm), &fd, sizeof(int));
2050Sigor@sysoev.ru 
2060Sigor@sysoev.ru     } else {
2070Sigor@sysoev.ru         msg.msg_control = NULL;
2080Sigor@sysoev.ru         msg.msg_controllen = 0;
2090Sigor@sysoev.ru     }
2100Sigor@sysoev.ru 
2110Sigor@sysoev.ru     return sendmsg(s, &msg, 0);
2120Sigor@sysoev.ru }
2130Sigor@sysoev.ru 
2140Sigor@sysoev.ru 
2150Sigor@sysoev.ru static ssize_t
2160Sigor@sysoev.ru nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob)
2170Sigor@sysoev.ru {
2180Sigor@sysoev.ru     ssize_t             n;
2190Sigor@sysoev.ru     struct msghdr       msg;
2200Sigor@sysoev.ru     union {
2210Sigor@sysoev.ru         struct cmsghdr  cm;
2220Sigor@sysoev.ru         char            space[CMSG_SPACE(sizeof(int))];
2230Sigor@sysoev.ru     } cmsg;
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru     msg.msg_name = NULL;
2260Sigor@sysoev.ru     msg.msg_namelen = 0;
2270Sigor@sysoev.ru     msg.msg_iov = iob;
2280Sigor@sysoev.ru     msg.msg_iovlen = niob;
2290Sigor@sysoev.ru     msg.msg_control = (caddr_t) &cmsg;
2300Sigor@sysoev.ru     msg.msg_controllen = sizeof(cmsg);
2310Sigor@sysoev.ru 
2320Sigor@sysoev.ru     *fd = -1;
2330Sigor@sysoev.ru 
2340Sigor@sysoev.ru     n = recvmsg(s, &msg, 0);
2350Sigor@sysoev.ru 
2360Sigor@sysoev.ru     if (n > 0
2370Sigor@sysoev.ru         && cmsg.cm.cmsg_len == CMSG_LEN(sizeof(int))
2380Sigor@sysoev.ru         && cmsg.cm.cmsg_level == SOL_SOCKET
2390Sigor@sysoev.ru         && cmsg.cm.cmsg_type == SCM_RIGHTS)
2400Sigor@sysoev.ru     {
2410Sigor@sysoev.ru         /* (*fd) = *(int *) CMSG_DATA(&cmsg.cm); */
2420Sigor@sysoev.ru         nxt_memcpy(fd, CMSG_DATA(&cmsg.cm), sizeof(int));
2430Sigor@sysoev.ru     }
2440Sigor@sysoev.ru 
2450Sigor@sysoev.ru     return n;
2460Sigor@sysoev.ru }
2470Sigor@sysoev.ru 
2480Sigor@sysoev.ru #else
2490Sigor@sysoev.ru 
2500Sigor@sysoev.ru /* Solaris 4.3BSD sockets. */
2510Sigor@sysoev.ru 
2520Sigor@sysoev.ru static ssize_t
2530Sigor@sysoev.ru nxt_sendmsg(nxt_socket_t s, nxt_fd_t fd, nxt_iobuf_t *iob, nxt_uint_t niob)
2540Sigor@sysoev.ru {
2550Sigor@sysoev.ru     struct msghdr  msg;
2560Sigor@sysoev.ru 
2570Sigor@sysoev.ru     msg.msg_name = NULL;
2580Sigor@sysoev.ru     msg.msg_namelen = 0;
2590Sigor@sysoev.ru     msg.msg_iov = iob;
2600Sigor@sysoev.ru     msg.msg_iovlen = niob;
2610Sigor@sysoev.ru 
2620Sigor@sysoev.ru     if (fd != -1) {
2630Sigor@sysoev.ru         msg.msg_accrights = (caddr_t) &fd;
2640Sigor@sysoev.ru         msg.msg_accrightslen = sizeof(int);
2650Sigor@sysoev.ru 
2660Sigor@sysoev.ru     } else {
2670Sigor@sysoev.ru         msg.msg_accrights = NULL;
2680Sigor@sysoev.ru         msg.msg_accrightslen = 0;
2690Sigor@sysoev.ru     }
2700Sigor@sysoev.ru 
2710Sigor@sysoev.ru     return sendmsg(s, &msg, 0);
2720Sigor@sysoev.ru }
2730Sigor@sysoev.ru 
2740Sigor@sysoev.ru 
2750Sigor@sysoev.ru static ssize_t
2760Sigor@sysoev.ru nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob)
2770Sigor@sysoev.ru {
2780Sigor@sysoev.ru     struct msghdr  msg;
2790Sigor@sysoev.ru 
2800Sigor@sysoev.ru     *fd = -1;
2810Sigor@sysoev.ru 
2820Sigor@sysoev.ru     msg.msg_name = NULL;
2830Sigor@sysoev.ru     msg.msg_namelen = 0;
2840Sigor@sysoev.ru     msg.msg_iov = iob;
2850Sigor@sysoev.ru     msg.msg_iovlen = niob;
2860Sigor@sysoev.ru     msg.msg_accrights = (caddr_t) fd;
2870Sigor@sysoev.ru     msg.msg_accrightslen = sizeof(int);
2880Sigor@sysoev.ru 
2890Sigor@sysoev.ru     return recvmsg(s, &msg, 0);
2900Sigor@sysoev.ru }
2910Sigor@sysoev.ru 
2920Sigor@sysoev.ru #endif
293