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