1 /* $OpenBSD: io.c,v 1.13 2021/03/04 13:01:41 claudio Exp $ */ 2 /* 3 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/queue.h> 19 #include <sys/socket.h> 20 21 #include <assert.h> 22 #include <err.h> 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <stdint.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 #include <imsg.h> 30 31 #include "extern.h" 32 33 void 34 io_socket_blocking(int fd) 35 { 36 int fl; 37 38 if ((fl = fcntl(fd, F_GETFL, 0)) == -1) 39 err(1, "fcntl"); 40 if (fcntl(fd, F_SETFL, fl & ~O_NONBLOCK) == -1) 41 err(1, "fcntl"); 42 } 43 44 void 45 io_socket_nonblocking(int fd) 46 { 47 int fl; 48 49 if ((fl = fcntl(fd, F_GETFL, 0)) == -1) 50 err(1, "fcntl"); 51 if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) == -1) 52 err(1, "fcntl"); 53 } 54 55 /* 56 * Like io_simple_write() but into a buffer. 57 */ 58 void 59 io_simple_buffer(struct ibuf *b, const void *res, size_t sz) 60 { 61 if (ibuf_add(b, res, sz) == -1) 62 err(1, NULL); 63 } 64 65 /* 66 * Add a sz sized buffer into the io buffer. 67 */ 68 void 69 io_buf_buffer(struct ibuf *b, const void *p, size_t sz) 70 { 71 if (ibuf_add(b, &sz, sizeof(size_t)) == -1) 72 err(1, NULL); 73 if (sz > 0) 74 if (ibuf_add(b, p, sz) == -1) 75 err(1, NULL); 76 } 77 78 /* 79 * Add a string into the io buffer. 80 */ 81 void 82 io_str_buffer(struct ibuf *b, const char *p) 83 { 84 size_t sz = (p == NULL) ? 0 : strlen(p); 85 86 io_buf_buffer(b, p, sz); 87 } 88 89 /* 90 * Read of a binary buffer that must be on a blocking descriptor. 91 * Does nothing if "sz" is zero. 92 * This will fail and exit on EOF. 93 */ 94 void 95 io_simple_read(int fd, void *res, size_t sz) 96 { 97 ssize_t ssz; 98 char *tmp; 99 100 tmp = res; /* arithmetic on a pointer to void is a GNU extension */ 101 again: 102 if (sz == 0) 103 return; 104 if ((ssz = read(fd, tmp, sz)) == -1) 105 err(1, "read"); 106 else if (ssz == 0) 107 errx(1, "read: unexpected end of file"); 108 else if ((size_t)ssz == sz) 109 return; 110 sz -= ssz; 111 tmp += ssz; 112 goto again; 113 } 114 115 /* 116 * Read a binary buffer, allocating space for it. 117 * If the buffer is zero-sized, this won't allocate "res", but 118 * will still initialise it to NULL. 119 */ 120 void 121 io_buf_read_alloc(int fd, void **res, size_t *sz) 122 { 123 124 *res = NULL; 125 io_simple_read(fd, sz, sizeof(size_t)); 126 if (*sz == 0) 127 return; 128 if ((*res = malloc(*sz)) == NULL) 129 err(1, NULL); 130 io_simple_read(fd, *res, *sz); 131 } 132 133 /* 134 * Read a string (returns NULL for zero-length strings), allocating 135 * space for it. 136 */ 137 void 138 io_str_read(int fd, char **res) 139 { 140 size_t sz; 141 142 io_simple_read(fd, &sz, sizeof(size_t)); 143 if (sz == 0) { 144 *res = NULL; 145 return; 146 } 147 if ((*res = calloc(sz + 1, 1)) == NULL) 148 err(1, NULL); 149 io_simple_read(fd, *res, sz); 150 } 151 152 /* 153 * Read data from socket but receive a file descriptor at the same time. 154 */ 155 int 156 io_recvfd(int fd, void *res, size_t sz) 157 { 158 struct iovec iov; 159 struct msghdr msg; 160 struct cmsghdr *cmsg; 161 union { 162 struct cmsghdr hdr; 163 char buf[CMSG_SPACE(sizeof(int))]; 164 } cmsgbuf; 165 int outfd = -1; 166 char *b = res; 167 ssize_t n; 168 169 memset(&msg, 0, sizeof(msg)); 170 memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 171 172 iov.iov_base = res; 173 iov.iov_len = sz; 174 175 msg.msg_iov = &iov; 176 msg.msg_iovlen = 1; 177 msg.msg_control = &cmsgbuf.buf; 178 msg.msg_controllen = sizeof(cmsgbuf.buf); 179 180 while ((n = recvmsg(fd, &msg, 0)) == -1) { 181 if (errno == EINTR) 182 continue; 183 err(1, "recvmsg"); 184 } 185 186 if (n == 0) 187 errx(1, "recvmsg: unexpected end of file"); 188 189 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 190 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 191 if (cmsg->cmsg_level == SOL_SOCKET && 192 cmsg->cmsg_type == SCM_RIGHTS) { 193 int i, j, f; 194 195 j = ((char *)cmsg + cmsg->cmsg_len - 196 (char *)CMSG_DATA(cmsg)) / sizeof(int); 197 for (i = 0; i < j; i++) { 198 f = ((int *)CMSG_DATA(cmsg))[i]; 199 if (i == 0) 200 outfd = f; 201 else 202 close(f); 203 } 204 } 205 } 206 207 b += n; 208 sz -= n; 209 while (sz > 0) { 210 /* short receive */ 211 n = recv(fd, b, sz, 0); 212 if (n == -1) { 213 if (errno == EINTR) 214 continue; 215 err(1, "recv"); 216 } 217 if (n == 0) 218 errx(1, "recv: unexpected end of file"); 219 220 b += n; 221 sz -= n; 222 } 223 224 return outfd; 225 } 226