]> git.proxmox.com Git - mirror_frr.git/blob - lib/ptm_lib.c
Merge pull request #13113 from sri-mohan1/sri-mohan-ldp
[mirror_frr.git] / lib / ptm_lib.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* PTM Library
3 * Copyright (C) 2015 Cumulus Networks, Inc.
4 */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9
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
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)
38
39 typedef struct ptm_lib_msg_ctxt_s {
40 int cmd_id;
41 csv_t *csv;
42 ptmlib_msg_type type;
43 } ptm_lib_msg_ctxt_t;
44
45 static 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)
48 {
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
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);
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);
66 }
67
68 static int _ptm_lib_decode_header(csv_t *csv, int *msglen, int *version,
69 int *type, int *cmd_id, char *client_name)
70 {
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");
80 return -1;
81 }
82 hdr = csv_field_iter(rec, &fld);
83 if (hdr == NULL) {
84 DLOG("malformed CSV\n");
85 return -1;
86 }
87 *msglen = atoi(hdr);
88 hdr = csv_field_iter_next(&fld);
89 if (hdr == NULL) {
90 DLOG("malformed CSV\n");
91 return -1;
92 }
93 *version = atoi(hdr);
94 hdr = csv_field_iter_next(&fld);
95 if (hdr == NULL) {
96 DLOG("malformed CSV\n");
97 return -1;
98 }
99 *type = atoi(hdr);
100 hdr = csv_field_iter_next(&fld);
101 if (hdr == NULL) {
102 DLOG("malformed CSV\n");
103 return -1;
104 }
105 *cmd_id = atoi(hdr);
106 hdr = csv_field_iter_next(&fld);
107 if (hdr == NULL) {
108 DLOG("malformed CSV\n");
109 return -1;
110 }
111 /* remove leading spaces */
112 for (i = j = 0; i < csv_field_len(fld); i++) {
113 if (!isspace((unsigned char)hdr[i])) {
114 client_name[j] = hdr[i];
115 j++;
116 }
117 }
118 client_name[j] = '\0';
119
120 return 0;
121 }
122
123 int ptm_lib_append_msg(ptm_lib_handle_t *hdl, void *ctxt, const char *key,
124 const char *val)
125 {
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) {
131 ERRLOG("%s: no context \n", __func__);
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) {
142 ERRLOG("%s: Could not append key \n", __func__);
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) {
150 ERRLOG("%s: Could not append val \n", __func__);
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;
160 }
161
162 int ptm_lib_init_msg(ptm_lib_handle_t *hdl, int cmd_id, int type, void *in_ctxt,
163 void **out_ctxt)
164 {
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) {
174 ERRLOG("%s: Could not allocate csv \n", __func__);
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) {
182 ERRLOG("%s: Could not allocate record \n", __func__);
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) {
190 ERRLOG("%s: Could not allocate context \n", __func__);
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;
214 }
215
216 int 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) {
222 ERRLOG("%s: no context \n", __func__);
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
235 int ptm_lib_complete_msg(ptm_lib_handle_t *hdl, void *ctxt, char *buf, int *len)
236 {
237 ptm_lib_msg_ctxt_t *p_ctxt = ctxt;
238 csv_t *csv;
239 csv_record_t *rec;
240
241 if (!p_ctxt) {
242 ERRLOG("%s: no context \n", __func__);
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)) {
256 ERRLOG("%s: cannot serialize\n", __func__);
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;
267 }
268
269 int ptm_lib_find_key_in_msg(void *ctxt, const char *key, char *val)
270 {
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;
301 }
302
303 static int _ptm_lib_read_ptm_socket(int fd, char *buf, int len)
304 {
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;
333 }
334
335 int ptm_lib_process_msg(ptm_lib_handle_t *hdl, int fd, char *inbuf, int inlen,
336 void *arg)
337 {
338 int rc, len;
339 char client_name[32];
340 int cmd_id = 0, type = 0, ver = 0, msglen = 0;
341 csv_t *csv;
342 ptm_lib_msg_ctxt_t *p_ctxt = NULL;
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");
352 return -1;
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);
378 return -1;
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");
385 return -1;
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) {
413 ERRLOG("%s: Could not allocate context \n", __func__);
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;
446 }
447
448 ptm_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)
451 {
452 ptm_lib_handle_t *hdl;
453
454 hdl = calloc(1, sizeof(*hdl));
455
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 }
462
463 return hdl;
464 }
465
466 void ptm_lib_deregister(ptm_lib_handle_t *hdl)
467 {
468 if (hdl) {
469 memset(hdl, 0x00, sizeof(*hdl));
470 free(hdl);
471 }
472 }