#include "jsmn.h" #include "../libnetdata.h" #include "json.h" #include "libnetdata/libnetdata.h" #include "../../health/health.h" #define JSON_TOKENS 1024 int json_tokens = JSON_TOKENS; /** * Json Tokenise * * Map the string given inside tokens. * * @param js is the string used to create the tokens * @param len is the string length * @param count the number of tokens present in the string * * @return it returns the json parsed in tokens */ #ifdef ENABLE_JSONC json_object *json_tokenise(char *js) { if(!js) { netdata_log_error("JSON: json string is empty."); return NULL; } json_object *token = json_tokener_parse(js); if(!token) { netdata_log_error("JSON: Invalid json string."); return NULL; } return token; } #else jsmntok_t *json_tokenise(char *js, size_t len, size_t *count) { int n = json_tokens; if(!js || !len) { netdata_log_error("JSON: json string is empty."); return NULL; } jsmn_parser parser; jsmn_init(&parser); jsmntok_t *tokens = mallocz(sizeof(jsmntok_t) * n); if(!tokens) return NULL; int ret = jsmn_parse(&parser, js, len, tokens, n); while (ret == JSMN_ERROR_NOMEM) { n *= 2; jsmntok_t *new = reallocz(tokens, sizeof(jsmntok_t) * n); if(!new) { freez(tokens); return NULL; } tokens = new; ret = jsmn_parse(&parser, js, len, tokens, n); } if (ret == JSMN_ERROR_INVAL) { netdata_log_error("JSON: Invalid json string."); freez(tokens); return NULL; } else if (ret == JSMN_ERROR_PART) { netdata_log_error("JSON: Truncated JSON string."); freez(tokens); return NULL; } if(count) *count = (size_t)ret; if(json_tokens < n) json_tokens = n; return tokens; } #endif /** * Callback Print * * Set callback print case necessary and wrinte an information inside a buffer to write in the log. * * @param e a pointer for a structure that has the complete information about json structure. * * @return It always return 0 */ int json_callback_print(JSON_ENTRY *e) { BUFFER *wb=buffer_create(300, NULL); buffer_sprintf(wb,"%s = ", e->name); char txt[50]; switch(e->type) { case JSON_OBJECT: e->callback_function = json_callback_print; buffer_strcat(wb,"OBJECT"); break; case JSON_ARRAY: e->callback_function = json_callback_print; sprintf(txt,"ARRAY[%lu]", (long unsigned int) e->data.items); buffer_strcat(wb, txt); break; case JSON_STRING: buffer_strcat(wb, e->data.string); break; case JSON_NUMBER: sprintf(txt, NETDATA_DOUBLE_FORMAT_AUTO, e->data.number); buffer_strcat(wb,txt); break; case JSON_BOOLEAN: buffer_strcat(wb, e->data.boolean?"TRUE":"FALSE"); break; case JSON_NULL: buffer_strcat(wb,"NULL"); break; } netdata_log_info("JSON: %s", buffer_tostring(wb)); buffer_free(wb); return 0; } /** * JSONC Set String * * Set the string value of the structure JSON_ENTRY. * * @param e the output structure */ static inline void json_jsonc_set_string(JSON_ENTRY *e,char *key,const char *value) { size_t len = strlen(key); if(len > JSON_NAME_LEN) len = JSON_NAME_LEN; e->type = JSON_STRING; memcpy(e->name,key,len); e->name[len] = 0x00; e->data.string = (char *) value; } #ifdef ENABLE_JSONC /** * JSONC set Boolean * * Set the boolean value of the structure JSON_ENTRY * * @param e the output structure * @param value the input value */ static inline void json_jsonc_set_boolean(JSON_ENTRY *e,int value) { e->type = JSON_BOOLEAN; e->data.boolean = value; } static inline void json_jsonc_set_integer(JSON_ENTRY *e, char *key, int64_t value) { size_t len = strlen(key); if(len > JSON_NAME_LEN) len = JSON_NAME_LEN; e->type = JSON_NUMBER; memcpy(e->name, key, len); e->name[len] = 0; e->data.number = (NETDATA_DOUBLE)value; } /** * Parse Array * * Parse the array object. * * @param ptr the pointer for the object that we will parse. * @param callback_data additional data to be used together the callback function * @param callback_function function used to create a silencer. */ static inline void json_jsonc_parse_array(json_object *ptr, void *callback_data,int (*callback_function)(struct json_entry *)) { int end = json_object_array_length(ptr); JSON_ENTRY e; if(end) { int i; i = 0; enum json_type type; do { json_object *jvalue = json_object_array_get_idx(ptr, i); if(jvalue) { e.callback_data = callback_data; e.type = JSON_OBJECT; callback_function(&e); json_object_object_foreach(jvalue, key, val) { type = json_object_get_type(val); if (type == json_type_array) { e.type = JSON_ARRAY; json_jsonc_parse_array(val, callback_data, callback_function); } else if (type == json_type_object) { json_walk(val,callback_data,callback_function); } else if (type == json_type_string) { json_jsonc_set_string(&e,key,json_object_get_string(val)); callback_function(&e); } else if (type == json_type_boolean) { json_jsonc_set_boolean(&e,json_object_get_boolean(val)); callback_function(&e); } } } } while (++i < end); } } #else /** * Walk string * * Set JSON_ENTRY to string and map the values from jsmntok_t. * * @param js the original string * @param t the tokens * @param start the first position * @param e the output structure. * * @return It always return 1 */ size_t json_walk_string(char *js, jsmntok_t *t, size_t start, JSON_ENTRY *e) { char old = js[t[start].end]; js[t[start].end] = '\0'; e->original_string = &js[t[start].start]; e->type = JSON_STRING; e->data.string = e->original_string; if(e->callback_function) e->callback_function(e); js[t[start].end] = old; return 1; } /** * Walk Primitive * * Define the data type of the string * * @param js the original string * @param t the tokens * @param start the first position * @param e the output structure. * * @return It always return 1 */ size_t json_walk_primitive(char *js, jsmntok_t *t, size_t start, JSON_ENTRY *e) { char old = js[t[start].end]; js[t[start].end] = '\0'; e->original_string = &js[t[start].start]; switch(e->original_string[0]) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': case '.': e->type = JSON_NUMBER; e->data.number = strtold(e->original_string, NULL); break; case 't': case 'T': e->type = JSON_BOOLEAN; e->data.boolean = 1; break; case 'f': case 'F': e->type = JSON_BOOLEAN; e->data.boolean = 0; break; case 'n': case 'N': default: e->type = JSON_NULL; break; } if(e->callback_function) e->callback_function(e); js[t[start].end] = old; return 1; } /** * Array * * Measure the array length * * @param js the original string * @param t the tokens * @param nest the length of structure t * @param start the first position * @param e the structure with values and callback to be used inside the function. * * @return It returns the array length */ size_t json_walk_array(char *js, jsmntok_t *t, size_t nest, size_t start, JSON_ENTRY *e) { JSON_ENTRY ne; char old = js[t[start].end]; js[t[start].end] = '\0'; ne.original_string = &js[t[start].start]; memcpy(&ne, e, sizeof(JSON_ENTRY)); ne.type = JSON_ARRAY; ne.data.items = t[start].size; ne.callback_function = e->callback_function; ne.name[0]='\0'; ne.fullname[0]='\0'; if(e->callback_function) e->callback_function(&ne); js[t[start].end] = old; size_t i, init = start, size = t[start].size; start++; for(i = 0; i < size ; i++) { ne.pos = i; if (strlen(e->name) > JSON_NAME_LEN - 24 || strlen(e->fullname) > JSON_FULLNAME_LEN -24) { netdata_log_info("JSON: JSON walk_array ignoring element with name:%s fullname:%s",e->name, e->fullname); continue; } snprintfz(ne.name, JSON_NAME_LEN, "%s[%lu]", e->name, i); snprintfz(ne.fullname, JSON_FULLNAME_LEN, "%s[%lu]", e->fullname, i); switch(t[start].type) { case JSMN_PRIMITIVE: start += json_walk_primitive(js, t, start, &ne); break; case JSMN_OBJECT: start += json_walk_object(js, t, nest + 1, start, &ne); break; case JSMN_ARRAY: start += json_walk_array(js, t, nest + 1, start, &ne); break; case JSMN_STRING: start += json_walk_string(js, t, start, &ne); break; } } return start - init; } /** * Object * * Measure the Object length * * @param js the original string * @param t the tokens * @param nest the length of structure t * @param start the first position * @param e the output structure. * * @return It returns the Object length */ size_t json_walk_object(char *js, jsmntok_t *t, size_t nest, size_t start, JSON_ENTRY *e) { JSON_ENTRY ne = { .name = "", .fullname = "", .callback_data = NULL, .callback_function = NULL }; char old = js[t[start].end]; js[t[start].end] = '\0'; ne.original_string = &js[t[start].start]; memcpy(&ne, e, sizeof(JSON_ENTRY)); ne.type = JSON_OBJECT; ne.callback_function = e->callback_function; if(e->callback_function) e->callback_function(&ne); js[t[start].end] = old; int key = 1; size_t i, init = start, size = t[start].size; start++; for(i = 0; i < size ; i++) { switch(t[start].type) { case JSMN_PRIMITIVE: start += json_walk_primitive(js, t, start, &ne); key = 1; break; case JSMN_OBJECT: start += json_walk_object(js, t, nest + 1, start, &ne); key = 1; break; case JSMN_ARRAY: start += json_walk_array(js, t, nest + 1, start, &ne); key = 1; break; case JSMN_STRING: default: if(key) { int len = t[start].end - t[start].start; if (unlikely(len>JSON_NAME_LEN)) len=JSON_NAME_LEN; strncpy(ne.name, &js[t[start].start], len); ne.name[len] = '\0'; len=strlen(e->fullname) + strlen(e->fullname[0]?".":"") + strlen(ne.name); char *c = mallocz((len+1)*sizeof(char)); sprintf(c,"%s%s%s", e->fullname, e->fullname[0]?".":"", ne.name); if (unlikely(len>JSON_FULLNAME_LEN)) len=JSON_FULLNAME_LEN; strncpy(ne.fullname, c, len); freez(c); start++; key = 0; } else { start += json_walk_string(js, t, start, &ne); key = 1; } break; } } return start - init; } #endif /** * Tree * * Call the correct walk function according its type. * * @param t the json object to work * @param callback_data additional data to be used together the callback function * @param callback_function function used to create a silencer. * * @return It always return 1 */ #ifdef ENABLE_JSONC size_t json_walk(json_object *t, void *callback_data, int (*callback_function)(struct json_entry *)) { JSON_ENTRY e; e.callback_data = callback_data; enum json_type type; json_object_object_foreach(t, key, val) { type = json_object_get_type(val); if (type == json_type_array) { e.type = JSON_ARRAY; json_jsonc_parse_array(val,NULL,health_silencers_json_read_callback); } else if (type == json_type_object) { e.type = JSON_OBJECT; } else if (type == json_type_string) { json_jsonc_set_string(&e,key,json_object_get_string(val)); callback_function(&e); } else if (type == json_type_boolean) { json_jsonc_set_boolean(&e,json_object_get_boolean(val)); callback_function(&e); } else if (type == json_type_int) { json_jsonc_set_integer(&e,key,json_object_get_int64(val)); callback_function(&e); } } return 1; } #else /** * Tree * * Call the correct walk function according its type. * * @param js the original string * @param t the tokens * @param callback_data additional data to be used together the callback function * @param callback_function function used to create a silencer. * * @return It always return 1 */ size_t json_walk_tree(char *js, jsmntok_t *t, void *callback_data, int (*callback_function)(struct json_entry *)) { JSON_ENTRY e = { .name = "", .fullname = "", .callback_data = callback_data, .callback_function = callback_function }; switch (t[0].type) { case JSMN_OBJECT: e.type = JSON_OBJECT; json_walk_object(js, t, 0, 0, &e); break; case JSMN_ARRAY: e.type = JSON_ARRAY; json_walk_array(js, t, 0, 0, &e); break; case JSMN_PRIMITIVE: case JSMN_STRING: break; } return 1; } #endif /** * JSON Parse * * Parse the json message with the callback function * * @param js the string that the callback function will parse * @param callback_data additional data to be used together the callback function * @param callback_function function used to create a silencer. * * @return JSON_OK case everything happened as expected, JSON_CANNOT_PARSE case there were errors in the * parsing process and JSON_CANNOT_DOWNLOAD case the string given(js) is NULL. */ int json_parse(char *js, void *callback_data, int (*callback_function)(JSON_ENTRY *)) { if(js) { #ifdef ENABLE_JSONC json_object *tokens = json_tokenise(js); #else size_t count; jsmntok_t *tokens = json_tokenise(js, strlen(js), &count); #endif if(tokens) { #ifdef ENABLE_JSONC json_walk(tokens, callback_data, callback_function); json_object_put(tokens); #else json_walk_tree(js, tokens, callback_data, callback_function); freez(tokens); #endif return JSON_OK; } return JSON_CANNOT_PARSE; } return JSON_CANNOT_DOWNLOAD; } /* int json_test(char *str) { return json_parse(str, NULL, json_callback_print); } */