back to topotato report
topotato coverage report
Current view: top level - lib - buffer.c (source / functions) Hit Total Coverage
Test: test_bgp_disable_addpath_rx.py::BGPDisableAddpathRx Lines: 81 211 38.4 %
Date: 2023-02-24 18:37:08 Functions: 12 17 70.6 %

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

Generated by: LCOV version v1.16-topotato