xref: /unit/src/nxt_socketpair.c (revision 0:a63ceefd6ab0)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
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 static ssize_t nxt_sendmsg(nxt_socket_t s, nxt_fd_t fd, nxt_iobuf_t *iob,
24    nxt_uint_t niob);
25 static ssize_t nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob,
26    nxt_uint_t niob);
27 
28 
29 nxt_int_t
30 nxt_socketpair_create(nxt_socket_t *pair)
31 {
32     if (nxt_slow_path(socketpair(AF_UNIX, NXT_UNIX_SOCKET, 0, pair) != 0)) {
33         nxt_thread_log_alert("socketpair() failed %E", nxt_errno);
34         return NXT_ERROR;
35     }
36 
37     nxt_thread_log_debug("socketpair(): %d:%d", pair[0], pair[1]);
38 
39     if (nxt_slow_path(nxt_socket_nonblocking(pair[0]) != NXT_OK)) {
40         goto fail;
41     }
42 
43     if (nxt_slow_path(fcntl(pair[0], F_SETFD, FD_CLOEXEC) == -1)) {
44         goto fail;
45     }
46 
47     if (nxt_slow_path(nxt_socket_nonblocking(pair[1]) != NXT_OK)) {
48         goto fail;
49     }
50 
51     if (nxt_slow_path(fcntl(pair[1], F_SETFD, FD_CLOEXEC) == -1)) {
52         goto fail;
53     }
54 
55     return NXT_OK;
56 
57 fail:
58 
59     nxt_socketpair_close(pair);
60 
61     return NXT_ERROR;
62 }
63 
64 
65 void
66 nxt_socketpair_close(nxt_socket_t *pair)
67 {
68     nxt_socket_close(pair[0]);
69     nxt_socket_close(pair[1]);
70 }
71 
72 
73 ssize_t
74 nxt_socketpair_send(nxt_event_fd_t *ev, nxt_fd_t fd, nxt_iobuf_t *iob,
75     nxt_uint_t niob)
76 {
77     ssize_t    n;
78     nxt_err_t  err;
79 
80     for ( ;; ) {
81         n = nxt_sendmsg(ev->fd, fd, iob, niob);
82 
83         err = (n == -1) ? nxt_socket_errno : 0;
84 
85         nxt_log_debug(ev->log, "sendmsg(%d, %FD, %ui): %z",
86                                ev->fd, fd, niob, n);
87 
88         if (n > 0) {
89             return n;
90         }
91 
92         /* n == -1 */
93 
94         switch (err) {
95 
96         case NXT_EAGAIN:
97             nxt_log_debug(ev->log, "sendmsg(%d) not ready", ev->fd);
98             ev->write_ready = 0;
99             return NXT_AGAIN;
100 
101         case NXT_EINTR:
102             nxt_log_debug(ev->log, "sendmsg(%d) interrupted", ev->fd);
103             continue;
104 
105         default:
106             nxt_log_error(NXT_LOG_CRIT, ev->log,
107                           "sendmsg(%d, %FD, %ui) failed %E",
108                           ev->fd, fd, niob, err);
109             return NXT_ERROR;
110         }
111     }
112 }
113 
114 
115 ssize_t
116 nxt_socketpair_recv(nxt_event_fd_t *ev, nxt_fd_t *fd, nxt_iobuf_t *iob,
117     nxt_uint_t niob)
118 {
119     ssize_t    n;
120     nxt_err_t  err;
121 
122     for ( ;; ) {
123         n = nxt_recvmsg(ev->fd, fd, iob, niob);
124 
125         err = (n == -1) ? nxt_socket_errno : 0;
126 
127         nxt_log_debug(ev->log, "recvmsg(%d, %FD, %ui): %z",
128                                ev->fd, *fd, niob, n);
129 
130         if (n > 0) {
131             return n;
132         }
133 
134         if (n == 0) {
135             ev->closed = 1;
136             ev->read_ready = 0;
137             return n;
138         }
139 
140         /* n == -1 */
141 
142         switch (err) {
143 
144         case NXT_EAGAIN:
145             nxt_log_debug(ev->log, "recvmsg(%d) not ready", ev->fd);
146             ev->read_ready = 0;
147             return NXT_AGAIN;
148 
149         case NXT_EINTR:
150             nxt_log_debug(ev->log, "recvmsg(%d) interrupted", ev->fd);
151             continue;
152 
153         default:
154             nxt_log_error(NXT_LOG_CRIT, ev->log,
155                           "recvmsg(%d, %p, %ui) failed %E",
156                           ev->fd, fd, niob, err);
157             return NXT_ERROR;
158         }
159     }
160 }
161 
162 
163 #if (NXT_HAVE_MSGHDR_MSG_CONTROL)
164 
165 /*
166  * Linux, FreeBSD, Solaris X/Open sockets,
167  * MacOSX, NetBSD, AIX, HP-UX X/Open sockets.
168  */
169 
170 static ssize_t
171 nxt_sendmsg(nxt_socket_t s, nxt_fd_t fd, nxt_iobuf_t *iob, nxt_uint_t niob)
172 {
173     struct msghdr       msg;
174     union {
175         struct cmsghdr  cm;
176         char            space[CMSG_SPACE(sizeof(int))];
177     } cmsg;
178 
179     msg.msg_name = NULL;
180     msg.msg_namelen = 0;
181     msg.msg_iov = iob;
182     msg.msg_iovlen = niob;
183     /* Flags are cleared just to suppress valgrind warning. */
184     msg.msg_flags = 0;
185 
186     if (fd != -1) {
187         msg.msg_control = (caddr_t) &cmsg;
188         msg.msg_controllen = sizeof(cmsg);
189 
190         cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
191         cmsg.cm.cmsg_level = SOL_SOCKET;
192         cmsg.cm.cmsg_type = SCM_RIGHTS;
193 
194         /*
195          * nxt_memcpy() is used instead of simple
196          *   *(int *) CMSG_DATA(&cmsg.cm) = fd;
197          * because GCC 4.4 with -O2/3/s optimization may issue a warning:
198          *   dereferencing type-punned pointer will break strict-aliasing rules
199          *
200          * Fortunately, GCC with -O1 compiles this nxt_memcpy()
201          * in the same simple assignment as in the code above.
202          */
203         nxt_memcpy(CMSG_DATA(&cmsg.cm), &fd, sizeof(int));
204 
205     } else {
206         msg.msg_control = NULL;
207         msg.msg_controllen = 0;
208     }
209 
210     return sendmsg(s, &msg, 0);
211 }
212 
213 
214 static ssize_t
215 nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob)
216 {
217     ssize_t             n;
218     struct msghdr       msg;
219     union {
220         struct cmsghdr  cm;
221         char            space[CMSG_SPACE(sizeof(int))];
222     } cmsg;
223 
224     msg.msg_name = NULL;
225     msg.msg_namelen = 0;
226     msg.msg_iov = iob;
227     msg.msg_iovlen = niob;
228     msg.msg_control = (caddr_t) &cmsg;
229     msg.msg_controllen = sizeof(cmsg);
230 
231     *fd = -1;
232 
233     n = recvmsg(s, &msg, 0);
234 
235     if (n > 0
236         && cmsg.cm.cmsg_len == CMSG_LEN(sizeof(int))
237         && cmsg.cm.cmsg_level == SOL_SOCKET
238         && cmsg.cm.cmsg_type == SCM_RIGHTS)
239     {
240         /* (*fd) = *(int *) CMSG_DATA(&cmsg.cm); */
241         nxt_memcpy(fd, CMSG_DATA(&cmsg.cm), sizeof(int));
242     }
243 
244     return n;
245 }
246 
247 #else
248 
249 /* Solaris 4.3BSD sockets. */
250 
251 static ssize_t
252 nxt_sendmsg(nxt_socket_t s, nxt_fd_t fd, nxt_iobuf_t *iob, nxt_uint_t niob)
253 {
254     struct msghdr  msg;
255 
256     msg.msg_name = NULL;
257     msg.msg_namelen = 0;
258     msg.msg_iov = iob;
259     msg.msg_iovlen = niob;
260 
261     if (fd != -1) {
262         msg.msg_accrights = (caddr_t) &fd;
263         msg.msg_accrightslen = sizeof(int);
264 
265     } else {
266         msg.msg_accrights = NULL;
267         msg.msg_accrightslen = 0;
268     }
269 
270     return sendmsg(s, &msg, 0);
271 }
272 
273 
274 static ssize_t
275 nxt_recvmsg(nxt_socket_t s, nxt_fd_t *fd, nxt_iobuf_t *iob, nxt_uint_t niob)
276 {
277     struct msghdr  msg;
278 
279     *fd = -1;
280 
281     msg.msg_name = NULL;
282     msg.msg_namelen = 0;
283     msg.msg_iov = iob;
284     msg.msg_iovlen = niob;
285     msg.msg_accrights = (caddr_t) fd;
286     msg.msg_accrightslen = sizeof(int);
287 
288     return recvmsg(s, &msg, 0);
289 }
290 
291 #endif
292