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