back to topotato report
topotato coverage report
Current view: top level - lib - buffer.c (source / functions) Hit Total Coverage
Test: test_bgp_ecmp_enhe.py::BGP_Unnumbered_ECMP Lines: 82 211 38.9 %
Date: 2023-11-16 17:19:14 Functions: 12 32 37.5 %

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

Generated by: LCOV version v1.16-topotato