| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include "csv.h" |
| #include "ptm_lib.h" |
| |
| #define DEBUG_E 0 |
| #define DEBUG_V 0 |
| |
| #define ERRLOG(fmt, ...) \ |
| do { \ |
| if (DEBUG_E) \ |
| fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \ |
| __LINE__, __func__, ##__VA_ARGS__); \ |
| } while (0) |
| |
| #define DLOG(fmt, ...) \ |
| do { \ |
| if (DEBUG_V) \ |
| fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \ |
| __LINE__, __func__, ##__VA_ARGS__); \ |
| } while (0) |
| |
| typedef struct ptm_lib_msg_ctxt_s { |
| int cmd_id; |
| csv_t *csv; |
| ptmlib_msg_type type; |
| } ptm_lib_msg_ctxt_t; |
| |
| static csv_record_t *_ptm_lib_encode_header(csv_t *csv, csv_record_t *rec, |
| int msglen, int version, int type, |
| int cmd_id, char *client_name) |
| { |
| char msglen_buf[16], vers_buf[16], type_buf[16], cmdid_buf[16]; |
| char client_buf[32]; |
| csv_record_t *rec1; |
| |
| snprintf(msglen_buf, sizeof(msglen_buf), "%4d", msglen); |
| snprintf(vers_buf, sizeof(vers_buf), "%4d", version); |
| snprintf(type_buf, sizeof(type_buf), "%4d", type); |
| snprintf(cmdid_buf, sizeof(cmdid_buf), "%4d", cmd_id); |
| snprintf(client_buf, 17, "%16.16s", client_name); |
| if (rec) { |
| rec1 = csv_encode_record(csv, rec, 5, msglen_buf, vers_buf, |
| type_buf, cmdid_buf, client_buf); |
| } else { |
| rec1 = csv_encode(csv, 5, msglen_buf, vers_buf, type_buf, |
| cmdid_buf, client_buf); |
| } |
| return (rec1); |
| } |
| |
| static int _ptm_lib_decode_header(csv_t *csv, int *msglen, int *version, |
| int *type, int *cmd_id, char *client_name) |
| { |
| char *hdr; |
| csv_record_t *rec; |
| csv_field_t *fld; |
| int i, j; |
| |
| csv_decode(csv, NULL); |
| rec = csv_record_iter(csv); |
| if (rec == NULL) { |
| DLOG("malformed CSV\n"); |
| return -1; |
| } |
| hdr = csv_field_iter(rec, &fld); |
| if (hdr == NULL) { |
| DLOG("malformed CSV\n"); |
| return -1; |
| } |
| *msglen = atoi(hdr); |
| hdr = csv_field_iter_next(&fld); |
| if (hdr == NULL) { |
| DLOG("malformed CSV\n"); |
| return -1; |
| } |
| *version = atoi(hdr); |
| hdr = csv_field_iter_next(&fld); |
| if (hdr == NULL) { |
| DLOG("malformed CSV\n"); |
| return -1; |
| } |
| *type = atoi(hdr); |
| hdr = csv_field_iter_next(&fld); |
| if (hdr == NULL) { |
| DLOG("malformed CSV\n"); |
| return -1; |
| } |
| *cmd_id = atoi(hdr); |
| hdr = csv_field_iter_next(&fld); |
| if (hdr == NULL) { |
| DLOG("malformed CSV\n"); |
| return -1; |
| } |
| |
| for (i = j = 0; i < csv_field_len(fld); i++) { |
| if (!isspace((unsigned char)hdr[i])) { |
| client_name[j] = hdr[i]; |
| j++; |
| } |
| } |
| client_name[j] = '\0'; |
| |
| return 0; |
| } |
| |
| int ptm_lib_append_msg(ptm_lib_handle_t *hdl, void *ctxt, const char *key, |
| const char *val) |
| { |
| ptm_lib_msg_ctxt_t *p_ctxt = ctxt; |
| csv_t *csv; |
| csv_record_t *mh_rec, *rec; |
| |
| if (!p_ctxt) { |
| ERRLOG("%s: no context \n", __func__); |
| return -1; |
| } |
| |
| csv = p_ctxt->csv; |
| mh_rec = csv_record_iter(csv); |
| rec = csv_record_iter_next(mh_rec); |
| |
| |
| rec = csv_append_record(csv, rec, 1, key); |
| if (!rec) { |
| ERRLOG("%s: Could not append key \n", __func__); |
| return -1; |
| } |
| |
| rec = csv_record_iter_next(rec); |
| |
| rec = csv_append_record(csv, rec, 1, val); |
| if (!rec) { |
| ERRLOG("%s: Could not append val \n", __func__); |
| return -1; |
| } |
| |
| |
| _ptm_lib_encode_header(csv, mh_rec, (csvlen(csv) - PTMLIB_MSG_HDR_LEN), |
| PTMLIB_MSG_VERSION, p_ctxt->type, p_ctxt->cmd_id, |
| hdl->client_name); |
| |
| return 0; |
| } |
| |
| int ptm_lib_init_msg(ptm_lib_handle_t *hdl, int cmd_id, int type, void *in_ctxt, |
| void **out_ctxt) |
| { |
| ptm_lib_msg_ctxt_t *p_ctxt; |
| ptm_lib_msg_ctxt_t *p_in_ctxt = in_ctxt; |
| csv_t *csv; |
| csv_record_t *rec, *d_rec; |
| |
| |
| csv = csv_init(NULL, NULL, PTMLIB_MSG_SZ); |
| |
| if (!csv) { |
| ERRLOG("%s: Could not allocate csv \n", __func__); |
| return -1; |
| } |
| |
| rec = _ptm_lib_encode_header(csv, NULL, 0, PTMLIB_MSG_VERSION, type, |
| cmd_id, hdl->client_name); |
| |
| if (!rec) { |
| ERRLOG("%s: Could not allocate record \n", __func__); |
| csv_clean(csv); |
| csv_free(csv); |
| return -1; |
| } |
| |
| p_ctxt = calloc(1, sizeof(*p_ctxt)); |
| if (!p_ctxt) { |
| ERRLOG("%s: Could not allocate context \n", __func__); |
| csv_clean(csv); |
| csv_free(csv); |
| return -1; |
| } |
| |
| p_ctxt->csv = csv; |
| p_ctxt->cmd_id = cmd_id; |
| p_ctxt->type = type; |
| |
| *(ptm_lib_msg_ctxt_t **)out_ctxt = p_ctxt; |
| |
| |
| if (p_in_ctxt) { |
| |
| rec = csv_record_iter(p_in_ctxt->csv); |
| csv_clone_record(p_in_ctxt->csv, rec, &d_rec); |
| csv_insert_record(csv, d_rec); |
| |
| rec = csv_record_iter_next(rec); |
| csv_clone_record(p_in_ctxt->csv, rec, &d_rec); |
| csv_insert_record(csv, d_rec); |
| } |
| return 0; |
| } |
| |
| int ptm_lib_cleanup_msg(ptm_lib_handle_t *hdl, void *ctxt) |
| { |
| ptm_lib_msg_ctxt_t *p_ctxt = ctxt; |
| csv_t *csv; |
| |
| if (!p_ctxt) { |
| ERRLOG("%s: no context \n", __func__); |
| return -1; |
| } |
| |
| csv = p_ctxt->csv; |
| |
| csv_clean(csv); |
| csv_free(csv); |
| free(p_ctxt); |
| |
| return 0; |
| } |
| |
| int ptm_lib_complete_msg(ptm_lib_handle_t *hdl, void *ctxt, char *buf, int *len) |
| { |
| ptm_lib_msg_ctxt_t *p_ctxt = ctxt; |
| csv_t *csv; |
| csv_record_t *rec; |
| |
| if (!p_ctxt) { |
| ERRLOG("%s: no context \n", __func__); |
| return -1; |
| } |
| |
| csv = p_ctxt->csv; |
| rec = csv_record_iter(csv); |
| |
| _ptm_lib_encode_header(csv, rec, (csvlen(csv) - PTMLIB_MSG_HDR_LEN), |
| PTMLIB_MSG_VERSION, p_ctxt->type, p_ctxt->cmd_id, |
| hdl->client_name); |
| |
| |
| if (buf && len) { |
| if (csv_serialize(csv, buf, *len)) { |
| ERRLOG("%s: cannot serialize\n", __func__); |
| return -1; |
| } |
| *len = csvlen(csv); |
| } |
| |
| csv_clean(csv); |
| csv_free(csv); |
| free(p_ctxt); |
| |
| return 0; |
| } |
| |
| int ptm_lib_find_key_in_msg(void *ctxt, const char *key, char *val) |
| { |
| ptm_lib_msg_ctxt_t *p_ctxt = ctxt; |
| csv_t *csv = p_ctxt->csv; |
| csv_record_t *hrec, *drec; |
| csv_field_t *hfld, *dfld; |
| char *hstr, *dstr; |
| |
| |
| |
| |
| |
| |
| if (csv_num_records(csv) > 2) { |
| hrec = csv_record_iter(csv); |
| hrec = csv_record_iter_next(hrec); |
| } else { |
| hrec = csv_record_iter(csv); |
| } |
| drec = csv_record_iter_next(hrec); |
| val[0] = '\0'; |
| for (hstr = csv_field_iter(hrec, &hfld), |
| dstr = csv_field_iter(drec, &dfld); |
| (hstr && dstr); hstr = csv_field_iter_next(&hfld), |
| dstr = csv_field_iter_next(&dfld)) { |
| if (!strncmp(hstr, key, csv_field_len(hfld))) { |
| snprintf(val, csv_field_len(dfld) + 1, "%s", dstr); |
| return 0; |
| } |
| } |
| |
| return -1; |
| } |
| |
| static int _ptm_lib_read_ptm_socket(int fd, char *buf, int len) |
| { |
| int retries = 0, rc; |
| int bytes_read = 0; |
| |
| while (bytes_read != len) { |
| rc = recv(fd, (void *)(buf + bytes_read), (len - bytes_read), |
| MSG_DONTWAIT); |
| if (rc <= 0) { |
| if (errno && (errno != EAGAIN) |
| && (errno != EWOULDBLOCK)) { |
| ERRLOG("fatal recv error(%s), closing connection, rc %d\n", |
| strerror(errno), rc); |
| return (rc); |
| } else { |
| if (retries++ < 2) { |
| usleep(10000); |
| continue; |
| } |
| DLOG("max retries - recv error(%d - %s) bytes read %d (%d)\n", |
| errno, strerror(errno), bytes_read, len); |
| return (bytes_read); |
| } |
| break; |
| } else { |
| bytes_read += rc; |
| } |
| } |
| |
| return bytes_read; |
| } |
| |
| int ptm_lib_process_msg(ptm_lib_handle_t *hdl, int fd, char *inbuf, int inlen, |
| void *arg) |
| { |
| int rc, len; |
| char client_name[32]; |
| int cmd_id = 0, type = 0, ver = 0, msglen = 0; |
| csv_t *csv; |
| ptm_lib_msg_ctxt_t *p_ctxt = NULL; |
| |
| len = _ptm_lib_read_ptm_socket(fd, inbuf, PTMLIB_MSG_HDR_LEN); |
| if (len <= 0) |
| return (len); |
| |
| csv = csv_init(NULL, inbuf, PTMLIB_MSG_HDR_LEN); |
| |
| if (!csv) { |
| DLOG("Cannot allocate csv for hdr\n"); |
| return -1; |
| } |
| |
| rc = _ptm_lib_decode_header(csv, &msglen, &ver, &type, &cmd_id, |
| client_name); |
| |
| csv_clean(csv); |
| csv_free(csv); |
| |
| if (rc < 0) { |
| |
| |
| |
| |
| if (len == PTMLIB_MSG_HDR_LEN) { |
| len += _ptm_lib_read_ptm_socket( |
| fd, (inbuf + PTMLIB_MSG_HDR_LEN), |
| inlen - PTMLIB_MSG_HDR_LEN); |
| if (len <= 0) |
| return (len); |
| } |
| |
| inbuf[len] = '\0'; |
| |
| if (strcmp(inbuf, PTMLIB_CMD_GET_STATUS)) { |
| DLOG("unsupported legacy cmd %s\n", inbuf); |
| return -1; |
| } |
| |
| ptm_lib_init_msg(hdl, 0, PTMLIB_MSG_TYPE_CMD, NULL, |
| (void *)&p_ctxt); |
| if (!p_ctxt) { |
| DLOG("couldnt allocate context\n"); |
| return -1; |
| } |
| ptm_lib_append_msg(hdl, p_ctxt, "cmd", PTMLIB_CMD_GET_STATUS); |
| |
| } else { |
| |
| if (msglen > inlen) { |
| DLOG("msglen [%d] > inlen [%d]\n", msglen, inlen); |
| return -1; |
| } |
| |
| |
| len = _ptm_lib_read_ptm_socket(fd, inbuf, msglen); |
| if (len <= 0) { |
| return (len); |
| } |
| |
| inbuf[len] = '\0'; |
| |
| csv = csv_init(NULL, NULL, PTMLIB_MSG_SZ); |
| if (!csv) { |
| ERRLOG("Cannot allocate csv for msg\n"); |
| return -1; |
| } |
| |
| csv_decode(csv, inbuf); |
| p_ctxt = calloc(1, sizeof(*p_ctxt)); |
| if (!p_ctxt) { |
| ERRLOG("%s: Could not allocate context \n", __func__); |
| csv_clean(csv); |
| csv_free(csv); |
| return -1; |
| } |
| |
| p_ctxt->csv = csv; |
| p_ctxt->cmd_id = cmd_id; |
| p_ctxt->type = type; |
| } |
| |
| switch (p_ctxt->type) { |
| case PTMLIB_MSG_TYPE_NOTIFICATION: |
| if (hdl->notify_cb) |
| hdl->notify_cb(arg, p_ctxt); |
| break; |
| case PTMLIB_MSG_TYPE_CMD: |
| if (hdl->cmd_cb) |
| hdl->cmd_cb(arg, p_ctxt); |
| break; |
| case PTMLIB_MSG_TYPE_RESPONSE: |
| if (hdl->response_cb) |
| hdl->response_cb(arg, p_ctxt); |
| break; |
| default: |
| return -1; |
| } |
| |
| csv_clean(p_ctxt->csv); |
| csv_free(p_ctxt->csv); |
| free(p_ctxt); |
| |
| return len; |
| } |
| |
| ptm_lib_handle_t *ptm_lib_register(char *client_name, ptm_cmd_cb cmd_cb, |
| ptm_notify_cb notify_cb, |
| ptm_response_cb response_cb) |
| { |
| ptm_lib_handle_t *hdl; |
| |
| hdl = calloc(1, sizeof(*hdl)); |
| |
| if (hdl) { |
| strncpy(hdl->client_name, client_name, PTMLIB_MAXNAMELEN - 1); |
| hdl->cmd_cb = cmd_cb; |
| hdl->notify_cb = notify_cb; |
| hdl->response_cb = response_cb; |
| } |
| |
| return hdl; |
| } |
| |
| void ptm_lib_deregister(ptm_lib_handle_t *hdl) |
| { |
| if (hdl) { |
| memset(hdl, 0x00, sizeof(*hdl)); |
| free(hdl); |
| } |
| } |