]> git.proxmox.com Git - mirror_frr.git/blame - lib/ptm_lib.c
Merge pull request #5244 from ton31337/fix/do_not_include_nexthop_dash_dash_7.1
[mirror_frr.git] / lib / ptm_lib.c
CommitLineData
50e24903
DS
1/* PTM Library
2 * Copyright (C) 2015 Cumulus Networks, Inc.
c43ed2e4 3 *
50e24903 4 * This file is part of Quagga.
c43ed2e4 5 *
50e24903
DS
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 *
896014f4
DL
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
c43ed2e4 19 */
b45ac5f5
DL
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
c43ed2e4
DS
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
d62a17ae 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)
c43ed2e4
DS
53
54typedef struct ptm_lib_msg_ctxt_s {
d62a17ae 55 int cmd_id;
56 csv_t *csv;
57 ptmlib_msg_type type;
c43ed2e4
DS
58} ptm_lib_msg_ctxt_t;
59
d62a17ae 60static 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)
c43ed2e4 63{
d62a17ae 64 char msglen_buf[16], vers_buf[16], type_buf[16], cmdid_buf[16];
65 char client_buf[32];
66 csv_record_t *rec1;
67
2ec42b85 68 sprintf(msglen_buf, "%4d", msglen);
69 sprintf(vers_buf, "%4d", version);
70 sprintf(type_buf, "%4d", type);
71 sprintf(cmdid_buf, "%4d", cmd_id);
d62a17ae 72 snprintf(client_buf, 17, "%16.16s", client_name);
73 if (rec) {
74 rec1 = csv_encode_record(csv, rec, 5, msglen_buf, vers_buf,
75 type_buf, cmdid_buf, client_buf);
76 } else {
77 rec1 = csv_encode(csv, 5, msglen_buf, vers_buf, type_buf,
78 cmdid_buf, client_buf);
79 }
80 return (rec1);
c43ed2e4
DS
81}
82
d62a17ae 83static int _ptm_lib_decode_header(csv_t *csv, int *msglen, int *version,
84 int *type, int *cmd_id, char *client_name)
c43ed2e4 85{
d62a17ae 86 char *hdr;
87 csv_record_t *rec;
88 csv_field_t *fld;
89 int i, j;
90
91 csv_decode(csv, NULL);
92 rec = csv_record_iter(csv);
93 if (rec == NULL) {
94 DLOG("malformed CSV\n");
95 return (-1);
96 }
97 hdr = csv_field_iter(rec, &fld);
98 if (hdr == NULL) {
99 DLOG("malformed CSV\n");
100 return (-1);
101 }
102 *msglen = atoi(hdr);
103 hdr = csv_field_iter_next(&fld);
104 if (hdr == NULL) {
105 DLOG("malformed CSV\n");
106 return (-1);
107 }
108 *version = atoi(hdr);
109 hdr = csv_field_iter_next(&fld);
110 if (hdr == NULL) {
111 DLOG("malformed CSV\n");
112 return (-1);
113 }
114 *type = atoi(hdr);
115 hdr = csv_field_iter_next(&fld);
116 if (hdr == NULL) {
117 DLOG("malformed CSV\n");
118 return (-1);
119 }
120 *cmd_id = atoi(hdr);
121 hdr = csv_field_iter_next(&fld);
122 if (hdr == NULL) {
123 DLOG("malformed CSV\n");
124 return (-1);
125 }
126 /* remove leading spaces */
127 for (i = j = 0; i < csv_field_len(fld); i++) {
a7ce0ad1 128 if (!isspace((int)hdr[i])) {
d62a17ae 129 client_name[j] = hdr[i];
130 j++;
131 }
132 }
133 client_name[j] = '\0';
134
135 return (0);
c43ed2e4
DS
136}
137
d62a17ae 138int ptm_lib_append_msg(ptm_lib_handle_t *hdl, void *ctxt, const char *key,
139 const char *val)
c43ed2e4 140{
d62a17ae 141 ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
142 csv_t *csv;
143 csv_record_t *mh_rec, *rec;
144
145 if (!p_ctxt) {
146 ERRLOG("%s: no context \n", __FUNCTION__);
147 return -1;
148 }
149
150 csv = p_ctxt->csv;
151 mh_rec = csv_record_iter(csv);
152 rec = csv_record_iter_next(mh_rec);
153
154 /* append to the hdr record */
155 rec = csv_append_record(csv, rec, 1, key);
156 if (!rec) {
157 ERRLOG("%s: Could not append key \n", __FUNCTION__);
158 return -1;
159 }
160
161 rec = csv_record_iter_next(rec);
162 /* append to the data record */
163 rec = csv_append_record(csv, rec, 1, val);
164 if (!rec) {
165 ERRLOG("%s: Could not append val \n", __FUNCTION__);
166 return -1;
167 }
168
169 /* update the msg hdr */
170 _ptm_lib_encode_header(csv, mh_rec, (csvlen(csv) - PTMLIB_MSG_HDR_LEN),
171 PTMLIB_MSG_VERSION, p_ctxt->type, p_ctxt->cmd_id,
172 hdl->client_name);
173
174 return 0;
c43ed2e4
DS
175}
176
d62a17ae 177int ptm_lib_init_msg(ptm_lib_handle_t *hdl, int cmd_id, int type, void *in_ctxt,
178 void **out_ctxt)
c43ed2e4 179{
d62a17ae 180 ptm_lib_msg_ctxt_t *p_ctxt;
181 ptm_lib_msg_ctxt_t *p_in_ctxt = in_ctxt;
182 csv_t *csv;
183 csv_record_t *rec, *d_rec;
184
185 /* Initialize csv for using discrete record buffers */
186 csv = csv_init(NULL, NULL, PTMLIB_MSG_SZ);
187
188 if (!csv) {
189 ERRLOG("%s: Could not allocate csv \n", __FUNCTION__);
190 return -1;
191 }
192
193 rec = _ptm_lib_encode_header(csv, NULL, 0, PTMLIB_MSG_VERSION, type,
194 cmd_id, hdl->client_name);
195
196 if (!rec) {
197 ERRLOG("%s: Could not allocate record \n", __FUNCTION__);
198 csv_clean(csv);
199 csv_free(csv);
200 return -1;
201 }
202
203 p_ctxt = calloc(1, sizeof(*p_ctxt));
204 if (!p_ctxt) {
205 ERRLOG("%s: Could not allocate context \n", __FUNCTION__);
206 csv_clean(csv);
207 csv_free(csv);
208 return -1;
209 }
210
211 p_ctxt->csv = csv;
212 p_ctxt->cmd_id = cmd_id;
213 p_ctxt->type = type;
214
215 *(ptm_lib_msg_ctxt_t **)out_ctxt = p_ctxt;
216
217 /* caller supplied a context to initialize with? */
218 if (p_in_ctxt) {
219 /* insert the hdr rec */
220 rec = csv_record_iter(p_in_ctxt->csv);
221 csv_clone_record(p_in_ctxt->csv, rec, &d_rec);
222 csv_insert_record(csv, d_rec);
223 /* insert the data rec */
224 rec = csv_record_iter_next(rec);
225 csv_clone_record(p_in_ctxt->csv, rec, &d_rec);
226 csv_insert_record(csv, d_rec);
227 }
228 return 0;
c43ed2e4
DS
229}
230
b5f270ad
DS
231int ptm_lib_cleanup_msg(ptm_lib_handle_t *hdl, void *ctxt)
232{
233 ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
234 csv_t *csv;
235
236 if (!p_ctxt) {
237 ERRLOG("%s: no context \n", __FUNCTION__);
238 return -1;
239 }
240
241 csv = p_ctxt->csv;
242
243 csv_clean(csv);
244 csv_free(csv);
245 free(p_ctxt);
246
247 return 0;
248}
249
d62a17ae 250int ptm_lib_complete_msg(ptm_lib_handle_t *hdl, void *ctxt, char *buf, int *len)
c43ed2e4 251{
d62a17ae 252 ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
253 csv_t *csv;
254 csv_record_t *rec;
255
256 if (!p_ctxt) {
257 ERRLOG("%s: no context \n", __FUNCTION__);
258 return -1;
259 }
260
261 csv = p_ctxt->csv;
262 rec = csv_record_iter(csv);
263
264 _ptm_lib_encode_header(csv, rec, (csvlen(csv) - PTMLIB_MSG_HDR_LEN),
265 PTMLIB_MSG_VERSION, p_ctxt->type, p_ctxt->cmd_id,
266 hdl->client_name);
267
268 /* parse csv contents into string */
269 if (buf && len) {
270 if (csv_serialize(csv, buf, *len)) {
271 ERRLOG("%s: cannot serialize\n", __FUNCTION__);
272 return -1;
273 }
274 *len = csvlen(csv);
275 }
276
277 csv_clean(csv);
278 csv_free(csv);
279 free(p_ctxt);
280
281 return 0;
c43ed2e4
DS
282}
283
d62a17ae 284int ptm_lib_find_key_in_msg(void *ctxt, const char *key, char *val)
c43ed2e4 285{
d62a17ae 286 ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
287 csv_t *csv = p_ctxt->csv;
288 csv_record_t *hrec, *drec;
289 csv_field_t *hfld, *dfld;
290 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 if (csv_num_records(csv) > 2) {
298 hrec = csv_record_iter(csv);
299 hrec = csv_record_iter_next(hrec);
300 } else {
301 hrec = csv_record_iter(csv);
302 }
303 drec = csv_record_iter_next(hrec);
304 val[0] = '\0';
305 for (hstr = csv_field_iter(hrec, &hfld),
306 dstr = csv_field_iter(drec, &dfld);
307 (hstr && dstr); hstr = csv_field_iter_next(&hfld),
308 dstr = csv_field_iter_next(&dfld)) {
309 if (!strncmp(hstr, key, csv_field_len(hfld))) {
310 snprintf(val, csv_field_len(dfld) + 1, "%s", dstr);
311 return 0;
312 }
313 }
314
315 return -1;
c43ed2e4
DS
316}
317
d62a17ae 318static int _ptm_lib_read_ptm_socket(int fd, char *buf, int len)
c43ed2e4 319{
d62a17ae 320 int retries = 0, rc;
321 int bytes_read = 0;
322
323 while (bytes_read != len) {
324 rc = recv(fd, (void *)(buf + bytes_read), (len - bytes_read),
325 MSG_DONTWAIT);
326 if (rc <= 0) {
327 if (errno && (errno != EAGAIN)
328 && (errno != EWOULDBLOCK)) {
329 ERRLOG("fatal recv error(%s), closing connection, rc %d\n",
330 strerror(errno), rc);
331 return (rc);
332 } else {
333 if (retries++ < 2) {
334 usleep(10000);
335 continue;
336 }
337 DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n",
338 errno, strerror(errno), bytes_read, len);
339 return (bytes_read);
340 }
341 break;
342 } else {
343 bytes_read += rc;
344 }
345 }
346
347 return bytes_read;
c43ed2e4
DS
348}
349
d62a17ae 350int ptm_lib_process_msg(ptm_lib_handle_t *hdl, int fd, char *inbuf, int inlen,
351 void *arg)
c43ed2e4 352{
d62a17ae 353 int rc, len;
354 char client_name[32];
b599ec55 355 int cmd_id = 0, type = 0, ver = 0, msglen = 0;
d62a17ae 356 csv_t *csv;
649579af 357 ptm_lib_msg_ctxt_t *p_ctxt = NULL;
d62a17ae 358
359 len = _ptm_lib_read_ptm_socket(fd, inbuf, PTMLIB_MSG_HDR_LEN);
360 if (len <= 0)
361 return (len);
362
363 csv = csv_init(NULL, inbuf, PTMLIB_MSG_HDR_LEN);
364
365 if (!csv) {
366 DLOG("Cannot allocate csv for hdr\n");
367 return (-1);
368 }
369
370 rc = _ptm_lib_decode_header(csv, &msglen, &ver, &type, &cmd_id,
371 client_name);
372
373 csv_clean(csv);
374 csv_free(csv);
375
376 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 if (len == PTMLIB_MSG_HDR_LEN) {
382 len += _ptm_lib_read_ptm_socket(
383 fd, (inbuf + PTMLIB_MSG_HDR_LEN),
384 inlen - PTMLIB_MSG_HDR_LEN);
385 if (len <= 0)
386 return (len);
387 }
388
389 inbuf[len] = '\0';
390 /* we only support the get-status cmd */
391 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 ptm_lib_init_msg(hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL,
397 (void *)&p_ctxt);
398 if (!p_ctxt) {
399 DLOG("couldnt allocate context\n");
400 return (-1);
401 }
402 ptm_lib_append_msg(hdl, p_ctxt, "cmd", PTMLIB_CMD_GET_STATUS);
403
404 } else {
405
406 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 len = _ptm_lib_read_ptm_socket(fd, inbuf, msglen);
413 if (len <= 0) {
414 return (len);
415 }
416
417 inbuf[len] = '\0';
418
419 csv = csv_init(NULL, NULL, PTMLIB_MSG_SZ);
420 if (!csv) {
421 ERRLOG("Cannot allocate csv for msg\n");
422 return -1;
423 }
424
425 csv_decode(csv, inbuf);
426 p_ctxt = calloc(1, sizeof(*p_ctxt));
427 if (!p_ctxt) {
428 ERRLOG("%s: Could not allocate context \n",
429 __FUNCTION__);
430 csv_clean(csv);
431 csv_free(csv);
432 return -1;
433 }
434
435 p_ctxt->csv = csv;
436 p_ctxt->cmd_id = cmd_id;
437 p_ctxt->type = type;
438 }
439
440 switch (p_ctxt->type) {
441 case PTMLIB_MSG_TYPE_NOTIFICATION:
442 if (hdl->notify_cb)
443 hdl->notify_cb(arg, p_ctxt);
444 break;
445 case PTMLIB_MSG_TYPE_CMD:
446 if (hdl->cmd_cb)
447 hdl->cmd_cb(arg, p_ctxt);
448 break;
449 case PTMLIB_MSG_TYPE_RESPONSE:
450 if (hdl->response_cb)
451 hdl->response_cb(arg, p_ctxt);
452 break;
453 default:
454 return -1;
455 }
456
457 csv_clean(p_ctxt->csv);
458 csv_free(p_ctxt->csv);
459 free(p_ctxt);
460
461 return len;
c43ed2e4
DS
462}
463
d62a17ae 464ptm_lib_handle_t *ptm_lib_register(char *client_name, ptm_cmd_cb cmd_cb,
465 ptm_notify_cb notify_cb,
466 ptm_response_cb response_cb)
c43ed2e4 467{
d62a17ae 468 ptm_lib_handle_t *hdl;
c43ed2e4 469
d62a17ae 470 hdl = calloc(1, sizeof(*hdl));
c43ed2e4 471
d62a17ae 472 if (hdl) {
473 strncpy(hdl->client_name, client_name, PTMLIB_MAXNAMELEN - 1);
474 hdl->cmd_cb = cmd_cb;
475 hdl->notify_cb = notify_cb;
476 hdl->response_cb = response_cb;
477 }
c43ed2e4 478
d62a17ae 479 return hdl;
c43ed2e4
DS
480}
481
d62a17ae 482void ptm_lib_deregister(ptm_lib_handle_t *hdl)
c43ed2e4 483{
d62a17ae 484 if (hdl) {
485 memset(hdl, 0x00, sizeof(*hdl));
486 free(hdl);
487 }
c43ed2e4 488}