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