LCOV - code coverage report
Current view: top level - lib - buffer.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 57 178 32.0 %
Date: 2015-11-19 Functions: 7 13 53.8 %

          Line data    Source code
       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         143 : buffer_new (size_t size)
      71             : {
      72             :   struct buffer *b;
      73             : 
      74         143 :   b = XCALLOC (MTYPE_BUFFER, sizeof (struct buffer));
      75             : 
      76         143 :   if (size)
      77           0 :     b->size = size;
      78             :   else
      79             :     {
      80             :       static size_t default_size;
      81         143 :       if (!default_size)
      82             :         {
      83          46 :           long pgsz = sysconf(_SC_PAGESIZE);
      84          46 :           default_size = ((((BUFFER_SIZE_DEFAULT-1)/pgsz)+1)*pgsz);
      85             :         }
      86         143 :       b->size = default_size;
      87             :     }
      88             : 
      89         143 :   return b;
      90             : }
      91             : 
      92             : /* Free buffer. */
      93             : void
      94          91 : buffer_free (struct buffer *b)
      95             : {
      96          91 :   buffer_reset(b);
      97          91 :   XFREE (MTYPE_BUFFER, b);
      98          91 : }
      99             : 
     100             : /* Make string clone. */
     101             : char *
     102           0 : buffer_getstr (struct buffer *b)
     103             : {
     104           0 :   size_t totlen = 0;
     105             :   struct buffer_data *data;
     106             :   char *s;
     107             :   char *p;
     108             : 
     109           0 :   for (data = b->head; data; data = data->next)
     110           0 :     totlen += data->cp - data->sp;
     111           0 :   if (!(s = XMALLOC(MTYPE_TMP, totlen+1)))
     112           0 :     return NULL;
     113           0 :   p = s;
     114           0 :   for (data = b->head; data; data = data->next)
     115             :     {
     116           0 :       memcpy(p, data->data + data->sp, data->cp - data->sp);
     117           0 :       p += data->cp - data->sp;
     118             :     }
     119           0 :   *p = '\0';
     120           0 :   return s;
     121             : }
     122             : 
     123             : /* Return 1 if buffer is empty. */
     124             : int
     125           0 : buffer_empty (struct buffer *b)
     126             : {
     127           0 :   return (b->head == NULL);
     128             : }
     129             : 
     130             : /* Clear and free all allocated data. */
     131             : void
     132          93 : buffer_reset (struct buffer *b)
     133             : {
     134             :   struct buffer_data *data;
     135             :   struct buffer_data *next;
     136             :   
     137          93 :   for (data = b->head; data; data = next)
     138             :     {
     139           0 :       next = data->next;
     140           0 :       BUFFER_DATA_FREE(data);
     141             :     }
     142          93 :   b->head = b->tail = NULL;
     143          93 : }
     144             : 
     145             : /* Add buffer_data to the end of buffer. */
     146             : static struct buffer_data *
     147         628 : buffer_add (struct buffer *b)
     148             : {
     149             :   struct buffer_data *d;
     150             : 
     151         628 :   d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data[b->size]));
     152         628 :   d->cp = d->sp = 0;
     153         628 :   d->next = NULL;
     154             : 
     155         628 :   if (b->tail)
     156           2 :     b->tail->next = d;
     157             :   else
     158         626 :     b->head = d;
     159         628 :   b->tail = d;
     160             : 
     161         628 :   return d;
     162             : }
     163             : 
     164             : /* Write data to buffer. */
     165             : void
     166       14124 : buffer_put(struct buffer *b, const void *p, size_t size)
     167             : {
     168       14124 :   struct buffer_data *data = b->tail;
     169       14124 :   const char *ptr = p;
     170             : 
     171             :   /* We use even last one byte of data buffer. */
     172       42374 :   while (size)    
     173             :     {
     174             :       size_t chunk;
     175             : 
     176             :       /* If there is no data buffer add it. */
     177       14126 :       if (data == NULL || data->cp == b->size)
     178         628 :         data = buffer_add (b);
     179             : 
     180       14126 :       chunk = ((size <= (b->size - data->cp)) ? size : (b->size - data->cp));
     181       14126 :       memcpy ((data->data + data->cp), ptr, chunk);
     182       14126 :       size -= chunk;
     183       14126 :       ptr += chunk;
     184       14126 :       data->cp += chunk;
     185             :     }
     186       14124 : }
     187             : 
     188             : /* Insert character into the buffer. */
     189             : void
     190           0 : buffer_putc (struct buffer *b, u_char c)
     191             : {
     192           0 :   buffer_put(b, &c, 1);
     193           0 : }
     194             : 
     195             : /* Put string to the buffer. */
     196             : void
     197           0 : buffer_putstr (struct buffer *b, const char *c)
     198             : {
     199           0 :   buffer_put(b, c, strlen(c));
     200           0 : }
     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          47 : 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          47 :   if (!b->head)
     212          47 :     return BUFFER_EMPTY;
     213           0 :   head_sp = (head = b->head)->sp;
     214             :   /* Flush all data. */
     215           0 :   while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING)
     216             :     {
     217           0 :       if ((b->head == head) && (head_sp == head->sp) && (errno != EINTR))
     218             :         /* No data was flushed, so kernel buffer must be full. */
     219           0 :         return ret;
     220           0 :       head_sp = (head = b->head)->sp;
     221             :     }
     222             : 
     223           0 :   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           0 : 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           0 :   char more[] = " --More-- ";
     238           0 :   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           0 :   if (!b->head)
     245           0 :     return BUFFER_EMPTY;
     246             : 
     247           0 :   if (height < 1)
     248             :     {
     249           0 :       zlog_warn("%s called with non-positive window height %d, forcing to 1",
     250             :                 __func__, height);
     251           0 :       height = 1;
     252             :     }
     253           0 :   else if (height >= 2)
     254           0 :     height--;
     255           0 :   if (width < 1)
     256             :     {
     257           0 :       zlog_warn("%s called with non-positive window width %d, forcing to 1",
     258             :                 __func__, width);
     259           0 :       width = 1;
     260             :     }
     261             : 
     262             :   /* For erase and more data add two to b's buffer_data count.*/
     263           0 :   if (b->head->next == NULL)
     264             :     {
     265           0 :       iov_alloc = array_size(small_iov);
     266           0 :       iov = small_iov;
     267             :     }
     268             :   else
     269             :     {
     270           0 :       iov_alloc = ((height*(width+2))/b->size)+10;
     271           0 :       iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
     272             :     }
     273           0 :   iov_index = 0;
     274             : 
     275             :   /* Previously print out is performed. */
     276           0 :   if (erase_flag)
     277             :     {
     278           0 :       iov[iov_index].iov_base = erase;
     279           0 :       iov[iov_index].iov_len = sizeof erase;
     280           0 :       iov_index++;
     281             :     }
     282             : 
     283             :   /* Output data. */
     284           0 :   column = 1;  /* Column position of next character displayed. */
     285           0 :   for (data = b->head; data && (height > 0); data = data->next)
     286             :     {
     287             :       size_t cp;
     288             : 
     289           0 :       cp = data->sp;
     290           0 :       while ((cp < data->cp) && (height > 0))
     291             :         {
     292             :           /* Calculate lines remaining and column position after displaying
     293             :              this character. */
     294           0 :           if (data->data[cp] == '\r')
     295           0 :             column = 1;
     296           0 :           else if ((data->data[cp] == '\n') || (column == width))
     297             :             {
     298           0 :               column = 1;
     299           0 :               height--;
     300             :             }
     301             :           else
     302           0 :             column++;
     303           0 :           cp++;
     304             :         }
     305           0 :       iov[iov_index].iov_base = (char *)(data->data + data->sp);
     306           0 :       iov[iov_index++].iov_len = cp-data->sp;
     307           0 :       data->sp = cp;
     308             : 
     309           0 :       if (iov_index == iov_alloc)
     310             :         /* This should not ordinarily happen. */
     311             :         {
     312           0 :           iov_alloc *= 2;
     313           0 :           if (iov != small_iov)
     314             :             {
     315           0 :               zlog_warn("%s: growing iov array to %d; "
     316             :                         "width %d, height %d, size %lu",
     317           0 :                         __func__, iov_alloc, width, height, (u_long)b->size);
     318           0 :               iov = XREALLOC(MTYPE_TMP, iov, iov_alloc*sizeof(*iov));
     319             :             }
     320             :           else
     321             :             {
     322             :               /* This should absolutely never occur. */
     323           0 :               zlog_err("%s: corruption detected: iov_small overflowed; "
     324             :                        "head %p, tail %p, head->next %p",
     325           0 :                        __func__, b->head, b->tail, b->head->next);
     326           0 :               iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov));
     327           0 :               memcpy(iov, small_iov, sizeof(small_iov));
     328             :             }
     329             :         }
     330             :     }
     331             : 
     332             :   /* In case of `more' display need. */
     333           0 :   if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag)
     334             :     {
     335           0 :       iov[iov_index].iov_base = more;
     336           0 :       iov[iov_index].iov_len = sizeof more;
     337           0 :       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           0 :     struct iovec *c_iov = iov;
     346           0 :     nbytes = 0; /* Make sure it's initialized. */
     347             : 
     348           0 :     while (iov_index > 0)
     349             :       {
     350             :          int iov_size;
     351             : 
     352           0 :          iov_size = ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
     353           0 :          if ((nbytes = writev(fd, c_iov, iov_size)) < 0)
     354             :            {
     355           0 :              zlog_warn("%s: writev to fd %d failed: %s",
     356           0 :                        __func__, fd, safe_strerror(errno));
     357           0 :              break;
     358             :            }
     359             : 
     360             :          /* move pointer io-vector */
     361           0 :          c_iov += iov_size;
     362           0 :          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           0 :   while (b->head && (b->head->sp == b->head->cp))
     373             :     {
     374             :       struct buffer_data *del;
     375           0 :       if (!(b->head = (del = b->head)->next))
     376           0 :         b->tail = NULL;
     377           0 :       BUFFER_DATA_FREE(del);
     378             :     }
     379             : 
     380           0 :   if (iov != small_iov)
     381           0 :     XFREE (MTYPE_TMP, iov);
     382             : 
     383           0 :   return (nbytes < 0) ? BUFFER_ERROR :
     384           0 :                         (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         626 : 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         626 :   size_t iovcnt = 0;
     410         626 :   size_t nbyte = 0;
     411             : 
     412        1880 :   for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
     413         628 :        d = d->next, iovcnt++)
     414             :     {
     415         628 :       iov[iovcnt].iov_base = d->data+d->sp;
     416         628 :       nbyte += (iov[iovcnt].iov_len = d->cp-d->sp);
     417             :     }
     418             : 
     419         626 :   if (!nbyte)
     420             :     /* No data to flush: should we issue a warning message? */
     421           0 :     return BUFFER_EMPTY;
     422             : 
     423             :   /* only place where written should be sign compared */
     424         626 :   if ((ssize_t)(written = writev(fd,iov,iovcnt)) < 0)
     425             :     {
     426           0 :       if (ERRNO_IO_RETRY(errno))
     427             :         /* Calling code should try again later. */
     428           0 :         return BUFFER_PENDING;
     429           0 :       zlog_warn("%s: write error on fd %d: %s",
     430           0 :                 __func__, fd, safe_strerror(errno));
     431           0 :       return BUFFER_ERROR;
     432             :     }
     433             : 
     434             :   /* Free printed buffer data. */
     435        1880 :   while (written > 0)
     436             :     {
     437             :       struct buffer_data *d;
     438         628 :       if (!(d = b->head))
     439             :         {
     440           0 :           zlog_err("%s: corruption detected: buffer queue empty, "
     441             :                    "but written is %lu", __func__, (u_long)written);
     442           0 :           break;
     443             :         }
     444         628 :       if (written < d->cp-d->sp)
     445             :         {
     446           0 :           d->sp += written;
     447           0 :           return BUFFER_PENDING;
     448             :         }
     449             : 
     450         628 :       written -= (d->cp-d->sp);
     451         628 :       if (!(b->head = d->next))
     452         626 :         b->tail = NULL;
     453         628 :       BUFFER_DATA_FREE(d);
     454             :     }
     455             : 
     456         626 :   return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
     457             : 
     458             : #undef MAX_CHUNKS
     459             : #undef MAX_FLUSH
     460             : }
     461             : 
     462             : buffer_status_t
     463           0 : 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           0 :   if (b->head)
     476             :     /* Buffer is not empty, so do not attempt to write the new data. */
     477           0 :     nbytes = 0;
     478           0 :   else if ((nbytes = write(fd, p, size)) < 0)
     479             :     {
     480           0 :       if (ERRNO_IO_RETRY(errno))
     481           0 :         nbytes = 0;
     482             :       else
     483             :         {
     484           0 :           zlog_warn("%s: write error on fd %d: %s",
     485           0 :                     __func__, fd, safe_strerror(errno));
     486           0 :           return BUFFER_ERROR;
     487             :         }
     488             :     }
     489             :   /* Add any remaining data to the buffer. */
     490             :   {
     491           0 :     size_t written = nbytes;
     492           0 :     if (written < size)
     493           0 :       buffer_put(b, ((const char *)p)+written, size-written);
     494             :   }
     495           0 :   return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
     496             : }

Generated by: LCOV version 1.10