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