skalibs

Mirror/fork of https://skarnet.org/software/skalibs/
git clone https://ccx.te2000.cz/git/skalibs
Log | Files | Refs | README | LICENSE

unixmessage_sender_flush.c (3556B)


      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 
     33 int unixmessage_sender_flush (unixmessage_sender *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 }