]> git.proxmox.com Git - mirror_frr.git/blame - lib/buffer.c
2004-12-29 Greg Troxel <gdt@poblano.ir.bbn.com>
[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"
49ff6d9d 28#include <stddef.h>
718e3744 29
30/* Make buffer data. */
49ff6d9d 31static struct buffer_data *
718e3744 32buffer_data_new (size_t size)
33{
34 struct buffer_data *d;
35
49ff6d9d 36 d = XMALLOC (MTYPE_BUFFER_DATA, offsetof(struct buffer_data,data[size]));
37 d->cp = d->sp = 0;
718e3744 38 return d;
39}
40
49ff6d9d 41static void
718e3744 42buffer_data_free (struct buffer_data *d)
43{
718e3744 44 XFREE (MTYPE_BUFFER_DATA, d);
45}
46
47/* Make new buffer. */
48struct buffer *
49buffer_new (size_t size)
50{
51 struct buffer *b;
52
53 b = XMALLOC (MTYPE_BUFFER, sizeof (struct buffer));
54 memset (b, 0, sizeof (struct buffer));
55
56 b->size = size;
57
58 return b;
59}
60
61/* Free buffer. */
62void
63buffer_free (struct buffer *b)
64{
65 struct buffer_data *d;
66 struct buffer_data *next;
67
68 d = b->head;
69 while (d)
70 {
71 next = d->next;
72 buffer_data_free (d);
73 d = next;
74 }
75
76 d = b->unused_head;
77 while (d)
78 {
79 next = d->next;
80 buffer_data_free (d);
81 d = next;
82 }
83
84 XFREE (MTYPE_BUFFER, b);
85}
86
87/* Make string clone. */
88char *
89buffer_getstr (struct buffer *b)
90{
91 return strdup ((char *)b->head->data);
92}
93
94/* Return 1 if buffer is empty. */
95int
96buffer_empty (struct buffer *b)
97{
98 if (b->tail == NULL || b->tail->cp == b->tail->sp)
99 return 1;
100 else
101 return 0;
102}
103
104/* Clear and free all allocated data. */
105void
106buffer_reset (struct buffer *b)
107{
108 struct buffer_data *data;
109 struct buffer_data *next;
110
111 for (data = b->head; data; data = next)
112 {
113 next = data->next;
114 buffer_data_free (data);
115 }
116 b->head = b->tail = NULL;
117 b->alloc = 0;
118 b->length = 0;
119}
120
121/* Add buffer_data to the end of buffer. */
122void
123buffer_add (struct buffer *b)
124{
125 struct buffer_data *d;
126
127 d = buffer_data_new (b->size);
128
129 if (b->tail == NULL)
130 {
131 d->prev = NULL;
132 d->next = NULL;
133 b->head = d;
134 b->tail = d;
135 }
136 else
137 {
138 d->prev = b->tail;
139 d->next = NULL;
140
141 b->tail->next = d;
142 b->tail = d;
143 }
144
145 b->alloc++;
146}
147
148/* Write data to buffer. */
149int
9035efaa 150buffer_write (struct buffer *b, const void *p, size_t size)
718e3744 151{
152 struct buffer_data *data;
9035efaa 153 const char *ptr = p;
718e3744 154 data = b->tail;
155 b->length += size;
156
157 /* We use even last one byte of data buffer. */
158 while (size)
159 {
49ff6d9d 160 size_t chunk;
161
718e3744 162 /* If there is no data buffer add it. */
163 if (data == NULL || data->cp == b->size)
164 {
165 buffer_add (b);
166 data = b->tail;
167 }
168
49ff6d9d 169 chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp));
170 memcpy ((data->data + data->cp), ptr, chunk);
171 size -= chunk;
172 ptr += chunk;
173 data->cp += chunk;
718e3744 174 }
175 return 1;
176}
177
178/* Insert character into the buffer. */
179int
180buffer_putc (struct buffer *b, u_char c)
181{
182 buffer_write (b, &c, 1);
183 return 1;
184}
185
186/* Insert word (2 octets) into ther buffer. */
187int
188buffer_putw (struct buffer *b, u_short c)
189{
190 buffer_write (b, (char *)&c, 2);
191 return 1;
192}
193
194/* Put string to the buffer. */
195int
9035efaa 196buffer_putstr (struct buffer *b, const char *c)
718e3744 197{
198 size_t size;
199
5228ad27 200 size = strlen (c);
201 buffer_write (b, (void *) c, size);
718e3744 202 return 1;
203}
204
205/* Flush specified size to the fd. */
206void
207buffer_flush (struct buffer *b, int fd, size_t size)
208{
209 int iov_index;
210 struct iovec *iovec;
211 struct buffer_data *data;
212 struct buffer_data *out;
213 struct buffer_data *next;
214
215 iovec = malloc (sizeof (struct iovec) * b->alloc);
216 iov_index = 0;
217
218 for (data = b->head; data; data = data->next)
219 {
220 iovec[iov_index].iov_base = (char *)(data->data + data->sp);
221
222 if (size <= (data->cp - data->sp))
223 {
224 iovec[iov_index++].iov_len = size;
225 data->sp += size;
49ff6d9d 226 b->length -= size;
718e3744 227 if (data->sp == data->cp)
228 data = data->next;
229 break;
230 }
231 else
232 {
233 iovec[iov_index++].iov_len = data->cp - data->sp;
49ff6d9d 234 b->length -= (data->cp - data->sp);
718e3744 235 size -= data->cp - data->sp;
236 data->sp = data->cp;
237 }
238 }
239
240 /* Write buffer to the fd. */
241 writev (fd, iovec, iov_index);
242
243 /* Free printed buffer data. */
244 for (out = b->head; out && out != data; out = next)
245 {
246 next = out->next;
247 if (next)
248 next->prev = NULL;
249 else
250 b->tail = next;
251 b->head = next;
252
253 buffer_data_free (out);
254 b->alloc--;
255 }
256
257 free (iovec);
258}
259
260/* Flush all buffer to the fd. */
261int
262buffer_flush_all (struct buffer *b, int fd)
263{
264 int ret;
265 struct buffer_data *d;
266 int iov_index;
267 struct iovec *iovec;
268
269 if (buffer_empty (b))
270 return 0;
271
272 iovec = malloc (sizeof (struct iovec) * b->alloc);
273 iov_index = 0;
274
275 for (d = b->head; d; d = d->next)
276 {
277 iovec[iov_index].iov_base = (char *)(d->data + d->sp);
278 iovec[iov_index].iov_len = d->cp - d->sp;
279 iov_index++;
280 }
281 ret = writev (fd, iovec, iov_index);
282
283 free (iovec);
284
285 buffer_reset (b);
286
287 return ret;
288}
289
290/* Flush all buffer to the fd. */
291int
292buffer_flush_vty_all (struct buffer *b, int fd, int erase_flag,
293 int no_more_flag)
294{
295 int nbytes;
296 int iov_index;
297 struct iovec *iov;
298 struct iovec small_iov[3];
299 char more[] = " --More-- ";
300 char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
301 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
302 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
303 struct buffer_data *data;
304 struct buffer_data *out;
305 struct buffer_data *next;
306
307 /* For erase and more data add two to b's buffer_data count.*/
308 if (b->alloc == 1)
309 iov = small_iov;
310 else
311 iov = XCALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2));
312
313 data = b->head;
314 iov_index = 0;
315
316 /* Previously print out is performed. */
317 if (erase_flag)
318 {
319 iov[iov_index].iov_base = erase;
320 iov[iov_index].iov_len = sizeof erase;
321 iov_index++;
322 }
323
324 /* Output data. */
325 for (data = b->head; data; data = data->next)
326 {
327 iov[iov_index].iov_base = (char *)(data->data + data->sp);
328 iov[iov_index].iov_len = data->cp - data->sp;
329 iov_index++;
330 }
331
332 /* In case of `more' display need. */
333 if (! buffer_empty (b) && !no_more_flag)
334 {
335 iov[iov_index].iov_base = more;
336 iov[iov_index].iov_len = sizeof more;
337 iov_index++;
338 }
339
340 /* We use write or writev*/
341 nbytes = writev (fd, iov, iov_index);
342
343 /* Error treatment. */
344 if (nbytes < 0)
345 {
346 if (errno == EINTR)
347 ;
348 if (errno == EWOULDBLOCK)
349 ;
350 }
351
352 /* Free printed buffer data. */
353 for (out = b->head; out && out != data; out = next)
354 {
355 next = out->next;
356 if (next)
357 next->prev = NULL;
358 else
359 b->tail = next;
360 b->head = next;
361
49ff6d9d 362 b->length -= (out->cp-out->sp);
718e3744 363 buffer_data_free (out);
364 b->alloc--;
365 }
366
367 if (iov != small_iov)
368 XFREE (MTYPE_TMP, iov);
369
370 return nbytes;
371}
372
373/* Flush buffer to the file descriptor. Mainly used from vty
374 interface. */
375int
8c328f11 376buffer_flush_vty (struct buffer *b, int fd, unsigned int size,
718e3744 377 int erase_flag, int no_more_flag)
378{
379 int nbytes;
380 int iov_index;
381 struct iovec *iov;
382 struct iovec small_iov[3];
383 char more[] = " --More-- ";
384 char erase[] = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
385 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
386 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
387 struct buffer_data *data;
388 struct buffer_data *out;
389 struct buffer_data *next;
390
391#ifdef IOV_MAX
392 int iov_size;
393 int total_size;
394 struct iovec *c_iov;
395 int c_nbytes;
396#endif /* IOV_MAX */
397
398 /* For erase and more data add two to b's buffer_data count.*/
399 if (b->alloc == 1)
400 iov = small_iov;
401 else
402 iov = XCALLOC (MTYPE_TMP, sizeof (struct iovec) * (b->alloc + 2));
403
404 data = b->head;
405 iov_index = 0;
406
407 /* Previously print out is performed. */
408 if (erase_flag)
409 {
410 iov[iov_index].iov_base = erase;
411 iov[iov_index].iov_len = sizeof erase;
412 iov_index++;
413 }
414
415 /* Output data. */
416 for (data = b->head; data; data = data->next)
417 {
418 iov[iov_index].iov_base = (char *)(data->data + data->sp);
419
420 if (size <= (data->cp - data->sp))
421 {
422 iov[iov_index++].iov_len = size;
423 data->sp += size;
49ff6d9d 424 b->length -= size;
718e3744 425 if (data->sp == data->cp)
426 data = data->next;
427 break;
428 }
429 else
430 {
431 iov[iov_index++].iov_len = data->cp - data->sp;
432 size -= (data->cp - data->sp);
49ff6d9d 433 b->length -= (data->cp - data->sp);
718e3744 434 data->sp = data->cp;
435 }
436 }
437
438 /* In case of `more' display need. */
439 if (!buffer_empty (b) && !no_more_flag)
440 {
441 iov[iov_index].iov_base = more;
442 iov[iov_index].iov_len = sizeof more;
443 iov_index++;
444 }
445
446 /* We use write or writev*/
447
448#ifdef IOV_MAX
449 /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
450 example: Solaris2.6 are defined IOV_MAX size at 16. */
451 c_iov = iov;
452 total_size = iov_index;
453 nbytes = 0;
454
455 while( total_size > 0 )
456 {
457 /* initialize write vector size at once */
458 iov_size = ( total_size > IOV_MAX ) ? IOV_MAX : total_size;
459
460 c_nbytes = writev (fd, c_iov, iov_size );
461
462 if( c_nbytes < 0 )
463 {
464 if(errno == EINTR)
465 ;
466 ;
467 if(errno == EWOULDBLOCK)
468 ;
469 ;
470 nbytes = c_nbytes;
471 break;
472
473 }
474
475 nbytes += c_nbytes;
476
477 /* move pointer io-vector */
478 c_iov += iov_size;
479 total_size -= iov_size;
480 }
481#else /* IOV_MAX */
482 nbytes = writev (fd, iov, iov_index);
483
484 /* Error treatment. */
485 if (nbytes < 0)
486 {
487 if (errno == EINTR)
488 ;
489 if (errno == EWOULDBLOCK)
490 ;
491 }
492#endif /* IOV_MAX */
493
494 /* Free printed buffer data. */
495 for (out = b->head; out && out != data; out = next)
496 {
497 next = out->next;
498 if (next)
499 next->prev = NULL;
500 else
501 b->tail = next;
502 b->head = next;
503
504 buffer_data_free (out);
505 b->alloc--;
506 }
507
508 if (iov != small_iov)
509 XFREE (MTYPE_TMP, iov);
510
511 return nbytes;
512}
513
514/* Calculate size of outputs then flush buffer to the file
515 descriptor. */
516int
517buffer_flush_window (struct buffer *b, int fd, int width, int height,
518 int erase, int no_more)
519{
520 unsigned long cp;
521 unsigned long size;
522 int lp;
523 int lineno;
524 struct buffer_data *data;
525
526 if (height >= 2)
527 height--;
528
529 /* We have to calculate how many bytes should be written. */
530 lp = 0;
531 lineno = 0;
532 size = 0;
533
534 for (data = b->head; data; data = data->next)
535 {
536 cp = data->sp;
537
538 while (cp < data->cp)
539 {
540 if (data->data[cp] == '\n' || lp == width)
541 {
542 lineno++;
543 if (lineno == height)
544 {
545 cp++;
546 size++;
547 goto flush;
548 }
549 lp = 0;
550 }
551 cp++;
552 lp++;
553 size++;
554 }
555 }
556
557 /* Write data to the file descriptor. */
558 flush:
559
560 return buffer_flush_vty (b, fd, size, erase, no_more);
561}
49ff6d9d 562
563/* This function (unlike other buffer_flush* functions above) is designed
564to work with non-blocking sockets. It does not attempt to write out
565all of the queued data, just a "big" chunk. It returns 0 if it was
566able to empty out the buffers completely, or 1 if more flushing is
567required later. */
568int
569buffer_flush_available(struct buffer *b, int fd)
570{
571
572/* These are just reasonable values to make sure a significant amount of
573data is written. There's no need to go crazy and try to write it all
574in one shot. */
575#ifdef IOV_MAX
576#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
577#else
578#define MAX_CHUNKS 16
579#endif
580#define MAX_FLUSH 131072
581
582 struct buffer_data *d;
583 struct buffer_data *next;
2265d20c 584 size_t written;
49ff6d9d 585 struct iovec iov[MAX_CHUNKS];
2265d20c 586 size_t iovcnt = 0;
49ff6d9d 587 size_t nbyte = 0;
588
589 for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
590 d = d->next, iovcnt++)
591 {
592 iov[iovcnt].iov_base = d->data+d->sp;
593 nbyte += (iov[iovcnt].iov_len = d->cp-d->sp);
594 }
595
2265d20c 596 /* only place where written should be sign compared */
597 if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0)
49ff6d9d 598 {
599 if ((errno != EAGAIN) && (errno != EINTR))
600 zlog_warn("buffer_flush_available write error on fd %d: %s",
6099b3b5 601 fd,safe_strerror(errno));
49ff6d9d 602 return 1;
603 }
604
605 /* Free printed buffer data. */
606 for (d = b->head; (written > 0) && d; d = next)
607 {
608 if (written < d->cp-d->sp)
609 {
610 d->sp += written;
611 b->length -= written;
612 return 1;
613 }
614
615 written -= (d->cp-d->sp);
616 next = d->next;
617 if (next)
618 next->prev = NULL;
619 else
620 b->tail = next;
621 b->head = next;
622
623 b->length -= (d->cp-d->sp);
624 buffer_data_free (d);
625 b->alloc--;
626 }
627
628 return (b->head != NULL);
629
630#undef MAX_CHUNKS
631#undef MAX_FLUSH
632}