1 /* ISC license. */
2 
3 #include <skalibs/nonposix.h>
4 
5 #include <sys/socket.h>
6 #include <sys/uio.h>
7 #include <string.h>
8 #include <stdint.h>
9 #include <unistd.h>
10 #include <errno.h>
11 
12 #include <skalibs/uint16.h>
13 #include <skalibs/uint32.h>
14 #include <skalibs/disize.h>
15 #include <skalibs/allreadwrite.h>
16 #include <skalibs/genalloc.h>
17 #include <skalibs/djbunix.h>
18 #include <skalibs/unixmessage.h>
19 #include <skalibs/posixishard.h>
20 
21 union aligner_u
22 {
23   struct cmsghdr cmsghdr ;
24   int i ;
25 } ;
26 
27 
28  /*
29     XXX: sendmsg/recvmsg is badly, badly specified.
30     XXX: We assume ancillary data is attached to the first byte.
31  */
32 
unixmessage_sender_flush(unixmessage_sender_t * b)33 int unixmessage_sender_flush (unixmessage_sender_t *b)
34 {
35   disize last = { .left = b->data.len, .right = genalloc_len(int, &b->fds) } ;
36   disize *offsets = genalloc_s(disize, &b->offsets) ;
37   size_t n = genalloc_len(disize, &b->offsets) ;
38   ssize_t r ;
39 
40   if (b->shorty) /* we had a short write, gotta send the remainder first */
41   {
42     disize *next = b->head+1 < n ? offsets + b->head+1 : &last ;
43     size_t len = next->left - offsets[b->head].left ;
44     if (b->shorty <= len)
45       r = fd_write(b->fd, b->data.s + offsets[b->head].left + (len - b->shorty), b->shorty) ;
46     else
47     {
48       size_t nfds = next->right - offsets[b->head].right ;
49       char pack[6] ;
50       struct iovec v[2] =
51       {
52         { .iov_base = pack + 6 - (b->shorty - len), .iov_len = b->shorty - len },
53         { .iov_base = b->data.s + offsets[b->head].left, .iov_len = len }
54       } ;
55       uint32_pack_big(pack, (uint32_t)len) ;
56       uint16_pack_big(pack + 4, (uint16_t)nfds) ;
57       r = fd_writev(b->fd, v, 2) ;
58     }
59     if (r <= 0) return 0 ;
60     b->shorty -= r ;
61     if (b->shorty) return (errno = EWOULDBLOCK, 0) ;
62   }
63 
64   for (; b->head < n ; b->head++)
65   {
66     disize *next = b->head+1 < n ? offsets + b->head+1 : &last ;
67     size_t len = next->left - offsets[b->head].left ;
68     size_t nfds = next->right - offsets[b->head].right ;
69     char pack[6] ;
70     struct iovec v[2] =
71     {
72       { .iov_base = pack, .iov_len = 6 },
73       { .iov_base = b->data.s + offsets[b->head].left, .iov_len = len }
74     } ;
75     union aligner_u ancilbuf[1 + CMSG_SPACE(nfds * sizeof(int)) / sizeof(union aligner_u)] ;
76     struct msghdr hdr =
77     {
78       .msg_name = 0,
79       .msg_namelen = 0,
80       .msg_iov = v,
81       .msg_iovlen = 2,
82       .msg_control = nfds ? ancilbuf : 0,
83       .msg_controllen = nfds ? CMSG_SPACE(nfds * sizeof(int)) : 0
84     } ;
85     uint32_pack_big(pack, (uint32_t)len) ;
86     uint16_pack_big(pack + 4, (uint16_t)nfds) ;
87     if (nfds)
88     {
89       struct cmsghdr *cp = CMSG_FIRSTHDR(&hdr) ;
90       size_t i = 0 ;
91       memset(hdr.msg_control, 0, hdr.msg_controllen) ;
92       cp->cmsg_level = SOL_SOCKET ;
93       cp->cmsg_type = SCM_RIGHTS ;
94       cp->cmsg_len = CMSG_LEN(nfds * sizeof(int)) ;
95       for (; i < nfds ; i++)
96       {
97         int fd = genalloc_s(int, &b->fds)[offsets[b->head].right + i] ;
98         ((int *)CMSG_DATA(cp))[i] = fd < 0 ? -(fd+1) : fd ;
99       }
100     }
101     do r = sendmsg(b->fd, &hdr, MSG_NOSIGNAL) ;
102     while (r < 0 && errno == EINTR) ;
103     if (r <= 0) return 0 ;
104     if (nfds)
105     {
106       size_t i = 0 ;
107       for (; i < nfds ; i++)
108       {
109         int fd = genalloc_s(int, &b->fds)[offsets[b->head].right + i] ;
110         if (fd < 0) (*b->closecb)(-(fd+1), b->closecbdata) ;
111       }
112     }
113     if ((size_t)r < 6 + len)
114     {
115       b->shorty = 6 + len - r ;
116       return (errno = EWOULDBLOCK, 0) ;
117     }
118   }
119   b->data.len = 0 ;
120   genalloc_setlen(int, &b->fds, 0) ;
121   genalloc_setlen(disize, &b->offsets, 0) ;
122   b->head = 0 ;
123   return 1 ;
124 }
125