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