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