Home | History | Annotate | Line # | Download | only in compat
imsg-buffer.c revision 1.7.2.1
      1 /*	$OpenBSD: imsg-buffer.c,v 1.10 2017/04/11 09:57:19 reyk Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2003, 2004 Henning Brauer <henning (at) openbsd.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #include <sys/types.h>
     20 #include <sys/socket.h>
     21 #include <sys/uio.h>
     22 
     23 #include <limits.h>
     24 #include <errno.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <unistd.h>
     28 
     29 #include "compat.h"
     30 #include "imsg.h"
     31 
     32 int	ibuf_realloc(struct ibuf *, size_t);
     33 void	ibuf_enqueue(struct msgbuf *, struct ibuf *);
     34 void	ibuf_dequeue(struct msgbuf *, struct ibuf *);
     35 
     36 struct ibuf *
     37 ibuf_open(size_t len)
     38 {
     39 	struct ibuf	*buf;
     40 
     41 	if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
     42 		return (NULL);
     43 	if ((buf->buf = malloc(len)) == NULL) {
     44 		free(buf);
     45 		return (NULL);
     46 	}
     47 	buf->size = buf->max = len;
     48 	buf->fd = -1;
     49 
     50 	return (buf);
     51 }
     52 
     53 struct ibuf *
     54 ibuf_dynamic(size_t len, size_t max)
     55 {
     56 	struct ibuf	*buf;
     57 
     58 	if (max < len)
     59 		return (NULL);
     60 
     61 	if ((buf = ibuf_open(len)) == NULL)
     62 		return (NULL);
     63 
     64 	if (max > 0)
     65 		buf->max = max;
     66 
     67 	return (buf);
     68 }
     69 
     70 int
     71 ibuf_realloc(struct ibuf *buf, size_t len)
     72 {
     73 	u_char	*b;
     74 
     75 	/* on static buffers max is eq size and so the following fails */
     76 	if (buf->wpos + len > buf->max) {
     77 		errno = ERANGE;
     78 		return (-1);
     79 	}
     80 
     81 	b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
     82 	if (b == NULL)
     83 		return (-1);
     84 	buf->buf = b;
     85 	buf->size = buf->wpos + len;
     86 
     87 	return (0);
     88 }
     89 
     90 int
     91 ibuf_add(struct ibuf *buf, const void *data, size_t len)
     92 {
     93 	if (buf->wpos + len > buf->size)
     94 		if (ibuf_realloc(buf, len) == -1)
     95 			return (-1);
     96 
     97 	memcpy(buf->buf + buf->wpos, data, len);
     98 	buf->wpos += len;
     99 	return (0);
    100 }
    101 
    102 void *
    103 ibuf_reserve(struct ibuf *buf, size_t len)
    104 {
    105 	void	*b;
    106 
    107 	if (buf->wpos + len > buf->size)
    108 		if (ibuf_realloc(buf, len) == -1)
    109 			return (NULL);
    110 
    111 	b = buf->buf + buf->wpos;
    112 	buf->wpos += len;
    113 	return (b);
    114 }
    115 
    116 void *
    117 ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
    118 {
    119 	/* only allowed to seek in already written parts */
    120 	if (pos + len > buf->wpos)
    121 		return (NULL);
    122 
    123 	return (buf->buf + pos);
    124 }
    125 
    126 size_t
    127 ibuf_size(struct ibuf *buf)
    128 {
    129 	return (buf->wpos);
    130 }
    131 
    132 size_t
    133 ibuf_left(struct ibuf *buf)
    134 {
    135 	return (buf->max - buf->wpos);
    136 }
    137 
    138 void
    139 ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
    140 {
    141 	ibuf_enqueue(msgbuf, buf);
    142 }
    143 
    144 int
    145 ibuf_write(struct msgbuf *msgbuf)
    146 {
    147 	struct iovec	 iov[IOV_MAX];
    148 	struct ibuf	*buf;
    149 	unsigned int	 i = 0;
    150 	ssize_t	n;
    151 
    152 	memset(&iov, 0, sizeof(iov));
    153 	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
    154 		if (i >= IOV_MAX)
    155 			break;
    156 		iov[i].iov_base = buf->buf + buf->rpos;
    157 		iov[i].iov_len = buf->wpos - buf->rpos;
    158 		i++;
    159 	}
    160 
    161 again:
    162 	if ((n = writev(msgbuf->fd, iov, i)) == -1) {
    163 		if (errno == EINTR)
    164 			goto again;
    165 		if (errno == ENOBUFS)
    166 			errno = EAGAIN;
    167 		return (-1);
    168 	}
    169 
    170 	if (n == 0) {			/* connection closed */
    171 		errno = 0;
    172 		return (0);
    173 	}
    174 
    175 	msgbuf_drain(msgbuf, n);
    176 
    177 	return (1);
    178 }
    179 
    180 void
    181 ibuf_free(struct ibuf *buf)
    182 {
    183 	if (buf == NULL)
    184 		return;
    185 	freezero(buf->buf, buf->size);
    186 	free(buf);
    187 }
    188 
    189 void
    190 msgbuf_init(struct msgbuf *msgbuf)
    191 {
    192 	msgbuf->queued = 0;
    193 	msgbuf->fd = -1;
    194 	TAILQ_INIT(&msgbuf->bufs);
    195 }
    196 
    197 void
    198 msgbuf_drain(struct msgbuf *msgbuf, size_t n)
    199 {
    200 	struct ibuf	*buf, *next;
    201 
    202 	for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
    203 	    buf = next) {
    204 		next = TAILQ_NEXT(buf, entry);
    205 		if (buf->rpos + n >= buf->wpos) {
    206 			n -= buf->wpos - buf->rpos;
    207 			ibuf_dequeue(msgbuf, buf);
    208 		} else {
    209 			buf->rpos += n;
    210 			n = 0;
    211 		}
    212 	}
    213 }
    214 
    215 void
    216 msgbuf_clear(struct msgbuf *msgbuf)
    217 {
    218 	struct ibuf	*buf;
    219 
    220 	while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
    221 		ibuf_dequeue(msgbuf, buf);
    222 }
    223 
    224 int
    225 msgbuf_write(struct msgbuf *msgbuf)
    226 {
    227 	struct iovec	 iov[IOV_MAX];
    228 	struct ibuf	*buf;
    229 	unsigned int	 i = 0;
    230 	ssize_t		 n;
    231 	struct msghdr	 msg;
    232 	struct cmsghdr	*cmsg;
    233 	union {
    234 		struct cmsghdr	hdr;
    235 		char		buf[CMSG_SPACE(sizeof(int))];
    236 	} cmsgbuf;
    237 
    238 	memset(&iov, 0, sizeof(iov));
    239 	memset(&msg, 0, sizeof(msg));
    240 	memset(&cmsgbuf, 0, sizeof(cmsgbuf));
    241 	TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
    242 		if (i >= IOV_MAX)
    243 			break;
    244 		iov[i].iov_base = buf->buf + buf->rpos;
    245 		iov[i].iov_len = buf->wpos - buf->rpos;
    246 		i++;
    247 		if (buf->fd != -1)
    248 			break;
    249 	}
    250 
    251 	msg.msg_iov = iov;
    252 	msg.msg_iovlen = i;
    253 
    254 	if (buf != NULL && buf->fd != -1) {
    255 		msg.msg_control = (caddr_t)&cmsgbuf.buf;
    256 		msg.msg_controllen = CMSG_SPACE(sizeof(int));
    257 		cmsg = CMSG_FIRSTHDR(&msg);
    258 		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
    259 		cmsg->cmsg_level = SOL_SOCKET;
    260 		cmsg->cmsg_type = SCM_RIGHTS;
    261 		*(int *)CMSG_DATA(cmsg) = buf->fd;
    262 	}
    263 
    264 again:
    265 	if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
    266 		if (errno == EINTR)
    267 			goto again;
    268 		if (errno == ENOBUFS)
    269 			errno = EAGAIN;
    270 		return (-1);
    271 	}
    272 
    273 	if (n == 0) {			/* connection closed */
    274 		errno = 0;
    275 		return (0);
    276 	}
    277 
    278 	/*
    279 	 * assumption: fd got sent if sendmsg sent anything
    280 	 * this works because fds are passed one at a time
    281 	 */
    282 	if (buf != NULL && buf->fd != -1) {
    283 		close(buf->fd);
    284 		buf->fd = -1;
    285 	}
    286 
    287 	msgbuf_drain(msgbuf, n);
    288 
    289 	return (1);
    290 }
    291 
    292 void
    293 ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
    294 {
    295 	TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
    296 	msgbuf->queued++;
    297 }
    298 
    299 void
    300 ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
    301 {
    302 	TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
    303 
    304 	if (buf->fd != -1)
    305 		close(buf->fd);
    306 
    307 	msgbuf->queued--;
    308 	ibuf_free(buf);
    309 }
    310