1 /*********************************************************************
2 * Copyright 2015 Cumulus Networks, LLC. All rights reserved.
4 * library file used by clients for sending commands and parsing response
16 #include <sys/socket.h>
23 #define ERRLOG(fmt, ...) \
24 do { if (DEBUG_E) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
25 __LINE__, __func__, ##__VA_ARGS__); } while (0)
27 #define DLOG(fmt, ...) \
28 do { if (DEBUG_V) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
29 __LINE__, __func__, ##__VA_ARGS__); } while (0)
31 typedef struct ptm_lib_msg_ctxt_s
{
38 _ptm_lib_encode_header(csv_t
*csv
,
46 char msglen_buf
[16], vers_buf
[16], type_buf
[16], cmdid_buf
[16];
50 sprintf(msglen_buf
, "%4u", msglen
);
51 sprintf(vers_buf
, "%4u", version
);
52 sprintf(type_buf
, "%4u", type
);
53 sprintf(cmdid_buf
, "%4u", cmd_id
);
54 snprintf(client_buf
, 17, "%16.16s", client_name
);
56 rec1
= csv_encode_record(csv
, rec
, 5, msglen_buf
, vers_buf
,
57 type_buf
, cmdid_buf
, client_buf
);
59 rec1
= csv_encode(csv
, 5, msglen_buf
, vers_buf
,
60 type_buf
, cmdid_buf
, client_buf
);
66 _ptm_lib_decode_header (csv_t
*csv
,
78 csv_decode(csv
, NULL
);
79 rec
= csv_record_iter(csv
);
81 DLOG("malformed CSV\n");
84 hdr
= csv_field_iter(rec
, &fld
);
86 DLOG("malformed CSV\n");
90 hdr
= csv_field_iter_next(&fld
);
92 DLOG("malformed CSV\n");
96 hdr
= csv_field_iter_next(&fld
);
98 DLOG("malformed CSV\n");
102 hdr
= csv_field_iter_next(&fld
);
104 DLOG("malformed CSV\n");
108 hdr
= csv_field_iter_next(&fld
);
110 DLOG("malformed CSV\n");
113 /* remove leading spaces */
114 for (i
= j
= 0; i
< csv_field_len(fld
); i
++) {
115 if (!isspace(hdr
[i
])) {
116 client_name
[j
] = hdr
[i
];
120 client_name
[j
] = '\0';
126 ptm_lib_append_msg(ptm_lib_handle_t
*hdl
, void *ctxt
,
127 const char *key
, const char *val
)
129 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
131 csv_record_t
*mh_rec
, *rec
;
134 ERRLOG("%s: no context \n", __FUNCTION__
);
139 mh_rec
= csv_record_iter(csv
);
140 rec
= csv_record_iter_next(mh_rec
);
142 /* append to the hdr record */
143 rec
= csv_append_record(csv
, rec
, 1, key
);
145 ERRLOG("%s: Could not append key \n", __FUNCTION__
);
149 rec
= csv_record_iter_next(rec
);
150 /* append to the data record */
151 rec
= csv_append_record(csv
, rec
, 1, val
);
153 ERRLOG("%s: Could not append val \n", __FUNCTION__
);
157 /* update the msg hdr */
158 _ptm_lib_encode_header(csv
, mh_rec
,
159 (csvlen(csv
) - PTMLIB_MSG_HDR_LEN
),
160 PTMLIB_MSG_VERSION
, p_ctxt
->type
,
161 p_ctxt
->cmd_id
, hdl
->client_name
);
167 ptm_lib_init_msg(ptm_lib_handle_t
*hdl
, int cmd_id
, int type
,
168 void *in_ctxt
, void **out_ctxt
)
170 ptm_lib_msg_ctxt_t
*p_ctxt
;
171 ptm_lib_msg_ctxt_t
*p_in_ctxt
= in_ctxt
;
173 csv_record_t
*rec
, *d_rec
;
175 /* Initialize csv for using discrete record buffers */
176 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
179 ERRLOG("%s: Could not allocate csv \n", __FUNCTION__
);
183 rec
= _ptm_lib_encode_header(csv
, NULL
, 0,
184 PTMLIB_MSG_VERSION
, type
,
185 cmd_id
, hdl
->client_name
);
188 ERRLOG("%s: Could not allocate record \n", __FUNCTION__
);
194 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
196 ERRLOG("%s: Could not allocate context \n", __FUNCTION__
);
203 p_ctxt
->cmd_id
= cmd_id
;
206 *(ptm_lib_msg_ctxt_t
**)out_ctxt
= p_ctxt
;
208 /* caller supplied a context to initialize with? */
210 /* insert the hdr rec */
211 rec
= csv_record_iter(p_in_ctxt
->csv
);
212 csv_clone_record (p_in_ctxt
->csv
, rec
, &d_rec
);
213 csv_insert_record (csv
, d_rec
);
214 /* insert the data rec */
215 rec
= csv_record_iter_next(rec
);
216 csv_clone_record (p_in_ctxt
->csv
, rec
, &d_rec
);
217 csv_insert_record (csv
, d_rec
);
223 ptm_lib_complete_msg(ptm_lib_handle_t
*hdl
, void *ctxt
,
226 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
231 ERRLOG("%s: no context \n", __FUNCTION__
);
236 rec
= csv_record_iter(csv
);
238 _ptm_lib_encode_header(csv
, rec
,
239 (csvlen(csv
) - PTMLIB_MSG_HDR_LEN
),
240 PTMLIB_MSG_VERSION
, p_ctxt
->type
,
241 p_ctxt
->cmd_id
, hdl
->client_name
);
243 /* parse csv contents into string */
245 if (csv_serialize(csv
, buf
, *len
)) {
246 ERRLOG("%s: cannot serialize\n", __FUNCTION__
);
260 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
);
284 hstr
= csv_field_iter_next(&hfld
),
285 dstr
= csv_field_iter_next(&dfld
)) {
286 if (!strncmp(hstr
, key
, csv_field_len(hfld
))) {
287 snprintf(val
, csv_field_len(dfld
)+1, "%s", dstr
);
296 _ptm_lib_read_ptm_socket(int fd
, char *buf
, int len
)
301 while (bytes_read
!= len
) {
302 rc
= recv(fd
, (void *) (buf
+ bytes_read
), (len
- bytes_read
),
305 if (errno
&& (errno
!= EAGAIN
) && (errno
!= EWOULDBLOCK
)) {
306 ERRLOG("fatal recv error(%s), closing connection, rc %d\n",
307 strerror(errno
), rc
);
314 DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n",
315 errno
, strerror(errno
), bytes_read
, len
);
328 ptm_lib_process_msg(ptm_lib_handle_t
*hdl
, int fd
,
329 char *inbuf
, int inlen
, void *arg
)
332 char client_name
[32];
333 int cmd_id
, type
, ver
, msglen
;
335 ptm_lib_msg_ctxt_t
*p_ctxt
;
337 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, PTMLIB_MSG_HDR_LEN
);
341 csv
= csv_init(NULL
, inbuf
, PTMLIB_MSG_HDR_LEN
);
344 DLOG("Cannot allocate csv for hdr\n");
348 rc
= _ptm_lib_decode_header(csv
, &msglen
, &ver
, &type
, &cmd_id
, client_name
);
354 /* could not decode the CSV - maybe its legacy cmd?
355 * get the entire cmd from the socket and see if we can process it
357 if (len
== PTMLIB_MSG_HDR_LEN
) {
358 len
+= _ptm_lib_read_ptm_socket(fd
, (inbuf
+PTMLIB_MSG_HDR_LEN
),
359 inlen
- PTMLIB_MSG_HDR_LEN
);
365 /* we only support the get-status cmd */
366 if (strcmp(inbuf
, PTMLIB_CMD_GET_STATUS
)) {
367 DLOG("unsupported legacy cmd %s\n", inbuf
);
370 /* internally create a csv-style cmd */
371 ptm_lib_init_msg(hdl
, 0, PTMLIB_MSG_TYPE_CMD
, NULL
, (void *)&p_ctxt
);
373 DLOG("couldnt allocate context\n");
376 ptm_lib_append_msg(hdl
, p_ctxt
, "cmd", PTMLIB_CMD_GET_STATUS
);
380 if (msglen
> inlen
) {
381 DLOG("msglen [%d] > inlen [%d]\n", msglen
, inlen
);
385 /* read the rest of the msg */
386 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, msglen
);
393 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
395 ERRLOG("Cannot allocate csv for msg\n");
399 csv_decode(csv
, inbuf
);
400 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
402 ERRLOG("%s: Could not allocate context \n", __FUNCTION__
);
409 p_ctxt
->cmd_id
= cmd_id
;
413 switch(p_ctxt
->type
) {
414 case PTMLIB_MSG_TYPE_NOTIFICATION
:
416 hdl
->notify_cb(arg
, p_ctxt
);
418 case PTMLIB_MSG_TYPE_CMD
:
420 hdl
->cmd_cb(arg
, p_ctxt
);
422 case PTMLIB_MSG_TYPE_RESPONSE
:
423 if (hdl
->response_cb
)
424 hdl
->response_cb(arg
, p_ctxt
);
430 csv_clean(p_ctxt
->csv
);
431 csv_free(p_ctxt
->csv
);
438 ptm_lib_register(char *client_name
,
440 ptm_notify_cb notify_cb
,
441 ptm_response_cb response_cb
)
443 ptm_lib_handle_t
*hdl
;
445 hdl
= calloc(1, sizeof(*hdl
));
448 strcpy(hdl
->client_name
, client_name
);
449 hdl
->cmd_cb
= cmd_cb
;
450 hdl
->notify_cb
= notify_cb
;
451 hdl
->response_cb
= response_cb
;
458 ptm_lib_deregister(ptm_lib_handle_t
*hdl
)
461 memset(hdl
, 0x00, sizeof(*hdl
));