1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2015 Cumulus Networks, Inc.
18 #include <sys/socket.h>
25 #define ERRLOG(fmt, ...) \
28 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
29 __LINE__, __func__, ##__VA_ARGS__); \
32 #define DLOG(fmt, ...) \
35 fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
36 __LINE__, __func__, ##__VA_ARGS__); \
39 typedef struct ptm_lib_msg_ctxt_s
{
45 static csv_record_t
*_ptm_lib_encode_header(csv_t
*csv
, csv_record_t
*rec
,
46 int msglen
, int version
, int type
,
47 int cmd_id
, char *client_name
)
49 char msglen_buf
[16], vers_buf
[16], type_buf
[16], cmdid_buf
[16];
53 snprintf(msglen_buf
, sizeof(msglen_buf
), "%4d", msglen
);
54 snprintf(vers_buf
, sizeof(vers_buf
), "%4d", version
);
55 snprintf(type_buf
, sizeof(type_buf
), "%4d", type
);
56 snprintf(cmdid_buf
, sizeof(cmdid_buf
), "%4d", cmd_id
);
57 snprintf(client_buf
, 17, "%16.16s", client_name
);
59 rec1
= csv_encode_record(csv
, rec
, 5, msglen_buf
, vers_buf
,
60 type_buf
, cmdid_buf
, client_buf
);
62 rec1
= csv_encode(csv
, 5, msglen_buf
, vers_buf
, type_buf
,
63 cmdid_buf
, client_buf
);
68 static int _ptm_lib_decode_header(csv_t
*csv
, int *msglen
, int *version
,
69 int *type
, int *cmd_id
, char *client_name
)
76 csv_decode(csv
, NULL
);
77 rec
= csv_record_iter(csv
);
79 DLOG("malformed CSV\n");
82 hdr
= csv_field_iter(rec
, &fld
);
84 DLOG("malformed CSV\n");
88 hdr
= csv_field_iter_next(&fld
);
90 DLOG("malformed CSV\n");
94 hdr
= csv_field_iter_next(&fld
);
96 DLOG("malformed CSV\n");
100 hdr
= csv_field_iter_next(&fld
);
102 DLOG("malformed CSV\n");
106 hdr
= csv_field_iter_next(&fld
);
108 DLOG("malformed CSV\n");
111 /* remove leading spaces */
112 for (i
= j
= 0; i
< csv_field_len(fld
); i
++) {
113 if (!isspace((unsigned char)hdr
[i
])) {
114 client_name
[j
] = hdr
[i
];
118 client_name
[j
] = '\0';
123 int ptm_lib_append_msg(ptm_lib_handle_t
*hdl
, void *ctxt
, const char *key
,
126 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
128 csv_record_t
*mh_rec
, *rec
;
131 ERRLOG("%s: no context \n", __func__
);
136 mh_rec
= csv_record_iter(csv
);
137 rec
= csv_record_iter_next(mh_rec
);
139 /* append to the hdr record */
140 rec
= csv_append_record(csv
, rec
, 1, key
);
142 ERRLOG("%s: Could not append key \n", __func__
);
146 rec
= csv_record_iter_next(rec
);
147 /* append to the data record */
148 rec
= csv_append_record(csv
, rec
, 1, val
);
150 ERRLOG("%s: Could not append val \n", __func__
);
154 /* update the msg hdr */
155 _ptm_lib_encode_header(csv
, mh_rec
, (csvlen(csv
) - PTMLIB_MSG_HDR_LEN
),
156 PTMLIB_MSG_VERSION
, p_ctxt
->type
, p_ctxt
->cmd_id
,
162 int ptm_lib_init_msg(ptm_lib_handle_t
*hdl
, int cmd_id
, int type
, void *in_ctxt
,
165 ptm_lib_msg_ctxt_t
*p_ctxt
;
166 ptm_lib_msg_ctxt_t
*p_in_ctxt
= in_ctxt
;
168 csv_record_t
*rec
, *d_rec
;
170 /* Initialize csv for using discrete record buffers */
171 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
174 ERRLOG("%s: Could not allocate csv \n", __func__
);
178 rec
= _ptm_lib_encode_header(csv
, NULL
, 0, PTMLIB_MSG_VERSION
, type
,
179 cmd_id
, hdl
->client_name
);
182 ERRLOG("%s: Could not allocate record \n", __func__
);
188 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
190 ERRLOG("%s: Could not allocate context \n", __func__
);
197 p_ctxt
->cmd_id
= cmd_id
;
200 *(ptm_lib_msg_ctxt_t
**)out_ctxt
= p_ctxt
;
202 /* caller supplied a context to initialize with? */
204 /* insert the hdr rec */
205 rec
= csv_record_iter(p_in_ctxt
->csv
);
206 csv_clone_record(p_in_ctxt
->csv
, rec
, &d_rec
);
207 csv_insert_record(csv
, d_rec
);
208 /* insert the data rec */
209 rec
= csv_record_iter_next(rec
);
210 csv_clone_record(p_in_ctxt
->csv
, rec
, &d_rec
);
211 csv_insert_record(csv
, d_rec
);
216 int ptm_lib_cleanup_msg(ptm_lib_handle_t
*hdl
, void *ctxt
)
218 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
222 ERRLOG("%s: no context \n", __func__
);
235 int ptm_lib_complete_msg(ptm_lib_handle_t
*hdl
, void *ctxt
, char *buf
, int *len
)
237 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
242 ERRLOG("%s: no context \n", __func__
);
247 rec
= csv_record_iter(csv
);
249 _ptm_lib_encode_header(csv
, rec
, (csvlen(csv
) - PTMLIB_MSG_HDR_LEN
),
250 PTMLIB_MSG_VERSION
, p_ctxt
->type
, p_ctxt
->cmd_id
,
253 /* parse csv contents into string */
255 if (csv_serialize(csv
, buf
, *len
)) {
256 ERRLOG("%s: cannot serialize\n", __func__
);
269 int ptm_lib_find_key_in_msg(void *ctxt
, const char *key
, char *val
)
271 ptm_lib_msg_ctxt_t
*p_ctxt
= ctxt
;
272 csv_t
*csv
= p_ctxt
->csv
;
273 csv_record_t
*hrec
, *drec
;
274 csv_field_t
*hfld
, *dfld
;
278 * skip over ptm hdr if present
279 * The next hdr is the keys (column name)
280 * The next hdr is the data
282 if (csv_num_records(csv
) > 2) {
283 hrec
= csv_record_iter(csv
);
284 hrec
= csv_record_iter_next(hrec
);
286 hrec
= csv_record_iter(csv
);
288 drec
= csv_record_iter_next(hrec
);
290 for (hstr
= csv_field_iter(hrec
, &hfld
),
291 dstr
= csv_field_iter(drec
, &dfld
);
292 (hstr
&& dstr
); hstr
= csv_field_iter_next(&hfld
),
293 dstr
= csv_field_iter_next(&dfld
)) {
294 if (!strncmp(hstr
, key
, csv_field_len(hfld
))) {
295 snprintf(val
, csv_field_len(dfld
) + 1, "%s", dstr
);
303 static int _ptm_lib_read_ptm_socket(int fd
, char *buf
, int len
)
308 while (bytes_read
!= len
) {
309 rc
= recv(fd
, (void *)(buf
+ bytes_read
), (len
- bytes_read
),
312 if (errno
&& (errno
!= EAGAIN
)
313 && (errno
!= EWOULDBLOCK
)) {
314 ERRLOG("fatal recv error(%s), closing connection, rc %d\n",
315 strerror(errno
), rc
);
322 DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n",
323 errno
, strerror(errno
), bytes_read
, len
);
335 int ptm_lib_process_msg(ptm_lib_handle_t
*hdl
, int fd
, char *inbuf
, int inlen
,
339 char client_name
[32];
340 int cmd_id
= 0, type
= 0, ver
= 0, msglen
= 0;
342 ptm_lib_msg_ctxt_t
*p_ctxt
= NULL
;
344 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, PTMLIB_MSG_HDR_LEN
);
348 csv
= csv_init(NULL
, inbuf
, PTMLIB_MSG_HDR_LEN
);
351 DLOG("Cannot allocate csv for hdr\n");
355 rc
= _ptm_lib_decode_header(csv
, &msglen
, &ver
, &type
, &cmd_id
,
362 /* could not decode the CSV - maybe its legacy cmd?
363 * get the entire cmd from the socket and see if we can process
366 if (len
== PTMLIB_MSG_HDR_LEN
) {
367 len
+= _ptm_lib_read_ptm_socket(
368 fd
, (inbuf
+ PTMLIB_MSG_HDR_LEN
),
369 inlen
- PTMLIB_MSG_HDR_LEN
);
375 /* we only support the get-status cmd */
376 if (strcmp(inbuf
, PTMLIB_CMD_GET_STATUS
)) {
377 DLOG("unsupported legacy cmd %s\n", inbuf
);
380 /* internally create a csv-style cmd */
381 ptm_lib_init_msg(hdl
, 0, PTMLIB_MSG_TYPE_CMD
, NULL
,
384 DLOG("couldnt allocate context\n");
387 ptm_lib_append_msg(hdl
, p_ctxt
, "cmd", PTMLIB_CMD_GET_STATUS
);
391 if (msglen
> inlen
) {
392 DLOG("msglen [%d] > inlen [%d]\n", msglen
, inlen
);
396 /* read the rest of the msg */
397 len
= _ptm_lib_read_ptm_socket(fd
, inbuf
, msglen
);
404 csv
= csv_init(NULL
, NULL
, PTMLIB_MSG_SZ
);
406 ERRLOG("Cannot allocate csv for msg\n");
410 csv_decode(csv
, inbuf
);
411 p_ctxt
= calloc(1, sizeof(*p_ctxt
));
413 ERRLOG("%s: Could not allocate context \n", __func__
);
420 p_ctxt
->cmd_id
= cmd_id
;
424 switch (p_ctxt
->type
) {
425 case PTMLIB_MSG_TYPE_NOTIFICATION
:
427 hdl
->notify_cb(arg
, p_ctxt
);
429 case PTMLIB_MSG_TYPE_CMD
:
431 hdl
->cmd_cb(arg
, p_ctxt
);
433 case PTMLIB_MSG_TYPE_RESPONSE
:
434 if (hdl
->response_cb
)
435 hdl
->response_cb(arg
, p_ctxt
);
441 csv_clean(p_ctxt
->csv
);
442 csv_free(p_ctxt
->csv
);
448 ptm_lib_handle_t
*ptm_lib_register(char *client_name
, ptm_cmd_cb cmd_cb
,
449 ptm_notify_cb notify_cb
,
450 ptm_response_cb response_cb
)
452 ptm_lib_handle_t
*hdl
;
454 hdl
= calloc(1, sizeof(*hdl
));
457 strncpy(hdl
->client_name
, client_name
, PTMLIB_MAXNAMELEN
- 1);
458 hdl
->cmd_cb
= cmd_cb
;
459 hdl
->notify_cb
= notify_cb
;
460 hdl
->response_cb
= response_cb
;
466 void ptm_lib_deregister(ptm_lib_handle_t
*hdl
)
469 memset(hdl
, 0x00, sizeof(*hdl
));