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>