xref: /unit/src/nxt_socketpair.c (revision 1996:35873fa78fed)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 #include <nxt_socket_msg.h>
9 
10 /*
11  * SOCK_SEQPACKET protocol is supported for AF_UNIX in Solaris 8 X/Open
12  * sockets, Linux 2.6.4, FreeBSD 9.0, NetBSD 6.0, and OpenBSD 5.0.
13  */
14 
15 /* SOCK_SEQPACKET is disabled to test SOCK_DGRAM on all platforms. */
16 #if (0 || NXT_HAVE_AF_UNIX_SOCK_SEQPACKET)
17 #define NXT_UNIX_SOCKET  SOCK_SEQPACKET
18 #else
19 #define NXT_UNIX_SOCKET  SOCK_DGRAM
20 #endif
21 
22 
23 nxt_int_t
nxt_socketpair_create(nxt_task_t * task,nxt_socket_t * pair)24 nxt_socketpair_create(nxt_task_t *task, nxt_socket_t *pair)
25 {
26     if (nxt_slow_path(socketpair(AF_UNIX, NXT_UNIX_SOCKET, 0, pair) != 0)) {
27         nxt_alert(task, "socketpair() failed %E", nxt_errno);
28         return NXT_ERROR;
29     }
30 
31     nxt_debug(task, "socketpair(): %d:%d", pair[0], pair[1]);
32 
33     if (nxt_slow_path(nxt_socket_nonblocking(task, pair[0]) != NXT_OK)) {
34         goto fail;
35     }
36 
37     if (nxt_slow_path(fcntl(pair[0], F_SETFD, FD_CLOEXEC) == -1)) {
38         goto fail;
39     }
40 
41     if (nxt_slow_path(nxt_socket_nonblocking(task, pair[1]) != NXT_OK)) {
42         goto fail;
43     }
44 
45     if (nxt_slow_path(fcntl(pair[1], F_SETFD, FD_CLOEXEC) == -1)) {
46         goto fail;
47     }
48 
49 #if NXT_HAVE_SOCKOPT_SO_PASSCRED
50     int  enable_creds = 1;
51 
52     if (nxt_slow_path(setsockopt(pair[0], SOL_SOCKET, SO_PASSCRED,
53                       &enable_creds, sizeof(enable_creds)) == -1))
54     {
55         nxt_alert(task, "failed to set SO_PASSCRED %E", nxt_errno);
56         goto fail;
57     }
58 
59     if (nxt_slow_path(setsockopt(pair[1], SOL_SOCKET, SO_PASSCRED,
60                       &enable_creds, sizeof(enable_creds)) == -1))
61     {
62         nxt_alert(task, "failed to set SO_PASSCRED %E", nxt_errno);
63         goto fail;
64     }
65 #endif
66 
67     return NXT_OK;
68 
69 fail:
70 
71     nxt_socketpair_close(task, pair);
72 
73     return NXT_ERROR;
74 }
75 
76 
77 void
nxt_socketpair_close(nxt_task_t * task,nxt_socket_t * pair)78 nxt_socketpair_close(nxt_task_t *task, nxt_socket_t *pair)
79 {
80     nxt_socket_close(task, pair[0]);
81     nxt_socket_close(task, pair[1]);
82 }
83 
84 
85 ssize_t
nxt_socketpair_send(nxt_fd_event_t * ev,nxt_fd_t * fd,nxt_iobuf_t * iob,nxt_uint_t niob)86 nxt_socketpair_send(nxt_fd_event_t *ev, nxt_fd_t *fd, nxt_iobuf_t *iob,
87     nxt_uint_t niob)
88 {
89     ssize_t         n;
90     nxt_err_t       err;
91     nxt_send_oob_t  oob;
92 
93     nxt_socket_msg_oob_init(&oob, fd);
94 
95     for ( ;; ) {
96         n = nxt_sendmsg(ev->fd, iob, niob, &oob);
97 
98         err = (n == -1) ? nxt_socket_errno : 0;
99 
100         nxt_debug(ev->task, "sendmsg(%d, %FD, %FD, %ui): %z", ev->fd, fd[0],
101                   fd[1], niob, n);
102 
103         if (n > 0) {
104             return n;
105         }
106 
107         /* n == -1 */
108 
109         switch (err) {
110 
111         case NXT_EAGAIN:
112             nxt_debug(ev->task, "sendmsg(%d) not ready", ev->fd);
113             break;
114 
115         /*
116          * Returned (at least on OSX) when trying to send many small messages.
117          */
118         case NXT_ENOBUFS:
119             nxt_debug(ev->task, "sendmsg(%d) no buffers", ev->fd);
120             break;
121 
122         case NXT_EINTR:
123             nxt_debug(ev->task, "sendmsg(%d) interrupted", ev->fd);
124             continue;
125 
126         default:
127             nxt_alert(ev->task, "sendmsg(%d, %FD, %FD, %ui) failed %E",
128                       ev->fd, fd[0], fd[1], niob, err);
129 
130             return NXT_ERROR;
131         }
132 
133         ev->write_ready = 0;
134 
135         return NXT_AGAIN;
136     }
137 }
138 
139 
140 ssize_t
nxt_socketpair_recv(nxt_fd_event_t * ev,nxt_iobuf_t * iob,nxt_uint_t niob,void * oob)141 nxt_socketpair_recv(nxt_fd_event_t *ev, nxt_iobuf_t *iob, nxt_uint_t niob,
142     void *oob)
143 {
144     ssize_t    n;
145     nxt_err_t  err;
146 
147     for ( ;; ) {
148         n = nxt_recvmsg(ev->fd, iob, niob, oob);
149 
150         err = (n == -1) ? nxt_socket_errno : 0;
151 
152         nxt_debug(ev->task, "recvmsg(%d, %ui, %uz): %z",
153                   ev->fd, niob, ((nxt_recv_oob_t *) oob)->size, n);
154 
155         if (n > 0) {
156             return n;
157         }
158 
159         if (n == 0) {
160             ev->closed = 1;
161             ev->read_ready = 0;
162 
163             return n;
164         }
165 
166         /* n == -1 */
167 
168         switch (err) {
169 
170         case NXT_EAGAIN:
171             nxt_debug(ev->task, "recvmsg(%d) not ready", ev->fd);
172             ev->read_ready = 0;
173 
174             return NXT_AGAIN;
175 
176         case NXT_EINTR:
177             nxt_debug(ev->task, "recvmsg(%d) interrupted", ev->fd);
178             continue;
179 
180         default:
181             nxt_alert(ev->task, "recvmsg(%d, %ui) failed %E",
182                       ev->fd, niob, err);
183 
184             return NXT_ERROR;
185         }
186     }
187 }
188