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
31int	 imsg_get_fd(struct imsgbuf *);
32
33void
34imsg_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
44ssize_t
45imsg_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
94ssize_t
95imsg_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
134int
135imsg_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
153int
154imsg_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 */
178struct ibuf *
179imsg_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
205int
206imsg_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
216void
217imsg_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
232void
233imsg_free(struct imsg *imsg)
234{
235	free(imsg->data);
236}
237
238int
239imsg_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
254int
255imsg_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
263void
264imsg_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