Viewing: liblustreapi_json.c
// SPDX-License-Identifier: LGPL-2.1+
/*
* Copyright (c) 2014, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
*
* lustreapi library for json calls
*
* Author: Michael MacDonald <michael.macdonald@intel.com>
* Author: Bruno Faccini <bruno.faccini@intel.com>
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <malloc.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/types.h>
#ifdef HAVE_LINUX_UNISTD_H
#include <linux/unistd.h>
#else
#include <unistd.h>
#endif
#include <libcfs/util/string.h>
#include <lustre/lustreapi.h>
/** 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)
*/
static int 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 (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;
}
/**
* llapi_json_add_item() - Add an item to a list of JSON items.
* @json_items: Item list handle
* @key: Item key name
* @type: Item key type
* @val: Item key value
*
* Return:
* * %0 on success.
* * %negative 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;
int rc = 0;
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) {
rc = -ENOMEM;
goto out_item;
}
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) {
rc = -ENOMEM;
goto out_key;
}
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);
rc = -EINVAL;
goto out_key;
}
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 rc;
out_key:
free(new_item->lji_key);
out_item:
free(new_item);
return rc;
}