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