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
17 * along with Quagga; see the file COPYING. If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
29 #include <sys/socket.h>
36 #define ERRLOG(fmt, ...) \
37 do { if (DEBUG_E) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
38 __LINE__, __func__, ##__VA_ARGS__); } while (0)
40 #define DLOG(fmt, ...) \
41 do { if (DEBUG_V) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
42 __LINE__, __func__, ##__VA_ARGS__); } while (0)
44 typedef struct ptm_lib_msg_ctxt_s
{
51 _ptm_lib_encode_header(csv_t
*csv
,
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
,
73 type_buf
, cmdid_buf
, client_buf
);
79 _ptm_lib_decode_header (csv_t
*csv
,
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(hdr
[i
])) {
129 client_name
[j
] = hdr
[i
];
133 client_name
[j
] = '\0';
139 ptm_lib_append_msg(ptm_lib_handle_t
*hdl
, void *ctxt
,
140 const char *key
, const char *val
)
142 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
144 csv_record_t
*mh_rec
, *rec
;
147 ERRLOG("%s: no context \n", __FUNCTION__
);
152 mh_rec
= csv_record_iter(csv
);
153 rec
= csv_record_iter_next(mh_rec
);
155 /* append to the hdr record */
156 rec
= csv_append_record(csv
, rec
, 1, key
);
158 ERRLOG("%s: Could not append key \n", __FUNCTION__
);
162 rec
= csv_record_iter_next(rec
);
163 /* append to the data record */
164 rec
= csv_append_record(csv
, rec
, 1, val
);
166 ERRLOG("%s: Could not append val \n", __FUNCTION__
);
170 /* update the msg hdr */
171 _ptm_lib_encode_header(csv
, mh_rec
,
172 (csvlen(csv
) - PTMLIB_MSG_HDR_LEN
),
173 PTMLIB_MSG_VERSION
, p_ctxt
->type
,
174 p_ctxt
->cmd_id
, hdl
->client_name
);
180 ptm_lib_init_msg(ptm_lib_handle_t
*hdl
, int cmd_id
, int type
,
181 void *in_ctxt
, void **out_ctxt
)
183 ptm_lib_msg_ctxt_t
*p_ctxt
;
184 ptm_lib_msg_ctxt_t
*p_in_ctxt
= in_ctxt
;
186 csv_record_t
*rec
, *d_rec
;
188 /* Initialize csv for using discrete record buffers */
189 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
192 ERRLOG("%s: Could not allocate csv \n", __FUNCTION__
);
196 rec
= _ptm_lib_encode_header(csv
, NULL
, 0,
197 PTMLIB_MSG_VERSION
, type
,
198 cmd_id
, hdl
->client_name
);
201 ERRLOG("%s: Could not allocate record \n", __FUNCTION__
);
207 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
209 ERRLOG("%s: Could not allocate context \n", __FUNCTION__
);
216 p_ctxt
->cmd_id
= cmd_id
;
219 *(ptm_lib_msg_ctxt_t
**)out_ctxt
= p_ctxt
;
221 /* caller supplied a context to initialize with? */
223 /* insert the hdr rec */
224 rec
= csv_record_iter(p_in_ctxt
->csv
);
225 csv_clone_record (p_in_ctxt
->csv
, rec
, &d_rec
);
226 csv_insert_record (csv
, d_rec
);
227 /* insert the data rec */
228 rec
= csv_record_iter_next(rec
);
229 csv_clone_record (p_in_ctxt
->csv
, rec
, &d_rec
);
230 csv_insert_record (csv
, d_rec
);
236 ptm_lib_complete_msg(ptm_lib_handle_t
*hdl
, void *ctxt
,
239 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
244 ERRLOG("%s: no context \n", __FUNCTION__
);
249 rec
= csv_record_iter(csv
);
251 _ptm_lib_encode_header(csv
, rec
,
252 (csvlen(csv
) - PTMLIB_MSG_HDR_LEN
),
253 PTMLIB_MSG_VERSION
, p_ctxt
->type
,
254 p_ctxt
->cmd_id
, hdl
->client_name
);
256 /* parse csv contents into string */
258 if (csv_serialize(csv
, buf
, *len
)) {
259 ERRLOG("%s: cannot serialize\n", __FUNCTION__
);
273 ptm_lib_find_key_in_msg(void *ctxt
, const char *key
, char *val
)
275 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
276 csv_t
*csv
= p_ctxt
->csv
;
277 csv_record_t
*hrec
, *drec
;
278 csv_field_t
*hfld
, *dfld
;
282 * skip over ptm hdr if present
283 * The next hdr is the keys (column name)
284 * The next hdr is the data
286 if (csv_num_records(csv
) > 2) {
287 hrec
= csv_record_iter(csv
);
288 hrec
= csv_record_iter_next(hrec
);
290 hrec
= csv_record_iter(csv
);
292 drec
= csv_record_iter_next(hrec
);
294 for(hstr
= csv_field_iter(hrec
, &hfld
),
295 dstr
= csv_field_iter(drec
, &dfld
);
297 hstr
= csv_field_iter_next(&hfld
),
298 dstr
= csv_field_iter_next(&dfld
)) {
299 if (!strncmp(hstr
, key
, csv_field_len(hfld
))) {
300 snprintf(val
, csv_field_len(dfld
)+1, "%s", dstr
);
309 _ptm_lib_read_ptm_socket(int fd
, char *buf
, int len
)
314 while (bytes_read
!= len
) {
315 rc
= recv(fd
, (void *) (buf
+ bytes_read
), (len
- bytes_read
),
318 if (errno
&& (errno
!= EAGAIN
) && (errno
!= EWOULDBLOCK
)) {
319 ERRLOG("fatal recv error(%s), closing connection, rc %d\n",
320 strerror(errno
), rc
);
327 DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n",
328 errno
, strerror(errno
), bytes_read
, len
);
341 ptm_lib_process_msg(ptm_lib_handle_t
*hdl
, int fd
,
342 char *inbuf
, int inlen
, void *arg
)
345 char client_name
[32];
346 int cmd_id
, type
, ver
, msglen
;
348 ptm_lib_msg_ctxt_t
*p_ctxt
;
350 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, PTMLIB_MSG_HDR_LEN
);
354 csv
= csv_init(NULL
, inbuf
, PTMLIB_MSG_HDR_LEN
);
357 DLOG("Cannot allocate csv for hdr\n");
361 rc
= _ptm_lib_decode_header(csv
, &msglen
, &ver
, &type
, &cmd_id
, client_name
);
367 /* could not decode the CSV - maybe its legacy cmd?
368 * get the entire cmd from the socket and see if we can process it
370 if (len
== PTMLIB_MSG_HDR_LEN
) {
371 len
+= _ptm_lib_read_ptm_socket(fd
, (inbuf
+PTMLIB_MSG_HDR_LEN
),
372 inlen
- PTMLIB_MSG_HDR_LEN
);
378 /* we only support the get-status cmd */
379 if (strcmp(inbuf
, PTMLIB_CMD_GET_STATUS
)) {
380 DLOG("unsupported legacy cmd %s\n", inbuf
);
383 /* internally create a csv-style cmd */
384 ptm_lib_init_msg(hdl
, 0, PTMLIB_MSG_TYPE_CMD
, NULL
, (void *)&p_ctxt
);
386 DLOG("couldnt allocate context\n");
389 ptm_lib_append_msg(hdl
, p_ctxt
, "cmd", PTMLIB_CMD_GET_STATUS
);
393 if (msglen
> inlen
) {
394 DLOG("msglen [%d] > inlen [%d]\n", msglen
, inlen
);
398 /* read the rest of the msg */
399 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, msglen
);
406 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
408 ERRLOG("Cannot allocate csv for msg\n");
412 csv_decode(csv
, inbuf
);
413 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
415 ERRLOG("%s: Could not allocate context \n", __FUNCTION__
);
422 p_ctxt
->cmd_id
= cmd_id
;
426 switch(p_ctxt
->type
) {
427 case PTMLIB_MSG_TYPE_NOTIFICATION
:
429 hdl
->notify_cb(arg
, p_ctxt
);
431 case PTMLIB_MSG_TYPE_CMD
:
433 hdl
->cmd_cb(arg
, p_ctxt
);
435 case PTMLIB_MSG_TYPE_RESPONSE
:
436 if (hdl
->response_cb
)
437 hdl
->response_cb(arg
, p_ctxt
);
443 csv_clean(p_ctxt
->csv
);
444 csv_free(p_ctxt
->csv
);
451 ptm_lib_register(char *client_name
,
453 ptm_notify_cb notify_cb
,
454 ptm_response_cb response_cb
)
456 ptm_lib_handle_t
*hdl
;
458 hdl
= calloc(1, sizeof(*hdl
));
461 strncpy(hdl
->client_name
, client_name
, PTMLIB_MAXNAMELEN
- 1);
462 hdl
->cmd_cb
= cmd_cb
;
463 hdl
->notify_cb
= notify_cb
;
464 hdl
->response_cb
= response_cb
;
471 ptm_lib_deregister(ptm_lib_handle_t
*hdl
)
474 memset(hdl
, 0x00, sizeof(*hdl
));