/* * LGPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (c) 2014, Intel Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 or (at your discretion) any later version. * (LGPL) version 2.1 accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl-2.1.html * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * LGPL HEADER END */ /* * lustre/utils/liblustreapi_json.c * * lustreapi library for json calls * * Author: Michael MacDonald * Author: Bruno Faccini */ #include #include #include #include #include #include #include #include #ifdef HAVE_LINUX_UNISTD_H #include #else #include #endif #include #include /** Quick-n'-dirty JSON string escape routine. * \param[out] out_string JSON-escaped string, allocated here * \param[in] in_string Unescaped string * * \retval 0 on success. * \retval -errno on error. * * http://json.org/ * http://www.ietf.org/rfc/rfc4627.txt (section 2.5) */ int llapi_json_escape_string(char **out_string, char *in_string) { int i; char escape_chars[] = {'\b', '\f', '\n', '\r', '\t', '"', '\\', '\0'}; char *escaped_chars[] = {"\\\\b", "\\\\f", "\\\\n", "\\\\r", "\\\\t", "\\\\\"", "\\\\\\\\"}; char *src = in_string; char *idx, *dst, *tmp; char *escaped_string; size_t tmp_len, escaped_length = strlen(in_string); /* add up the extra space needed for the escapes */ while (*src) { idx = strchr(escape_chars, *src); if (idx != NULL) { tmp = escaped_chars[idx - escape_chars]; escaped_length += strlen(tmp); } src++; } escaped_string = calloc(1, escaped_length + 1); if (escaped_string == NULL) return -ENOMEM; src = in_string; dst = escaped_string; for (i = 0; *src && i <= escaped_length; i++) { idx = strchr(escape_chars, *src); if (idx != NULL) { tmp = escaped_chars[idx - escape_chars]; tmp_len = strlen(tmp); memcpy(dst, tmp, tmp_len); dst += tmp_len; src++; } else { *dst = *src; src++; dst++; } } *dst = '\0'; *out_string = escaped_string; return 0; } /** Write a list of JSON items to a filehandle. * \param json_items list of JSON items to be written * \param fp open filehandle to use for write * * \retval 0 on success. * \retval -errno on error. */ int llapi_json_write_list(struct llapi_json_item_list **json_items, FILE *fp) { int i; char *escaped_string = NULL; struct llapi_json_item_list *list; struct llapi_json_item *item; if (json_items == NULL || *json_items == NULL) return -EINVAL; list = *json_items; item = list->ljil_items; fprintf(fp, "{"); for (i = 0; i < list->ljil_item_count; i++) { if (item == NULL) { llapi_err_noerrno(LLAPI_MSG_ERROR, "%d json items but %d is NULL!", list->ljil_item_count, i); /* Don't bomb out here so that we still emit * valid JSON. */ break; } fprintf(fp, "\"%s\": ", item->lji_key); switch (item->lji_type) { case LLAPI_JSON_INTEGER: fprintf(fp, "%d", item->lji_integer); break; case LLAPI_JSON_BIGNUM: fprintf(fp, "%llu", (unsigned long long)item->lji_u64); break; case LLAPI_JSON_REAL: fprintf(fp, "%f", item->lji_real); break; case LLAPI_JSON_STRING: if (llapi_json_escape_string(&escaped_string, item->lji_string) < 0) { if (escaped_string != NULL) free(escaped_string); return -errno; } fprintf(fp, "\"%s\"", escaped_string); if (escaped_string != NULL) free(escaped_string); break; default: llapi_err_noerrno(LLAPI_MSG_ERROR, "Invalid item type: %d", item->lji_type); /* Ensure valid JSON */ fprintf(fp, "\"\""); break; } if (i < list->ljil_item_count - 1) fprintf(fp, ", "); item = item->lji_next; } fprintf(fp, "}\n"); return 0; } /** Create a list to hold JSON items. * \param[out] json_items Item list handle, allocated here * * \retval 0 on success. * \retval -errno on error. */ int llapi_json_init_list(struct llapi_json_item_list **json_items) { struct llapi_json_item_list *new_list; new_list = calloc(1, sizeof(*new_list)); if (new_list == NULL) return -ENOMEM; new_list->ljil_item_count = 0; *json_items = new_list; return 0; } /** Deallocate a list of JSON items. * \param json_items Item list handle, deallocated here * * \retval 0 on success. * \retval -errno on error. */ int llapi_json_destroy_list(struct llapi_json_item_list **json_items) { int i; struct llapi_json_item_list *list; struct llapi_json_item *cur_item; struct llapi_json_item *last_item; if (json_items == NULL || *json_items == NULL) return -EINVAL; list = *json_items; cur_item = list->ljil_items; for (i = 0; i < list->ljil_item_count; i++) { if (cur_item == NULL) { llapi_err_noerrno(LLAPI_MSG_ERROR, "%d json items but %d is NULL!", list->ljil_item_count, i); return -EINVAL; } if (cur_item->lji_key != NULL) free(cur_item->lji_key); if (cur_item->lji_type == LLAPI_JSON_STRING && cur_item->lji_string != NULL) free(cur_item->lji_string); last_item = cur_item; cur_item = last_item->lji_next; free(last_item); } free(list); *json_items = NULL; return 0; } /** Add an item to a list of JSON items. * \param json_items Item list handle * \param key Item key name * \param type Item key type * \param val Item key value * * \retval 0 on success. * \retval -errno on error. */ int llapi_json_add_item(struct llapi_json_item_list **json_items, char *key, __u32 type, void *val) { struct llapi_json_item_list *list; struct llapi_json_item *new_item; size_t len; if (json_items == NULL || *json_items == NULL) return -EINVAL; if (val == NULL) return -EINVAL; list = *json_items; new_item = calloc(1, sizeof(*new_item)); if (new_item == NULL) return -ENOMEM; len = strlen(key) + 1; new_item->lji_key = calloc(len, sizeof(char)); if (new_item->lji_key == NULL) return -ENOMEM; snprintf(new_item->lji_key, len, "%s", key); new_item->lji_type = type; new_item->lji_next = NULL; switch (new_item->lji_type) { case LLAPI_JSON_INTEGER: new_item->lji_integer = *(int *)val; break; case LLAPI_JSON_BIGNUM: new_item->lji_u64 = *(__u64 *)val; break; case LLAPI_JSON_REAL: new_item->lji_real = *(double *)val; break; case LLAPI_JSON_STRING: len = strlen((char *)val) + 1; new_item->lji_string = calloc(len, sizeof(char)); if (new_item->lji_string == NULL) return -ENOMEM; snprintf(new_item->lji_string, len, "%s", (char *)val); break; default: llapi_err_noerrno(LLAPI_MSG_ERROR, "Unknown JSON type: %d", new_item->lji_type); return -EINVAL; } if (list->ljil_item_count == 0) { list->ljil_items = new_item; } else { new_item->lji_next = list->ljil_items; list->ljil_items = new_item; } list->ljil_item_count++; return 0; }