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

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: 
1.1.1.3 ! misho     151:   d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data) + b->size);
1.1       misho     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",
1.1.1.3 ! misho     325:                       __func__, (void *)b->head, (void *)b->tail,
        !           326:                       (void *)b->head->next);
1.1       misho     327:              iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
                    328:              memcpy(iov, small_iov, sizeof(small_iov));
                    329:            }
                    330:        }
                    331:     }
                    332: 
                    333:   /* In case of `more' display need. */
                    334:   if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag)
                    335:     {
                    336:       iov[iov_index].iov_base = more;
                    337:       iov[iov_index].iov_len = sizeof more;
                    338:       iov_index++;
                    339:     }
                    340: 
                    341: 
                    342: #ifdef IOV_MAX
                    343:   /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
                    344:      example: Solaris2.6 are defined IOV_MAX size at 16.     */
                    345:   {
                    346:     struct iovec *c_iov = iov;
                    347:     nbytes = 0; /* Make sure it's initialized. */
                    348: 
                    349:     while (iov_index > 0)
                    350:       {
                    351:         int iov_size;
                    352: 
                    353:         iov_size = ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
                    354:         if ((nbytes = writev(fd, c_iov, iov_size)) < 0)
                    355:           {
                    356:             zlog_warn("%s: writev to fd %d failed: %s",
                    357:                       __func__, fd, safe_strerror(errno));
                    358:             break;
                    359:           }
                    360: 
                    361:         /* move pointer io-vector */
                    362:         c_iov += iov_size;
                    363:         iov_index -= iov_size;
                    364:       }
                    365:   }
                    366: #else  /* IOV_MAX */
                    367:    if ((nbytes = writev (fd, iov, iov_index)) < 0)
                    368:      zlog_warn("%s: writev to fd %d failed: %s",
                    369:               __func__, fd, safe_strerror(errno));
                    370: #endif /* IOV_MAX */
                    371: 
                    372:   /* Free printed buffer data. */
                    373:   while (b->head && (b->head->sp == b->head->cp))
                    374:     {
                    375:       struct buffer_data *del;
                    376:       if (!(b->head = (del = b->head)->next))
                    377:         b->tail = NULL;
                    378:       BUFFER_DATA_FREE(del);
                    379:     }
                    380: 
                    381:   if (iov != small_iov)
                    382:     XFREE (MTYPE_TMP, iov);
                    383: 
                    384:   return (nbytes < 0) ? BUFFER_ERROR :
                    385:                        (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
                    386: }
                    387: 
                    388: /* This function (unlike other buffer_flush* functions above) is designed
                    389: to work with non-blocking sockets.  It does not attempt to write out
                    390: all of the queued data, just a "big" chunk.  It returns 0 if it was
                    391: able to empty out the buffers completely, 1 if more flushing is
                    392: required later, or -1 on a fatal write error. */
                    393: buffer_status_t
                    394: buffer_flush_available(struct buffer *b, int fd)
                    395: {
                    396: 
                    397: /* These are just reasonable values to make sure a significant amount of
                    398: data is written.  There's no need to go crazy and try to write it all
                    399: in one shot. */
                    400: #ifdef IOV_MAX
                    401: #define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
                    402: #else
                    403: #define MAX_CHUNKS 16
                    404: #endif
                    405: #define MAX_FLUSH 131072
                    406: 
                    407:   struct buffer_data *d;
                    408:   size_t written;
                    409:   struct iovec iov[MAX_CHUNKS];
                    410:   size_t iovcnt = 0;
                    411:   size_t nbyte = 0;
                    412: 
                    413:   for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
                    414:        d = d->next, iovcnt++)
                    415:     {
                    416:       iov[iovcnt].iov_base = d->data+d->sp;
                    417:       nbyte += (iov[iovcnt].iov_len = d->cp-d->sp);
                    418:     }
                    419: 
                    420:   if (!nbyte)
                    421:     /* No data to flush: should we issue a warning message? */
                    422:     return BUFFER_EMPTY;
                    423: 
                    424:   /* only place where written should be sign compared */
                    425:   if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0)
                    426:     {
                    427:       if (ERRNO_IO_RETRY(errno))
                    428:        /* Calling code should try again later. */
                    429:         return BUFFER_PENDING;
                    430:       zlog_warn("%s: write error on fd %d: %s",
                    431:                __func__, fd, safe_strerror(errno));
                    432:       return BUFFER_ERROR;
                    433:     }
                    434: 
                    435:   /* Free printed buffer data. */
                    436:   while (written > 0)
                    437:     {
                    438:       struct buffer_data *d;
                    439:       if (!(d = b->head))
                    440:         {
                    441:           zlog_err("%s: corruption detected: buffer queue empty, "
                    442:                   "but written is %lu", __func__, (u_long)written);
                    443:          break;
                    444:         }
                    445:       if (written < d->cp-d->sp)
                    446:         {
                    447:          d->sp += written;
                    448:          return BUFFER_PENDING;
                    449:        }
                    450: 
                    451:       written -= (d->cp-d->sp);
                    452:       if (!(b->head = d->next))
                    453:         b->tail = NULL;
                    454:       BUFFER_DATA_FREE(d);
                    455:     }
                    456: 
                    457:   return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
                    458: 
                    459: #undef MAX_CHUNKS
                    460: #undef MAX_FLUSH
                    461: }
                    462: 
                    463: buffer_status_t
                    464: buffer_write(struct buffer *b, int fd, const void *p, size_t size)
                    465: {
                    466:   ssize_t nbytes;
                    467: 
                    468: #if 0
                    469:   /* Should we attempt to drain any previously buffered data?  This could help
                    470:      reduce latency in pushing out the data if we are stuck in a long-running
                    471:      thread that is preventing the main select loop from calling the flush
                    472:      thread... */
                    473:   if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
                    474:     return BUFFER_ERROR;
                    475: #endif
                    476:   if (b->head)
                    477:     /* Buffer is not empty, so do not attempt to write the new data. */
                    478:     nbytes = 0;
                    479:   else if ((nbytes = write(fd, p, size)) < 0)
                    480:     {
                    481:       if (ERRNO_IO_RETRY(errno))
                    482:         nbytes = 0;
                    483:       else
                    484:         {
                    485:          zlog_warn("%s: write error on fd %d: %s",
                    486:                    __func__, fd, safe_strerror(errno));
                    487:          return BUFFER_ERROR;
                    488:        }
                    489:     }
                    490:   /* Add any remaining data to the buffer. */
                    491:   {
                    492:     size_t written = nbytes;
                    493:     if (written < size)
                    494:       buffer_put(b, ((const char *)p)+written, size-written);
                    495:   }
                    496:   return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
                    497: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>