Home | History | Annotate | Line # | Download | only in compat
imsg.c revision 1.7
      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 (at) 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