]> git.proxmox.com Git - mirror_frr.git/blob - lib/buffer.c
Merge pull request #2856 from opensourcerouting/bfd-work
[mirror_frr.git] / lib / buffer.c
1 /*
2 * Buffering of output and input.
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.
11 *
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 *
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
20 */
21
22 #include <zebra.h>
23
24 #include "memory.h"
25 #include "buffer.h"
26 #include "log.h"
27 #include "network.h"
28 #include "lib_errors.h"
29
30 #include <stddef.h>
31
32 DEFINE_MTYPE_STATIC(LIB, BUFFER, "Buffer")
33 DEFINE_MTYPE_STATIC(LIB, BUFFER_DATA, "Buffer data")
34
35 /* Buffer master. */
36 struct 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;
43 };
44
45 /* Data container. */
46 struct buffer_data {
47 struct buffer_data *next;
48
49 /* Location to add new data. */
50 size_t cp;
51
52 /* Pointer to data not yet flushed. */
53 size_t sp;
54
55 /* Actual data stream (variable length). */
56 unsigned char data[]; /* real dimension is buffer->size */
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
65 #define BUFFER_DATA_FREE(D) XFREE(MTYPE_BUFFER_DATA, (D))
66
67 /* Make new buffer. */
68 struct buffer *buffer_new(size_t size)
69 {
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;
84 }
85
86 return b;
87 }
88
89 /* Free buffer. */
90 void buffer_free(struct buffer *b)
91 {
92 buffer_reset(b);
93 XFREE(MTYPE_BUFFER, b);
94 }
95
96 /* Make string clone. */
97 char *buffer_getstr(struct buffer *b)
98 {
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;
115 }
116
117 /* Return 1 if buffer is empty. */
118 int buffer_empty(struct buffer *b)
119 {
120 return (b->head == NULL);
121 }
122
123 /* Clear and free all allocated data. */
124 void buffer_reset(struct buffer *b)
125 {
126 struct buffer_data *data;
127 struct buffer_data *next;
128
129 for (data = b->head; data; data = next) {
130 next = data->next;
131 BUFFER_DATA_FREE(data);
132 }
133 b->head = b->tail = NULL;
134 }
135
136 /* Add buffer_data to the end of buffer. */
137 static struct buffer_data *buffer_add(struct buffer *b)
138 {
139 struct buffer_data *d;
140
141 d = XMALLOC(MTYPE_BUFFER_DATA,
142 offsetof(struct buffer_data, data) + b->size);
143 d->cp = d->sp = 0;
144 d->next = NULL;
145
146 if (b->tail)
147 b->tail->next = d;
148 else
149 b->head = d;
150 b->tail = d;
151
152 return d;
153 }
154
155 /* Write data to buffer. */
156 void buffer_put(struct buffer *b, const void *p, size_t size)
157 {
158 struct buffer_data *data = b->tail;
159 const char *ptr = p;
160
161 /* We use even last one byte of data buffer. */
162 while (size) {
163 size_t chunk;
164
165 /* If there is no data buffer add it. */
166 if (data == NULL || data->cp == b->size)
167 data = buffer_add(b);
168
169 chunk = ((size <= (b->size - data->cp)) ? size
170 : (b->size - data->cp));
171 memcpy((data->data + data->cp), ptr, chunk);
172 size -= chunk;
173 ptr += chunk;
174 data->cp += chunk;
175 }
176 }
177
178 /* Insert character into the buffer. */
179 void buffer_putc(struct buffer *b, uint8_t c)
180 {
181 buffer_put(b, &c, 1);
182 }
183
184 /* Put string to the buffer. */
185 void buffer_putstr(struct buffer *b, const char *c)
186 {
187 buffer_put(b, c, strlen(c));
188 }
189
190 /* Expand \n to \r\n */
191 void buffer_put_crlf(struct buffer *b, const void *origp, size_t origsize)
192 {
193 struct buffer_data *data = b->tail;
194 const char *p = origp, *end = p + origsize, *lf;
195 size_t size;
196
197 lf = memchr(p, '\n', end - p);
198
199 /* We use even last one byte of data buffer. */
200 while (p < end) {
201 size_t avail, chunk;
202
203 /* If there is no data buffer add it. */
204 if (data == NULL || data->cp == b->size)
205 data = buffer_add(b);
206
207 size = (lf ? lf : end) - p;
208 avail = b->size - data->cp;
209
210 chunk = (size <= avail) ? size : avail;
211 memcpy(data->data + data->cp, p, chunk);
212
213 p += chunk;
214 data->cp += chunk;
215
216 if (lf && size <= avail) {
217 /* we just copied up to (including) a '\n' */
218 if (data->cp == b->size)
219 data = buffer_add(b);
220 data->data[data->cp++] = '\r';
221 if (data->cp == b->size)
222 data = buffer_add(b);
223 data->data[data->cp++] = '\n';
224
225 p++;
226 lf = memchr(p, '\n', end - p);
227 }
228 }
229 }
230
231 /* Keep flushing data to the fd until the buffer is empty or an error is
232 encountered or the operation would block. */
233 buffer_status_t buffer_flush_all(struct buffer *b, int fd)
234 {
235 buffer_status_t ret;
236 struct buffer_data *head;
237 size_t head_sp;
238
239 if (!b->head)
240 return BUFFER_EMPTY;
241 head_sp = (head = b->head)->sp;
242 /* Flush all data. */
243 while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
244 if ((b->head == head) && (head_sp == head->sp)
245 && (errno != EINTR))
246 /* No data was flushed, so kernel buffer must be full.
247 */
248 return ret;
249 head_sp = (head = b->head)->sp;
250 }
251
252 return ret;
253 }
254
255 /* Flush enough data to fill a terminal window of the given scene (used only
256 by vty telnet interface). */
257 buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width,
258 int height, int erase_flag,
259 int no_more_flag)
260 {
261 int nbytes;
262 int iov_alloc;
263 int iov_index;
264 struct iovec *iov;
265 struct iovec small_iov[3];
266 char more[] = " --More-- ";
267 char erase[] = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
268 0x08, 0x08, ' ', ' ', ' ', ' ', ' ', ' ',
269 ' ', ' ', ' ', ' ', 0x08, 0x08, 0x08, 0x08,
270 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
271 struct buffer_data *data;
272 int column;
273
274 if (!b->head)
275 return BUFFER_EMPTY;
276
277 if (height < 1) {
278 zlog_warn(
279 "%s called with non-positive window height %d, forcing to 1",
280 __func__, height);
281 height = 1;
282 } else if (height >= 2)
283 height--;
284 if (width < 1) {
285 zlog_warn(
286 "%s called with non-positive window width %d, forcing to 1",
287 __func__, width);
288 width = 1;
289 }
290
291 /* For erase and more data add two to b's buffer_data count.*/
292 if (b->head->next == NULL) {
293 iov_alloc = array_size(small_iov);
294 iov = small_iov;
295 } else {
296 iov_alloc = ((height * (width + 2)) / b->size) + 10;
297 iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
298 }
299 iov_index = 0;
300
301 /* Previously print out is performed. */
302 if (erase_flag) {
303 iov[iov_index].iov_base = erase;
304 iov[iov_index].iov_len = sizeof erase;
305 iov_index++;
306 }
307
308 /* Output data. */
309 column = 1; /* Column position of next character displayed. */
310 for (data = b->head; data && (height > 0); data = data->next) {
311 size_t cp;
312
313 cp = data->sp;
314 while ((cp < data->cp) && (height > 0)) {
315 /* Calculate lines remaining and column position after
316 displaying
317 this character. */
318 if (data->data[cp] == '\r')
319 column = 1;
320 else if ((data->data[cp] == '\n')
321 || (column == width)) {
322 column = 1;
323 height--;
324 } else
325 column++;
326 cp++;
327 }
328 iov[iov_index].iov_base = (char *)(data->data + data->sp);
329 iov[iov_index++].iov_len = cp - data->sp;
330 data->sp = cp;
331
332 if (iov_index == iov_alloc)
333 /* This should not ordinarily happen. */
334 {
335 iov_alloc *= 2;
336 if (iov != small_iov) {
337 zlog_warn(
338 "%s: growing iov array to %d; "
339 "width %d, height %d, size %lu",
340 __func__, iov_alloc, width, height,
341 (unsigned long)b->size);
342 iov = XREALLOC(MTYPE_TMP, iov,
343 iov_alloc * sizeof(*iov));
344 } else {
345 /* This should absolutely never occur. */
346 flog_err_sys(
347 LIB_ERR_SYSTEM_CALL,
348 "%s: corruption detected: iov_small overflowed; "
349 "head %p, tail %p, head->next %p",
350 __func__, (void *)b->head,
351 (void *)b->tail, (void *)b->head->next);
352 iov = XMALLOC(MTYPE_TMP,
353 iov_alloc * sizeof(*iov));
354 memcpy(iov, small_iov, sizeof(small_iov));
355 }
356 }
357 }
358
359 /* In case of `more' display need. */
360 if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
361 iov[iov_index].iov_base = more;
362 iov[iov_index].iov_len = sizeof more;
363 iov_index++;
364 }
365
366
367 #ifdef IOV_MAX
368 /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
369 example: Solaris2.6 are defined IOV_MAX size at 16. */
370 {
371 struct iovec *c_iov = iov;
372 nbytes = 0; /* Make sure it's initialized. */
373
374 while (iov_index > 0) {
375 int iov_size;
376
377 iov_size =
378 ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
379 if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
380 zlog_warn("%s: writev to fd %d failed: %s",
381 __func__, fd, safe_strerror(errno));
382 break;
383 }
384
385 /* move pointer io-vector */
386 c_iov += iov_size;
387 iov_index -= iov_size;
388 }
389 }
390 #else /* IOV_MAX */
391 if ((nbytes = writev(fd, iov, iov_index)) < 0)
392 zlog_warn("%s: writev to fd %d failed: %s", __func__, fd,
393 safe_strerror(errno));
394 #endif /* IOV_MAX */
395
396 /* Free printed buffer data. */
397 while (b->head && (b->head->sp == b->head->cp)) {
398 struct buffer_data *del;
399 if (!(b->head = (del = b->head)->next))
400 b->tail = NULL;
401 BUFFER_DATA_FREE(del);
402 }
403
404 if (iov != small_iov)
405 XFREE(MTYPE_TMP, iov);
406
407 return (nbytes < 0) ? BUFFER_ERROR
408 : (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
409 }
410
411 /* This function (unlike other buffer_flush* functions above) is designed
412 to work with non-blocking sockets. It does not attempt to write out
413 all of the queued data, just a "big" chunk. It returns 0 if it was
414 able to empty out the buffers completely, 1 if more flushing is
415 required later, or -1 on a fatal write error. */
416 buffer_status_t buffer_flush_available(struct buffer *b, int fd)
417 {
418
419 /* These are just reasonable values to make sure a significant amount of
420 data is written. There's no need to go crazy and try to write it all
421 in one shot. */
422 #ifdef IOV_MAX
423 #define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
424 #else
425 #define MAX_CHUNKS 16
426 #endif
427 #define MAX_FLUSH 131072
428
429 struct buffer_data *d;
430 size_t written;
431 struct iovec iov[MAX_CHUNKS];
432 size_t iovcnt = 0;
433 size_t nbyte = 0;
434
435 if (fd < 0)
436 return BUFFER_ERROR;
437
438 for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
439 d = d->next, iovcnt++) {
440 iov[iovcnt].iov_base = d->data + d->sp;
441 nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
442 }
443
444 if (!nbyte)
445 /* No data to flush: should we issue a warning message? */
446 return BUFFER_EMPTY;
447
448 /* only place where written should be sign compared */
449 if ((ssize_t)(written = writev(fd, iov, iovcnt)) < 0) {
450 if (ERRNO_IO_RETRY(errno))
451 /* Calling code should try again later. */
452 return BUFFER_PENDING;
453 zlog_warn("%s: write error on fd %d: %s", __func__, fd,
454 safe_strerror(errno));
455 return BUFFER_ERROR;
456 }
457
458 /* Free printed buffer data. */
459 while (written > 0) {
460 struct buffer_data *d;
461 if (!(d = b->head)) {
462 flog_err(
463 LIB_ERR_DEVELOPMENT,
464 "%s: corruption detected: buffer queue empty, but written is %lu",
465 __func__, (unsigned long)written);
466 break;
467 }
468 if (written < d->cp - d->sp) {
469 d->sp += written;
470 return BUFFER_PENDING;
471 }
472
473 written -= (d->cp - d->sp);
474 if (!(b->head = d->next))
475 b->tail = NULL;
476 BUFFER_DATA_FREE(d);
477 }
478
479 return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
480
481 #undef MAX_CHUNKS
482 #undef MAX_FLUSH
483 }
484
485 buffer_status_t buffer_write(struct buffer *b, int fd, const void *p,
486 size_t size)
487 {
488 ssize_t nbytes;
489
490 #if 0
491 /*
492 * Should we attempt to drain any previously buffered data?
493 * This could help reduce latency in pushing out the data if
494 * we are stuck in a long-running thread that is preventing
495 * the main select loop from calling the flush thread...
496 */
497 if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
498 return BUFFER_ERROR;
499 #endif
500 if (b->head)
501 /* Buffer is not empty, so do not attempt to write the new data.
502 */
503 nbytes = 0;
504 else if ((nbytes = write(fd, p, size)) < 0) {
505 if (ERRNO_IO_RETRY(errno))
506 nbytes = 0;
507 else {
508 zlog_warn("%s: write error on fd %d: %s", __func__, fd,
509 safe_strerror(errno));
510 return BUFFER_ERROR;
511 }
512 }
513 /* Add any remaining data to the buffer. */
514 {
515 size_t written = nbytes;
516 if (written < size)
517 buffer_put(b, ((const char *)p) + written,
518 size - written);
519 }
520 return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
521 }