back to topotato report
topotato coverage report
Current view: top level - lib - ptm_lib.c (source / functions) Hit Total Coverage
Test: test_bgp_minimum_holdtime.py::TestBGPMinimumHoldtime Lines: 0 216 0.0 %
Date: 2023-02-24 18:37:25 Functions: 0 11 0.0 %

          Line data    Source code
       1             : /* PTM Library
       2             :  * Copyright (C) 2015 Cumulus Networks, Inc.
       3             :  *
       4             :  * This file is part of Quagga.
       5             :  *
       6             :  * Quagga is free software; you can redistribute it and/or modify it
       7             :  * under the terms of the GNU General Public License as published by the
       8             :  * Free Software Foundation; either version 2, or (at your option) any
       9             :  * later version.
      10             :  *
      11             :  * Quagga is distributed in the hope that it will be useful, but
      12             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License along
      17             :  * with this program; see the file COPYING; if not, write to the Free Software
      18             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      19             :  */
      20             : 
      21             : #ifdef HAVE_CONFIG_H
      22             : #include "config.h"
      23             : #endif
      24             : 
      25             : #include <stdio.h>
      26             : #include <stdlib.h>
      27             : #include <stdbool.h>
      28             : #include <stddef.h>
      29             : #include <string.h>
      30             : #include <ctype.h>
      31             : #include <unistd.h>
      32             : #include <errno.h>
      33             : #include <sys/socket.h>
      34             : #include "csv.h"
      35             : #include "ptm_lib.h"
      36             : 
      37             : #define DEBUG_E 0
      38             : #define DEBUG_V 0
      39             : 
      40             : #define ERRLOG(fmt, ...)                                                       \
      41             :         do {                                                                   \
      42             :                 if (DEBUG_E)                                                   \
      43             :                         fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__,          \
      44             :                                 __LINE__, __func__, ##__VA_ARGS__);            \
      45             :         } while (0)
      46             : 
      47             : #define DLOG(fmt, ...)                                                         \
      48             :         do {                                                                   \
      49             :                 if (DEBUG_V)                                                   \
      50             :                         fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__,          \
      51             :                                 __LINE__, __func__, ##__VA_ARGS__);            \
      52             :         } while (0)
      53             : 
      54             : typedef struct ptm_lib_msg_ctxt_s {
      55             :         int cmd_id;
      56             :         csv_t *csv;
      57             :         ptmlib_msg_type type;
      58             : } ptm_lib_msg_ctxt_t;
      59             : 
      60           0 : static csv_record_t *_ptm_lib_encode_header(csv_t *csv, csv_record_t *rec,
      61             :                                             int msglen, int version, int type,
      62             :                                             int cmd_id, char *client_name)
      63             : {
      64           0 :         char msglen_buf[16], vers_buf[16], type_buf[16], cmdid_buf[16];
      65           0 :         char client_buf[32];
      66           0 :         csv_record_t *rec1;
      67             : 
      68           0 :         snprintf(msglen_buf, sizeof(msglen_buf), "%4d", msglen);
      69           0 :         snprintf(vers_buf, sizeof(vers_buf), "%4d", version);
      70           0 :         snprintf(type_buf, sizeof(type_buf), "%4d", type);
      71           0 :         snprintf(cmdid_buf, sizeof(cmdid_buf), "%4d", cmd_id);
      72           0 :         snprintf(client_buf, 17, "%16.16s", client_name);
      73           0 :         if (rec) {
      74           0 :                 rec1 = csv_encode_record(csv, rec, 5, msglen_buf, vers_buf,
      75             :                                          type_buf, cmdid_buf, client_buf);
      76             :         } else {
      77           0 :                 rec1 = csv_encode(csv, 5, msglen_buf, vers_buf, type_buf,
      78             :                                   cmdid_buf, client_buf);
      79             :         }
      80           0 :         return (rec1);
      81             : }
      82             : 
      83           0 : static int _ptm_lib_decode_header(csv_t *csv, int *msglen, int *version,
      84             :                                   int *type, int *cmd_id, char *client_name)
      85             : {
      86           0 :         char *hdr;
      87           0 :         csv_record_t *rec;
      88           0 :         csv_field_t *fld;
      89           0 :         int i, j;
      90             : 
      91           0 :         csv_decode(csv, NULL);
      92           0 :         rec = csv_record_iter(csv);
      93           0 :         if (rec == NULL) {
      94             :                 DLOG("malformed CSV\n");
      95             :                 return -1;
      96             :         }
      97           0 :         hdr = csv_field_iter(rec, &fld);
      98           0 :         if (hdr == NULL) {
      99             :                 DLOG("malformed CSV\n");
     100             :                 return -1;
     101             :         }
     102           0 :         *msglen = atoi(hdr);
     103           0 :         hdr = csv_field_iter_next(&fld);
     104           0 :         if (hdr == NULL) {
     105             :                 DLOG("malformed CSV\n");
     106             :                 return -1;
     107             :         }
     108           0 :         *version = atoi(hdr);
     109           0 :         hdr = csv_field_iter_next(&fld);
     110           0 :         if (hdr == NULL) {
     111             :                 DLOG("malformed CSV\n");
     112             :                 return -1;
     113             :         }
     114           0 :         *type = atoi(hdr);
     115           0 :         hdr = csv_field_iter_next(&fld);
     116           0 :         if (hdr == NULL) {
     117             :                 DLOG("malformed CSV\n");
     118             :                 return -1;
     119             :         }
     120           0 :         *cmd_id = atoi(hdr);
     121           0 :         hdr = csv_field_iter_next(&fld);
     122           0 :         if (hdr == NULL) {
     123             :                 DLOG("malformed CSV\n");
     124             :                 return -1;
     125             :         }
     126             :         /* remove leading spaces */
     127           0 :         for (i = j = 0; i < csv_field_len(fld); i++) {
     128           0 :                 if (!isspace((unsigned char)hdr[i])) {
     129           0 :                         client_name[j] = hdr[i];
     130           0 :                         j++;
     131             :                 }
     132             :         }
     133           0 :         client_name[j] = '\0';
     134             : 
     135           0 :         return 0;
     136             : }
     137             : 
     138           0 : int ptm_lib_append_msg(ptm_lib_handle_t *hdl, void *ctxt, const char *key,
     139             :                        const char *val)
     140             : {
     141           0 :         ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
     142           0 :         csv_t *csv;
     143           0 :         csv_record_t *mh_rec, *rec;
     144             : 
     145           0 :         if (!p_ctxt) {
     146             :                 ERRLOG("%s: no context \n", __func__);
     147             :                 return -1;
     148             :         }
     149             : 
     150           0 :         csv = p_ctxt->csv;
     151           0 :         mh_rec = csv_record_iter(csv);
     152           0 :         rec = csv_record_iter_next(mh_rec);
     153             : 
     154             :         /* append to the hdr record */
     155           0 :         rec = csv_append_record(csv, rec, 1, key);
     156           0 :         if (!rec) {
     157             :                 ERRLOG("%s: Could not append key \n", __func__);
     158             :                 return -1;
     159             :         }
     160             : 
     161           0 :         rec = csv_record_iter_next(rec);
     162             :         /* append to the data record */
     163           0 :         rec = csv_append_record(csv, rec, 1, val);
     164           0 :         if (!rec) {
     165             :                 ERRLOG("%s: Could not append val \n", __func__);
     166             :                 return -1;
     167             :         }
     168             : 
     169             :         /* update the msg hdr */
     170           0 :         _ptm_lib_encode_header(csv, mh_rec, (csvlen(csv) - PTMLIB_MSG_HDR_LEN),
     171           0 :                                PTMLIB_MSG_VERSION, p_ctxt->type, p_ctxt->cmd_id,
     172           0 :                                hdl->client_name);
     173             : 
     174           0 :         return 0;
     175             : }
     176             : 
     177           0 : int ptm_lib_init_msg(ptm_lib_handle_t *hdl, int cmd_id, int type, void *in_ctxt,
     178             :                      void **out_ctxt)
     179             : {
     180           0 :         ptm_lib_msg_ctxt_t *p_ctxt;
     181           0 :         ptm_lib_msg_ctxt_t *p_in_ctxt = in_ctxt;
     182           0 :         csv_t *csv;
     183           0 :         csv_record_t *rec, *d_rec;
     184             : 
     185             :         /* Initialize csv for using discrete record buffers */
     186           0 :         csv = csv_init(NULL, NULL, PTMLIB_MSG_SZ);
     187             : 
     188           0 :         if (!csv) {
     189             :                 ERRLOG("%s: Could not allocate csv \n", __func__);
     190             :                 return -1;
     191             :         }
     192             : 
     193           0 :         rec = _ptm_lib_encode_header(csv, NULL, 0, PTMLIB_MSG_VERSION, type,
     194           0 :                                      cmd_id, hdl->client_name);
     195             : 
     196           0 :         if (!rec) {
     197           0 :                 ERRLOG("%s: Could not allocate record \n", __func__);
     198           0 :                 csv_clean(csv);
     199           0 :                 csv_free(csv);
     200           0 :                 return -1;
     201             :         }
     202             : 
     203           0 :         p_ctxt = calloc(1, sizeof(*p_ctxt));
     204           0 :         if (!p_ctxt) {
     205           0 :                 ERRLOG("%s: Could not allocate context \n", __func__);
     206           0 :                 csv_clean(csv);
     207           0 :                 csv_free(csv);
     208           0 :                 return -1;
     209             :         }
     210             : 
     211           0 :         p_ctxt->csv = csv;
     212           0 :         p_ctxt->cmd_id = cmd_id;
     213           0 :         p_ctxt->type = type;
     214             : 
     215           0 :         *(ptm_lib_msg_ctxt_t **)out_ctxt = p_ctxt;
     216             : 
     217             :         /* caller supplied a context to initialize with? */
     218           0 :         if (p_in_ctxt) {
     219             :                 /* insert the hdr rec */
     220           0 :                 rec = csv_record_iter(p_in_ctxt->csv);
     221           0 :                 csv_clone_record(p_in_ctxt->csv, rec, &d_rec);
     222           0 :                 csv_insert_record(csv, d_rec);
     223             :                 /* insert the data rec */
     224           0 :                 rec = csv_record_iter_next(rec);
     225           0 :                 csv_clone_record(p_in_ctxt->csv, rec, &d_rec);
     226           0 :                 csv_insert_record(csv, d_rec);
     227             :         }
     228             :         return 0;
     229             : }
     230             : 
     231           0 : int ptm_lib_cleanup_msg(ptm_lib_handle_t *hdl, void *ctxt)
     232             : {
     233           0 :         ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
     234           0 :         csv_t *csv;
     235             : 
     236           0 :         if (!p_ctxt) {
     237             :                 ERRLOG("%s: no context \n", __func__);
     238             :                 return -1;
     239             :         }
     240             : 
     241           0 :         csv = p_ctxt->csv;
     242             : 
     243           0 :         csv_clean(csv);
     244           0 :         csv_free(csv);
     245           0 :         free(p_ctxt);
     246             : 
     247           0 :         return 0;
     248             : }
     249             : 
     250           0 : int ptm_lib_complete_msg(ptm_lib_handle_t *hdl, void *ctxt, char *buf, int *len)
     251             : {
     252           0 :         ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
     253           0 :         csv_t *csv;
     254           0 :         csv_record_t *rec;
     255             : 
     256           0 :         if (!p_ctxt) {
     257             :                 ERRLOG("%s: no context \n", __func__);
     258             :                 return -1;
     259             :         }
     260             : 
     261           0 :         csv = p_ctxt->csv;
     262           0 :         rec = csv_record_iter(csv);
     263             : 
     264           0 :         _ptm_lib_encode_header(csv, rec, (csvlen(csv) - PTMLIB_MSG_HDR_LEN),
     265           0 :                                PTMLIB_MSG_VERSION, p_ctxt->type, p_ctxt->cmd_id,
     266           0 :                                hdl->client_name);
     267             : 
     268             :         /* parse csv contents into string */
     269           0 :         if (buf && len) {
     270           0 :                 if (csv_serialize(csv, buf, *len)) {
     271             :                         ERRLOG("%s: cannot serialize\n", __func__);
     272             :                         return -1;
     273             :                 }
     274           0 :                 *len = csvlen(csv);
     275             :         }
     276             : 
     277           0 :         csv_clean(csv);
     278           0 :         csv_free(csv);
     279           0 :         free(p_ctxt);
     280             : 
     281           0 :         return 0;
     282             : }
     283             : 
     284           0 : int ptm_lib_find_key_in_msg(void *ctxt, const char *key, char *val)
     285             : {
     286           0 :         ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
     287           0 :         csv_t *csv = p_ctxt->csv;
     288           0 :         csv_record_t *hrec, *drec;
     289           0 :         csv_field_t *hfld, *dfld;
     290           0 :         char *hstr, *dstr;
     291             : 
     292             :         /**
     293             :          * skip over ptm hdr if present
     294             :          * The next hdr is the keys (column name)
     295             :          * The next hdr is the data
     296             :          */
     297           0 :         if (csv_num_records(csv) > 2) {
     298           0 :                 hrec = csv_record_iter(csv);
     299           0 :                 hrec = csv_record_iter_next(hrec);
     300             :         } else {
     301           0 :                 hrec = csv_record_iter(csv);
     302             :         }
     303           0 :         drec = csv_record_iter_next(hrec);
     304           0 :         val[0] = '\0';
     305           0 :         for (hstr = csv_field_iter(hrec, &hfld),
     306           0 :             dstr = csv_field_iter(drec, &dfld);
     307           0 :              (hstr && dstr); hstr = csv_field_iter_next(&hfld),
     308           0 :             dstr = csv_field_iter_next(&dfld)) {
     309           0 :                 if (!strncmp(hstr, key, csv_field_len(hfld))) {
     310           0 :                         snprintf(val, csv_field_len(dfld) + 1, "%s", dstr);
     311           0 :                         return 0;
     312             :                 }
     313             :         }
     314             : 
     315             :         return -1;
     316             : }
     317             : 
     318           0 : static int _ptm_lib_read_ptm_socket(int fd, char *buf, int len)
     319             : {
     320           0 :         int retries = 0, rc;
     321           0 :         int bytes_read = 0;
     322             : 
     323           0 :         while (bytes_read != len) {
     324           0 :                 rc = recv(fd, (void *)(buf + bytes_read), (len - bytes_read),
     325             :                           MSG_DONTWAIT);
     326           0 :                 if (rc <= 0) {
     327           0 :                         if (errno && (errno != EAGAIN)
     328             :                             && (errno != EWOULDBLOCK)) {
     329           0 :                                 ERRLOG("fatal recv error(%s), closing connection, rc %d\n",
     330             :                                        strerror(errno), rc);
     331           0 :                                 return (rc);
     332             :                         } else {
     333           0 :                                 if (retries++ < 2) {
     334           0 :                                         usleep(10000);
     335           0 :                                         continue;
     336             :                                 }
     337           0 :                                 DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n",
     338             :                                      errno, strerror(errno), bytes_read, len);
     339           0 :                                 return (bytes_read);
     340             :                         }
     341           0 :                         break;
     342             :                 } else {
     343           0 :                         bytes_read += rc;
     344             :                 }
     345             :         }
     346             : 
     347             :         return bytes_read;
     348             : }
     349             : 
     350           0 : int ptm_lib_process_msg(ptm_lib_handle_t *hdl, int fd, char *inbuf, int inlen,
     351             :                         void *arg)
     352             : {
     353           0 :         int rc, len;
     354           0 :         char client_name[32];
     355           0 :         int cmd_id = 0, type = 0, ver = 0, msglen = 0;
     356           0 :         csv_t *csv;
     357           0 :         ptm_lib_msg_ctxt_t *p_ctxt = NULL;
     358             : 
     359           0 :         len = _ptm_lib_read_ptm_socket(fd, inbuf, PTMLIB_MSG_HDR_LEN);
     360           0 :         if (len <= 0)
     361             :                 return (len);
     362             : 
     363           0 :         csv = csv_init(NULL, inbuf, PTMLIB_MSG_HDR_LEN);
     364             : 
     365           0 :         if (!csv) {
     366             :                 DLOG("Cannot allocate csv for hdr\n");
     367             :                 return -1;
     368             :         }
     369             : 
     370           0 :         rc = _ptm_lib_decode_header(csv, &msglen, &ver, &type, &cmd_id,
     371             :                                     client_name);
     372             : 
     373           0 :         csv_clean(csv);
     374           0 :         csv_free(csv);
     375             : 
     376           0 :         if (rc < 0) {
     377             :                 /* could not decode the CSV - maybe its legacy cmd?
     378             :                  * get the entire cmd from the socket and see if we can process
     379             :                  * it
     380             :                  */
     381           0 :                 if (len == PTMLIB_MSG_HDR_LEN) {
     382           0 :                         len += _ptm_lib_read_ptm_socket(
     383             :                                 fd, (inbuf + PTMLIB_MSG_HDR_LEN),
     384             :                                 inlen - PTMLIB_MSG_HDR_LEN);
     385           0 :                         if (len <= 0)
     386             :                                 return (len);
     387             :                 }
     388             : 
     389           0 :                 inbuf[len] = '\0';
     390             :                 /* we only support the get-status cmd */
     391           0 :                 if (strcmp(inbuf, PTMLIB_CMD_GET_STATUS)) {
     392             :                         DLOG("unsupported legacy cmd %s\n", inbuf);
     393             :                         return -1;
     394             :                 }
     395             :                 /* internally create a csv-style cmd */
     396           0 :                 ptm_lib_init_msg(hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL,
     397             :                                  (void *)&p_ctxt);
     398           0 :                 if (!p_ctxt) {
     399             :                         DLOG("couldnt allocate context\n");
     400             :                         return -1;
     401             :                 }
     402           0 :                 ptm_lib_append_msg(hdl, p_ctxt, "cmd", PTMLIB_CMD_GET_STATUS);
     403             : 
     404             :         } else {
     405             : 
     406           0 :                 if (msglen > inlen) {
     407             :                         DLOG("msglen [%d] > inlen [%d]\n", msglen, inlen);
     408             :                         return -1;
     409             :                 }
     410             : 
     411             :                 /* read the rest of the msg */
     412           0 :                 len = _ptm_lib_read_ptm_socket(fd, inbuf, msglen);
     413           0 :                 if (len <= 0) {
     414             :                         return (len);
     415             :                 }
     416             : 
     417           0 :                 inbuf[len] = '\0';
     418             : 
     419           0 :                 csv = csv_init(NULL, NULL, PTMLIB_MSG_SZ);
     420           0 :                 if (!csv) {
     421             :                         ERRLOG("Cannot allocate csv for msg\n");
     422             :                         return -1;
     423             :                 }
     424             : 
     425           0 :                 csv_decode(csv, inbuf);
     426           0 :                 p_ctxt = calloc(1, sizeof(*p_ctxt));
     427           0 :                 if (!p_ctxt) {
     428           0 :                         ERRLOG("%s: Could not allocate context \n", __func__);
     429           0 :                         csv_clean(csv);
     430           0 :                         csv_free(csv);
     431           0 :                         return -1;
     432             :                 }
     433             : 
     434           0 :                 p_ctxt->csv = csv;
     435           0 :                 p_ctxt->cmd_id = cmd_id;
     436           0 :                 p_ctxt->type = type;
     437             :         }
     438             : 
     439           0 :         switch (p_ctxt->type) {
     440           0 :         case PTMLIB_MSG_TYPE_NOTIFICATION:
     441           0 :                 if (hdl->notify_cb)
     442           0 :                         hdl->notify_cb(arg, p_ctxt);
     443             :                 break;
     444           0 :         case PTMLIB_MSG_TYPE_CMD:
     445           0 :                 if (hdl->cmd_cb)
     446           0 :                         hdl->cmd_cb(arg, p_ctxt);
     447             :                 break;
     448           0 :         case PTMLIB_MSG_TYPE_RESPONSE:
     449           0 :                 if (hdl->response_cb)
     450           0 :                         hdl->response_cb(arg, p_ctxt);
     451             :                 break;
     452             :         default:
     453             :                 return -1;
     454             :         }
     455             : 
     456           0 :         csv_clean(p_ctxt->csv);
     457           0 :         csv_free(p_ctxt->csv);
     458           0 :         free(p_ctxt);
     459             : 
     460           0 :         return len;
     461             : }
     462             : 
     463           0 : ptm_lib_handle_t *ptm_lib_register(char *client_name, ptm_cmd_cb cmd_cb,
     464             :                                    ptm_notify_cb notify_cb,
     465             :                                    ptm_response_cb response_cb)
     466             : {
     467           0 :         ptm_lib_handle_t *hdl;
     468             : 
     469           0 :         hdl = calloc(1, sizeof(*hdl));
     470             : 
     471           0 :         if (hdl) {
     472           0 :                 strncpy(hdl->client_name, client_name, PTMLIB_MAXNAMELEN - 1);
     473           0 :                 hdl->cmd_cb = cmd_cb;
     474           0 :                 hdl->notify_cb = notify_cb;
     475           0 :                 hdl->response_cb = response_cb;
     476             :         }
     477             : 
     478           0 :         return hdl;
     479             : }
     480             : 
     481           0 : void ptm_lib_deregister(ptm_lib_handle_t *hdl)
     482             : {
     483           0 :         if (hdl) {
     484           0 :                 memset(hdl, 0x00, sizeof(*hdl));
     485           0 :                 free(hdl);
     486             :         }
     487           0 : }

Generated by: LCOV version v1.16-topotato