1 /* $OpenBSD: imsg.c,v 1.10 2015/07/19 07:18:59 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 * $FreeBSD$ 19 */ 20 21 #include <sys/types.h> 22 #include <sys/queue.h> 23 #include <sys/socket.h> 24 #include <sys/uio.h> 25 26 #include <errno.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "imsg.h" 32 33 int imsg_fd_overhead = 0; 34 35 int imsg_get_fd(struct imsgbuf *); 36 37 void 38 imsg_init(struct imsgbuf *ibuf, int fd) 39 { 40 msgbuf_init(&ibuf->w); 41 memset(&ibuf->r, 0, sizeof(ibuf->r)); 42 ibuf->fd = fd; 43 ibuf->w.fd = fd; 44 ibuf->pid = getpid(); 45 TAILQ_INIT(&ibuf->fds); 46 } 47 48 ssize_t 49 imsg_read(struct imsgbuf *ibuf) 50 { 51 struct msghdr msg; 52 struct cmsghdr *cmsg; 53 union { 54 struct cmsghdr hdr; 55 char buf[CMSG_SPACE(sizeof(int) * 1)]; 56 } cmsgbuf; 57 struct iovec iov; 58 ssize_t n = -1; 59 int fd; 60 struct imsg_fd *ifd; 61 62 memset(&msg, 0, sizeof(msg)); 63 memset(&cmsgbuf, 0, sizeof(cmsgbuf)); 64 65 iov.iov_base = ibuf->r.buf + ibuf->r.wpos; 66 iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; 67 msg.msg_iov = &iov; 68 msg.msg_iovlen = 1; 69 msg.msg_control = &cmsgbuf.buf; 70 msg.msg_controllen = sizeof(cmsgbuf.buf); 71 72 if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) 73 return (-1); 74 75 again: 76 if (getdtablecount() + imsg_fd_overhead + 77 (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int) 78 >= getdtablesize()) { 79 errno = EAGAIN; 80 free(ifd); 81 return (-1); 82 } 83 84 if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { 85 if (errno == EMSGSIZE) 86 goto fail; 87 if (errno != EINTR && errno != EAGAIN) 88 goto fail; 89 goto again; 90 } 91 92 ibuf->r.wpos += n; 93 94 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; 95 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 96 if (cmsg->cmsg_level == SOL_SOCKET && 97 cmsg->cmsg_type == SCM_RIGHTS) { 98 int i; 99 int j; 100 101 /* 102 * We only accept one file descriptor. Due to C 103 * padding rules, our control buffer might contain 104 * more than one fd, and we must close them. 105 */ 106 j = ((char *)cmsg + cmsg->cmsg_len - 107 (char *)CMSG_DATA(cmsg)) / sizeof(int); 108 for (i = 0; i < j; i++) { 109 fd = ((int *)CMSG_DATA(cmsg))[i]; 110 if (ifd != NULL) { 111 ifd->fd = fd; 112 TAILQ_INSERT_TAIL(&ibuf->fds, ifd, 113 entry); 114 ifd = NULL; 115 } else 116 close(fd); 117 } 118 } 119 /* we do not handle other ctl data level */ 120 } 121 122 fail: 123 if (ifd) 124 free(ifd); 125 return (n); 126 } 127 128 ssize_t 129 imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) 130 { 131 size_t av, left, datalen; 132 133 av = ibuf->r.wpos; 134 135 if (IMSG_HEADER_SIZE > av) 136 return (0); 137 138 memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); 139 if (imsg->hdr.len < IMSG_HEADER_SIZE || 140 imsg->hdr.len > MAX_IMSGSIZE) { 141 errno = ERANGE; 142 return (-1); 143 } 144 if (imsg->hdr.len > av) 145 return (0); 146 datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 147 ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; 148 if (datalen == 0) 149 imsg->data = NULL; 150 else if ((imsg->data = malloc(datalen)) == NULL) 151 return (-1); 152 153 if (imsg->hdr.flags & IMSGF_HASFD) 154 imsg->fd = imsg_get_fd(ibuf); 155 else 156 imsg->fd = -1; 157 158 memcpy(imsg->data, ibuf->r.rptr, datalen); 159 160 if (imsg->hdr.len < av) { 161 left = av - imsg->hdr.len; 162 memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); 163 ibuf->r.wpos = left; 164 } else 165 ibuf->r.wpos = 0; 166 167 return (datalen + IMSG_HEADER_SIZE); 168 } 169 170 int 171 imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, 172 pid_t pid, int fd, const void *data, u_int16_t datalen) 173 { 174 struct ibuf *wbuf; 175 176 if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) 177 return (-1); 178 179 if (imsg_add(wbuf, data, datalen) == -1) 180 return (-1); 181 182 wbuf->fd = fd; 183 184 imsg_close(ibuf, wbuf); 185 186 return (1); 187 } 188 189 int 190 imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, 191 pid_t pid, int fd, const struct iovec *iov, int iovcnt) 192 { 193 struct ibuf *wbuf; 194 int i, datalen = 0; 195 196 for (i = 0; i < iovcnt; i++) 197 datalen += iov[i].iov_len; 198 199 if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) 200 return (-1); 201 202 for (i = 0; i < iovcnt; i++) 203 if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) 204 return (-1); 205 206 wbuf->fd = fd; 207 208 imsg_close(ibuf, wbuf); 209 210 return (1); 211 } 212 213 /* ARGSUSED */ 214 struct ibuf * 215 imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, 216 pid_t pid, u_int16_t datalen) 217 { 218 struct ibuf *wbuf; 219 struct imsg_hdr hdr; 220 221 datalen += IMSG_HEADER_SIZE; 222 if (datalen > MAX_IMSGSIZE) { 223 errno = ERANGE; 224 return (NULL); 225 } 226 227 hdr.type = type; 228 hdr.flags = 0; 229 hdr.peerid = peerid; 230 if ((hdr.pid = pid) == 0) 231 hdr.pid = ibuf->pid; 232 if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { 233 return (NULL); 234 } 235 if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) 236 return (NULL); 237 238 return (wbuf); 239 } 240 241 int 242 imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen) 243 { 244 if (datalen) 245 if (ibuf_add(msg, data, datalen) == -1) { 246 ibuf_free(msg); 247 return (-1); 248 } 249 return (datalen); 250 } 251 252 void 253 imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) 254 { 255 struct imsg_hdr *hdr; 256 257 hdr = (struct imsg_hdr *)msg->buf; 258 259 hdr->flags &= ~IMSGF_HASFD; 260 if (msg->fd != -1) 261 hdr->flags |= IMSGF_HASFD; 262 263 hdr->len = (u_int16_t)msg->wpos; 264 265 ibuf_close(&ibuf->w, msg); 266 } 267 268 void 269 imsg_free(struct imsg *imsg) 270 { 271 free(imsg->data); 272 } 273 274 int 275 imsg_get_fd(struct imsgbuf *ibuf) 276 { 277 int fd; 278 struct imsg_fd *ifd; 279 280 if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) 281 return (-1); 282 283 fd = ifd->fd; 284 TAILQ_REMOVE(&ibuf->fds, ifd, entry); 285 free(ifd); 286 287 return (fd); 288 } 289 290 int 291 imsg_flush(struct imsgbuf *ibuf) 292 { 293 while (ibuf->w.queued) 294 if (msgbuf_write(&ibuf->w) <= 0) 295 return (-1); 296 return (0); 297 } 298 299 void 300 imsg_clear(struct imsgbuf *ibuf) 301 { 302 int fd; 303 304 msgbuf_clear(&ibuf->w); 305 while ((fd = imsg_get_fd(ibuf)) != -1) 306 close(fd); 307 } 308