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