blob: 1935aa11c2d900657747144ce7a8bf652cd1f715 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file buffer.c
2 * Buffering of output and input. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02003/*
Harald Welte3fb0b6f2010-05-19 19:02:52 +02004 * Copyright (C) 1998 Kunihiro Ishiguro
5 *
6 * This file is part of GNU Zebra.
7 *
8 * GNU Zebra is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published
10 * by the Free Software Foundation; either version 2, or (at your
11 * option) any later version.
12 *
13 * GNU Zebra is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Zebra; see the file COPYING. If not, write to the
Jaroslav Škarvada2b82c1c2015-11-11 16:02:54 +010020 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
Harald Welte3fb0b6f2010-05-19 19:02:52 +020022 */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <string.h>
28#include <errno.h>
29#include <stddef.h>
30#include <sys/uio.h>
31
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010032#include <osmocom/core/talloc.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020033#include <osmocom/vty/buffer.h>
34#include <osmocom/vty/vty.h>
35
36/* Buffer master. */
37struct buffer {
38 /* Data list. */
39 struct buffer_data *head;
40 struct buffer_data *tail;
41
42 /* Size of each buffer_data chunk. */
43 size_t size;
44};
45
46/* Data container. */
47struct buffer_data {
48 struct buffer_data *next;
49
50 /* Location to add new data. */
51 size_t cp;
52
53 /* Pointer to data not yet flushed. */
54 size_t sp;
55
56 /* Actual data stream (variable length). */
57 unsigned char data[0]; /* real dimension is buffer->size */
58};
59
60/* It should always be true that: 0 <= sp <= cp <= size */
61
62/* Default buffer size (used if none specified). It is rounded up to the
63 next page boundery. */
64#define BUFFER_SIZE_DEFAULT 4096
65
66#define BUFFER_DATA_FREE(D) talloc_free((D))
67
68/* Make new buffer. */
69struct buffer *buffer_new(void *ctx, size_t size)
70{
71 struct buffer *b;
72
73 b = talloc_zero(ctx, struct buffer);
74
75 if (size)
76 b->size = size;
77 else {
78 static size_t default_size;
79 if (!default_size) {
80 long pgsz = sysconf(_SC_PAGESIZE);
81 default_size =
82 ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz);
83 }
84 b->size = default_size;
85 }
86
87 return b;
88}
89
90/* Free buffer. */
91void buffer_free(struct buffer *b)
92{
93 buffer_reset(b);
94 talloc_free(b);
95}
96
97/* Make string clone. */
98char *buffer_getstr(struct buffer *b)
99{
100 size_t totlen = 0;
101 struct buffer_data *data;
102 char *s;
103 char *p;
104
105 for (data = b->head; data; data = data->next)
106 totlen += data->cp - data->sp;
107 if (!(s = _talloc_zero(tall_vty_ctx, (totlen + 1), "buffer_getstr")))
108 return NULL;
109 p = s;
110 for (data = b->head; data; data = data->next) {
111 memcpy(p, data->data + data->sp, data->cp - data->sp);
112 p += data->cp - data->sp;
113 }
114 *p = '\0';
115 return s;
116}
117
118/* Return 1 if buffer is empty. */
119int buffer_empty(struct buffer *b)
120{
121 return (b->head == NULL);
122}
123
124/* Clear and free all allocated data. */
125void buffer_reset(struct buffer *b)
126{
127 struct buffer_data *data;
128 struct buffer_data *next;
129
130 for (data = b->head; data; data = next) {
131 next = data->next;
132 BUFFER_DATA_FREE(data);
133 }
134 b->head = b->tail = NULL;
135}
136
137/* Add buffer_data to the end of buffer. */
138static struct buffer_data *buffer_add(struct buffer *b)
139{
140 struct buffer_data *d;
141
142 d = _talloc_zero(b,
143 offsetof(struct buffer_data, data[b->size]),
144 "buffer_add");
145 if (!d)
146 return NULL;
147 d->cp = d->sp = 0;
148 d->next = NULL;
149
150 if (b->tail)
151 b->tail->next = d;
152 else
153 b->head = d;
154 b->tail = d;
155
156 return d;
157}
158
159/* Write data to buffer. */
160void buffer_put(struct buffer *b, const void *p, size_t size)
161{
162 struct buffer_data *data = b->tail;
163 const char *ptr = p;
164
165 /* We use even last one byte of data buffer. */
166 while (size) {
167 size_t chunk;
168
169 /* If there is no data buffer add it. */
170 if (data == NULL || data->cp == b->size)
171 data = buffer_add(b);
172
173 chunk =
174 ((size <=
175 (b->size - data->cp)) ? size : (b->size - data->cp));
176 memcpy((data->data + data->cp), ptr, chunk);
177 size -= chunk;
178 ptr += chunk;
179 data->cp += chunk;
180 }
181}
182
183/* Insert character into the buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100184void buffer_putc(struct buffer *b, unsigned char c)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200185{
186 buffer_put(b, &c, 1);
187}
188
189/* Put string to the buffer. */
190void buffer_putstr(struct buffer *b, const char *c)
191{
192 buffer_put(b, c, strlen(c));
193}
194
195/* Keep flushing data to the fd until the buffer is empty or an error is
196 encountered or the operation would block. */
197buffer_status_t buffer_flush_all(struct buffer *b, int fd)
198{
199 buffer_status_t ret;
200 struct buffer_data *head;
201 size_t head_sp;
202
203 if (!b->head)
204 return BUFFER_EMPTY;
205 head_sp = (head = b->head)->sp;
206 /* Flush all data. */
207 while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
208 if ((b->head == head) && (head_sp == head->sp)
209 && (errno != EINTR))
210 /* No data was flushed, so kernel buffer must be full. */
211 return ret;
212 head_sp = (head = b->head)->sp;
213 }
214
215 return ret;
216}
217
218#if 0
219/* Flush enough data to fill a terminal window of the given scene (used only
220 by vty telnet interface). */
221buffer_status_t
222buffer_flush_window(struct buffer * b, int fd, int width, int height,
223 int erase_flag, int no_more_flag)
224{
225 int nbytes;
226 int iov_alloc;
227 int iov_index;
228 struct iovec *iov;
229 struct iovec small_iov[3];
230 char more[] = " --More-- ";
231 char erase[] =
232 { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
233 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
234 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
235 };
236 struct buffer_data *data;
237 int column;
238
239 if (!b->head)
240 return BUFFER_EMPTY;
241
242 if (height < 1) {
243 zlog_warn
244 ("%s called with non-positive window height %d, forcing to 1",
245 __func__, height);
246 height = 1;
247 } else if (height >= 2)
248 height--;
249 if (width < 1) {
250 zlog_warn
251 ("%s called with non-positive window width %d, forcing to 1",
252 __func__, width);
253 width = 1;
254 }
255
256 /* For erase and more data add two to b's buffer_data count. */
257 if (b->head->next == NULL) {
258 iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]);
259 iov = small_iov;
260 } else {
261 iov_alloc = ((height * (width + 2)) / b->size) + 10;
262 iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
263 }
264 iov_index = 0;
265
266 /* Previously print out is performed. */
267 if (erase_flag) {
268 iov[iov_index].iov_base = erase;
269 iov[iov_index].iov_len = sizeof erase;
270 iov_index++;
271 }
272
273 /* Output data. */
274 column = 1; /* Column position of next character displayed. */
275 for (data = b->head; data && (height > 0); data = data->next) {
276 size_t cp;
277
278 cp = data->sp;
279 while ((cp < data->cp) && (height > 0)) {
280 /* Calculate lines remaining and column position after displaying
281 this character. */
282 if (data->data[cp] == '\r')
283 column = 1;
284 else if ((data->data[cp] == '\n') || (column == width)) {
285 column = 1;
286 height--;
287 } else
288 column++;
289 cp++;
290 }
291 iov[iov_index].iov_base = (char *)(data->data + data->sp);
292 iov[iov_index++].iov_len = cp - data->sp;
293 data->sp = cp;
294
295 if (iov_index == iov_alloc)
296 /* This should not ordinarily happen. */
297 {
298 iov_alloc *= 2;
299 if (iov != small_iov) {
300 zlog_warn("%s: growing iov array to %d; "
301 "width %d, height %d, size %lu",
302 __func__, iov_alloc, width, height,
Harald Welte78a870e2014-03-11 10:45:38 +0100303 (unsigned long) b->size);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200304 iov =
305 XREALLOC(MTYPE_TMP, iov,
306 iov_alloc * sizeof(*iov));
307 } else {
308 /* This should absolutely never occur. */
309 zlog_err
310 ("%s: corruption detected: iov_small overflowed; "
311 "head %p, tail %p, head->next %p",
312 __func__, b->head, b->tail, b->head->next);
313 iov =
314 XMALLOC(MTYPE_TMP,
315 iov_alloc * sizeof(*iov));
316 memcpy(iov, small_iov, sizeof(small_iov));
317 }
318 }
319 }
320
321 /* In case of `more' display need. */
322 if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
323 iov[iov_index].iov_base = more;
324 iov[iov_index].iov_len = sizeof more;
325 iov_index++;
326 }
327#ifdef IOV_MAX
328 /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
329 example: Solaris2.6 are defined IOV_MAX size at 16. */
330 {
331 struct iovec *c_iov = iov;
332 nbytes = 0; /* Make sure it's initialized. */
333
334 while (iov_index > 0) {
335 int iov_size;
336
337 iov_size =
338 ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
339 if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
340 zlog_warn("%s: writev to fd %d failed: %s",
341 __func__, fd, safe_strerror(errno));
342 break;
343 }
344
345 /* move pointer io-vector */
346 c_iov += iov_size;
347 iov_index -= iov_size;
348 }
349 }
350#else /* IOV_MAX */
351 if ((nbytes = writev(fd, iov, iov_index)) < 0)
352 zlog_warn("%s: writev to fd %d failed: %s",
353 __func__, fd, safe_strerror(errno));
354#endif /* IOV_MAX */
355
356 /* Free printed buffer data. */
357 while (b->head && (b->head->sp == b->head->cp)) {
358 struct buffer_data *del;
359 if (!(b->head = (del = b->head)->next))
360 b->tail = NULL;
361 BUFFER_DATA_FREE(del);
362 }
363
364 if (iov != small_iov)
365 XFREE(MTYPE_TMP, iov);
366
367 return (nbytes < 0) ? BUFFER_ERROR :
368 (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
369}
370#endif
371
372/* This function (unlike other buffer_flush* functions above) is designed
373to work with non-blocking sockets. It does not attempt to write out
374all of the queued data, just a "big" chunk. It returns 0 if it was
375able to empty out the buffers completely, 1 if more flushing is
376required later, or -1 on a fatal write error. */
377buffer_status_t buffer_flush_available(struct buffer * b, int fd)
378{
379
380/* These are just reasonable values to make sure a significant amount of
381data is written. There's no need to go crazy and try to write it all
382in one shot. */
383#ifdef IOV_MAX
384#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
385#else
386#define MAX_CHUNKS 16
387#endif
388#define MAX_FLUSH 131072
389
390 struct buffer_data *d;
391 size_t written;
392 struct iovec iov[MAX_CHUNKS];
393 size_t iovcnt = 0;
394 size_t nbyte = 0;
395
396 for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
397 d = d->next, iovcnt++) {
398 iov[iovcnt].iov_base = d->data + d->sp;
399 nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
400 }
401
402 if (!nbyte)
403 /* No data to flush: should we issue a warning message? */
404 return BUFFER_EMPTY;
405
406 /* only place where written should be sign compared */
407 if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) {
408 if (ERRNO_IO_RETRY(errno))
409 /* Calling code should try again later. */
410 return BUFFER_PENDING;
411 return BUFFER_ERROR;
412 }
413
414 /* Free printed buffer data. */
415 while (written > 0) {
416 struct buffer_data *d;
417 if (!(d = b->head))
418 break;
419 if (written < d->cp - d->sp) {
420 d->sp += written;
421 return BUFFER_PENDING;
422 }
423
424 written -= (d->cp - d->sp);
425 if (!(b->head = d->next))
426 b->tail = NULL;
427 BUFFER_DATA_FREE(d);
428 }
429
430 return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
431
432#undef MAX_CHUNKS
433#undef MAX_FLUSH
434}
435
436buffer_status_t
437buffer_write(struct buffer * b, int fd, const void *p, size_t size)
438{
439 ssize_t nbytes;
440
441#if 0
442 /* Should we attempt to drain any previously buffered data? This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */
443
444 if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
445 return BUFFER_ERROR;
446#endif
447 if (b->head)
448 /* Buffer is not empty, so do not attempt to write the new data. */
449 nbytes = 0;
450 else if ((nbytes = write(fd, p, size)) < 0) {
451 if (ERRNO_IO_RETRY(errno))
452 nbytes = 0;
453 else
454 return BUFFER_ERROR;
455 }
456 /* Add any remaining data to the buffer. */
457 {
458 size_t written = nbytes;
459 if (written < size)
460 buffer_put(b, ((const char *)p) + written,
461 size - written);
462 }
463 return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
464}