xref: /netbsd-src/external/bsd/tmux/dist/compat/imsg.c (revision 1b9578b8c2c1f848eeb16dabbfd7d1f0d9fdefbd)
1 /* $Id: imsg.c,v 1.2 2011/05/30 16:20:58 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 #ifdef __clang__
52 		char	buf[128];
53 #else
54 		char	buf[CMSG_SPACE(sizeof(int) * 16)];
55 #endif
56 	} cmsgbuf;
57 	struct iovec		 iov;
58 	ssize_t			 n;
59 	int			 fd;
60 	struct imsg_fd		*ifd;
61 
62 	bzero(&msg, sizeof(msg));
63 
64 	iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
65 	iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
66 	msg.msg_iov = &iov;
67 	msg.msg_iovlen = 1;
68 	msg.msg_control = &cmsgbuf.buf;
69 	msg.msg_controllen = CMSG_SPACE(sizeof(int) * 16);
70 
71 	if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
72 		if (errno != EINTR && errno != EAGAIN) {
73 			return (-1);
74 		}
75 		return (-2);
76 	}
77 
78 	ibuf->r.wpos += n;
79 
80 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
81 	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
82 		if (cmsg->cmsg_level == SOL_SOCKET &&
83 		    cmsg->cmsg_type == SCM_RIGHTS) {
84 			fd = (*(int *)CMSG_DATA(cmsg));
85 			if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) {
86 				close(fd);
87 				return (-1);
88 			}
89 			ifd->fd = fd;
90 			TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry);
91 		}
92 		/* we do not handle other ctl data level */
93 	}
94 
95 	return (n);
96 }
97 
98 ssize_t
99 imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
100 {
101 	size_t			 av, left, datalen;
102 
103 	av = ibuf->r.wpos;
104 
105 	if (IMSG_HEADER_SIZE > av)
106 		return (0);
107 
108 	memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
109 	if (imsg->hdr.len < IMSG_HEADER_SIZE ||
110 	    imsg->hdr.len > MAX_IMSGSIZE) {
111 		errno = ERANGE;
112 		return (-1);
113 	}
114 	if (imsg->hdr.len > av)
115 		return (0);
116 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
117 	ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
118 	if ((imsg->data = malloc(datalen)) == NULL && datalen != 0)
119 		return (-1);
120 
121 	if (imsg->hdr.flags & IMSGF_HASFD)
122 		imsg->fd = imsg_get_fd(ibuf);
123 	else
124 		imsg->fd = -1;
125 
126 	memcpy(imsg->data, ibuf->r.rptr, datalen);
127 
128 	if (imsg->hdr.len < av) {
129 		left = av - imsg->hdr.len;
130 		memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
131 		ibuf->r.wpos = left;
132 	} else
133 		ibuf->r.wpos = 0;
134 
135 	return (datalen + IMSG_HEADER_SIZE);
136 }
137 
138 int
139 imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
140     pid_t pid, int fd, void *data, u_int16_t datalen)
141 {
142 	struct ibuf	*wbuf;
143 
144 	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
145 		return (-1);
146 
147 	if (imsg_add(wbuf, data, datalen) == -1)
148 		return (-1);
149 
150 	wbuf->fd = fd;
151 
152 	imsg_close(ibuf, wbuf);
153 
154 	return (1);
155 }
156 
157 int
158 imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
159     pid_t pid, int fd, const struct iovec *iov, int iovcnt)
160 {
161 	struct ibuf	*wbuf;
162 	int		 i, datalen = 0;
163 
164 	for (i = 0; i < iovcnt; i++)
165 		datalen += iov[i].iov_len;
166 
167 	if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
168 		return (-1);
169 
170 	for (i = 0; i < iovcnt; i++)
171 		if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
172 			return (-1);
173 
174 	wbuf->fd = fd;
175 
176 	imsg_close(ibuf, wbuf);
177 
178 	return (1);
179 }
180 
181 /* ARGSUSED */
182 struct ibuf *
183 imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
184     pid_t pid, u_int16_t datalen)
185 {
186 	struct ibuf	*wbuf;
187 	struct imsg_hdr	 hdr;
188 
189 	datalen += IMSG_HEADER_SIZE;
190 	if (datalen > MAX_IMSGSIZE) {
191 		errno = ERANGE;
192 		return (NULL);
193 	}
194 
195 	hdr.type = type;
196 	hdr.flags = 0;
197 	hdr.peerid = peerid;
198 	if ((hdr.pid = pid) == 0)
199 		hdr.pid = ibuf->pid;
200 	if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
201 		return (NULL);
202 	}
203 	if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
204 		return (NULL);
205 
206 	return (wbuf);
207 }
208 
209 int
210 imsg_add(struct ibuf *msg, void *data, u_int16_t datalen)
211 {
212 	if (datalen)
213 		if (ibuf_add(msg, data, datalen) == -1) {
214 			ibuf_free(msg);
215 			return (-1);
216 		}
217 	return (datalen);
218 }
219 
220 void
221 imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
222 {
223 	struct imsg_hdr	*hdr;
224 
225 	hdr = (struct imsg_hdr *)msg->buf;
226 
227 	hdr->flags &= ~IMSGF_HASFD;
228 	if (msg->fd != -1)
229 		hdr->flags |= IMSGF_HASFD;
230 
231 	hdr->len = (u_int16_t)msg->wpos;
232 
233 	ibuf_close(&ibuf->w, msg);
234 }
235 
236 void
237 imsg_free(struct imsg *imsg)
238 {
239 	free(imsg->data);
240 }
241 
242 int
243 imsg_get_fd(struct imsgbuf *ibuf)
244 {
245 	int		 fd;
246 	struct imsg_fd	*ifd;
247 
248 	if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
249 		return (-1);
250 
251 	fd = ifd->fd;
252 	TAILQ_REMOVE(&ibuf->fds, ifd, entry);
253 	free(ifd);
254 
255 	return (fd);
256 }
257 
258 int
259 imsg_flush(struct imsgbuf *ibuf)
260 {
261 	while (ibuf->w.queued)
262 		if (msgbuf_write(&ibuf->w) < 0)
263 			return (-1);
264 	return (0);
265 }
266 
267 void
268 imsg_clear(struct imsgbuf *ibuf)
269 {
270 	int	fd;
271 
272 	msgbuf_clear(&ibuf->w);
273 	while ((fd = imsg_get_fd(ibuf)) != -1)
274 		close(fd);
275 }
276