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