Annotation of embedaddon/quagga/lib/buffer.c, revision 1.1.1.2

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:     {
1.1.1.2 ! misho     265:       iov_alloc = array_size(small_iov);
1.1       misho     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>