1*7e5ac32bSCraig 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 +
75*7e5ac32bSCraig 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