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