1 /* $OpenBSD: imsg-buffer.c,v 1.1 2010/05/26 16:44:32 nicm 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 if ((n = writev(msgbuf->fd, iov, i)) == -1) { 161 if (errno == EAGAIN || errno == ENOBUFS || 162 errno == EINTR) /* try later */ 163 return (0); 164 else 165 return (-1); 166 } 167 168 if (n == 0) { /* connection closed */ 169 errno = 0; 170 return (-2); 171 } 172 173 msgbuf_drain(msgbuf, n); 174 175 return (0); 176 } 177 178 void 179 ibuf_free(struct ibuf *buf) 180 { 181 free(buf->buf); 182 free(buf); 183 } 184 185 void 186 msgbuf_init(struct msgbuf *msgbuf) 187 { 188 msgbuf->queued = 0; 189 msgbuf->fd = -1; 190 TAILQ_INIT(&msgbuf->bufs); 191 } 192 193 void 194 msgbuf_drain(struct msgbuf *msgbuf, size_t n) 195 { 196 struct ibuf *buf, *next; 197 198 for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; 199 buf = next) { 200 next = TAILQ_NEXT(buf, entry); 201 if (buf->rpos + n >= buf->wpos) { 202 n -= buf->wpos - buf->rpos; 203 ibuf_dequeue(msgbuf, buf); 204 } else { 205 buf->rpos += n; 206 n = 0; 207 } 208 } 209 } 210 211 void 212 msgbuf_clear(struct msgbuf *msgbuf) 213 { 214 struct ibuf *buf; 215 216 while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) 217 ibuf_dequeue(msgbuf, buf); 218 } 219 220 int 221 msgbuf_write(struct msgbuf *msgbuf) 222 { 223 struct iovec iov[IOV_MAX]; 224 struct ibuf *buf; 225 unsigned int i = 0; 226 ssize_t n; 227 struct msghdr msg; 228 struct cmsghdr *cmsg; 229 union { 230 struct cmsghdr hdr; 231 char buf[CMSG_SPACE(sizeof(int))]; 232 } cmsgbuf; 233 234 bzero(&iov, sizeof(iov)); 235 bzero(&msg, sizeof(msg)); 236 TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { 237 if (i >= IOV_MAX) 238 break; 239 iov[i].iov_base = buf->buf + buf->rpos; 240 iov[i].iov_len = buf->wpos - buf->rpos; 241 i++; 242 if (buf->fd != -1) 243 break; 244 } 245 246 msg.msg_iov = iov; 247 msg.msg_iovlen = i; 248 249 if (buf != NULL && buf->fd != -1) { 250 msg.msg_control = (caddr_t)&cmsgbuf.buf; 251 msg.msg_controllen = sizeof(cmsgbuf.buf); 252 cmsg = CMSG_FIRSTHDR(&msg); 253 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 254 cmsg->cmsg_level = SOL_SOCKET; 255 cmsg->cmsg_type = SCM_RIGHTS; 256 *(int *)CMSG_DATA(cmsg) = buf->fd; 257 } 258 259 if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { 260 if (errno == EAGAIN || errno == ENOBUFS || 261 errno == EINTR) /* try later */ 262 return (0); 263 else 264 return (-1); 265 } 266 267 if (n == 0) { /* connection closed */ 268 errno = 0; 269 return (-2); 270 } 271 272 /* 273 * assumption: fd got sent if sendmsg sent anything 274 * this works because fds are passed one at a time 275 */ 276 if (buf != NULL && buf->fd != -1) { 277 close(buf->fd); 278 buf->fd = -1; 279 } 280 281 msgbuf_drain(msgbuf, n); 282 283 return (0); 284 } 285 286 void 287 ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) 288 { 289 TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); 290 msgbuf->queued++; 291 } 292 293 void 294 ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) 295 { 296 TAILQ_REMOVE(&msgbuf->bufs, buf, entry); 297 298 if (buf->fd != -1) 299 close(buf->fd); 300 301 msgbuf->queued--; 302 ibuf_free(buf); 303 } 304