2 * Copyright (C) 2015 Cumulus Networks, Inc.
4 * This file is part of Quagga.
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
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.
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
33 #include <sys/socket.h>
40 #define ERRLOG(fmt, ...) \
43 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
44 __LINE__, __func__, ##__VA_ARGS__); \
47 #define DLOG(fmt, ...) \
50 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
51 __LINE__, __func__, ##__VA_ARGS__); \
54 typedef struct ptm_lib_msg_ctxt_s
{
60 static csv_record_t
*_ptm_lib_encode_header(csv_t
*csv
, csv_record_t
*rec
,
61 int msglen
, int version
, int type
,
62 int cmd_id
, char *client_name
)
64 char msglen_buf
[16], vers_buf
[16], type_buf
[16], cmdid_buf
[16];
68 snprintf(msglen_buf
, sizeof(msglen_buf
), "%4d", msglen
);
69 snprintf(vers_buf
, sizeof(vers_buf
), "%4d", version
);
70 snprintf(type_buf
, sizeof(type_buf
), "%4d", type
);
71 snprintf(cmdid_buf
, sizeof(cmdid_buf
), "%4d", cmd_id
);
72 snprintf(client_buf
, 17, "%16.16s", client_name
);
74 rec1
= csv_encode_record(csv
, rec
, 5, msglen_buf
, vers_buf
,
75 type_buf
, cmdid_buf
, client_buf
);
77 rec1
= csv_encode(csv
, 5, msglen_buf
, vers_buf
, type_buf
,
78 cmdid_buf
, client_buf
);
83 static int _ptm_lib_decode_header(csv_t
*csv
, int *msglen
, int *version
,
84 int *type
, int *cmd_id
, char *client_name
)
91 csv_decode(csv
, NULL
);
92 rec
= csv_record_iter(csv
);
94 DLOG("malformed CSV\n");
97 hdr
= csv_field_iter(rec
, &fld
);
99 DLOG("malformed CSV\n");
103 hdr
= csv_field_iter_next(&fld
);
105 DLOG("malformed CSV\n");
108 *version
= atoi(hdr
);
109 hdr
= csv_field_iter_next(&fld
);
111 DLOG("malformed CSV\n");
115 hdr
= csv_field_iter_next(&fld
);
117 DLOG("malformed CSV\n");
121 hdr
= csv_field_iter_next(&fld
);
123 DLOG("malformed CSV\n");
126 /* remove leading spaces */
127 for (i
= j
= 0; i
< csv_field_len(fld
); i
++) {
128 if (!isspace((unsigned char)hdr
[i
])) {
129 client_name
[j
] = hdr
[i
];
133 client_name
[j
] = '\0';
138 int ptm_lib_append_msg(ptm_lib_handle_t
*hdl
, void *ctxt
, const char *key
,
141 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
143 csv_record_t
*mh_rec
, *rec
;
146 ERRLOG("%s: no context \n", __func__
);
151 mh_rec
= csv_record_iter(csv
);
152 rec
= csv_record_iter_next(mh_rec
);
154 /* append to the hdr record */
155 rec
= csv_append_record(csv
, rec
, 1, key
);
157 ERRLOG("%s: Could not append key \n", __func__
);
161 rec
= csv_record_iter_next(rec
);
162 /* append to the data record */
163 rec
= csv_append_record(csv
, rec
, 1, val
);
165 ERRLOG("%s: Could not append val \n", __func__
);
169 /* update the msg hdr */
170 _ptm_lib_encode_header(csv
, mh_rec
, (csvlen(csv
) - PTMLIB_MSG_HDR_LEN
),
171 PTMLIB_MSG_VERSION
, p_ctxt
->type
, p_ctxt
->cmd_id
,
177 int ptm_lib_init_msg(ptm_lib_handle_t
*hdl
, int cmd_id
, int type
, void *in_ctxt
,
180 ptm_lib_msg_ctxt_t
*p_ctxt
;
181 ptm_lib_msg_ctxt_t
*p_in_ctxt
= in_ctxt
;
183 csv_record_t
*rec
, *d_rec
;
185 /* Initialize csv for using discrete record buffers */
186 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
189 ERRLOG("%s: Could not allocate csv \n", __func__
);
193 rec
= _ptm_lib_encode_header(csv
, NULL
, 0, PTMLIB_MSG_VERSION
, type
,
194 cmd_id
, hdl
->client_name
);
197 ERRLOG("%s: Could not allocate record \n", __func__
);
203 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
205 ERRLOG("%s: Could not allocate context \n", __func__
);
212 p_ctxt
->cmd_id
= cmd_id
;
215 *(ptm_lib_msg_ctxt_t
**)out_ctxt
= p_ctxt
;
217 /* caller supplied a context to initialize with? */
219 /* insert the hdr rec */
220 rec
= csv_record_iter(p_in_ctxt
->csv
);
221 csv_clone_record(p_in_ctxt
->csv
, rec
, &d_rec
);
222 csv_insert_record(csv
, d_rec
);
223 /* insert the data rec */
224 rec
= csv_record_iter_next(rec
);
225 csv_clone_record(p_in_ctxt
->csv
, rec
, &d_rec
);
226 csv_insert_record(csv
, d_rec
);
231 int ptm_lib_cleanup_msg(ptm_lib_handle_t
*hdl
, void *ctxt
)
233 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
237 ERRLOG("%s: no context \n", __func__
);
250 int ptm_lib_complete_msg(ptm_lib_handle_t
*hdl
, void *ctxt
, char *buf
, int *len
)
252 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
257 ERRLOG("%s: no context \n", __func__
);
262 rec
= csv_record_iter(csv
);
264 _ptm_lib_encode_header(csv
, rec
, (csvlen(csv
) - PTMLIB_MSG_HDR_LEN
),
265 PTMLIB_MSG_VERSION
, p_ctxt
->type
, p_ctxt
->cmd_id
,
268 /* parse csv contents into string */
270 if (csv_serialize(csv
, buf
, *len
)) {
271 ERRLOG("%s: cannot serialize\n", __func__
);
284 int ptm_lib_find_key_in_msg(void *ctxt
, const char *key
, char *val
)
286 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
287 csv_t
*csv
= p_ctxt
->csv
;
288 csv_record_t
*hrec
, *drec
;
289 csv_field_t
*hfld
, *dfld
;
293 * skip over ptm hdr if present
294 * The next hdr is the keys (column name)
295 * The next hdr is the data
297 if (csv_num_records(csv
) > 2) {
298 hrec
= csv_record_iter(csv
);
299 hrec
= csv_record_iter_next(hrec
);
301 hrec
= csv_record_iter(csv
);
303 drec
= csv_record_iter_next(hrec
);
305 for (hstr
= csv_field_iter(hrec
, &hfld
),
306 dstr
= csv_field_iter(drec
, &dfld
);
307 (hstr
&& dstr
); hstr
= csv_field_iter_next(&hfld
),
308 dstr
= csv_field_iter_next(&dfld
)) {
309 if (!strncmp(hstr
, key
, csv_field_len(hfld
))) {
310 snprintf(val
, csv_field_len(dfld
) + 1, "%s", dstr
);
318 static int _ptm_lib_read_ptm_socket(int fd
, char *buf
, int len
)
323 while (bytes_read
!= len
) {
324 rc
= recv(fd
, (void *)(buf
+ bytes_read
), (len
- bytes_read
),
327 if (errno
&& (errno
!= EAGAIN
)
328 && (errno
!= EWOULDBLOCK
)) {
329 ERRLOG("fatal recv error(%s), closing connection, rc %d\n",
330 strerror(errno
), rc
);
337 DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n",
338 errno
, strerror(errno
), bytes_read
, len
);
350 int ptm_lib_process_msg(ptm_lib_handle_t
*hdl
, int fd
, char *inbuf
, int inlen
,
354 char client_name
[32];
355 int cmd_id
= 0, type
= 0, ver
= 0, msglen
= 0;
357 ptm_lib_msg_ctxt_t
*p_ctxt
= NULL
;
359 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, PTMLIB_MSG_HDR_LEN
);
363 csv
= csv_init(NULL
, inbuf
, PTMLIB_MSG_HDR_LEN
);
366 DLOG("Cannot allocate csv for hdr\n");
370 rc
= _ptm_lib_decode_header(csv
, &msglen
, &ver
, &type
, &cmd_id
,
377 /* could not decode the CSV - maybe its legacy cmd?
378 * get the entire cmd from the socket and see if we can process
381 if (len
== PTMLIB_MSG_HDR_LEN
) {
382 len
+= _ptm_lib_read_ptm_socket(
383 fd
, (inbuf
+ PTMLIB_MSG_HDR_LEN
),
384 inlen
- PTMLIB_MSG_HDR_LEN
);
390 /* we only support the get-status cmd */
391 if (strcmp(inbuf
, PTMLIB_CMD_GET_STATUS
)) {
392 DLOG("unsupported legacy cmd %s\n", inbuf
);
395 /* internally create a csv-style cmd */
396 ptm_lib_init_msg(hdl
, 0, PTMLIB_MSG_TYPE_CMD
, NULL
,
399 DLOG("couldnt allocate context\n");
402 ptm_lib_append_msg(hdl
, p_ctxt
, "cmd", PTMLIB_CMD_GET_STATUS
);
406 if (msglen
> inlen
) {
407 DLOG("msglen [%d] > inlen [%d]\n", msglen
, inlen
);
411 /* read the rest of the msg */
412 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, msglen
);
419 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
421 ERRLOG("Cannot allocate csv for msg\n");
425 csv_decode(csv
, inbuf
);
426 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
428 ERRLOG("%s: Could not allocate context \n", __func__
);
435 p_ctxt
->cmd_id
= cmd_id
;
439 switch (p_ctxt
->type
) {
440 case PTMLIB_MSG_TYPE_NOTIFICATION
:
442 hdl
->notify_cb(arg
, p_ctxt
);
444 case PTMLIB_MSG_TYPE_CMD
:
446 hdl
->cmd_cb(arg
, p_ctxt
);
448 case PTMLIB_MSG_TYPE_RESPONSE
:
449 if (hdl
->response_cb
)
450 hdl
->response_cb(arg
, p_ctxt
);
456 csv_clean(p_ctxt
->csv
);
457 csv_free(p_ctxt
->csv
);
463 ptm_lib_handle_t
*ptm_lib_register(char *client_name
, ptm_cmd_cb cmd_cb
,
464 ptm_notify_cb notify_cb
,
465 ptm_response_cb response_cb
)
467 ptm_lib_handle_t
*hdl
;
469 hdl
= calloc(1, sizeof(*hdl
));
472 strncpy(hdl
->client_name
, client_name
, PTMLIB_MAXNAMELEN
- 1);
473 hdl
->cmd_cb
= cmd_cb
;
474 hdl
->notify_cb
= notify_cb
;
475 hdl
->response_cb
= response_cb
;
481 void ptm_lib_deregister(ptm_lib_handle_t
*hdl
)
484 memset(hdl
, 0x00, sizeof(*hdl
));