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