1 /* $OpenBSD: iobuf.c,v 1.16 2021/06/14 17:58:15 eric Exp $ */ 2 /* 3 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/uio.h> 19 20 #include <errno.h> 21 #include <limits.h> 22 #include <stdarg.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #ifdef IO_TLS 27 #include <tls.h> 28 #endif 29 #include <unistd.h> 30 31 #include "iobuf.h" 32 33 #define IOBUF_MAX 65536 34 #define IOBUFQ_MIN 4096 35 36 struct ioqbuf *ioqbuf_alloc(struct iobuf *, size_t); 37 void iobuf_drain(struct iobuf *, size_t); 38 39 int 40 iobuf_init(struct iobuf *io, size_t size, size_t max) 41 { 42 memset(io, 0, sizeof *io); 43 44 if (max == 0) 45 max = IOBUF_MAX; 46 47 if (size == 0) 48 size = max; 49 50 if (size > max) 51 return (-1); 52 53 if ((io->buf = calloc(size, 1)) == NULL) 54 return (-1); 55 56 io->size = size; 57 io->max = max; 58 59 return (0); 60 } 61 62 void 63 iobuf_clear(struct iobuf *io) 64 { 65 struct ioqbuf *q; 66 67 free(io->buf); 68 69 while ((q = io->outq)) { 70 io->outq = q->next; 71 free(q); 72 } 73 74 memset(io, 0, sizeof (*io)); 75 } 76 77 void 78 iobuf_drain(struct iobuf *io, size_t n) 79 { 80 struct ioqbuf *q; 81 size_t left = n; 82 83 while ((q = io->outq) && left) { 84 if ((q->wpos - q->rpos) > left) { 85 q->rpos += left; 86 left = 0; 87 } else { 88 left -= q->wpos - q->rpos; 89 io->outq = q->next; 90 free(q); 91 } 92 } 93 94 io->queued -= (n - left); 95 if (io->outq == NULL) 96 io->outqlast = NULL; 97 } 98 99 int 100 iobuf_extend(struct iobuf *io, size_t n) 101 { 102 char *t; 103 104 if (n > io->max) 105 return (-1); 106 107 if (io->max - io->size < n) 108 return (-1); 109 110 t = recallocarray(io->buf, io->size, io->size + n, 1); 111 if (t == NULL) 112 return (-1); 113 114 io->size += n; 115 io->buf = t; 116 117 return (0); 118 } 119 120 size_t 121 iobuf_left(struct iobuf *io) 122 { 123 return io->size - io->wpos; 124 } 125 126 size_t 127 iobuf_space(struct iobuf *io) 128 { 129 return io->size - (io->wpos - io->rpos); 130 } 131 132 size_t 133 iobuf_len(struct iobuf *io) 134 { 135 return io->wpos - io->rpos; 136 } 137 138 char * 139 iobuf_data(struct iobuf *io) 140 { 141 return io->buf + io->rpos; 142 } 143 144 void 145 iobuf_drop(struct iobuf *io, size_t n) 146 { 147 if (n >= iobuf_len(io)) { 148 io->rpos = io->wpos = 0; 149 return; 150 } 151 152 io->rpos += n; 153 } 154 155 char * 156 iobuf_getline(struct iobuf *iobuf, size_t *rlen) 157 { 158 char *buf; 159 size_t len, i; 160 161 buf = iobuf_data(iobuf); 162 len = iobuf_len(iobuf); 163 164 for (i = 0; i + 1 <= len; i++) 165 if (buf[i] == '\n') { 166 /* Note: the returned address points into the iobuf 167 * buffer. We NUL-end it for convenience, and discard 168 * the data from the iobuf, so that the caller doesn't 169 * have to do it. The data remains "valid" as long 170 * as the iobuf does not overwrite it, that is until 171 * the next call to iobuf_normalize() or iobuf_extend(). 172 */ 173 iobuf_drop(iobuf, i + 1); 174 buf[i] = '\0'; 175 if (rlen) 176 *rlen = i; 177 return (buf); 178 } 179 180 return (NULL); 181 } 182 183 void 184 iobuf_normalize(struct iobuf *io) 185 { 186 if (io->rpos == 0) 187 return; 188 189 if (io->rpos == io->wpos) { 190 io->rpos = io->wpos = 0; 191 return; 192 } 193 194 memmove(io->buf, io->buf + io->rpos, io->wpos - io->rpos); 195 io->wpos -= io->rpos; 196 io->rpos = 0; 197 } 198 199 ssize_t 200 iobuf_read(struct iobuf *io, int fd) 201 { 202 ssize_t n; 203 204 n = read(fd, io->buf + io->wpos, iobuf_left(io)); 205 if (n == -1) { 206 /* XXX is this really what we want? */ 207 if (errno == EAGAIN || errno == EINTR) 208 return (IOBUF_WANT_READ); 209 return (IOBUF_ERROR); 210 } 211 if (n == 0) 212 return (IOBUF_CLOSED); 213 214 io->wpos += n; 215 216 return (n); 217 } 218 219 struct ioqbuf * 220 ioqbuf_alloc(struct iobuf *io, size_t len) 221 { 222 struct ioqbuf *q; 223 224 if (len < IOBUFQ_MIN) 225 len = IOBUFQ_MIN; 226 227 if ((q = malloc(sizeof(*q) + len)) == NULL) 228 return (NULL); 229 230 q->rpos = 0; 231 q->wpos = 0; 232 q->size = len; 233 q->next = NULL; 234 q->buf = (char *)(q) + sizeof(*q); 235 236 if (io->outqlast == NULL) 237 io->outq = q; 238 else 239 io->outqlast->next = q; 240 io->outqlast = q; 241 242 return (q); 243 } 244 245 size_t 246 iobuf_queued(struct iobuf *io) 247 { 248 return io->queued; 249 } 250 251 void * 252 iobuf_reserve(struct iobuf *io, size_t len) 253 { 254 struct ioqbuf *q; 255 void *r; 256 257 if (len == 0) 258 return (NULL); 259 260 if (((q = io->outqlast) == NULL) || q->size - q->wpos <= len) { 261 if ((q = ioqbuf_alloc(io, len)) == NULL) 262 return (NULL); 263 } 264 265 r = q->buf + q->wpos; 266 q->wpos += len; 267 io->queued += len; 268 269 return (r); 270 } 271 272 int 273 iobuf_queue(struct iobuf *io, const void *data, size_t len) 274 { 275 void *buf; 276 277 if (len == 0) 278 return (0); 279 280 if ((buf = iobuf_reserve(io, len)) == NULL) 281 return (-1); 282 283 memmove(buf, data, len); 284 285 return (len); 286 } 287 288 int 289 iobuf_queuev(struct iobuf *io, const struct iovec *iov, int iovcnt) 290 { 291 int i; 292 size_t len = 0; 293 char *buf; 294 295 for (i = 0; i < iovcnt; i++) 296 len += iov[i].iov_len; 297 298 if ((buf = iobuf_reserve(io, len)) == NULL) 299 return (-1); 300 301 for (i = 0; i < iovcnt; i++) { 302 if (iov[i].iov_len == 0) 303 continue; 304 memmove(buf, iov[i].iov_base, iov[i].iov_len); 305 buf += iov[i].iov_len; 306 } 307 308 return (0); 309 310 } 311 312 int 313 iobuf_fqueue(struct iobuf *io, const char *fmt, ...) 314 { 315 va_list ap; 316 int len; 317 318 va_start(ap, fmt); 319 len = iobuf_vfqueue(io, fmt, ap); 320 va_end(ap); 321 322 return (len); 323 } 324 325 int 326 iobuf_vfqueue(struct iobuf *io, const char *fmt, va_list ap) 327 { 328 char *buf; 329 int len; 330 331 len = vasprintf(&buf, fmt, ap); 332 333 if (len == -1) 334 return (-1); 335 336 len = iobuf_queue(io, buf, len); 337 free(buf); 338 339 return (len); 340 } 341 342 ssize_t 343 iobuf_write(struct iobuf *io, int fd) 344 { 345 struct iovec iov[IOV_MAX]; 346 struct ioqbuf *q; 347 int i; 348 ssize_t n; 349 350 i = 0; 351 for (q = io->outq; q ; q = q->next) { 352 if (i >= IOV_MAX) 353 break; 354 iov[i].iov_base = q->buf + q->rpos; 355 iov[i].iov_len = q->wpos - q->rpos; 356 i++; 357 } 358 359 n = writev(fd, iov, i); 360 if (n == -1) { 361 if (errno == EAGAIN || errno == EINTR) 362 return (IOBUF_WANT_WRITE); 363 if (errno == EPIPE) 364 return (IOBUF_CLOSED); 365 return (IOBUF_ERROR); 366 } 367 368 iobuf_drain(io, n); 369 370 return (n); 371 } 372 373 int 374 iobuf_flush(struct iobuf *io, int fd) 375 { 376 ssize_t s; 377 378 while (io->queued) 379 if ((s = iobuf_write(io, fd)) < 0) 380 return (s); 381 382 return (0); 383 } 384 385 #ifdef IO_TLS 386 387 int 388 iobuf_flush_tls(struct iobuf *io, struct tls *tls) 389 { 390 ssize_t s; 391 392 while (io->queued) 393 if ((s = iobuf_write_tls(io, tls)) < 0) 394 return (s); 395 396 return (0); 397 } 398 399 ssize_t 400 iobuf_write_tls(struct iobuf *io, struct tls *tls) 401 { 402 struct ioqbuf *q; 403 ssize_t n; 404 405 q = io->outq; 406 407 n = tls_write(tls, q->buf + q->rpos, q->wpos - q->rpos); 408 if (n == TLS_WANT_POLLIN) 409 return (IOBUF_WANT_READ); 410 else if (n == TLS_WANT_POLLOUT) 411 return (IOBUF_WANT_WRITE); 412 else if (n == 0) 413 return (IOBUF_CLOSED); 414 else if (n == -1) 415 return (IOBUF_ERROR); 416 417 iobuf_drain(io, n); 418 419 return (n); 420 } 421 422 ssize_t 423 iobuf_read_tls(struct iobuf *io, struct tls *tls) 424 { 425 ssize_t n; 426 427 n = tls_read(tls, io->buf + io->wpos, iobuf_left(io)); 428 if (n == TLS_WANT_POLLIN) 429 return (IOBUF_WANT_READ); 430 else if (n == TLS_WANT_POLLOUT) 431 return (IOBUF_WANT_WRITE); 432 else if (n == 0) 433 return (IOBUF_CLOSED); 434 else if (n == -1) 435 return (IOBUF_ERROR); 436 437 io->wpos += n; 438 439 return (n); 440 } 441 442 #endif /* IO_TLS */ 443