xref: /freebsd/lib/libopenbsd/imsg.c (revision b3e76948)
17e5ac32bSCraig Rodrigues /*	$OpenBSD: imsg.c,v 1.13 2015/12/09 11:54:12 tb Exp $	*/
270623c80SCraig Rodrigues 
370623c80SCraig Rodrigues /*
470623c80SCraig Rodrigues  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
570623c80SCraig Rodrigues  *
670623c80SCraig Rodrigues  * Permission to use, copy, modify, and distribute this software for any
770623c80SCraig Rodrigues  * purpose with or without fee is hereby granted, provided that the above
870623c80SCraig Rodrigues  * copyright notice and this permission notice appear in all copies.
970623c80SCraig Rodrigues  *
1070623c80SCraig Rodrigues  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1170623c80SCraig Rodrigues  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1270623c80SCraig Rodrigues  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1370623c80SCraig Rodrigues  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1470623c80SCraig Rodrigues  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1570623c80SCraig Rodrigues  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1670623c80SCraig Rodrigues  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1770623c80SCraig Rodrigues  */
1870623c80SCraig Rodrigues 
1970623c80SCraig Rodrigues #include <sys/types.h>
2070623c80SCraig Rodrigues #include <sys/queue.h>
2170623c80SCraig Rodrigues #include <sys/socket.h>
2270623c80SCraig Rodrigues #include <sys/uio.h>
2370623c80SCraig Rodrigues 
2470623c80SCraig Rodrigues #include <errno.h>
2570623c80SCraig Rodrigues #include <stdlib.h>
2670623c80SCraig Rodrigues #include <string.h>
2770623c80SCraig Rodrigues #include <unistd.h>
2870623c80SCraig Rodrigues 
2970623c80SCraig Rodrigues #include "imsg.h"
3070623c80SCraig Rodrigues 
3170623c80SCraig Rodrigues int	 imsg_fd_overhead = 0;
3270623c80SCraig Rodrigues 
3370623c80SCraig Rodrigues int	 imsg_get_fd(struct imsgbuf *);
3470623c80SCraig Rodrigues 
3570623c80SCraig Rodrigues void
imsg_init(struct imsgbuf * ibuf,int fd)3670623c80SCraig Rodrigues imsg_init(struct imsgbuf *ibuf, int fd)
3770623c80SCraig Rodrigues {
3870623c80SCraig Rodrigues 	msgbuf_init(&ibuf->w);
3970623c80SCraig Rodrigues 	memset(&ibuf->r, 0, sizeof(ibuf->r));
4070623c80SCraig Rodrigues 	ibuf->fd = fd;
4170623c80SCraig Rodrigues 	ibuf->w.fd = fd;
4270623c80SCraig Rodrigues 	ibuf->pid = getpid();
4370623c80SCraig Rodrigues 	TAILQ_INIT(&ibuf->fds);
4470623c80SCraig Rodrigues }
4570623c80SCraig Rodrigues 
4670623c80SCraig Rodrigues ssize_t
imsg_read(struct imsgbuf * ibuf)4770623c80SCraig Rodrigues imsg_read(struct imsgbuf *ibuf)
4870623c80SCraig Rodrigues {
4970623c80SCraig Rodrigues 	struct msghdr		 msg;
5070623c80SCraig Rodrigues 	struct cmsghdr		*cmsg;
5170623c80SCraig Rodrigues 	union {
5270623c80SCraig Rodrigues 		struct cmsghdr hdr;
5370623c80SCraig Rodrigues 		char	buf[CMSG_SPACE(sizeof(int) * 1)];
5470623c80SCraig Rodrigues 	} cmsgbuf;
5570623c80SCraig Rodrigues 	struct iovec		 iov;
5670623c80SCraig Rodrigues 	ssize_t			 n = -1;
5770623c80SCraig Rodrigues 	int			 fd;
5870623c80SCraig Rodrigues 	struct imsg_fd		*ifd;
5970623c80SCraig Rodrigues 
6070623c80SCraig Rodrigues 	memset(&msg, 0, sizeof(msg));
6170623c80SCraig Rodrigues 	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
6270623c80SCraig Rodrigues 
6370623c80SCraig Rodrigues 	iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
6470623c80SCraig Rodrigues 	iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
6570623c80SCraig Rodrigues 	msg.msg_iov = &iov;
6670623c80SCraig Rodrigues 	msg.msg_iovlen = 1;
6770623c80SCraig Rodrigues 	msg.msg_control = &cmsgbuf.buf;
6870623c80SCraig Rodrigues 	msg.msg_controllen = sizeof(cmsgbuf.buf);
6970623c80SCraig Rodrigues 
7070623c80SCraig Rodrigues 	if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
7170623c80SCraig Rodrigues 		return (-1);
7270623c80SCraig Rodrigues 
7370623c80SCraig Rodrigues again:
7470623c80SCraig Rodrigues 	if (getdtablecount() + imsg_fd_overhead +
757e5ac32bSCraig Rodrigues 	    (int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
7670623c80SCraig Rodrigues 	    >= getdtablesize()) {
7770623c80SCraig Rodrigues 		errno = EAGAIN;
7870623c80SCraig Rodrigues 		free(ifd);
7970623c80SCraig Rodrigues 		return (-1);
8070623c80SCraig Rodrigues 	}
8170623c80SCraig Rodrigues 
8270623c80SCraig Rodrigues 	if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
83fbb0e1b5SCraig Rodrigues 		if (errno == EINTR)
8470623c80SCraig Rodrigues 			goto again;
85fbb0e1b5SCraig Rodrigues 		goto fail;
8670623c80SCraig Rodrigues 	}
8770623c80SCraig Rodrigues 
8870623c80SCraig Rodrigues 	ibuf->r.wpos += n;
8970623c80SCraig Rodrigues 
9070623c80SCraig Rodrigues 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
9170623c80SCraig Rodrigues 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
9270623c80SCraig Rodrigues 		if (cmsg->cmsg_level == SOL_SOCKET &&
9370623c80SCraig Rodrigues 		    cmsg->cmsg_type == SCM_RIGHTS) {
9470623c80SCraig Rodrigues 			int i;
9570623c80SCraig Rodrigues 			int j;
9670623c80SCraig Rodrigues 
9770623c80SCraig Rodrigues 			/*
9870623c80SCraig Rodrigues 			 * We only accept one file descriptor.  Due to C
9970623c80SCraig Rodrigues 			 * padding rules, our control buffer might contain
10070623c80SCraig Rodrigues 			 * more than one fd, and we must close them.
10170623c80SCraig Rodrigues 			 */
10270623c80SCraig Rodrigues 			j = ((char *)cmsg + cmsg->cmsg_len -
10370623c80SCraig Rodrigues 			    (char *)CMSG_DATA(cmsg)) / sizeof(int);
10470623c80SCraig Rodrigues 			for (i = 0; i < j; i++) {
10570623c80SCraig Rodrigues 				fd = ((int *)CMSG_DATA(cmsg))[i];
10670623c80SCraig Rodrigues 				if (ifd != NULL) {
10770623c80SCraig Rodrigues 					ifd->fd = fd;
10870623c80SCraig Rodrigues 					TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
10970623c80SCraig Rodrigues 					    entry);
11070623c80SCraig Rodrigues 					ifd = NULL;
11170623c80SCraig Rodrigues 				} else
11270623c80SCraig Rodrigues 					close(fd);
11370623c80SCraig Rodrigues 			}
11470623c80SCraig Rodrigues 		}
11570623c80SCraig Rodrigues 		/* we do not handle other ctl data level */
11670623c80SCraig Rodrigues 	}
11770623c80SCraig Rodrigues 
11870623c80SCraig Rodrigues fail:
11970623c80SCraig Rodrigues 	free(ifd);
12070623c80SCraig Rodrigues 	return (n);
12170623c80SCraig Rodrigues }
12270623c80SCraig Rodrigues 
12370623c80SCraig Rodrigues ssize_t
imsg_get(struct imsgbuf * ibuf,struct imsg * imsg)12470623c80SCraig Rodrigues imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
12570623c80SCraig Rodrigues {
12670623c80SCraig Rodrigues 	size_t			 av, left, datalen;
12770623c80SCraig Rodrigues 
12870623c80SCraig Rodrigues 	av = ibuf->r.wpos;
12970623c80SCraig Rodrigues 
13070623c80SCraig Rodrigues 	if (IMSG_HEADER_SIZE > av)
13170623c80SCraig Rodrigues 		return (0);
13270623c80SCraig Rodrigues 
13370623c80SCraig Rodrigues 	memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
13470623c80SCraig Rodrigues 	if (imsg->hdr.len < IMSG_HEADER_SIZE ||
13570623c80SCraig Rodrigues 	    imsg->hdr.len > MAX_IMSGSIZE) {
13670623c80SCraig Rodrigues 		errno = ERANGE;
13770623c80SCraig Rodrigues 		return (-1);
13870623c80SCraig Rodrigues 	}
13970623c80SCraig Rodrigues 	if (imsg->hdr.len > av)
14070623c80SCraig Rodrigues 		return (0);
14170623c80SCraig Rodrigues 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
14270623c80SCraig Rodrigues 	ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
14370623c80SCraig Rodrigues 	if (datalen == 0)
14470623c80SCraig Rodrigues 		imsg->data = NULL;
14570623c80SCraig Rodrigues 	else if ((imsg->data = malloc(datalen)) == NULL)
14670623c80SCraig Rodrigues 		return (-1);
14770623c80SCraig Rodrigues 
14870623c80SCraig Rodrigues 	if (imsg->hdr.flags & IMSGF_HASFD)
14970623c80SCraig Rodrigues 		imsg->fd = imsg_get_fd(ibuf);
15070623c80SCraig Rodrigues 	else
15170623c80SCraig Rodrigues 		imsg->fd = -1;
15270623c80SCraig Rodrigues 
15370623c80SCraig Rodrigues 	memcpy(imsg->data, ibuf->r.rptr, datalen);
15470623c80SCraig Rodrigues 
15570623c80SCraig Rodrigues 	if (imsg->hdr.len < av) {
15670623c80SCraig Rodrigues 		left = av - imsg->hdr.len;
15770623c80SCraig Rodrigues 		memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
15870623c80SCraig Rodrigues 		ibuf->r.wpos = left;
15970623c80SCraig Rodrigues 	} else
16070623c80SCraig Rodrigues 		ibuf->r.wpos = 0;
16170623c80SCraig Rodrigues 
16270623c80SCraig Rodrigues 	return (datalen + IMSG_HEADER_SIZE);
16370623c80SCraig Rodrigues }
16470623c80SCraig Rodrigues 
16570623c80SCraig Rodrigues int
imsg_compose(struct imsgbuf * ibuf,u_int32_t type,u_int32_t peerid,pid_t pid,int fd,const void * data,u_int16_t datalen)16670623c80SCraig Rodrigues imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
16770623c80SCraig Rodrigues     pid_t pid, int fd, const void *data, u_int16_t datalen)
16870623c80SCraig Rodrigues {
16970623c80SCraig Rodrigues 	struct ibuf	*wbuf;
17070623c80SCraig Rodrigues 
17170623c80SCraig Rodrigues 	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
17270623c80SCraig Rodrigues 		return (-1);
17370623c80SCraig Rodrigues 
17470623c80SCraig Rodrigues 	if (imsg_add(wbuf, data, datalen) == -1)
17570623c80SCraig Rodrigues 		return (-1);
17670623c80SCraig Rodrigues 
17770623c80SCraig Rodrigues 	wbuf->fd = fd;
17870623c80SCraig Rodrigues 
17970623c80SCraig Rodrigues 	imsg_close(ibuf, wbuf);
18070623c80SCraig Rodrigues 
18170623c80SCraig Rodrigues 	return (1);
18270623c80SCraig Rodrigues }
18370623c80SCraig Rodrigues 
18470623c80SCraig Rodrigues int
imsg_composev(struct imsgbuf * ibuf,u_int32_t type,u_int32_t peerid,pid_t pid,int fd,const struct iovec * iov,int iovcnt)18570623c80SCraig Rodrigues imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
18670623c80SCraig Rodrigues     pid_t pid, int fd, const struct iovec *iov, int iovcnt)
18770623c80SCraig Rodrigues {
18870623c80SCraig Rodrigues 	struct ibuf	*wbuf;
18970623c80SCraig Rodrigues 	int		 i, datalen = 0;
19070623c80SCraig Rodrigues 
19170623c80SCraig Rodrigues 	for (i = 0; i < iovcnt; i++)
19270623c80SCraig Rodrigues 		datalen += iov[i].iov_len;
19370623c80SCraig Rodrigues 
19470623c80SCraig Rodrigues 	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
19570623c80SCraig Rodrigues 		return (-1);
19670623c80SCraig Rodrigues 
19770623c80SCraig Rodrigues 	for (i = 0; i < iovcnt; i++)
19870623c80SCraig Rodrigues 		if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
19970623c80SCraig Rodrigues 			return (-1);
20070623c80SCraig Rodrigues 
20170623c80SCraig Rodrigues 	wbuf->fd = fd;
20270623c80SCraig Rodrigues 
20370623c80SCraig Rodrigues 	imsg_close(ibuf, wbuf);
20470623c80SCraig Rodrigues 
20570623c80SCraig Rodrigues 	return (1);
20670623c80SCraig Rodrigues }
20770623c80SCraig Rodrigues 
20870623c80SCraig Rodrigues /* ARGSUSED */
20970623c80SCraig Rodrigues struct ibuf *
imsg_create(struct imsgbuf * ibuf,u_int32_t type,u_int32_t peerid,pid_t pid,u_int16_t datalen)21070623c80SCraig Rodrigues imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
21170623c80SCraig Rodrigues     pid_t pid, u_int16_t datalen)
21270623c80SCraig Rodrigues {
21370623c80SCraig Rodrigues 	struct ibuf	*wbuf;
21470623c80SCraig Rodrigues 	struct imsg_hdr	 hdr;
21570623c80SCraig Rodrigues 
21670623c80SCraig Rodrigues 	datalen += IMSG_HEADER_SIZE;
21770623c80SCraig Rodrigues 	if (datalen > MAX_IMSGSIZE) {
21870623c80SCraig Rodrigues 		errno = ERANGE;
21970623c80SCraig Rodrigues 		return (NULL);
22070623c80SCraig Rodrigues 	}
22170623c80SCraig Rodrigues 
22270623c80SCraig Rodrigues 	hdr.type = type;
22370623c80SCraig Rodrigues 	hdr.flags = 0;
22470623c80SCraig Rodrigues 	hdr.peerid = peerid;
22570623c80SCraig Rodrigues 	if ((hdr.pid = pid) == 0)
22670623c80SCraig Rodrigues 		hdr.pid = ibuf->pid;
22770623c80SCraig Rodrigues 	if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
22870623c80SCraig Rodrigues 		return (NULL);
22970623c80SCraig Rodrigues 	}
23070623c80SCraig Rodrigues 	if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
23170623c80SCraig Rodrigues 		return (NULL);
23270623c80SCraig Rodrigues 
23370623c80SCraig Rodrigues 	return (wbuf);
23470623c80SCraig Rodrigues }
23570623c80SCraig Rodrigues 
23670623c80SCraig Rodrigues int
imsg_add(struct ibuf * msg,const void * data,u_int16_t datalen)23770623c80SCraig Rodrigues imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen)
23870623c80SCraig Rodrigues {
23970623c80SCraig Rodrigues 	if (datalen)
24070623c80SCraig Rodrigues 		if (ibuf_add(msg, data, datalen) == -1) {
24170623c80SCraig Rodrigues 			ibuf_free(msg);
24270623c80SCraig Rodrigues 			return (-1);
24370623c80SCraig Rodrigues 		}
24470623c80SCraig Rodrigues 	return (datalen);
24570623c80SCraig Rodrigues }
24670623c80SCraig Rodrigues 
24770623c80SCraig Rodrigues void
imsg_close(struct imsgbuf * ibuf,struct ibuf * msg)24870623c80SCraig Rodrigues imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
24970623c80SCraig Rodrigues {
25070623c80SCraig Rodrigues 	struct imsg_hdr	*hdr;
25170623c80SCraig Rodrigues 
25270623c80SCraig Rodrigues 	hdr = (struct imsg_hdr *)msg->buf;
25370623c80SCraig Rodrigues 
25470623c80SCraig Rodrigues 	hdr->flags &= ~IMSGF_HASFD;
25570623c80SCraig Rodrigues 	if (msg->fd != -1)
25670623c80SCraig Rodrigues 		hdr->flags |= IMSGF_HASFD;
25770623c80SCraig Rodrigues 
25870623c80SCraig Rodrigues 	hdr->len = (u_int16_t)msg->wpos;
25970623c80SCraig Rodrigues 
26070623c80SCraig Rodrigues 	ibuf_close(&ibuf->w, msg);
26170623c80SCraig Rodrigues }
26270623c80SCraig Rodrigues 
26370623c80SCraig Rodrigues void
imsg_free(struct imsg * imsg)26470623c80SCraig Rodrigues imsg_free(struct imsg *imsg)
26570623c80SCraig Rodrigues {
26670623c80SCraig Rodrigues 	free(imsg->data);
26770623c80SCraig Rodrigues }
26870623c80SCraig Rodrigues 
26970623c80SCraig Rodrigues int
imsg_get_fd(struct imsgbuf * ibuf)27070623c80SCraig Rodrigues imsg_get_fd(struct imsgbuf *ibuf)
27170623c80SCraig Rodrigues {
27270623c80SCraig Rodrigues 	int		 fd;
27370623c80SCraig Rodrigues 	struct imsg_fd	*ifd;
27470623c80SCraig Rodrigues 
27570623c80SCraig Rodrigues 	if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
27670623c80SCraig Rodrigues 		return (-1);
27770623c80SCraig Rodrigues 
27870623c80SCraig Rodrigues 	fd = ifd->fd;
27970623c80SCraig Rodrigues 	TAILQ_REMOVE(&ibuf->fds, ifd, entry);
28070623c80SCraig Rodrigues 	free(ifd);
28170623c80SCraig Rodrigues 
28270623c80SCraig Rodrigues 	return (fd);
28370623c80SCraig Rodrigues }
28470623c80SCraig Rodrigues 
28570623c80SCraig Rodrigues int
imsg_flush(struct imsgbuf * ibuf)28670623c80SCraig Rodrigues imsg_flush(struct imsgbuf *ibuf)
28770623c80SCraig Rodrigues {
28870623c80SCraig Rodrigues 	while (ibuf->w.queued)
28970623c80SCraig Rodrigues 		if (msgbuf_write(&ibuf->w) <= 0)
29070623c80SCraig Rodrigues 			return (-1);
29170623c80SCraig Rodrigues 	return (0);
29270623c80SCraig Rodrigues }
29370623c80SCraig Rodrigues 
29470623c80SCraig Rodrigues void
imsg_clear(struct imsgbuf * ibuf)29570623c80SCraig Rodrigues imsg_clear(struct imsgbuf *ibuf)
29670623c80SCraig Rodrigues {
29770623c80SCraig Rodrigues 	int	fd;
29870623c80SCraig Rodrigues 
29970623c80SCraig Rodrigues 	msgbuf_clear(&ibuf->w);
30070623c80SCraig Rodrigues 	while ((fd = imsg_get_fd(ibuf)) != -1)
30170623c80SCraig Rodrigues 		close(fd);
30270623c80SCraig Rodrigues }
303