1 /*
2 
3   Copyright (c) 2017 Martin Sustrik
4 
5   Permission is hereby granted, free of charge, to any person obtaining a copy
6   of this software and associated documentation files (the "Software"),
7   to deal in the Software without restriction, including without limitation
8   the rights to use, copy, modify, merge, publish, distribute, sublicense,
9   and/or sell copies of the Software, and to permit persons to whom
10   the Software is furnished to do so, subject to the following conditions:
11 
12   The above copyright notice and this permission notice shall be included
13   in all copies or substantial portions of the Software.
14 
15   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21   IN THE SOFTWARE.
22 
23 */
24 
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/un.h>
29 #include <unistd.h>
30 
31 #define DILL_DISABLE_RAW_NAMES
32 #include "libdillimpl.h"
33 #include "fd.h"
34 #include "utils.h"
35 
36 static int dill_ipc_resolve(const char *addr, struct sockaddr_un *su);
37 
38 dill_unique_id(dill_ipc_listener_type);
39 dill_unique_id(dill_ipc_type);
40 
41 /******************************************************************************/
42 /*  UNIX connection socket                                                    */
43 /******************************************************************************/
44 
45 static void *dill_ipc_hquery(struct dill_hvfs *hvfs, const void *type);
46 static void dill_ipc_hclose(struct dill_hvfs *hvfs);
47 static int dill_ipc_bsendl(struct dill_bsock_vfs *bvfs,
48     struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
49 static int dill_ipc_brecvl(struct dill_bsock_vfs *bvfs,
50     struct dill_iolist *first, struct dill_iolist *last, int64_t deadline);
51 
52 struct dill_ipc_conn {
53     struct dill_hvfs hvfs;
54     struct dill_bsock_vfs bvfs;
55     int fd;
56     struct dill_fd_rxbuf rxbuf;
57     unsigned int scm_rights : 1;
58     unsigned int rbusy : 1;
59     unsigned int sbusy : 1;
60     unsigned int indone : 1;
61     unsigned int outdone : 1;
62     unsigned int inerr : 1;
63     unsigned int outerr : 1;
64     unsigned int mem : 1;
65 };
66 
DILL_CHECK_STORAGE(dill_ipc_conn,dill_ipc_storage)67 DILL_CHECK_STORAGE(dill_ipc_conn, dill_ipc_storage)
68 
69 static int dill_ipc_makeconn(int fd, void *mem) {
70     /* Create the object. */
71     struct dill_ipc_conn *self = (struct dill_ipc_conn*)mem;
72     self->hvfs.query = dill_ipc_hquery;
73     self->hvfs.close = dill_ipc_hclose;
74     self->bvfs.bsendl = dill_ipc_bsendl;
75     self->bvfs.brecvl = dill_ipc_brecvl;
76     self->fd = fd;
77     dill_fd_initrxbuf(&self->rxbuf);
78     self->scm_rights = 1;
79     self->rbusy = 0;
80     self->sbusy = 0;
81     self->indone = 0;
82     self->outdone = 0;
83     self->inerr = 0;
84     self->outerr = 0;
85     self->mem = 1;
86     /* Create the handle. */
87     return dill_hmake(&self->hvfs);
88 }
89 
dill_ipc_hquery(struct dill_hvfs * hvfs,const void * type)90 static void *dill_ipc_hquery(struct dill_hvfs *hvfs, const void *type) {
91     struct dill_ipc_conn *self = (struct dill_ipc_conn*)hvfs;
92     if(type == dill_bsock_type) return &self->bvfs;
93     if(type == dill_ipc_type) return self;
94     errno = ENOTSUP;
95     return NULL;
96 }
97 
dill_ipc_fromfd_mem(int fd,struct dill_ipc_storage * mem)98 int dill_ipc_fromfd_mem(int fd, struct dill_ipc_storage *mem) {
99     int err;
100     if(dill_slow(!mem || fd < 0)) {err = EINVAL; goto error1;}
101     /* Make sure that the supplied file descriptor is of correct type. */
102     int rc = dill_fd_check(fd, SOCK_STREAM, AF_UNIX, -1, 0);
103     if(dill_slow(rc < 0)) {err = errno; goto error1;}
104     /* Take ownership of the file descriptor. */
105     fd = dill_fd_own(fd);
106     if(dill_slow(fd < 0)) {err = errno; goto error1;}
107     /* Set the socket to non-blocking mode */
108     rc = dill_fd_unblock(fd);
109     if(dill_slow(rc < 0)) {err = errno; goto error1;}
110     /* Create the handle */
111     int h = dill_ipc_makeconn(fd, mem);
112     if(dill_slow(h < 0)) {err = errno; goto error1;}
113     /* Return the handle */
114     return h;
115 error1:
116     errno = err;
117     return -1;
118 }
119 
dill_ipc_fromfd(int fd)120 int dill_ipc_fromfd(int fd) {
121     int err;
122     struct dill_ipc_conn *obj = malloc(sizeof(struct dill_ipc_conn));
123     if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
124     int s = dill_ipc_fromfd_mem(fd, (struct dill_ipc_storage*)obj);
125     if (dill_slow(s < 0)) {err = errno; goto error2;}
126     obj->mem = 0;
127     return s;
128 error2:
129     free(obj);
130 error1:
131     errno = err;
132     return -1;
133 }
134 
dill_ipc_connect_mem(const char * addr,struct dill_ipc_storage * mem,int64_t deadline)135 int dill_ipc_connect_mem(const char *addr, struct dill_ipc_storage *mem,
136       int64_t deadline) {
137     int err;
138     if(dill_slow(!mem)) {err = EINVAL; goto error1;}
139     /* Create a UNIX address out of the address string. */
140     struct sockaddr_un su;
141     int rc = dill_ipc_resolve(addr, &su);
142     if(rc < 0) {err = errno; goto error1;}
143     /* Open a socket. */
144     int s = socket(AF_UNIX, SOCK_STREAM, 0);
145     if(dill_slow(s < 0)) {err = errno; goto error1;}
146     /* Set it to non-blocking mode. */
147     rc = dill_fd_unblock(s);
148     if(dill_slow(rc < 0)) {err = errno; goto error2;}
149     /* Connect to the remote endpoint. */
150     rc = dill_fd_connect(s, (struct sockaddr*)&su, sizeof(su), deadline);
151     if(dill_slow(rc < 0)) {err = errno; goto error2;}
152     /* Create the handle. */
153     int h = dill_ipc_makeconn(s, mem);
154     if(dill_slow(h < 0)) {err = errno; goto error2;}
155     return h;
156 error2:
157     dill_fd_close(s);
158 error1:
159     errno = err;
160     return -1;
161 }
162 
dill_ipc_connect(const char * addr,int64_t deadline)163 int dill_ipc_connect(const char *addr, int64_t deadline) {
164     int err;
165     struct dill_ipc_conn *obj = malloc(sizeof(struct dill_ipc_conn));
166     if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
167     int s = dill_ipc_connect_mem(addr, (struct dill_ipc_storage*)obj, deadline);
168     if(dill_slow(s < 0)) {err = errno; goto error2;}
169     obj->mem = 0;
170     return s;
171 error2:
172     free(obj);
173 error1:
174     errno = err;
175     return -1;
176 }
177 
dill_ipc_bsendl(struct dill_bsock_vfs * bvfs,struct dill_iolist * first,struct dill_iolist * last,int64_t deadline)178 static int dill_ipc_bsendl(struct dill_bsock_vfs *bvfs,
179       struct dill_iolist *first, struct dill_iolist *last, int64_t deadline) {
180     struct dill_ipc_conn *self = dill_cont(bvfs, struct dill_ipc_conn, bvfs);
181     if(dill_slow(self->sbusy)) {errno = EBUSY; return -1;}
182     if(dill_slow(self->outdone)) {errno = EPIPE; return -1;}
183     if(dill_slow(self->outerr)) {errno = ECONNRESET; return -1;}
184     self->sbusy = 1;
185     ssize_t sz = dill_fd_send(self->fd, first, last, deadline);
186     self->sbusy = 0;
187     if(dill_fast(sz >= 0)) return sz;
188     self->outerr = 1;
189     return -1;
190 }
191 
dill_ipc_brecvl(struct dill_bsock_vfs * bvfs,struct dill_iolist * first,struct dill_iolist * last,int64_t deadline)192 static int dill_ipc_brecvl(struct dill_bsock_vfs *bvfs,
193       struct dill_iolist *first, struct dill_iolist *last, int64_t deadline) {
194     struct dill_ipc_conn *self = dill_cont(bvfs, struct dill_ipc_conn, bvfs);
195     if(dill_slow(self->rbusy)) {errno = EBUSY; return -1;}
196     if(dill_slow(self->indone)) {errno = EPIPE; return -1;}
197     if(dill_slow(self->inerr)) {errno = ECONNRESET; return -1;}
198     self->rbusy = 1;
199     /* If we want to use SCM_RIGHTS we can't do rx buffering. */
200     int rc = dill_fd_recv(self->fd, self->scm_rights ? NULL : &self->rxbuf,
201         first, last, deadline);
202     self->rbusy = 0;
203     if(dill_fast(rc == 0)) return 0;
204     if(errno == EPIPE) self->indone = 1;
205     else self->inerr = 1;
206     return -1;
207 }
208 
dill_ipc_sendfd(int s,int fd,int64_t deadline)209 int dill_ipc_sendfd(int s, int fd, int64_t deadline) {
210     struct dill_ipc_conn *self = dill_hquery(s, dill_ipc_type);
211     if(dill_slow(!self)) return -1;
212     if(dill_slow(!self->scm_rights)) {errno = ENOTSUP; return -1;}
213     if(dill_slow(fd < 0)) {errno = EINVAL; return -1;}
214     if(dill_slow(self->sbusy)) {errno = EBUSY; return -1;}
215     if(dill_slow(self->outdone)) {errno = EPIPE; return -1;}
216     if(dill_slow(self->outerr)) {errno = ECONNRESET; return -1;}
217     struct iovec iov;
218     unsigned char buf[] = {0xcc};
219     iov.iov_base = buf;
220     iov.iov_len = 1;
221     struct msghdr msg;
222     memset(&msg, 0, sizeof (msg));
223     msg.msg_iov = &iov;
224     msg.msg_iovlen = 1;
225     char control [sizeof(struct cmsghdr) + 10];
226     msg.msg_control = control;
227     msg.msg_controllen = sizeof(control);
228     struct cmsghdr *cmsg;
229     cmsg = CMSG_FIRSTHDR(&msg);
230     cmsg->cmsg_level = SOL_SOCKET;
231     cmsg->cmsg_type = SCM_RIGHTS;
232     cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
233     *((int*)CMSG_DATA(cmsg)) = fd;
234     msg.msg_controllen = cmsg->cmsg_len;
235     int rc = dill_fdout(self->fd, deadline);
236     if(dill_slow(rc < 0)) return -1;
237     ssize_t sz = sendmsg(self->fd, &msg, 0);
238     if(dill_slow(sz == 0)) {self->outdone = 1; errno = EPIPE; return -1;}
239     if(dill_slow(sz < 0)) {
240        if(errno == ECONNRESET) {self->outerr = 1; return -1;}
241        dill_assert(0);
242     }
243     return 0;
244 }
245 
dill_ipc_recvfd(int s,int64_t deadline)246 int dill_ipc_recvfd(int s, int64_t deadline) {
247     struct dill_ipc_conn *self = dill_hquery(s, dill_ipc_type);
248     if(dill_slow(!self)) return -1;
249     if(dill_slow(!self->scm_rights)) {errno = ENOTSUP; return -1;}
250     if(dill_slow(self->rbusy)) {errno = EBUSY; return -1;}
251     if(dill_slow(self->indone)) {errno = EPIPE; return -1;}
252     if(dill_slow(self->inerr)) {errno = ECONNRESET; return -1;}
253     char buf[1];
254     struct iovec iov;
255     iov.iov_base = buf;
256     iov.iov_len = sizeof(buf);
257     struct msghdr msg;
258     memset(&msg, 0, sizeof(msg));
259     msg.msg_iov = &iov;
260     msg.msg_iovlen = 1;
261     unsigned char control[1024];
262     msg.msg_control = control;
263     msg.msg_controllen = sizeof(control);
264     int rc = dill_fdin(self->fd, deadline);
265     if(dill_slow(rc < 0)) return -1;
266     ssize_t sz = recvmsg(self->fd, &msg, 0);
267     if(dill_slow(sz == 0)) {self->indone = 1; errno = EPIPE; return -1;}
268     if(dill_slow(sz < 0)) {
269        if(errno == ECONNRESET) {self->outerr = 1; return -1;}
270        dill_assert(0);
271     }
272     /* Loop over the auxiliary data to find the embedded file descriptor. */
273     int fd = -1;
274     struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
275     while(cmsg) {
276         if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type  == SCM_RIGHTS) {
277             fd = *(int*)CMSG_DATA(cmsg);
278             break;
279         }
280         cmsg = CMSG_NXTHDR(&msg, cmsg);
281     }
282     if(dill_slow(fd < 0)) {self->inerr = 1; errno = EPROTO; return -1;}
283     return fd;
284 }
285 
dill_ipc_done(int s,int64_t deadline)286 int dill_ipc_done(int s, int64_t deadline) {
287     struct dill_ipc_conn *self = dill_hquery(s, dill_ipc_type);
288     if(dill_slow(!self)) return -1;
289     if(dill_slow(self->outdone)) {errno = EPIPE; return -1;}
290     if(dill_slow(self->outerr)) {errno = ECONNRESET; return -1;}
291     /* Shutdown is done asynchronously on kernel level.
292        No need to use the deadline. */
293     int rc = shutdown(self->fd, SHUT_WR);
294     if(dill_slow(rc < 0)) {
295         if(errno == ENOTCONN) {self->outerr = 1; errno = ECONNRESET; return -1;}
296         if(errno == ENOBUFS) {self->outerr = 1; errno = ENOMEM; return -1;}
297         dill_assert(0);
298     }
299     self->outdone = 1;
300     return 0;
301 }
302 
dill_ipc_close(int s,int64_t deadline)303 int dill_ipc_close(int s, int64_t deadline) {
304     int err;
305     /* Listener socket needs no special treatment. */
306     if(dill_hquery(s, dill_ipc_listener_type)) {
307         return dill_hclose(s);
308     }
309     struct dill_ipc_conn *self = dill_hquery(s, dill_ipc_type);
310     if(dill_slow(!self)) return -1;
311     if(dill_slow(self->inerr || self->outerr)) {err = ECONNRESET; goto error;}
312     /* If not done already, flush the outbound data and start the terminal
313        handshake. */
314     if(!self->outdone) {
315         int rc = dill_ipc_done(s, deadline);
316         if(dill_slow(rc < 0)) {err = errno; goto error;}
317     }
318     /* Now we are going to read all the inbound data until we reach end of the
319        stream. That way we can be sure that the peer either received all our
320        data or consciously closed the connection without reading all of it. */
321     int rc = dill_ipc_brecvl(&self->bvfs, NULL, NULL, deadline);
322     dill_assert(rc < 0);
323     if(dill_slow(errno != EPIPE)) {err = errno; goto error;}
324     dill_ipc_hclose(&self->hvfs);
325     return 0;
326 error:
327     dill_ipc_hclose(&self->hvfs);
328     errno = err;
329     return -1;
330 }
331 
dill_ipc_hclose(struct dill_hvfs * hvfs)332 static void dill_ipc_hclose(struct dill_hvfs *hvfs) {
333     struct dill_ipc_conn *self = (struct dill_ipc_conn*)hvfs;
334     dill_fd_close(self->fd);
335     dill_fd_termrxbuf(&self->rxbuf);
336     if(!self->mem) free(self);
337 }
338 
339 /******************************************************************************/
340 /*  UNIX listener socket                                                      */
341 /******************************************************************************/
342 
343 static void *dill_ipc_listener_hquery(struct dill_hvfs *hvfs, const void *type);
344 static void dill_ipc_listener_hclose(struct dill_hvfs *hvfs);
345 
346 struct dill_ipc_listener {
347     struct dill_hvfs hvfs;
348     int fd;
349     unsigned int mem : 1;
350 };
351 
DILL_CHECK_STORAGE(dill_ipc_listener,dill_ipc_listener_storage)352 DILL_CHECK_STORAGE(dill_ipc_listener, dill_ipc_listener_storage)
353 
354 static void *dill_ipc_listener_hquery(struct dill_hvfs *hvfs,
355       const void *type) {
356     struct dill_ipc_listener *self = (struct dill_ipc_listener*)hvfs;
357     if(type == dill_ipc_listener_type) return self;
358     errno = ENOTSUP;
359     return NULL;
360 }
361 
dill_ipc_makelistener(int fd,struct dill_ipc_listener_storage * mem)362 static int dill_ipc_makelistener(int fd,
363       struct dill_ipc_listener_storage *mem) {
364     /* Create the object. */
365     struct dill_ipc_listener *self = (struct dill_ipc_listener*)mem;
366     self->hvfs.query = dill_ipc_listener_hquery;
367     self->hvfs.close = dill_ipc_listener_hclose;
368     self->fd = fd;
369     self->mem = 1;
370     /* Create the handle. */
371     return dill_hmake(&self->hvfs);
372 }
373 
dill_ipc_listener_fromfd(int fd)374 int dill_ipc_listener_fromfd(int fd) {
375     int err;
376     struct dill_ipc_listener *obj = malloc(sizeof(struct dill_ipc_listener));
377     if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
378     int s = dill_ipc_listener_fromfd_mem(fd,
379         (struct dill_ipc_listener_storage*)obj);
380     if (dill_slow(s < 0)) {err = errno; goto error2;}
381     obj->mem = 0;
382     return s;
383 error2:
384     free(obj);
385 error1:
386     errno = err;
387     return -1;
388 }
389 
dill_ipc_listener_fromfd_mem(int fd,struct dill_ipc_listener_storage * mem)390 int dill_ipc_listener_fromfd_mem(int fd,
391       struct dill_ipc_listener_storage *mem) {
392     int err;
393     if(dill_slow(!mem || fd < 0)) {err = EINVAL; goto error1;}
394     /* Make sure that the supplied file descriptor is of correct type. */
395     int rc = dill_fd_check(fd, SOCK_STREAM, AF_UNIX, -1, 1);
396     if(dill_slow(rc < 0)) {err = errno; goto error1;}
397     /* Take ownership of the file descriptor. */
398     fd = dill_fd_own(fd);
399     if(dill_slow(fd < 0)) {err = errno; goto error1;}
400     /* Set the socket to non-blocking mode */
401     rc = dill_fd_unblock(fd);
402     if(dill_slow(rc < 0)) {err = errno; goto error1;}
403     /* Create the handle */
404     int h = dill_ipc_makelistener(fd, mem);
405     if(dill_slow(h < 0)) {err = errno; goto error1;}
406     /* Return the handle */
407     return h;
408 error1:
409     errno = err;
410     return -1;
411 }
412 
dill_ipc_listen_mem(const char * addr,int backlog,struct dill_ipc_listener_storage * mem)413 int dill_ipc_listen_mem(const char *addr, int backlog,
414       struct dill_ipc_listener_storage *mem) {
415     int err;
416     /* Create a UNIX address out of the address string. */
417     struct sockaddr_un su;
418     int rc = dill_ipc_resolve(addr, &su);
419     if(rc < 0) {err = errno; goto error1;}
420     /* Open the listening socket. */
421     int s = socket(AF_UNIX, SOCK_STREAM, 0);
422     if(dill_slow(s < 0)) {err = errno; goto error1;}
423     /* Set it to non-blocking mode. */
424     rc = dill_fd_unblock(s);
425     if(dill_slow(rc < 0)) {err = errno; goto error2;}
426     /* Start listening for incoming connections. */
427     rc = bind(s, (struct sockaddr*)&su, sizeof(su));
428     if(dill_slow(rc < 0)) {err = errno; goto error2;}
429     rc = listen(s, backlog);
430     if(dill_slow(rc < 0)) {err = errno; goto error2;}
431     int h = dill_ipc_makelistener(s, mem);
432     if(dill_slow(h < 0)) {err = errno; goto error2;}
433     return h;
434 error2:
435     close(s);
436 error1:
437     errno = err;
438     return -1;
439 }
440 
dill_ipc_listen(const char * addr,int backlog)441 int dill_ipc_listen(const char *addr, int backlog) {
442     int err;
443     struct dill_ipc_listener *obj = malloc(sizeof(struct dill_ipc_listener));
444     if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
445     int ls = dill_ipc_listen_mem(addr, backlog,
446         (struct dill_ipc_listener_storage*)obj);
447     if(dill_slow(ls < 0)) {err = errno; goto error2;}
448     obj->mem = 0;
449     return ls;
450 error2:
451     free(obj);
452 error1:
453     errno = err;
454     return -1;
455 }
456 
dill_ipc_accept_mem(int s,struct dill_ipc_storage * mem,int64_t deadline)457 int dill_ipc_accept_mem(int s, struct dill_ipc_storage *mem, int64_t deadline) {
458     int err;
459     if(dill_slow(!mem)) {err = EINVAL; goto error1;}
460     /* Retrieve the listener object. */
461     struct dill_ipc_listener *lst = dill_hquery(s, dill_ipc_listener_type);
462     if(dill_slow(!lst)) {err = errno; goto error1;}
463     /* Try to get new connection in a non-blocking way. */
464     int as = dill_fd_accept(lst->fd, NULL, NULL, deadline);
465     if(dill_slow(as < 0)) {err = errno; goto error1;}
466     /* Set it to non-blocking mode. */
467     int rc = dill_fd_unblock(as);
468     if(dill_slow(rc < 0)) {err = errno; goto error2;}
469     /* Create the handle. */
470     int h = dill_ipc_makeconn(as, (struct dill_ipc_conn*)mem);
471     if(dill_slow(h < 0)) {err = errno; goto error2;}
472     return h;
473 error2:
474     dill_fd_close(as);
475 error1:
476     errno = err;
477     return -1;
478 }
479 
dill_ipc_accept(int s,int64_t deadline)480 int dill_ipc_accept(int s, int64_t deadline) {
481     int err;
482     struct dill_ipc_conn *obj = malloc(sizeof(struct dill_ipc_conn));
483     if(dill_slow(!obj)) {err = ENOMEM; goto error1;}
484     int as = dill_ipc_accept_mem(s, (struct dill_ipc_storage*)obj, deadline);
485     if(dill_slow(as < 0)) {err = errno; goto error2;}
486     obj->mem = 0;
487     return as;
488 error2:
489     free(obj);
490 error1:
491     errno = err;
492     return -1;
493 }
494 
dill_ipc_listener_hclose(struct dill_hvfs * hvfs)495 static void dill_ipc_listener_hclose(struct dill_hvfs *hvfs) {
496     struct dill_ipc_listener *self = (struct dill_ipc_listener*)hvfs;
497     dill_fd_close(self->fd);
498     if(!self->mem) free(self);
499 }
500 
501 /******************************************************************************/
502 /*  UNIX pair                                                                 */
503 /******************************************************************************/
504 
dill_ipc_pair_mem(struct dill_ipc_pair_storage * mem,int s[2])505 int dill_ipc_pair_mem(struct dill_ipc_pair_storage *mem, int s[2]) {
506     int err;
507     if(dill_slow(!mem)) {err = EINVAL; goto error1;}
508     /* Create the pair. */
509     int fds[2];
510     int rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
511     if(rc < 0) {err = errno; goto error1;}
512     /* Set the sockets to non-blocking mode. */
513     rc = dill_fd_unblock(fds[0]);
514     if(dill_slow(rc < 0)) {err = errno; goto error3;}
515     rc = dill_fd_unblock(fds[1]);
516     if(dill_slow(rc < 0)) {err = errno; goto error3;}
517     /* Create the handles. */
518     struct dill_ipc_conn *conns = (struct dill_ipc_conn*)mem;
519     s[0] = dill_ipc_makeconn(fds[0], &conns[0]);
520     if(dill_slow(s[0] < 0)) {err = errno; goto error3;}
521     s[1] = dill_ipc_makeconn(fds[1], &conns[1]);
522     if(dill_slow(s[1] < 0)) {err = errno; goto error4;}
523     return 0;
524 error4:
525     rc = dill_hclose(s[0]);
526     goto error2;
527 error3:
528     dill_fd_close(fds[0]);
529 error2:
530     dill_fd_close(fds[1]);
531 error1:
532     errno = err;
533     return -1;
534 }
535 
dill_ipc_pair(int s[2])536 int dill_ipc_pair(int s[2]) {
537     int err;
538     /* Create the pair. */
539     int fds[2];
540     int rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
541     if(rc < 0) {err = errno; goto error1;}
542     /* Set the sockets to non-blocking mode. */
543     rc = dill_fd_unblock(fds[0]);
544     if(dill_slow(rc < 0)) {err = errno; goto error3;}
545     rc = dill_fd_unblock(fds[1]);
546     if(dill_slow(rc < 0)) {err = errno; goto error3;}
547     /* Allocate the memory. */
548     struct dill_ipc_conn *conn0 = malloc(sizeof(struct dill_ipc_conn));
549     if(dill_slow(!conn0)) {err = ENOMEM; goto error3;}
550     struct dill_ipc_conn *conn1 = malloc(sizeof(struct dill_ipc_conn));
551     if(dill_slow(!conn1)) {err = ENOMEM; goto error4;}
552     /* Create the handles. */
553     s[0] = dill_ipc_makeconn(fds[0], conn0);
554     if(dill_slow(s[0] < 0)) {err = errno; goto error5;}
555     conn0->mem = 0;
556     s[1] = dill_ipc_makeconn(fds[1], conn1);
557     if(dill_slow(s[1] < 0)) {err = errno; goto error6;}
558     conn1->mem = 0;
559     return 0;
560 error6:
561     rc = dill_hclose(s[0]);
562     goto error2;
563 error5:
564     free(conn1);
565 error4:
566     free(conn0);
567 error3:
568     dill_fd_close(fds[0]);
569 error2:
570     dill_fd_close(fds[1]);
571 error1:
572     errno = err;
573     return -1;
574 }
575 
576 /******************************************************************************/
577 /*  Helpers                                                                   */
578 /******************************************************************************/
579 
dill_ipc_resolve(const char * addr,struct sockaddr_un * su)580 static int dill_ipc_resolve(const char *addr, struct sockaddr_un *su) {
581     dill_assert(su);
582     if(strlen(addr) >= sizeof(su->sun_path)) {errno = ENAMETOOLONG; return -1;}
583     su->sun_family = AF_UNIX;
584     strncpy(su->sun_path, addr, sizeof(su->sun_path));
585     return 0;
586 }
587 
588