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