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