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
28 #include <sys/socket.h>
35 #define ERRLOG(fmt, ...) \
36 do { if (DEBUG_E) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
37 __LINE__, __func__, ##__VA_ARGS__); } while (0)
39 #define DLOG(fmt, ...) \
40 do { if (DEBUG_V) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
41 __LINE__, __func__, ##__VA_ARGS__); } while (0)
43 typedef struct ptm_lib_msg_ctxt_s
{
50 _ptm_lib_encode_header(csv_t
*csv
,
58 char msglen_buf
[16], vers_buf
[16], type_buf
[16], cmdid_buf
[16];
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
);
68 rec1
= csv_encode_record(csv
, rec
, 5, msglen_buf
, vers_buf
,
69 type_buf
, cmdid_buf
, client_buf
);
71 rec1
= csv_encode(csv
, 5, msglen_buf
, vers_buf
,
72 type_buf
, cmdid_buf
, client_buf
);
78 _ptm_lib_decode_header (csv_t
*csv
,
90 csv_decode(csv
, NULL
);
91 rec
= csv_record_iter(csv
);
93 DLOG("malformed CSV\n");
96 hdr
= csv_field_iter(rec
, &fld
);
98 DLOG("malformed CSV\n");
102 hdr
= csv_field_iter_next(&fld
);
104 DLOG("malformed CSV\n");
107 *version
= atoi(hdr
);
108 hdr
= csv_field_iter_next(&fld
);
110 DLOG("malformed CSV\n");
114 hdr
= csv_field_iter_next(&fld
);
116 DLOG("malformed CSV\n");
120 hdr
= csv_field_iter_next(&fld
);
122 DLOG("malformed CSV\n");
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
];
132 client_name
[j
] = '\0';
138 ptm_lib_append_msg(ptm_lib_handle_t
*hdl
, void *ctxt
,
139 const char *key
, const char *val
)
141 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
143 csv_record_t
*mh_rec
, *rec
;
146 ERRLOG("%s: no context \n", __FUNCTION__
);
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", __FUNCTION__
);
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", __FUNCTION__
);
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
);
179 ptm_lib_init_msg(ptm_lib_handle_t
*hdl
, int cmd_id
, int type
,
180 void *in_ctxt
, void **out_ctxt
)
182 ptm_lib_msg_ctxt_t
*p_ctxt
;
183 ptm_lib_msg_ctxt_t
*p_in_ctxt
= in_ctxt
;
185 csv_record_t
*rec
, *d_rec
;
187 /* Initialize csv for using discrete record buffers */
188 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
191 ERRLOG("%s: Could not allocate csv \n", __FUNCTION__
);
195 rec
= _ptm_lib_encode_header(csv
, NULL
, 0,
196 PTMLIB_MSG_VERSION
, type
,
197 cmd_id
, hdl
->client_name
);
200 ERRLOG("%s: Could not allocate record \n", __FUNCTION__
);
206 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
208 ERRLOG("%s: Could not allocate context \n", __FUNCTION__
);
215 p_ctxt
->cmd_id
= cmd_id
;
218 *(ptm_lib_msg_ctxt_t
**)out_ctxt
= p_ctxt
;
220 /* caller supplied a context to initialize with? */
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
);
235 ptm_lib_complete_msg(ptm_lib_handle_t
*hdl
, void *ctxt
,
238 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
243 ERRLOG("%s: no context \n", __FUNCTION__
);
248 rec
= csv_record_iter(csv
);
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
);
255 /* parse csv contents into string */
257 if (csv_serialize(csv
, buf
, *len
)) {
258 ERRLOG("%s: cannot serialize\n", __FUNCTION__
);
272 ptm_lib_find_key_in_msg(void *ctxt
, const char *key
, char *val
)
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
;
281 * skip over ptm hdr if present
282 * The next hdr is the keys (column name)
283 * The next hdr is the data
285 if (csv_num_records(csv
) > 2) {
286 hrec
= csv_record_iter(csv
);
287 hrec
= csv_record_iter_next(hrec
);
289 hrec
= csv_record_iter(csv
);
291 drec
= csv_record_iter_next(hrec
);
293 for(hstr
= csv_field_iter(hrec
, &hfld
),
294 dstr
= csv_field_iter(drec
, &dfld
);
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
);
308 _ptm_lib_read_ptm_socket(int fd
, char *buf
, int len
)
313 while (bytes_read
!= len
) {
314 rc
= recv(fd
, (void *) (buf
+ bytes_read
), (len
- bytes_read
),
317 if (errno
&& (errno
!= EAGAIN
) && (errno
!= EWOULDBLOCK
)) {
318 ERRLOG("fatal recv error(%s), closing connection, rc %d\n",
319 strerror(errno
), rc
);
326 DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n",
327 errno
, strerror(errno
), bytes_read
, len
);
340 ptm_lib_process_msg(ptm_lib_handle_t
*hdl
, int fd
,
341 char *inbuf
, int inlen
, void *arg
)
344 char client_name
[32];
345 int cmd_id
, type
, ver
, msglen
;
347 ptm_lib_msg_ctxt_t
*p_ctxt
;
349 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, PTMLIB_MSG_HDR_LEN
);
353 csv
= csv_init(NULL
, inbuf
, PTMLIB_MSG_HDR_LEN
);
356 DLOG("Cannot allocate csv for hdr\n");
360 rc
= _ptm_lib_decode_header(csv
, &msglen
, &ver
, &type
, &cmd_id
, client_name
);
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
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
);
377 /* we only support the get-status cmd */
378 if (strcmp(inbuf
, PTMLIB_CMD_GET_STATUS
)) {
379 DLOG("unsupported legacy cmd %s\n", inbuf
);
382 /* internally create a csv-style cmd */
383 ptm_lib_init_msg(hdl
, 0, PTMLIB_MSG_TYPE_CMD
, NULL
, (void *)&p_ctxt
);
385 DLOG("couldnt allocate context\n");
388 ptm_lib_append_msg(hdl
, p_ctxt
, "cmd", PTMLIB_CMD_GET_STATUS
);
392 if (msglen
> inlen
) {
393 DLOG("msglen [%d] > inlen [%d]\n", msglen
, inlen
);
397 /* read the rest of the msg */
398 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, msglen
);
405 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
407 ERRLOG("Cannot allocate csv for msg\n");
411 csv_decode(csv
, inbuf
);
412 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
414 ERRLOG("%s: Could not allocate context \n", __FUNCTION__
);
421 p_ctxt
->cmd_id
= cmd_id
;
425 switch(p_ctxt
->type
) {
426 case PTMLIB_MSG_TYPE_NOTIFICATION
:
428 hdl
->notify_cb(arg
, p_ctxt
);
430 case PTMLIB_MSG_TYPE_CMD
:
432 hdl
->cmd_cb(arg
, p_ctxt
);
434 case PTMLIB_MSG_TYPE_RESPONSE
:
435 if (hdl
->response_cb
)
436 hdl
->response_cb(arg
, p_ctxt
);
442 csv_clean(p_ctxt
->csv
);
443 csv_free(p_ctxt
->csv
);
450 ptm_lib_register(char *client_name
,
452 ptm_notify_cb notify_cb
,
453 ptm_response_cb response_cb
)
455 ptm_lib_handle_t
*hdl
;
457 hdl
= calloc(1, sizeof(*hdl
));
460 strncpy(hdl
->client_name
, client_name
, PTMLIB_MAXNAMELEN
- 1);
461 hdl
->cmd_cb
= cmd_cb
;
462 hdl
->notify_cb
= notify_cb
;
463 hdl
->response_cb
= response_cb
;
470 ptm_lib_deregister(ptm_lib_handle_t
*hdl
)
473 memset(hdl
, 0x00, sizeof(*hdl
));