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, ...) \
38 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
39 __LINE__, __func__, ##__VA_ARGS__); \
42 #define DLOG(fmt, ...) \
45 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
46 __LINE__, __func__, ##__VA_ARGS__); \
49 typedef struct ptm_lib_msg_ctxt_s
{
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
)
59 char msglen_buf
[16], vers_buf
[16], type_buf
[16], cmdid_buf
[16];
63 sprintf(msglen_buf
, "%4u", msglen
);
64 sprintf(vers_buf
, "%4u", version
);
65 sprintf(type_buf
, "%4u", type
);
66 sprintf(cmdid_buf
, "%4u", cmd_id
);
67 snprintf(client_buf
, 17, "%16.16s", client_name
);
69 rec1
= csv_encode_record(csv
, rec
, 5, msglen_buf
, vers_buf
,
70 type_buf
, cmdid_buf
, client_buf
);
72 rec1
= csv_encode(csv
, 5, msglen_buf
, vers_buf
, type_buf
,
73 cmdid_buf
, client_buf
);
78 static int _ptm_lib_decode_header(csv_t
*csv
, int *msglen
, int *version
,
79 int *type
, int *cmd_id
, char *client_name
)
86 csv_decode(csv
, NULL
);
87 rec
= csv_record_iter(csv
);
89 DLOG("malformed CSV\n");
92 hdr
= csv_field_iter(rec
, &fld
);
94 DLOG("malformed CSV\n");
98 hdr
= csv_field_iter_next(&fld
);
100 DLOG("malformed CSV\n");
103 *version
= atoi(hdr
);
104 hdr
= csv_field_iter_next(&fld
);
106 DLOG("malformed CSV\n");
110 hdr
= csv_field_iter_next(&fld
);
112 DLOG("malformed CSV\n");
116 hdr
= csv_field_iter_next(&fld
);
118 DLOG("malformed CSV\n");
121 /* remove leading spaces */
122 for (i
= j
= 0; i
< csv_field_len(fld
); i
++) {
123 if (!isspace(hdr
[i
])) {
124 client_name
[j
] = hdr
[i
];
128 client_name
[j
] = '\0';
133 int ptm_lib_append_msg(ptm_lib_handle_t
*hdl
, void *ctxt
, const char *key
,
136 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
138 csv_record_t
*mh_rec
, *rec
;
141 ERRLOG("%s: no context \n", __FUNCTION__
);
146 mh_rec
= csv_record_iter(csv
);
147 rec
= csv_record_iter_next(mh_rec
);
149 /* append to the hdr record */
150 rec
= csv_append_record(csv
, rec
, 1, key
);
152 ERRLOG("%s: Could not append key \n", __FUNCTION__
);
156 rec
= csv_record_iter_next(rec
);
157 /* append to the data record */
158 rec
= csv_append_record(csv
, rec
, 1, val
);
160 ERRLOG("%s: Could not append val \n", __FUNCTION__
);
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
,
172 int ptm_lib_init_msg(ptm_lib_handle_t
*hdl
, int cmd_id
, int type
, void *in_ctxt
,
175 ptm_lib_msg_ctxt_t
*p_ctxt
;
176 ptm_lib_msg_ctxt_t
*p_in_ctxt
= in_ctxt
;
178 csv_record_t
*rec
, *d_rec
;
180 /* Initialize csv for using discrete record buffers */
181 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
184 ERRLOG("%s: Could not allocate csv \n", __FUNCTION__
);
188 rec
= _ptm_lib_encode_header(csv
, NULL
, 0, PTMLIB_MSG_VERSION
, type
,
189 cmd_id
, hdl
->client_name
);
192 ERRLOG("%s: Could not allocate record \n", __FUNCTION__
);
198 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
200 ERRLOG("%s: Could not allocate context \n", __FUNCTION__
);
207 p_ctxt
->cmd_id
= cmd_id
;
210 *(ptm_lib_msg_ctxt_t
**)out_ctxt
= p_ctxt
;
212 /* caller supplied a context to initialize with? */
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
);
226 int ptm_lib_complete_msg(ptm_lib_handle_t
*hdl
, void *ctxt
, char *buf
, int *len
)
228 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
233 ERRLOG("%s: no context \n", __FUNCTION__
);
238 rec
= csv_record_iter(csv
);
240 _ptm_lib_encode_header(csv
, rec
, (csvlen(csv
) - PTMLIB_MSG_HDR_LEN
),
241 PTMLIB_MSG_VERSION
, p_ctxt
->type
, p_ctxt
->cmd_id
,
244 /* parse csv contents into string */
246 if (csv_serialize(csv
, buf
, *len
)) {
247 ERRLOG("%s: cannot serialize\n", __FUNCTION__
);
260 int ptm_lib_find_key_in_msg(void *ctxt
, const char *key
, char *val
)
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
;
269 * skip over ptm hdr if present
270 * The next hdr is the keys (column name)
271 * The next hdr is the data
273 if (csv_num_records(csv
) > 2) {
274 hrec
= csv_record_iter(csv
);
275 hrec
= csv_record_iter_next(hrec
);
277 hrec
= csv_record_iter(csv
);
279 drec
= csv_record_iter_next(hrec
);
281 for (hstr
= csv_field_iter(hrec
, &hfld
),
282 dstr
= csv_field_iter(drec
, &dfld
);
283 (hstr
&& dstr
); hstr
= csv_field_iter_next(&hfld
),
284 dstr
= csv_field_iter_next(&dfld
)) {
285 if (!strncmp(hstr
, key
, csv_field_len(hfld
))) {
286 snprintf(val
, csv_field_len(dfld
) + 1, "%s", dstr
);
294 static int _ptm_lib_read_ptm_socket(int fd
, char *buf
, int len
)
299 while (bytes_read
!= len
) {
300 rc
= recv(fd
, (void *)(buf
+ bytes_read
), (len
- bytes_read
),
303 if (errno
&& (errno
!= EAGAIN
)
304 && (errno
!= EWOULDBLOCK
)) {
305 ERRLOG("fatal recv error(%s), closing connection, rc %d\n",
306 strerror(errno
), rc
);
313 DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n",
314 errno
, strerror(errno
), bytes_read
, len
);
326 int ptm_lib_process_msg(ptm_lib_handle_t
*hdl
, int fd
, char *inbuf
, int inlen
,
330 char client_name
[32];
331 int cmd_id
, type
, ver
, msglen
;
333 ptm_lib_msg_ctxt_t
*p_ctxt
;
335 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, PTMLIB_MSG_HDR_LEN
);
339 csv
= csv_init(NULL
, inbuf
, PTMLIB_MSG_HDR_LEN
);
342 DLOG("Cannot allocate csv for hdr\n");
346 rc
= _ptm_lib_decode_header(csv
, &msglen
, &ver
, &type
, &cmd_id
,
353 /* could not decode the CSV - maybe its legacy cmd?
354 * get the entire cmd from the socket and see if we can process
357 if (len
== PTMLIB_MSG_HDR_LEN
) {
358 len
+= _ptm_lib_read_ptm_socket(
359 fd
, (inbuf
+ PTMLIB_MSG_HDR_LEN
),
360 inlen
- PTMLIB_MSG_HDR_LEN
);
366 /* we only support the get-status cmd */
367 if (strcmp(inbuf
, PTMLIB_CMD_GET_STATUS
)) {
368 DLOG("unsupported legacy cmd %s\n", inbuf
);
371 /* internally create a csv-style cmd */
372 ptm_lib_init_msg(hdl
, 0, PTMLIB_MSG_TYPE_CMD
, NULL
,
375 DLOG("couldnt allocate context\n");
378 ptm_lib_append_msg(hdl
, p_ctxt
, "cmd", PTMLIB_CMD_GET_STATUS
);
382 if (msglen
> inlen
) {
383 DLOG("msglen [%d] > inlen [%d]\n", msglen
, inlen
);
387 /* read the rest of the msg */
388 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, msglen
);
395 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
397 ERRLOG("Cannot allocate csv for msg\n");
401 csv_decode(csv
, inbuf
);
402 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
404 ERRLOG("%s: Could not allocate context \n",
412 p_ctxt
->cmd_id
= cmd_id
;
416 switch (p_ctxt
->type
) {
417 case PTMLIB_MSG_TYPE_NOTIFICATION
:
419 hdl
->notify_cb(arg
, p_ctxt
);
421 case PTMLIB_MSG_TYPE_CMD
:
423 hdl
->cmd_cb(arg
, p_ctxt
);
425 case PTMLIB_MSG_TYPE_RESPONSE
:
426 if (hdl
->response_cb
)
427 hdl
->response_cb(arg
, p_ctxt
);
433 csv_clean(p_ctxt
->csv
);
434 csv_free(p_ctxt
->csv
);
440 ptm_lib_handle_t
*ptm_lib_register(char *client_name
, ptm_cmd_cb cmd_cb
,
441 ptm_notify_cb notify_cb
,
442 ptm_response_cb response_cb
)
444 ptm_lib_handle_t
*hdl
;
446 hdl
= calloc(1, sizeof(*hdl
));
449 strncpy(hdl
->client_name
, client_name
, PTMLIB_MAXNAMELEN
- 1);
450 hdl
->cmd_cb
= cmd_cb
;
451 hdl
->notify_cb
= notify_cb
;
452 hdl
->response_cb
= response_cb
;
458 void ptm_lib_deregister(ptm_lib_handle_t
*hdl
)
461 memset(hdl
, 0x00, sizeof(*hdl
));