1 /* $OpenBSD: imsg-buffer.c,v 1.2 2012/06/02 21:46:53 gilles Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004 Henning Brauer <henning@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/param.h> 20 #include <sys/queue.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 "imsg.h" 30 31 int ibuf_realloc(struct ibuf *, size_t); 32 void ibuf_enqueue(struct msgbuf *, struct ibuf *); 33 void ibuf_dequeue(struct msgbuf *, struct ibuf *); 34 35 struct ibuf * 36 ibuf_open(size_t len) 37 { 38 struct ibuf *buf; 39 40 if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) 41 return (NULL); 42 if ((buf->buf = malloc(len)) == NULL) { 43 free(buf); 44 return (NULL); 45 } 46 buf->size = buf->max = len; 47 buf->fd = -1; 48 49 return (buf); 50 } 51 52 struct ibuf * 53 ibuf_dynamic(size_t len, size_t max) 54 { 55 struct ibuf *buf; 56 57 if (max < len) 58 return (NULL); 59 60 if ((buf = ibuf_open(len)) == NULL) 61 return (NULL); 62 63 if (max > 0) 64 buf->max = max; 65 66 return (buf); 67 } 68 69 int 70 ibuf_realloc(struct ibuf *buf, size_t len) 71 { 72 u_char *b; 73 74 /* on static buffers max is eq size and so the following fails */ 75 if (buf->wpos + len > buf->max) { 76 errno = ENOMEM; 77 return (-1); 78 } 79 80 b = realloc(buf->buf, buf->wpos + len); 81 if (b == NULL) 82 return (-1); 83 buf->buf = b; 84 buf->size = buf->wpos + len; 85 86 return (0); 87 } 88 89 int 90 ibuf_add(struct ibuf *buf, const void *data, size_t len) 91 { 92 if (buf->wpos + len > buf->size) 93 if (ibuf_realloc(buf, len) == -1) 94 return (-1); 95 96 memcpy(buf->buf + buf->wpos, data, len); 97 buf->wpos += len; 98 return (0); 99 } 100 101 void * 102 ibuf_reserve(struct ibuf *buf, size_t len) 103 { 104 void *b; 105 106 if (buf->wpos + len > buf->size) 107 if (ibuf_realloc(buf, len) == -1) 108 return (NULL); 109 110 b = buf->buf + buf->wpos; 111 buf->wpos += len; 112 return (b); 113 } 114 115 void * 116 ibuf_seek(struct ibuf *buf, size_t pos, size_t len) 117 { 118 /* only allowed to seek in already written parts */ 119 if (pos + len > buf->wpos) 120 return (NULL); 121 122 return (buf->buf + pos); 123 } 124 125 size_t 126 ibuf_size(struct ibuf *buf) 127 { 128 return (buf->wpos); 129 } 130 131 size_t 132 ibuf_left(struct ibuf *buf) 133 { 134 return (buf->max - buf->wpos); 135 } 136 137 void 138 ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) 139 { 140 ibuf_enqueue(msgbuf, buf); 141 } 142 143 int 144 ibuf_write(struct msgbuf *msgbuf) 145 { 146 struct iovec iov[IOV_MAX]; 147 struct ibuf *buf; 148 unsigned int i = 0; 149 ssize_t n; 150 151 bzero(&iov, sizeof(iov)); 152 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 153 if (i >= IOV_MAX) 154 break; 155 iov[i].iov_base = buf->buf + buf->rpos; 156 iov[i].iov_len = buf->wpos - buf->rpos; 157 i++; 158 } 159 160 again: 161 if ((n = writev(msgbuf->fd, iov, i)) == -1) { 162 if (errno == EAGAIN || errno == EINTR) 163 goto again; 164 if (errno == ENOBUFS) 165 errno = EAGAIN; 166 return (-1); 167 } 168 169 if (n == 0) { /* connection closed */ 170 errno = 0; 171 return (0); 172 } 173 174 msgbuf_drain(msgbuf, n); 175 176 return (1); 177 } 178 179 void 180 ibuf_free(struct ibuf *buf) 181 { 182 free(buf->buf); 183 free(buf); 184 } 185 186 void 187 msgbuf_init(struct msgbuf *msgbuf) 188 { 189 msgbuf->queued = 0; 190 msgbuf->fd = -1; 191 TAILQ_INIT(&msgbuf->bufs); 192 } 193 194 void 195 msgbuf_drain(struct msgbuf *msgbuf, size_t n) 196 { 197 struct ibuf *buf, *next; 198 199 for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; 200 buf = next) { 201 next = TAILQ_NEXT(buf, entry); 202 if (buf->rpos + n >= buf->wpos) { 203 n -= buf->wpos - buf->rpos; 204 ibuf_dequeue(msgbuf, buf); 205 } else { 206 buf->rpos += n; 207 n = 0; 208 } 209 } 210 } 211 212 void 213 msgbuf_clear(struct msgbuf *msgbuf) 214 { 215 struct ibuf *buf; 216 217 while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) 218 ibuf_dequeue(msgbuf, buf); 219 } 220 221 int 222 msgbuf_write(struct msgbuf *msgbuf) 223 { 224 struct iovec iov[IOV_MAX]; 225 struct ibuf *buf; 226 unsigned int i = 0; 227 ssize_t n; 228 struct msghdr msg; 229 struct cmsghdr *cmsg; 230 union { 231 struct cmsghdr hdr; 232 char buf[CMSG_SPACE(sizeof(int))]; 233 } cmsgbuf; 234 235 bzero(&iov, sizeof(iov)); 236 bzero(&msg, sizeof(msg)); 237 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 238 if (i >= IOV_MAX) 239 break; 240 iov[i].iov_base = buf->buf + buf->rpos; 241 iov[i].iov_len = buf->wpos - buf->rpos; 242 i++; 243 if (buf->fd != -1) 244 break; 245 } 246 247 msg.msg_iov = iov; 248 msg.msg_iovlen = i; 249 250 if (buf != NULL && buf->fd != -1) { 251 msg.msg_control = (caddr_t)&cmsgbuf.buf; 252 msg.msg_controllen = sizeof(cmsgbuf.buf); 253 cmsg = CMSG_FIRSTHDR(&msg); 254 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 255 cmsg->cmsg_level = SOL_SOCKET; 256 cmsg->cmsg_type = SCM_RIGHTS; 257 *(int *)CMSG_DATA(cmsg) = buf->fd; 258 } 259 260 again: 261 if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { 262 if (errno == EAGAIN || errno == EINTR) 263 goto again; 264 if (errno == ENOBUFS) 265 errno = EAGAIN; 266 return (-1); 267 } 268 269 if (n == 0) { /* connection closed */ 270 errno = 0; 271 return (0); 272 } 273 274 /* 275 * assumption: fd got sent if sendmsg sent anything 276 * this works because fds are passed one at a time 277 */ 278 if (buf != NULL && buf->fd != -1) { 279 close(buf->fd); 280 buf->fd = -1; 281 } 282 283 msgbuf_drain(msgbuf, n); 284 285 return (1); 286 } 287 288 void 289 ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) 290 { 291 TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); 292 msgbuf->queued++; 293 } 294 295 void 296 ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) 297 { 298 TAILQ_REMOVE(&msgbuf->bufs, buf, entry); 299 300 if (buf->fd != -1) 301 close(buf->fd); 302 303 msgbuf->queued--; 304 ibuf_free(buf); 305 } 306