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 }