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