1 /* $OpenBSD: imsg.c,v 1.9 2015/07/12 18:40:49 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 #include <sys/uio.h> 22 23 #include <errno.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "tmux.h" 29 #include "imsg.h" 30 31 int imsg_fd_overhead = 0; 32 33 int imsg_get_fd(struct imsgbuf *); 34 35 int available_fds(unsigned int); 36 37 /* 38 * The original code calls getdtablecount() which is OpenBSD specific. Use 39 * available_fds() from OpenSMTPD instead. 40 */ 41 int 42 available_fds(unsigned int n) 43 { 44 unsigned int i; 45 int ret, fds[256]; 46 47 if (n > (sizeof(fds)/sizeof(fds[0]))) 48 return (1); 49 50 ret = 0; 51 for (i = 0; i < n; i++) { 52 fds[i] = -1; 53 if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 54 if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) 55 fds[i] = socket(AF_INET6, SOCK_DGRAM, 0); 56 if (fds[i] < 0) { 57 ret = 1; 58 break; 59 } 60 } 61 } 62 63 for (i = 0; i < n && fds[i] >= 0; i++) 64 close(fds[i]); 65 66 return (ret); 67 } 68 69 void 70 imsg_init(struct imsgbuf *ibuf, int fd) 71 { 72 msgbuf_init(&ibuf->w); 73 memset(&ibuf->r, 0, sizeof(ibuf->r)); 74 ibuf->fd = fd; 75 ibuf->w.fd = fd; 76 ibuf->pid = getpid(); 77 TAILQ_INIT(&ibuf->fds); 78 } 79 80 ssize_t 81 imsg_read(struct imsgbuf *ibuf) 82 { 83 struct msghdr msg; 84 struct cmsghdr *cmsg; 85 union { 86 struct cmsghdr hdr; 87 char buf[CMSG_SPACE(sizeof(int) * 1)]; 88 } cmsgbuf; 89 struct iovec iov; 90 ssize_t n = -1; 91 int fd; 92 struct imsg_fd *ifd; 93 94 memset(&msg, 0, sizeof(msg)); 95 memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 96 97 iov.iov_base = ibuf->r.buf + ibuf->r.wpos; 98 iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; 99 msg.msg_iov = &iov; 100 msg.msg_iovlen = 1; 101 msg.msg_control = &cmsgbuf.buf; 102 msg.msg_controllen = CMSG_SPACE(sizeof(int) * 16); 103 104 if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) 105 return (-1); 106 107 again: 108 if (available_fds(imsg_fd_overhead + 109 (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { 110 errno = EAGAIN; 111 free(ifd); 112 return (-1); 113 } 114 115 if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { 116 if (errno == EMSGSIZE) 117 goto fail; 118 if (errno != EINTR && errno != EAGAIN) 119 goto fail; 120 goto again; 121 } 122 123 ibuf->r.wpos += n; 124 125 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 126 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 127 if (cmsg->cmsg_level == SOL_SOCKET && 128 cmsg->cmsg_type == SCM_RIGHTS) { 129 int i; 130 int j; 131 132 /* 133 * We only accept one file descriptor. Due to C 134 * padding rules, our control buffer might contain 135 * more than one fd, and we must close them. 136 */ 137 j = ((char *)cmsg + cmsg->cmsg_len - 138 (char *)CMSG_DATA(cmsg)) / sizeof(int); 139 for (i = 0; i < j; i++) { 140 fd = ((int *)CMSG_DATA(cmsg))[i]; 141 if (ifd != NULL) { 142 ifd->fd = fd; 143 TAILQ_INSERT_TAIL(&ibuf->fds, ifd, 144 entry); 145 ifd = NULL; 146 } else 147 close(fd); 148 } 149 } 150 /* we do not handle other ctl data level */ 151 } 152 153 fail: 154 if (ifd) 155 free(ifd); 156 return (n); 157 } 158 159 ssize_t 160 imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) 161 { 162 size_t av, left, datalen; 163 164 av = ibuf->r.wpos; 165 166 if (IMSG_HEADER_SIZE > av) 167 return (0); 168 169 memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); 170 if (imsg->hdr.len < IMSG_HEADER_SIZE || 171 imsg->hdr.len > MAX_IMSGSIZE) { 172 errno = ERANGE; 173 return (-1); 174 } 175 if (imsg->hdr.len > av) 176 return (0); 177 datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 178 ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; 179 if ((imsg->data = malloc(datalen)) == NULL) 180 return (-1); 181 182 if (imsg->hdr.flags & IMSGF_HASFD) 183 imsg->fd = imsg_get_fd(ibuf); 184 else 185 imsg->fd = -1; 186 187 memcpy(imsg->data, ibuf->r.rptr, datalen); 188 189 if (imsg->hdr.len < av) { 190 left = av - imsg->hdr.len; 191 memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); 192 ibuf->r.wpos = left; 193 } else 194 ibuf->r.wpos = 0; 195 196 return (datalen + IMSG_HEADER_SIZE); 197 } 198 199 int 200 imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, 201 pid_t pid, int fd, const void *data, u_int16_t datalen) 202 { 203 struct ibuf *wbuf; 204 205 if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) 206 return (-1); 207 208 if (imsg_add(wbuf, data, datalen) == -1) 209 return (-1); 210 211 wbuf->fd = fd; 212 213 imsg_close(ibuf, wbuf); 214 215 return (1); 216 } 217 218 int 219 imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, 220 pid_t pid, int fd, const struct iovec *iov, int iovcnt) 221 { 222 struct ibuf *wbuf; 223 int i, datalen = 0; 224 225 for (i = 0; i < iovcnt; i++) 226 datalen += iov[i].iov_len; 227 228 if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) 229 return (-1); 230 231 for (i = 0; i < iovcnt; i++) 232 if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) 233 return (-1); 234 235 wbuf->fd = fd; 236 237 imsg_close(ibuf, wbuf); 238 239 return (1); 240 } 241 242 /* ARGSUSED */ 243 struct ibuf * 244 imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, 245 pid_t pid, u_int16_t datalen) 246 { 247 struct ibuf *wbuf; 248 struct imsg_hdr hdr; 249 250 datalen += IMSG_HEADER_SIZE; 251 if (datalen > MAX_IMSGSIZE) { 252 errno = ERANGE; 253 return (NULL); 254 } 255 256 hdr.type = type; 257 hdr.flags = 0; 258 hdr.peerid = peerid; 259 if ((hdr.pid = pid) == 0) 260 hdr.pid = ibuf->pid; 261 if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { 262 return (NULL); 263 } 264 if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) 265 return (NULL); 266 267 return (wbuf); 268 } 269 270 int 271 imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen) 272 { 273 if (datalen) 274 if (ibuf_add(msg, data, datalen) == -1) { 275 ibuf_free(msg); 276 return (-1); 277 } 278 return (datalen); 279 } 280 281 void 282 imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) 283 { 284 struct imsg_hdr *hdr; 285 286 hdr = (struct imsg_hdr *)msg->buf; 287 288 hdr->flags &= ~IMSGF_HASFD; 289 if (msg->fd != -1) 290 hdr->flags |= IMSGF_HASFD; 291 292 hdr->len = (u_int16_t)msg->wpos; 293 294 ibuf_close(&ibuf->w, msg); 295 } 296 297 void 298 imsg_free(struct imsg *imsg) 299 { 300 free(imsg->data); 301 } 302 303 int 304 imsg_get_fd(struct imsgbuf *ibuf) 305 { 306 int fd; 307 struct imsg_fd *ifd; 308 309 if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) 310 return (-1); 311 312 fd = ifd->fd; 313 TAILQ_REMOVE(&ibuf->fds, ifd, entry); 314 free(ifd); 315 316 return (fd); 317 } 318 319 int 320 imsg_flush(struct imsgbuf *ibuf) 321 { 322 while (ibuf->w.queued) 323 if (msgbuf_write(&ibuf->w) <= 0) 324 return (-1); 325 return (0); 326 } 327 328 void 329 imsg_clear(struct imsgbuf *ibuf) 330 { 331 int fd; 332 333 msgbuf_clear(&ibuf->w); 334 while ((fd = imsg_get_fd(ibuf)) != -1) 335 close(fd); 336 } 337