Message ID | 20200822105703.422356-1-colin.king@canonical.com |
---|---|
State | Accepted |
Headers | show |
Series | [1/2,V2] Replace json-c library with minimal replacement | expand |
On 2020-08-22 4:57 a.m., Colin King wrote: > From: Colin Ian King <colin.king@canonical.com> > > FWTS uses a subset of json-c functionality, so replace it with > our own implementation that supports this subset. This also > removes the dependency of two flavours of json libraries that > fwts has been using and also allows us to perform deeper memory > allocation and leaking tracking with static analysis tools. > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > --- > > V2: Add missing include files in kernelscan.c > > --- > configure.ac | 3 - > src/lib/include/fwts.h | 2 + > src/lib/include/fwts_json.h | 61 ++- > src/lib/src/Makefile.am | 1 + > src/lib/src/fwts_json.c | 917 ++++++++++++++++++++++++++++++++++++ > src/utilities/Makefile.am | 5 +- > src/utilities/kernelscan.c | 3 +- > 7 files changed, 971 insertions(+), 21 deletions(-) > create mode 100644 src/lib/src/fwts_json.c > > diff --git a/configure.ac b/configure.ac > index 39a445cd..f40c3678 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -9,8 +9,6 @@ > AC_PROG_LIBTOOL > AC_C_INLINE > AM_PROG_CC_C_O > - AC_SEARCH_LIBS([json_object_from_file], [json json-c], [], [ AC_MSG_ERROR([no available json library]) ]) > - AC_SEARCH_LIBS([json_object_object_get_ex], [json json-c], [ AC_DEFINE([JSON_HAS_GET_EX], [1], [Define if we have json_object_object_get_ex])], []) > AC_CHECK_FUNCS([localtime_r]) > AC_CHECK_FUNCS([dup2]) > AC_CHECK_FUNCS([getcwd]) > @@ -61,7 +59,6 @@ > AC_CHECK_HEADERS([time.h]) > AC_CHECK_HEADERS([sys/ioctl.h]) > AC_CHECK_HEADERS([sys/time.h]) > - AC_CHECK_HEADERS([json/json.h]) > AC_CHECK_HEADERS([glib.h]) > AC_CHECK_HEADERS([gio/gio.h]) > AC_CHECK_HEADERS([asm/opal-prd.h]) > diff --git a/src/lib/include/fwts.h b/src/lib/include/fwts.h > index 1e0d5870..6f13d262 100644 > --- a/src/lib/include/fwts.h > +++ b/src/lib/include/fwts.h > @@ -153,6 +153,8 @@ > > #define FWTS_JSON_DATA_PATH DATAROOTDIR "/fwts" > > +#include <inttypes.h> > + > #include "fwts_version.h" > #include "fwts_backtrace.h" > #include "fwts_types.h" > diff --git a/src/lib/include/fwts_json.h b/src/lib/include/fwts_json.h > index ad51bc45..0c316e02 100644 > --- a/src/lib/include/fwts_json.h > +++ b/src/lib/include/fwts_json.h > @@ -1,5 +1,5 @@ > /* > - * Copyright (C) 2012-2020 Canonical > + * Copyright (C) 2010-2020 Canonical > * > * This program is free software; you can redistribute it and/or > * modify it under the terms of the GNU General Public License > @@ -16,26 +16,15 @@ > * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > * > */ > - > #ifndef __FWTS_JSON_H__ > #define __FWTS_JSON_H__ > > -#include <json.h> > - > -#define __FWTS_JSON_ERR_PTR__ ((json_object*) -1) > /* > - * Older versions of json-c may return an error in an > - * object as a ((json_object*)-1), where as newer > - * versions return NULL, so check for these. Sigh. > + * Minimal subset of json for fwts > */ > -#define FWTS_JSON_ERROR(ptr) \ > - ( (ptr == NULL) || ((const json_object *)ptr == __FWTS_JSON_ERR_PTR__) ) > > -/* > - * json-c 0.13.99 does not define TRUE/FALSE anymore > - * the json-c maintainers replaced them with pure 1/0 > - * https://github.com/json-c/json-c/commit/0992aac61f8b > - */ > +#define FWTS_JSON_ERROR(ptr) (!ptr) > + > #ifndef FALSE > #define FALSE 0 > #endif > @@ -44,4 +33,46 @@ > #define TRUE 1 > #endif > > +/* > + * json types supported > + */ > +typedef enum { > + type_null, > + type_int, > + type_string, > + type_object, > + type_array, > +} json_type; > + > +/* > + * json object information > + */ > +typedef struct json_object { > + char *key; /* Null if undefined */ > + int length; /* Length of a collection of objects */ > + json_type type; /* Object type */ > + union { > + void *ptr; /* string or object array pointer */ > + int intval; /* integer value */ > + } u; > +} json_object; > + > +/* > + * minimal json c library functions as required by fwts > + */ > +json_object *json_object_from_file(const char *filename); > +json_object *json_object_object_get(json_object *obj, const char *key); > +int json_object_array_length(json_object *obj); > +json_object *json_object_array_get_idx(json_object *obj, int index); > +const char *json_object_get_string(json_object *obj); > +json_object *json_object_new_int(int); > +void json_object_object_add(json_object *obj, const char *key, json_object *value); > + > +json_object *json_object_new_object(void); > +json_object *json_object_new_array(void); > +char *json_object_to_json_string(json_object *obj); > +void json_object_put(json_object *obj); > +json_object *json_object_new_string(const char *str); > +int json_object_array_add(json_object *obj, json_object *item); > + > #endif > diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am > index ecc4136b..4593bb82 100644 > --- a/src/lib/src/Makefile.am > +++ b/src/lib/src/Makefile.am > @@ -82,6 +82,7 @@ libfwts_la_SOURCES = \ > fwts_interactive.c \ > fwts_ioport.c \ > fwts_ipmi.c \ > + fwts_json.c \ > fwts_keymap.c \ > fwts_klog.c \ > fwts_olog.c \ > diff --git a/src/lib/src/fwts_json.c b/src/lib/src/fwts_json.c > new file mode 100644 > index 00000000..b92a241a > --- /dev/null > +++ b/src/lib/src/fwts_json.c > @@ -0,0 +1,917 @@ > +/* > + * Copyright (C) 2010-2020 Canonical > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program 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 General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > + * > + * This is a simplified json parser and json object store > + * based on json but does not support keywords and is designed > + * just for the fwts json data files. It is not a full json > + * implementation and should not be used as such. > + * > + */ > +#include <stdlib.h> > +#include <stdio.h> > +#include <stdarg.h> > +#include <stdbool.h> > +#include <string.h> > +#include <unistd.h> > +#include <ctype.h> > + > +#include "fwts.h" > + > +/* > + * json file information > + */ > +typedef struct { > + FILE *fp; /* File pointer */ > + const char *filename; /* Name of file */ > + int linenum; /* Parser line number */ > + int charnum; /* Parser char position */ > + int error_reported; /* Error count */ > +} json_file; > + > +/* > + * json tokens, subset of full json token set as used > + * for fwts requirements > + */ > +typedef enum { > + token_lbrace, > + token_rbrace, > + token_lbracket, > + token_rbracket, > + token_colon, > + token_comma, > + token_int, > + token_string, > + token_true, > + token_false, > + token_null, > + token_error, > + token_eof, > +} json_token_type; > + > +/* > + * json parser token > + */ > +typedef struct { > + json_token_type type; /* token type */ > + long offset; /* offset in file for re-winding */ > + union { > + char *str; /* token string value */ > + int intval; /* token integer value */ > + } u; > +} json_token; > + > +/* > + * json_token_string() > + * convert json token to a human readable string > + */ > +char *json_token_string(json_token *jtoken) > +{ > + static char tmp[64]; > + > + switch (jtoken->type) { > + case token_lbrace: > + return "{"; > + case token_rbrace: > + return "}"; > + case token_lbracket: > + return "["; > + case token_rbracket: > + return "]"; > + case token_colon: > + return ":"; > + case token_comma: > + return ","; > + case token_int: > + (void)snprintf(tmp, sizeof(tmp), "%d", jtoken->u.intval); > + return tmp; > + case token_string: > + return jtoken->u.str; > + case token_error: > + return "<error>"; > + case token_eof: > + return "end of file"; > + default: > + break; > + } > + return "<illegal token>"; > +} > + > +/* > + * json_get_string() > + * parse a literal string > + */ > +json_token_type json_get_string(json_file *jfile, json_token *token) > +{ > + char buffer[4096]; > + size_t i = 0; > + > + for (;;) { > + int ch; > + > + ch = fgetc(jfile->fp); > + jfile->charnum++; > + if (ch == EOF) { > + fprintf(stderr, "json_parser: unexpected EOF in literal string\n"); > + token->u.str = NULL; > + return token_error; > + } > + > + if (ch == '\\') { > + ch = fgetc(jfile->fp); > + switch (ch) { > + case '\\': > + case '"': > + case '/': > + break; > + case 'b': > + ch = '\b'; > + break; > + case 'f': > + ch = '\f'; > + break; > + case 'n': > + ch = '\n'; > + break; > + case 'r': > + ch = '\r'; > + break; > + case 't': > + ch = '\t'; > + break; > + case 'u': > + fprintf(stderr, "json parser: escaped hex values not supported\n"); > + ch = '?'; > + break; > + } > + jfile->charnum++; > + } else if (ch == '"') { > + buffer[i] = '\0'; > + token->u.str = strdup(buffer); > + if (!token->u.str) { > + fprintf(stderr, "json parser: out of memory allocating %zd byte string\n", i); > + break; > + } > + return token_string; > + } > + buffer[i] = ch; > + i++; > + if (i >= sizeof(buffer)) { > + fprintf(stderr, "json parser: string too long, maximum size %zd bytes\n", sizeof(buffer) - 1); > + break; > + } > + } > + > + token->u.str = NULL; > + return token_error; > +} > + > +/* > + * json_get_int() > + * parse a simple integer > + */ > +json_token_type json_get_int(json_file *jfile, json_token *token) > +{ > + char buffer[64]; > + size_t i = 0; > + > + for (;;) { > + int ch; > + > + ch = fgetc(jfile->fp); > + if (!isdigit(ch)) { > + (void)ungetc(ch, jfile->fp); > + buffer[i] = '\0'; > + token->u.intval = atoi(buffer); > + return token_int; > + } > + jfile->charnum++; > + buffer[i] = ch; > + i++; > + if (i >= sizeof(buffer)) { > + fprintf(stderr, "json parser: integer too long, maximum size %zd bytes\n", sizeof(buffer) - 1); > + break; > + } > + } > + > + token->u.str = NULL; > + return token_error; > +} > + > +/* > + * json_unget_token() > + * push file pointer back to point before the token > + * to unpush the token > + */ > +int json_unget_token(json_file *jfile, json_token *token) > +{ > + return fseek(jfile->fp, token->offset, SEEK_SET); > +} > + > +/* > + * json_get_keyword() > + * get a keyword > + */ > +int json_get_keyword(json_file *jfile, json_token *token) > +{ > + char buffer[32]; > + size_t i = 0; > + > + token->u.str = NULL; > + *buffer = '\0'; > + > + for (;;) { > + int ch; > + > + ch = fgetc(jfile->fp); > + jfile->charnum++; > + if (ch == EOF) { > + fprintf(stderr, "json_parser: unexpected EOF in keyword string\n"); > + return token_error; > + } > + if (ch < 'a' || ch > 'z') > + break; > + > + buffer[i] = ch; > + i++; > + if (i >= sizeof(buffer)) { > + fprintf(stderr, "json parser: keyword too long, maximum size %zd bytes\n", sizeof(buffer) - 1); > + return token_error; > + } > + } > + if (!strcmp(buffer, "true")) > + return token_true; > + if (!strcmp(buffer, "false")) > + return token_false; > + if (!strcmp(buffer, "null")) > + return token_null; > + return token_error; > +} > + > +/* > + * json_get_token() > + * read next input character(s) and return a matching token > + */ > +json_token_type json_get_token(json_file *jfile, json_token *token) > +{ > + (void)memset(token, 0, sizeof(*token)); > + > + token->offset = ftell(jfile->fp); > + for (;;) { > + int ch; > + > + ch = fgetc(jfile->fp); > + jfile->charnum++; > + > + switch (ch) { > + case '\n': > + jfile->linenum++; > + continue; > + case ' ': > + case '\r': > + case '\t': > + continue; > + case EOF: > + token->type = token_eof; > + return token->type; > + case '{': > + token->type = token_lbrace; > + return token->type; > + case '}': > + token->type = token_rbrace; > + return token->type; > + case '[': > + token->type = token_lbracket; > + return token->type; > + case ']': > + token->type = token_rbracket; > + return token->type; > + case ':': > + token->type = token_colon; > + return token->type; > + case ',': > + token->type = token_comma; > + return token->type; > + case '"': > + token->type = json_get_string(jfile, token); > + return token->type; > + case '0'...'9': > + token->type = json_get_int(jfile, token); > + return token->type; > + case 'a'...'z': > + fprintf(stderr, "json_parser: keywords not supported\n"); > + token->type = token_error; > + return token->type; > + default: > + token->type = token_error; > + return token->type; > + } > + } > + > + /* Should never reach here */ > + token->type = token_error; > + return token->type; > +} > + > +/* > + * json_parse_error_where() > + * very simple parser error message, report where in the file > + * the parsing error occurred. > + */ > +void json_parse_error_where(json_file *jfile) > +{ > + if (jfile->error_reported == 0) > + fprintf(stderr, "json_parser: aborted at line %d, char %d of file %s\n", > + jfile->linenum, jfile->charnum, jfile->filename); > + jfile->error_reported++; > +} > + > +json_object *json_parse_object(json_file *jfile); > + > +/* > + * json_parse_array() > + * parse a json array > + */ > +json_object *json_parse_array(json_file *jfile) > +{ > + json_object *array_obj; > + > + array_obj = json_object_new_array(); > + if (!array_obj) { > + fprintf(stderr, "json_parser: out of memory allocating a json array object\n"); > + json_parse_error_where(jfile); > + return NULL; > + } > + > + for (;;) { > + json_object *obj; > + json_token token; > + > + obj = json_parse_object(jfile); > + if (!obj) { > + json_parse_error_where(jfile); > + free(array_obj); > + return NULL; > + } > + json_object_array_add(array_obj, obj); > + > + switch (json_get_token(jfile, &token)) { > + case token_rbracket: > + return array_obj; > + case token_comma: > + continue; > + default: > + if (json_unget_token(jfile, &token) != 0) { > + fprintf(stderr, "json_parser: failed to unget a token\n"); > + free(array_obj); > + return NULL; > + } > + break; > + } > + } > + return array_obj; > +} > + > +/* > + * json_parse_object() > + * parse a json object (simplified fwts json format only) > + */ > +json_object *json_parse_object(json_file *jfile) > +{ > + json_token token; > + json_object *obj, *val_obj; > + char *key = NULL; > + > + if (json_get_token(jfile, &token) != token_lbrace) { > + fprintf(stderr, "json_parser: expecting '{', got %s instead\n", json_token_string(&token)); > + return NULL; > + } > + > + obj = json_object_new_object(); > + if (!obj) > + goto err_nomem; > + > + for (;;) { > + switch (json_get_token(jfile, &token)) { > + case token_rbrace: > + return obj; > + case token_string: > + key = token.u.str; > + if (!key) > + goto err_nomem; > + token.u.str = NULL; > + break; > + default: > + fprintf(stderr, "json_parser: expecting } or key literal string, got %s instead\n", json_token_string(&token)); > + goto err_free; > + } > + if (json_get_token(jfile, &token) != token_colon) { > + fprintf(stderr, "json_parser: expecting ':', got %s instead\n", json_token_string(&token)); > + goto err_free; > + } > + switch (json_get_token(jfile, &token)) { > + case token_string: > + val_obj = json_object_new_string(token.u.str); > + if (!val_obj) > + goto err_nomem; > + json_object_object_add(obj, key, val_obj); > + free(key); > + break; > + case token_int: > + val_obj = json_object_new_int(token.u.intval); > + if (!val_obj) > + goto err_nomem; > + json_object_object_add(obj, key, val_obj); > + free(key); > + break; > + case token_lbracket: > + val_obj = json_parse_array(jfile); > + if (!val_obj) > + goto err_nomem; > + json_object_object_add(obj, key, val_obj); > + break; > + case token_lbrace: > + fprintf(stderr, "json_parser: nested objects not supported\n"); > + goto err_free; > + case token_true: > + case token_false: > + case token_null: > + fprintf(stderr, "json_parser: tokens %s not supported\n", json_token_string(&token)); > + goto err_free; > + default: > + fprintf(stderr, "json_parser: unexpected token %s\n", json_token_string(&token)); > + } > + switch (json_get_token(jfile, &token)) { > + case token_comma: > + continue; > + case token_rbrace: > + return obj; > + default: > + fprintf(stderr, "json_parser: expected , or }, got %s instead\n", json_token_string(&token)); > + goto err_free; > + } > + } > + > +err_nomem: > + fprintf(stderr, "json_parser: out of memory allocating a json object\n"); > + json_parse_error_where(jfile); > +err_free: > + free(obj); > + return NULL; > +} > + > +/* > + * json_object_from_file() > + * parse a simplified fwts json file and convert it into > + * a json object, return NULL if parsing failed or ran > + * out of memory > + */ > +json_object *json_object_from_file(const char *filename) > +{ > + json_object *obj; > + json_file jfile; > + > + jfile.filename = filename; > + jfile.linenum = 1; > + jfile.charnum = 0; > + jfile.error_reported = 0; > + > + jfile.fp = fopen(filename, "r"); > + if (!jfile.fp) > + return NULL; > + > + obj = json_parse_object(&jfile); > + > + fclose(jfile.fp); > + return obj; > +} > + > +/* > + * json_object_new_object() > + * return a new json object, NULL if failed > + */ > +json_object *json_object_new_object(void) > +{ > + json_object *obj; > + > + obj = calloc(1, sizeof(*obj)); > + if (!obj) > + return NULL; > + obj->type = type_object; > + obj->u.ptr = NULL; > + > + return obj; > +} > + > +/* > + * json_object_new_object() > + * return a new json object, NULL if failed > + */ > +int json_object_array_length(json_object *obj) > +{ > + if (!obj) > + return 0; > + if (obj->type != type_array) > + return 0; > + return obj->length; > +} > + > +/* > + * json_object_new_int() > + * return a new json integer object, NULL if failed > + */ > +json_object *json_object_new_int(int val) > +{ > + json_object *obj; > + > + obj = calloc(1, sizeof(*obj)); > + if (!obj) > + return NULL; > + obj->type = type_int; > + obj->u.intval = val; > + > + return obj; > +} > + > +/* > + * json_object_new_string() > + * return a new json string object, NULL if failed > + */ > +json_object *json_object_new_string(const char *str) > +{ > + json_object *obj; > + > + obj = calloc(1, sizeof(*obj)); > + if (!obj) > + return NULL; > + obj->type = type_string; > + obj->u.ptr = strdup(str); > + if (!obj->u.ptr) { > + free(obj); > + return NULL; > + } > + return obj; > +} > + > +/* > + * json_object_new_array() > + * return a new json array object, NULL if failed > + */ > +json_object *json_object_new_array(void) > +{ > + json_object *obj; > + > + obj = calloc(1, sizeof(*obj)); > + if (!obj) > + return NULL; > + obj->type = type_array; > + obj->length = 0; > + obj->u.ptr = NULL; > + > + return obj; > +} > + > +/* > + * json_object_array_add_item() > + * add an object to another object, return 0 if succeeded, > + * non-zero if failed > + */ > +static int json_object_array_add_item(json_object *obj, json_object *item) > +{ > + json_object **obj_ptr; > + > + if (obj->length < 0) > + return -1; > + obj_ptr = realloc(obj->u.ptr, sizeof(json_object *) * (obj->length + 1)); > + if (!obj_ptr) > + return -1; > + obj->u.ptr = (void *)obj_ptr; > + obj_ptr[obj->length] = item; > + obj->length++; > + return 0; > +} > + > +/* > + * json_object_array_add() > + * add a object to a json array object, return NULL if failed > + */ > +int json_object_array_add(json_object *obj, json_object *item) > +{ > + if (!obj || !item) > + return -1; > + if (obj->type != type_array) > + return -1; > + return json_object_array_add_item(obj, item); > +} > + > + > +/* > + * json_object_object_add() > + * add a key/valyue object to a json object, return NULL if failed > + */ > +void json_object_object_add(json_object *obj, const char *key, json_object *value) > +{ > + if (!obj || !key || !value) > + return; > + if (obj->type != type_object) > + return; > + value->key = strdup(key); > + if (!value->key) > + return; > + json_object_array_add_item(obj, value); > +} > + > +/* > + * json_object_array_get_idx() > + * get an object at position index from a json array object, > + * return NULL if failed > + */ > +json_object *json_object_array_get_idx(json_object *obj, int index) > +{ > + json_object **obj_array; > + > + if (!obj) > + return NULL; > + if (obj->type != type_array) > + return NULL; > + if (index >= obj->length) > + return NULL; > + obj_array = (json_object **)obj->u.ptr; > + if (obj_array == NULL) > + return NULL; > + return obj_array[index]; > +} > + > +/* > + * json_object_get_string() > + * return a C string from a json string object > + */ > +const char *json_object_get_string(json_object *obj) > +{ > + if (!obj) > + return NULL; > + if (obj->type != type_string) > + return NULL; > + return (const char *)obj->u.ptr; > +} > + > +/* > + * json_object_put() > + * free a json object and all sub-objects > + */ > +void json_object_put(json_object *obj) > +{ > + int i; > + json_object **obj_ptr; > + > + if (!obj) > + return; > + > + if (obj->key) > + free(obj->key); > + > + switch (obj->type) { > + case type_array: > + case type_object: > + obj_ptr = (json_object **)obj->u.ptr; > + > + for (i = 0; i < obj->length; i++) { > + json_object_put(obj_ptr[i]); > + } > + free(obj->u.ptr); > + break; > + case type_string: > + free(obj->u.ptr); > + break; > + case type_null: > + case type_int: > + default: > + break; > + } > + free(obj); > +} > + > +/* > + * str_append() > + * append a string to a string, return NULL if failed > + */ > +static char *str_append(char *str, char *append) > +{ > + char *new_str; > + size_t len; > + > + if (!append) > + return NULL; > + > + if (str) { > + len = strlen(append) + strlen(str) + 1; > + new_str = realloc(str, len); > + if (!new_str) { > + free(str); > + return NULL; > + } > + strcat(new_str, append); > + } else { > + len = strlen(append) + 1; > + new_str = malloc(len); > + if (!new_str) > + return NULL; > + strcpy(new_str, append); > + } > + return new_str; > +} > + > +/* > + * str_indent() > + * add 2 spaces per indent level to a string, returns > + * NULL if failed > + */ > +static char *str_indent(char *str, int indent) > +{ > + char buf[81]; > + int i; > + > + indent = indent + indent; > + if (indent > 80) > + indent = 80; > + > + for (i = 0; i < indent; i++) > + buf[i] = ' '; > + buf[i] = '\0'; > + > + return str_append(str, buf); > +} > + > +/* > + * json_object_to_json_string_indent() > + * turn a simplified fwts json object into a C string, returns > + * the stringified object or NULL if failed. Will traverse object > + * tree and add indentation based on recursion depth. > + */ > +static char *json_object_to_json_string_indent(json_object *obj, int indent) > +{ > + int i; > + json_object **obj_ptr; > + char *str = NULL; > + char buf[64]; > + > + if (!obj) > + return NULL; > + > + if (obj->type == type_object) { > + str = str_indent(str, indent); > + if (!str) > + return NULL; > + str = str_append(str, "{\n"); > + if (!str) > + return NULL; > + } > + > + if (obj->key) { > + str = str_indent(str, indent); > + if (!str) > + return NULL; > + str = str_append(str, "\""); > + if (!str) > + return NULL; > + str = str_append(str, obj->key); > + if (!str) > + return NULL; > + str = str_append(str, "\":"); > + if (!str) > + return NULL; > + } > + > + switch (obj->type) { > + case type_array: > + str = str_append(str, "[\n"); > + if (!str) > + return NULL; > + > + obj_ptr = (json_object **)obj->u.ptr; > + if (obj_ptr) { > + for (i = 0; i < obj->length; i++) { > + char *obj_str = json_object_to_json_string_indent(obj_ptr[i], indent + 1); > + > + if (!obj_str) { > + free(str); > + return NULL; > + } > + str = str_append(str, obj_str); > + if (!str) > + return NULL; > + } > + } > + str = str_append(str, "\n"); > + if (!str) > + return NULL; > + str = str_indent(str, indent); > + if (!str) > + return NULL; > + str = str_append(str, "]\n"); > + if (!str) > + return NULL; > + break; > + > + case type_object: > + obj_ptr = (json_object **)obj->u.ptr; > + if (obj_ptr) { > + for (i = 0; i < obj->length; i++) { > + char *obj_str = json_object_to_json_string_indent(obj_ptr[i], indent + 1); > + > + if (!obj_str) > + return NULL; > + str = str_append(str, obj_str); > + if (!str) > + return NULL; > + } > + } > + if (!str) > + return NULL; > + break; > + > + case type_string: > + str = str_append(str, "\""); > + if (!str) > + return NULL; > + str = str_append(str, (char *)obj->u.ptr); > + if (!str) > + return NULL; > + str = str_append(str, "\",\n"); > + if (!str) > + return NULL; > + break; > + > + case type_null: > + str = str_append(str, "(null)\n"); > + if (!str) > + return NULL; > + break; > + > + case type_int: > + snprintf(buf, sizeof(buf), "%d,\n", obj->u.intval); > + str = str_append(str, buf); > + if (!str) > + return NULL; > + break; > + default: > + return NULL; > + } > + > + if (obj->type == type_object) { > + str = str_indent(str, indent); > + if (!str) > + return NULL; > + str = str_append(str, "},\n"); > + if (!str) > + return NULL; > + } > + return str; > +} > + > +/* > + * json_object_to_json_string > + * convert simplified fwts object to a C string, returns > + * NULL if failed > + */ > +char *json_object_to_json_string(json_object *obj) > +{ > + return json_object_to_json_string_indent(obj, 0); > +} > + > +/* > + * json_object_object_get() > + * return value from key/value pair from an object, returns > + * NULL if it can't be found > + */ > +json_object *json_object_object_get(json_object *obj, const char *key) > +{ > + int i; > + json_object **obj_ptr; > + > + if (!obj || !key) > + return NULL; > + if (obj->type != type_object) > + return NULL; > + > + obj_ptr = (json_object **)obj->u.ptr; > + for (i = 0; i < obj->length; i++) { > + if (obj_ptr[i]->key && !strcmp(obj_ptr[i]->key, key)) > + return obj_ptr[i]; > + } > + return NULL; > +} > + > diff --git a/src/utilities/Makefile.am b/src/utilities/Makefile.am > index 98d6ebaa..2ae86038 100644 > --- a/src/utilities/Makefile.am > +++ b/src/utilities/Makefile.am > @@ -18,10 +18,11 @@ > > AM_CPPFLAGS = -Wall -Werror -Wextra -DDATAROOTDIR=\"$(datarootdir)\" \ > `pkg-config --silence-errors --cflags json` \ > - `pkg-config --silence-errors --cflags json-c` > + `pkg-config --silence-errors --cflags json-c` \ > + -I../lib/include > > bin_PROGRAMS = kernelscan > -kernelscan_SOURCES = kernelscan.c > +kernelscan_SOURCES = kernelscan.c ../lib/src/fwts_json.c > > > -include $(top_srcdir)/git.mk > diff --git a/src/utilities/kernelscan.c b/src/utilities/kernelscan.c > index 2e68ef33..49ffe5c1 100644 > --- a/src/utilities/kernelscan.c > +++ b/src/utilities/kernelscan.c > @@ -21,11 +21,12 @@ > #include <stdbool.h> > #include <stdlib.h> > #include <string.h> > +#include <stddef.h> > #include <ctype.h> > #include <unistd.h> > #include <sys/types.h> > #include <regex.h> > -#include <json.h> > +#include "fwts_json.h" > > #include "config.h" > > Acked-by: Alex Hung <alex.hung@canonical.com>
On 8/22/20 6:57 PM, Colin King wrote: > From: Colin Ian King <colin.king@canonical.com> > > FWTS uses a subset of json-c functionality, so replace it with > our own implementation that supports this subset. This also > removes the dependency of two flavours of json libraries that > fwts has been using and also allows us to perform deeper memory > allocation and leaking tracking with static analysis tools. > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > --- > > V2: Add missing include files in kernelscan.c > > --- > configure.ac | 3 - > src/lib/include/fwts.h | 2 + > src/lib/include/fwts_json.h | 61 ++- > src/lib/src/Makefile.am | 1 + > src/lib/src/fwts_json.c | 917 ++++++++++++++++++++++++++++++++++++ > src/utilities/Makefile.am | 5 +- > src/utilities/kernelscan.c | 3 +- > 7 files changed, 971 insertions(+), 21 deletions(-) > create mode 100644 src/lib/src/fwts_json.c > > diff --git a/configure.ac b/configure.ac > index 39a445cd..f40c3678 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -9,8 +9,6 @@ > AC_PROG_LIBTOOL > AC_C_INLINE > AM_PROG_CC_C_O > - AC_SEARCH_LIBS([json_object_from_file], [json json-c], [], [ AC_MSG_ERROR([no available json library]) ]) > - AC_SEARCH_LIBS([json_object_object_get_ex], [json json-c], [ AC_DEFINE([JSON_HAS_GET_EX], [1], [Define if we have json_object_object_get_ex])], []) > AC_CHECK_FUNCS([localtime_r]) > AC_CHECK_FUNCS([dup2]) > AC_CHECK_FUNCS([getcwd]) > @@ -61,7 +59,6 @@ > AC_CHECK_HEADERS([time.h]) > AC_CHECK_HEADERS([sys/ioctl.h]) > AC_CHECK_HEADERS([sys/time.h]) > - AC_CHECK_HEADERS([json/json.h]) > AC_CHECK_HEADERS([glib.h]) > AC_CHECK_HEADERS([gio/gio.h]) > AC_CHECK_HEADERS([asm/opal-prd.h]) > diff --git a/src/lib/include/fwts.h b/src/lib/include/fwts.h > index 1e0d5870..6f13d262 100644 > --- a/src/lib/include/fwts.h > +++ b/src/lib/include/fwts.h > @@ -153,6 +153,8 @@ > > #define FWTS_JSON_DATA_PATH DATAROOTDIR "/fwts" > > +#include <inttypes.h> > + > #include "fwts_version.h" > #include "fwts_backtrace.h" > #include "fwts_types.h" > diff --git a/src/lib/include/fwts_json.h b/src/lib/include/fwts_json.h > index ad51bc45..0c316e02 100644 > --- a/src/lib/include/fwts_json.h > +++ b/src/lib/include/fwts_json.h > @@ -1,5 +1,5 @@ > /* > - * Copyright (C) 2012-2020 Canonical > + * Copyright (C) 2010-2020 Canonical > * > * This program is free software; you can redistribute it and/or > * modify it under the terms of the GNU General Public License > @@ -16,26 +16,15 @@ > * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > * > */ > - > #ifndef __FWTS_JSON_H__ > #define __FWTS_JSON_H__ > > -#include <json.h> > - > -#define __FWTS_JSON_ERR_PTR__ ((json_object*) -1) > /* > - * Older versions of json-c may return an error in an > - * object as a ((json_object*)-1), where as newer > - * versions return NULL, so check for these. Sigh. > + * Minimal subset of json for fwts > */ > -#define FWTS_JSON_ERROR(ptr) \ > - ( (ptr == NULL) || ((const json_object *)ptr == __FWTS_JSON_ERR_PTR__) ) > > -/* > - * json-c 0.13.99 does not define TRUE/FALSE anymore > - * the json-c maintainers replaced them with pure 1/0 > - * https://github.com/json-c/json-c/commit/0992aac61f8b > - */ > +#define FWTS_JSON_ERROR(ptr) (!ptr) > + > #ifndef FALSE > #define FALSE 0 > #endif > @@ -44,4 +33,46 @@ > #define TRUE 1 > #endif > > +/* > + * json types supported > + */ > +typedef enum { > + type_null, > + type_int, > + type_string, > + type_object, > + type_array, > +} json_type; > + > +/* > + * json object information > + */ > +typedef struct json_object { > + char *key; /* Null if undefined */ > + int length; /* Length of a collection of objects */ > + json_type type; /* Object type */ > + union { > + void *ptr; /* string or object array pointer */ > + int intval; /* integer value */ > + } u; > +} json_object; > + > +/* > + * minimal json c library functions as required by fwts > + */ > +json_object *json_object_from_file(const char *filename); > +json_object *json_object_object_get(json_object *obj, const char *key); > +int json_object_array_length(json_object *obj); > +json_object *json_object_array_get_idx(json_object *obj, int index); > +const char *json_object_get_string(json_object *obj); > +json_object *json_object_new_int(int); > +void json_object_object_add(json_object *obj, const char *key, json_object *value); > + > +json_object *json_object_new_object(void); > +json_object *json_object_new_array(void); > +char *json_object_to_json_string(json_object *obj); > +void json_object_put(json_object *obj); > +json_object *json_object_new_string(const char *str); > +int json_object_array_add(json_object *obj, json_object *item); > + > #endif > diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am > index ecc4136b..4593bb82 100644 > --- a/src/lib/src/Makefile.am > +++ b/src/lib/src/Makefile.am > @@ -82,6 +82,7 @@ libfwts_la_SOURCES = \ > fwts_interactive.c \ > fwts_ioport.c \ > fwts_ipmi.c \ > + fwts_json.c \ > fwts_keymap.c \ > fwts_klog.c \ > fwts_olog.c \ > diff --git a/src/lib/src/fwts_json.c b/src/lib/src/fwts_json.c > new file mode 100644 > index 00000000..b92a241a > --- /dev/null > +++ b/src/lib/src/fwts_json.c > @@ -0,0 +1,917 @@ > +/* > + * Copyright (C) 2010-2020 Canonical > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program 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 General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > + * > + * This is a simplified json parser and json object store > + * based on json but does not support keywords and is designed > + * just for the fwts json data files. It is not a full json > + * implementation and should not be used as such. > + * > + */ > +#include <stdlib.h> > +#include <stdio.h> > +#include <stdarg.h> > +#include <stdbool.h> > +#include <string.h> > +#include <unistd.h> > +#include <ctype.h> > + > +#include "fwts.h" > + > +/* > + * json file information > + */ > +typedef struct { > + FILE *fp; /* File pointer */ > + const char *filename; /* Name of file */ > + int linenum; /* Parser line number */ > + int charnum; /* Parser char position */ > + int error_reported; /* Error count */ > +} json_file; > + > +/* > + * json tokens, subset of full json token set as used > + * for fwts requirements > + */ > +typedef enum { > + token_lbrace, > + token_rbrace, > + token_lbracket, > + token_rbracket, > + token_colon, > + token_comma, > + token_int, > + token_string, > + token_true, > + token_false, > + token_null, > + token_error, > + token_eof, > +} json_token_type; > + > +/* > + * json parser token > + */ > +typedef struct { > + json_token_type type; /* token type */ > + long offset; /* offset in file for re-winding */ > + union { > + char *str; /* token string value */ > + int intval; /* token integer value */ > + } u; > +} json_token; > + > +/* > + * json_token_string() > + * convert json token to a human readable string > + */ > +char *json_token_string(json_token *jtoken) > +{ > + static char tmp[64]; > + > + switch (jtoken->type) { > + case token_lbrace: > + return "{"; > + case token_rbrace: > + return "}"; > + case token_lbracket: > + return "["; > + case token_rbracket: > + return "]"; > + case token_colon: > + return ":"; > + case token_comma: > + return ","; > + case token_int: > + (void)snprintf(tmp, sizeof(tmp), "%d", jtoken->u.intval); > + return tmp; > + case token_string: > + return jtoken->u.str; > + case token_error: > + return "<error>"; > + case token_eof: > + return "end of file"; > + default: > + break; > + } > + return "<illegal token>"; > +} > + > +/* > + * json_get_string() > + * parse a literal string > + */ > +json_token_type json_get_string(json_file *jfile, json_token *token) > +{ > + char buffer[4096]; > + size_t i = 0; > + > + for (;;) { > + int ch; > + > + ch = fgetc(jfile->fp); > + jfile->charnum++; > + if (ch == EOF) { > + fprintf(stderr, "json_parser: unexpected EOF in literal string\n"); > + token->u.str = NULL; > + return token_error; > + } > + > + if (ch == '\\') { > + ch = fgetc(jfile->fp); > + switch (ch) { > + case '\\': > + case '"': > + case '/': > + break; > + case 'b': > + ch = '\b'; > + break; > + case 'f': > + ch = '\f'; > + break; > + case 'n': > + ch = '\n'; > + break; > + case 'r': > + ch = '\r'; > + break; > + case 't': > + ch = '\t'; > + break; > + case 'u': > + fprintf(stderr, "json parser: escaped hex values not supported\n"); > + ch = '?'; > + break; > + } > + jfile->charnum++; > + } else if (ch == '"') { > + buffer[i] = '\0'; > + token->u.str = strdup(buffer); > + if (!token->u.str) { > + fprintf(stderr, "json parser: out of memory allocating %zd byte string\n", i); > + break; > + } > + return token_string; > + } > + buffer[i] = ch; > + i++; > + if (i >= sizeof(buffer)) { > + fprintf(stderr, "json parser: string too long, maximum size %zd bytes\n", sizeof(buffer) - 1); > + break; > + } > + } > + > + token->u.str = NULL; > + return token_error; > +} > + > +/* > + * json_get_int() > + * parse a simple integer > + */ > +json_token_type json_get_int(json_file *jfile, json_token *token) > +{ > + char buffer[64]; > + size_t i = 0; > + > + for (;;) { > + int ch; > + > + ch = fgetc(jfile->fp); > + if (!isdigit(ch)) { > + (void)ungetc(ch, jfile->fp); > + buffer[i] = '\0'; > + token->u.intval = atoi(buffer); > + return token_int; > + } > + jfile->charnum++; > + buffer[i] = ch; > + i++; > + if (i >= sizeof(buffer)) { > + fprintf(stderr, "json parser: integer too long, maximum size %zd bytes\n", sizeof(buffer) - 1); > + break; > + } > + } > + > + token->u.str = NULL; > + return token_error; > +} > + > +/* > + * json_unget_token() > + * push file pointer back to point before the token > + * to unpush the token > + */ > +int json_unget_token(json_file *jfile, json_token *token) > +{ > + return fseek(jfile->fp, token->offset, SEEK_SET); > +} > + > +/* > + * json_get_keyword() > + * get a keyword > + */ > +int json_get_keyword(json_file *jfile, json_token *token) > +{ > + char buffer[32]; > + size_t i = 0; > + > + token->u.str = NULL; > + *buffer = '\0'; > + > + for (;;) { > + int ch; > + > + ch = fgetc(jfile->fp); > + jfile->charnum++; > + if (ch == EOF) { > + fprintf(stderr, "json_parser: unexpected EOF in keyword string\n"); > + return token_error; > + } > + if (ch < 'a' || ch > 'z') > + break; > + > + buffer[i] = ch; > + i++; > + if (i >= sizeof(buffer)) { > + fprintf(stderr, "json parser: keyword too long, maximum size %zd bytes\n", sizeof(buffer) - 1); > + return token_error; > + } > + } > + if (!strcmp(buffer, "true")) > + return token_true; > + if (!strcmp(buffer, "false")) > + return token_false; > + if (!strcmp(buffer, "null")) > + return token_null; > + return token_error; > +} > + > +/* > + * json_get_token() > + * read next input character(s) and return a matching token > + */ > +json_token_type json_get_token(json_file *jfile, json_token *token) > +{ > + (void)memset(token, 0, sizeof(*token)); > + > + token->offset = ftell(jfile->fp); > + for (;;) { > + int ch; > + > + ch = fgetc(jfile->fp); > + jfile->charnum++; > + > + switch (ch) { > + case '\n': > + jfile->linenum++; > + continue; > + case ' ': > + case '\r': > + case '\t': > + continue; > + case EOF: > + token->type = token_eof; > + return token->type; > + case '{': > + token->type = token_lbrace; > + return token->type; > + case '}': > + token->type = token_rbrace; > + return token->type; > + case '[': > + token->type = token_lbracket; > + return token->type; > + case ']': > + token->type = token_rbracket; > + return token->type; > + case ':': > + token->type = token_colon; > + return token->type; > + case ',': > + token->type = token_comma; > + return token->type; > + case '"': > + token->type = json_get_string(jfile, token); > + return token->type; > + case '0'...'9': > + token->type = json_get_int(jfile, token); > + return token->type; > + case 'a'...'z': > + fprintf(stderr, "json_parser: keywords not supported\n"); > + token->type = token_error; > + return token->type; > + default: > + token->type = token_error; > + return token->type; > + } > + } > + > + /* Should never reach here */ > + token->type = token_error; > + return token->type; > +} > + > +/* > + * json_parse_error_where() > + * very simple parser error message, report where in the file > + * the parsing error occurred. > + */ > +void json_parse_error_where(json_file *jfile) > +{ > + if (jfile->error_reported == 0) > + fprintf(stderr, "json_parser: aborted at line %d, char %d of file %s\n", > + jfile->linenum, jfile->charnum, jfile->filename); > + jfile->error_reported++; > +} > + > +json_object *json_parse_object(json_file *jfile); > + > +/* > + * json_parse_array() > + * parse a json array > + */ > +json_object *json_parse_array(json_file *jfile) > +{ > + json_object *array_obj; > + > + array_obj = json_object_new_array(); > + if (!array_obj) { > + fprintf(stderr, "json_parser: out of memory allocating a json array object\n"); > + json_parse_error_where(jfile); > + return NULL; > + } > + > + for (;;) { > + json_object *obj; > + json_token token; > + > + obj = json_parse_object(jfile); > + if (!obj) { > + json_parse_error_where(jfile); > + free(array_obj); > + return NULL; > + } > + json_object_array_add(array_obj, obj); > + > + switch (json_get_token(jfile, &token)) { > + case token_rbracket: > + return array_obj; > + case token_comma: > + continue; > + default: > + if (json_unget_token(jfile, &token) != 0) { > + fprintf(stderr, "json_parser: failed to unget a token\n"); > + free(array_obj); > + return NULL; > + } > + break; > + } > + } > + return array_obj; > +} > + > +/* > + * json_parse_object() > + * parse a json object (simplified fwts json format only) > + */ > +json_object *json_parse_object(json_file *jfile) > +{ > + json_token token; > + json_object *obj, *val_obj; > + char *key = NULL; > + > + if (json_get_token(jfile, &token) != token_lbrace) { > + fprintf(stderr, "json_parser: expecting '{', got %s instead\n", json_token_string(&token)); > + return NULL; > + } > + > + obj = json_object_new_object(); > + if (!obj) > + goto err_nomem; > + > + for (;;) { > + switch (json_get_token(jfile, &token)) { > + case token_rbrace: > + return obj; > + case token_string: > + key = token.u.str; > + if (!key) > + goto err_nomem; > + token.u.str = NULL; > + break; > + default: > + fprintf(stderr, "json_parser: expecting } or key literal string, got %s instead\n", json_token_string(&token)); > + goto err_free; > + } > + if (json_get_token(jfile, &token) != token_colon) { > + fprintf(stderr, "json_parser: expecting ':', got %s instead\n", json_token_string(&token)); > + goto err_free; > + } > + switch (json_get_token(jfile, &token)) { > + case token_string: > + val_obj = json_object_new_string(token.u.str); > + if (!val_obj) > + goto err_nomem; > + json_object_object_add(obj, key, val_obj); > + free(key); > + break; > + case token_int: > + val_obj = json_object_new_int(token.u.intval); > + if (!val_obj) > + goto err_nomem; > + json_object_object_add(obj, key, val_obj); > + free(key); > + break; > + case token_lbracket: > + val_obj = json_parse_array(jfile); > + if (!val_obj) > + goto err_nomem; > + json_object_object_add(obj, key, val_obj); > + break; > + case token_lbrace: > + fprintf(stderr, "json_parser: nested objects not supported\n"); > + goto err_free; > + case token_true: > + case token_false: > + case token_null: > + fprintf(stderr, "json_parser: tokens %s not supported\n", json_token_string(&token)); > + goto err_free; > + default: > + fprintf(stderr, "json_parser: unexpected token %s\n", json_token_string(&token)); > + } > + switch (json_get_token(jfile, &token)) { > + case token_comma: > + continue; > + case token_rbrace: > + return obj; > + default: > + fprintf(stderr, "json_parser: expected , or }, got %s instead\n", json_token_string(&token)); > + goto err_free; > + } > + } > + > +err_nomem: > + fprintf(stderr, "json_parser: out of memory allocating a json object\n"); > + json_parse_error_where(jfile); > +err_free: > + free(obj); > + return NULL; > +} > + > +/* > + * json_object_from_file() > + * parse a simplified fwts json file and convert it into > + * a json object, return NULL if parsing failed or ran > + * out of memory > + */ > +json_object *json_object_from_file(const char *filename) > +{ > + json_object *obj; > + json_file jfile; > + > + jfile.filename = filename; > + jfile.linenum = 1; > + jfile.charnum = 0; > + jfile.error_reported = 0; > + > + jfile.fp = fopen(filename, "r"); > + if (!jfile.fp) > + return NULL; > + > + obj = json_parse_object(&jfile); > + > + fclose(jfile.fp); > + return obj; > +} > + > +/* > + * json_object_new_object() > + * return a new json object, NULL if failed > + */ > +json_object *json_object_new_object(void) > +{ > + json_object *obj; > + > + obj = calloc(1, sizeof(*obj)); > + if (!obj) > + return NULL; > + obj->type = type_object; > + obj->u.ptr = NULL; > + > + return obj; > +} > + > +/* > + * json_object_new_object() > + * return a new json object, NULL if failed > + */ > +int json_object_array_length(json_object *obj) > +{ > + if (!obj) > + return 0; > + if (obj->type != type_array) > + return 0; > + return obj->length; > +} > + > +/* > + * json_object_new_int() > + * return a new json integer object, NULL if failed > + */ > +json_object *json_object_new_int(int val) > +{ > + json_object *obj; > + > + obj = calloc(1, sizeof(*obj)); > + if (!obj) > + return NULL; > + obj->type = type_int; > + obj->u.intval = val; > + > + return obj; > +} > + > +/* > + * json_object_new_string() > + * return a new json string object, NULL if failed > + */ > +json_object *json_object_new_string(const char *str) > +{ > + json_object *obj; > + > + obj = calloc(1, sizeof(*obj)); > + if (!obj) > + return NULL; > + obj->type = type_string; > + obj->u.ptr = strdup(str); > + if (!obj->u.ptr) { > + free(obj); > + return NULL; > + } > + return obj; > +} > + > +/* > + * json_object_new_array() > + * return a new json array object, NULL if failed > + */ > +json_object *json_object_new_array(void) > +{ > + json_object *obj; > + > + obj = calloc(1, sizeof(*obj)); > + if (!obj) > + return NULL; > + obj->type = type_array; > + obj->length = 0; > + obj->u.ptr = NULL; > + > + return obj; > +} > + > +/* > + * json_object_array_add_item() > + * add an object to another object, return 0 if succeeded, > + * non-zero if failed > + */ > +static int json_object_array_add_item(json_object *obj, json_object *item) > +{ > + json_object **obj_ptr; > + > + if (obj->length < 0) > + return -1; > + obj_ptr = realloc(obj->u.ptr, sizeof(json_object *) * (obj->length + 1)); > + if (!obj_ptr) > + return -1; > + obj->u.ptr = (void *)obj_ptr; > + obj_ptr[obj->length] = item; > + obj->length++; > + return 0; > +} > + > +/* > + * json_object_array_add() > + * add a object to a json array object, return NULL if failed > + */ > +int json_object_array_add(json_object *obj, json_object *item) > +{ > + if (!obj || !item) > + return -1; > + if (obj->type != type_array) > + return -1; > + return json_object_array_add_item(obj, item); > +} > + > + > +/* > + * json_object_object_add() > + * add a key/valyue object to a json object, return NULL if failed > + */ > +void json_object_object_add(json_object *obj, const char *key, json_object *value) > +{ > + if (!obj || !key || !value) > + return; > + if (obj->type != type_object) > + return; > + value->key = strdup(key); > + if (!value->key) > + return; > + json_object_array_add_item(obj, value); > +} > + > +/* > + * json_object_array_get_idx() > + * get an object at position index from a json array object, > + * return NULL if failed > + */ > +json_object *json_object_array_get_idx(json_object *obj, int index) > +{ > + json_object **obj_array; > + > + if (!obj) > + return NULL; > + if (obj->type != type_array) > + return NULL; > + if (index >= obj->length) > + return NULL; > + obj_array = (json_object **)obj->u.ptr; > + if (obj_array == NULL) > + return NULL; > + return obj_array[index]; > +} > + > +/* > + * json_object_get_string() > + * return a C string from a json string object > + */ > +const char *json_object_get_string(json_object *obj) > +{ > + if (!obj) > + return NULL; > + if (obj->type != type_string) > + return NULL; > + return (const char *)obj->u.ptr; > +} > + > +/* > + * json_object_put() > + * free a json object and all sub-objects > + */ > +void json_object_put(json_object *obj) > +{ > + int i; > + json_object **obj_ptr; > + > + if (!obj) > + return; > + > + if (obj->key) > + free(obj->key); > + > + switch (obj->type) { > + case type_array: > + case type_object: > + obj_ptr = (json_object **)obj->u.ptr; > + > + for (i = 0; i < obj->length; i++) { > + json_object_put(obj_ptr[i]); > + } > + free(obj->u.ptr); > + break; > + case type_string: > + free(obj->u.ptr); > + break; > + case type_null: > + case type_int: > + default: > + break; > + } > + free(obj); > +} > + > +/* > + * str_append() > + * append a string to a string, return NULL if failed > + */ > +static char *str_append(char *str, char *append) > +{ > + char *new_str; > + size_t len; > + > + if (!append) > + return NULL; > + > + if (str) { > + len = strlen(append) + strlen(str) + 1; > + new_str = realloc(str, len); > + if (!new_str) { > + free(str); > + return NULL; > + } > + strcat(new_str, append); > + } else { > + len = strlen(append) + 1; > + new_str = malloc(len); > + if (!new_str) > + return NULL; > + strcpy(new_str, append); > + } > + return new_str; > +} > + > +/* > + * str_indent() > + * add 2 spaces per indent level to a string, returns > + * NULL if failed > + */ > +static char *str_indent(char *str, int indent) > +{ > + char buf[81]; > + int i; > + > + indent = indent + indent; > + if (indent > 80) > + indent = 80; > + > + for (i = 0; i < indent; i++) > + buf[i] = ' '; > + buf[i] = '\0'; > + > + return str_append(str, buf); > +} > + > +/* > + * json_object_to_json_string_indent() > + * turn a simplified fwts json object into a C string, returns > + * the stringified object or NULL if failed. Will traverse object > + * tree and add indentation based on recursion depth. > + */ > +static char *json_object_to_json_string_indent(json_object *obj, int indent) > +{ > + int i; > + json_object **obj_ptr; > + char *str = NULL; > + char buf[64]; > + > + if (!obj) > + return NULL; > + > + if (obj->type == type_object) { > + str = str_indent(str, indent); > + if (!str) > + return NULL; > + str = str_append(str, "{\n"); > + if (!str) > + return NULL; > + } > + > + if (obj->key) { > + str = str_indent(str, indent); > + if (!str) > + return NULL; > + str = str_append(str, "\""); > + if (!str) > + return NULL; > + str = str_append(str, obj->key); > + if (!str) > + return NULL; > + str = str_append(str, "\":"); > + if (!str) > + return NULL; > + } > + > + switch (obj->type) { > + case type_array: > + str = str_append(str, "[\n"); > + if (!str) > + return NULL; > + > + obj_ptr = (json_object **)obj->u.ptr; > + if (obj_ptr) { > + for (i = 0; i < obj->length; i++) { > + char *obj_str = json_object_to_json_string_indent(obj_ptr[i], indent + 1); > + > + if (!obj_str) { > + free(str); > + return NULL; > + } > + str = str_append(str, obj_str); > + if (!str) > + return NULL; > + } > + } > + str = str_append(str, "\n"); > + if (!str) > + return NULL; > + str = str_indent(str, indent); > + if (!str) > + return NULL; > + str = str_append(str, "]\n"); > + if (!str) > + return NULL; > + break; > + > + case type_object: > + obj_ptr = (json_object **)obj->u.ptr; > + if (obj_ptr) { > + for (i = 0; i < obj->length; i++) { > + char *obj_str = json_object_to_json_string_indent(obj_ptr[i], indent + 1); > + > + if (!obj_str) > + return NULL; > + str = str_append(str, obj_str); > + if (!str) > + return NULL; > + } > + } > + if (!str) > + return NULL; > + break; > + > + case type_string: > + str = str_append(str, "\""); > + if (!str) > + return NULL; > + str = str_append(str, (char *)obj->u.ptr); > + if (!str) > + return NULL; > + str = str_append(str, "\",\n"); > + if (!str) > + return NULL; > + break; > + > + case type_null: > + str = str_append(str, "(null)\n"); > + if (!str) > + return NULL; > + break; > + > + case type_int: > + snprintf(buf, sizeof(buf), "%d,\n", obj->u.intval); > + str = str_append(str, buf); > + if (!str) > + return NULL; > + break; > + default: > + return NULL; > + } > + > + if (obj->type == type_object) { > + str = str_indent(str, indent); > + if (!str) > + return NULL; > + str = str_append(str, "},\n"); > + if (!str) > + return NULL; > + } > + return str; > +} > + > +/* > + * json_object_to_json_string > + * convert simplified fwts object to a C string, returns > + * NULL if failed > + */ > +char *json_object_to_json_string(json_object *obj) > +{ > + return json_object_to_json_string_indent(obj, 0); > +} > + > +/* > + * json_object_object_get() > + * return value from key/value pair from an object, returns > + * NULL if it can't be found > + */ > +json_object *json_object_object_get(json_object *obj, const char *key) > +{ > + int i; > + json_object **obj_ptr; > + > + if (!obj || !key) > + return NULL; > + if (obj->type != type_object) > + return NULL; > + > + obj_ptr = (json_object **)obj->u.ptr; > + for (i = 0; i < obj->length; i++) { > + if (obj_ptr[i]->key && !strcmp(obj_ptr[i]->key, key)) > + return obj_ptr[i]; > + } > + return NULL; > +} > + > diff --git a/src/utilities/Makefile.am b/src/utilities/Makefile.am > index 98d6ebaa..2ae86038 100644 > --- a/src/utilities/Makefile.am > +++ b/src/utilities/Makefile.am > @@ -18,10 +18,11 @@ > > AM_CPPFLAGS = -Wall -Werror -Wextra -DDATAROOTDIR=\"$(datarootdir)\" \ > `pkg-config --silence-errors --cflags json` \ > - `pkg-config --silence-errors --cflags json-c` > + `pkg-config --silence-errors --cflags json-c` \ > + -I../lib/include > > bin_PROGRAMS = kernelscan > -kernelscan_SOURCES = kernelscan.c > +kernelscan_SOURCES = kernelscan.c ../lib/src/fwts_json.c > > > -include $(top_srcdir)/git.mk > diff --git a/src/utilities/kernelscan.c b/src/utilities/kernelscan.c > index 2e68ef33..49ffe5c1 100644 > --- a/src/utilities/kernelscan.c > +++ b/src/utilities/kernelscan.c > @@ -21,11 +21,12 @@ > #include <stdbool.h> > #include <stdlib.h> > #include <string.h> > +#include <stddef.h> > #include <ctype.h> > #include <unistd.h> > #include <sys/types.h> > #include <regex.h> > -#include <json.h> > +#include "fwts_json.h" > > #include "config.h" > > Acked-by: Ivan Hu <ivan.hu@canonical.com>
diff --git a/configure.ac b/configure.ac index 39a445cd..f40c3678 100644 --- a/configure.ac +++ b/configure.ac @@ -9,8 +9,6 @@ AC_PROG_LIBTOOL AC_C_INLINE AM_PROG_CC_C_O - AC_SEARCH_LIBS([json_object_from_file], [json json-c], [], [ AC_MSG_ERROR([no available json library]) ]) - AC_SEARCH_LIBS([json_object_object_get_ex], [json json-c], [ AC_DEFINE([JSON_HAS_GET_EX], [1], [Define if we have json_object_object_get_ex])], []) AC_CHECK_FUNCS([localtime_r]) AC_CHECK_FUNCS([dup2]) AC_CHECK_FUNCS([getcwd]) @@ -61,7 +59,6 @@ AC_CHECK_HEADERS([time.h]) AC_CHECK_HEADERS([sys/ioctl.h]) AC_CHECK_HEADERS([sys/time.h]) - AC_CHECK_HEADERS([json/json.h]) AC_CHECK_HEADERS([glib.h]) AC_CHECK_HEADERS([gio/gio.h]) AC_CHECK_HEADERS([asm/opal-prd.h]) diff --git a/src/lib/include/fwts.h b/src/lib/include/fwts.h index 1e0d5870..6f13d262 100644 --- a/src/lib/include/fwts.h +++ b/src/lib/include/fwts.h @@ -153,6 +153,8 @@ #define FWTS_JSON_DATA_PATH DATAROOTDIR "/fwts" +#include <inttypes.h> + #include "fwts_version.h" #include "fwts_backtrace.h" #include "fwts_types.h" diff --git a/src/lib/include/fwts_json.h b/src/lib/include/fwts_json.h index ad51bc45..0c316e02 100644 --- a/src/lib/include/fwts_json.h +++ b/src/lib/include/fwts_json.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2020 Canonical + * Copyright (C) 2010-2020 Canonical * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -16,26 +16,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ - #ifndef __FWTS_JSON_H__ #define __FWTS_JSON_H__ -#include <json.h> - -#define __FWTS_JSON_ERR_PTR__ ((json_object*) -1) /* - * Older versions of json-c may return an error in an - * object as a ((json_object*)-1), where as newer - * versions return NULL, so check for these. Sigh. + * Minimal subset of json for fwts */ -#define FWTS_JSON_ERROR(ptr) \ - ( (ptr == NULL) || ((const json_object *)ptr == __FWTS_JSON_ERR_PTR__) ) -/* - * json-c 0.13.99 does not define TRUE/FALSE anymore - * the json-c maintainers replaced them with pure 1/0 - * https://github.com/json-c/json-c/commit/0992aac61f8b - */ +#define FWTS_JSON_ERROR(ptr) (!ptr) + #ifndef FALSE #define FALSE 0 #endif @@ -44,4 +33,46 @@ #define TRUE 1 #endif +/* + * json types supported + */ +typedef enum { + type_null, + type_int, + type_string, + type_object, + type_array, +} json_type; + +/* + * json object information + */ +typedef struct json_object { + char *key; /* Null if undefined */ + int length; /* Length of a collection of objects */ + json_type type; /* Object type */ + union { + void *ptr; /* string or object array pointer */ + int intval; /* integer value */ + } u; +} json_object; + +/* + * minimal json c library functions as required by fwts + */ +json_object *json_object_from_file(const char *filename); +json_object *json_object_object_get(json_object *obj, const char *key); +int json_object_array_length(json_object *obj); +json_object *json_object_array_get_idx(json_object *obj, int index); +const char *json_object_get_string(json_object *obj); +json_object *json_object_new_int(int); +void json_object_object_add(json_object *obj, const char *key, json_object *value); + +json_object *json_object_new_object(void); +json_object *json_object_new_array(void); +char *json_object_to_json_string(json_object *obj); +void json_object_put(json_object *obj); +json_object *json_object_new_string(const char *str); +int json_object_array_add(json_object *obj, json_object *item); + #endif diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am index ecc4136b..4593bb82 100644 --- a/src/lib/src/Makefile.am +++ b/src/lib/src/Makefile.am @@ -82,6 +82,7 @@ libfwts_la_SOURCES = \ fwts_interactive.c \ fwts_ioport.c \ fwts_ipmi.c \ + fwts_json.c \ fwts_keymap.c \ fwts_klog.c \ fwts_olog.c \ diff --git a/src/lib/src/fwts_json.c b/src/lib/src/fwts_json.c new file mode 100644 index 00000000..b92a241a --- /dev/null +++ b/src/lib/src/fwts_json.c @@ -0,0 +1,917 @@ +/* + * Copyright (C) 2010-2020 Canonical + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * This is a simplified json parser and json object store + * based on json but does not support keywords and is designed + * just for the fwts json data files. It is not a full json + * implementation and should not be used as such. + * + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#include "fwts.h" + +/* + * json file information + */ +typedef struct { + FILE *fp; /* File pointer */ + const char *filename; /* Name of file */ + int linenum; /* Parser line number */ + int charnum; /* Parser char position */ + int error_reported; /* Error count */ +} json_file; + +/* + * json tokens, subset of full json token set as used + * for fwts requirements + */ +typedef enum { + token_lbrace, + token_rbrace, + token_lbracket, + token_rbracket, + token_colon, + token_comma, + token_int, + token_string, + token_true, + token_false, + token_null, + token_error, + token_eof, +} json_token_type; + +/* + * json parser token + */ +typedef struct { + json_token_type type; /* token type */ + long offset; /* offset in file for re-winding */ + union { + char *str; /* token string value */ + int intval; /* token integer value */ + } u; +} json_token; + +/* + * json_token_string() + * convert json token to a human readable string + */ +char *json_token_string(json_token *jtoken) +{ + static char tmp[64]; + + switch (jtoken->type) { + case token_lbrace: + return "{"; + case token_rbrace: + return "}"; + case token_lbracket: + return "["; + case token_rbracket: + return "]"; + case token_colon: + return ":"; + case token_comma: + return ","; + case token_int: + (void)snprintf(tmp, sizeof(tmp), "%d", jtoken->u.intval); + return tmp; + case token_string: + return jtoken->u.str; + case token_error: + return "<error>"; + case token_eof: + return "end of file"; + default: + break; + } + return "<illegal token>"; +} + +/* + * json_get_string() + * parse a literal string + */ +json_token_type json_get_string(json_file *jfile, json_token *token) +{ + char buffer[4096]; + size_t i = 0; + + for (;;) { + int ch; + + ch = fgetc(jfile->fp); + jfile->charnum++; + if (ch == EOF) { + fprintf(stderr, "json_parser: unexpected EOF in literal string\n"); + token->u.str = NULL; + return token_error; + } + + if (ch == '\\') { + ch = fgetc(jfile->fp); + switch (ch) { + case '\\': + case '"': + case '/': + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case 'u': + fprintf(stderr, "json parser: escaped hex values not supported\n"); + ch = '?'; + break; + } + jfile->charnum++; + } else if (ch == '"') { + buffer[i] = '\0'; + token->u.str = strdup(buffer); + if (!token->u.str) { + fprintf(stderr, "json parser: out of memory allocating %zd byte string\n", i); + break; + } + return token_string; + } + buffer[i] = ch; + i++; + if (i >= sizeof(buffer)) { + fprintf(stderr, "json parser: string too long, maximum size %zd bytes\n", sizeof(buffer) - 1); + break; + } + } + + token->u.str = NULL; + return token_error; +} + +/* + * json_get_int() + * parse a simple integer + */ +json_token_type json_get_int(json_file *jfile, json_token *token) +{ + char buffer[64]; + size_t i = 0; + + for (;;) { + int ch; + + ch = fgetc(jfile->fp); + if (!isdigit(ch)) { + (void)ungetc(ch, jfile->fp); + buffer[i] = '\0'; + token->u.intval = atoi(buffer); + return token_int; + } + jfile->charnum++; + buffer[i] = ch; + i++; + if (i >= sizeof(buffer)) { + fprintf(stderr, "json parser: integer too long, maximum size %zd bytes\n", sizeof(buffer) - 1); + break; + } + } + + token->u.str = NULL; + return token_error; +} + +/* + * json_unget_token() + * push file pointer back to point before the token + * to unpush the token + */ +int json_unget_token(json_file *jfile, json_token *token) +{ + return fseek(jfile->fp, token->offset, SEEK_SET); +} + +/* + * json_get_keyword() + * get a keyword + */ +int json_get_keyword(json_file *jfile, json_token *token) +{ + char buffer[32]; + size_t i = 0; + + token->u.str = NULL; + *buffer = '\0'; + + for (;;) { + int ch; + + ch = fgetc(jfile->fp); + jfile->charnum++; + if (ch == EOF) { + fprintf(stderr, "json_parser: unexpected EOF in keyword string\n"); + return token_error; + } + if (ch < 'a' || ch > 'z') + break; + + buffer[i] = ch; + i++; + if (i >= sizeof(buffer)) { + fprintf(stderr, "json parser: keyword too long, maximum size %zd bytes\n", sizeof(buffer) - 1); + return token_error; + } + } + if (!strcmp(buffer, "true")) + return token_true; + if (!strcmp(buffer, "false")) + return token_false; + if (!strcmp(buffer, "null")) + return token_null; + return token_error; +} + +/* + * json_get_token() + * read next input character(s) and return a matching token + */ +json_token_type json_get_token(json_file *jfile, json_token *token) +{ + (void)memset(token, 0, sizeof(*token)); + + token->offset = ftell(jfile->fp); + for (;;) { + int ch; + + ch = fgetc(jfile->fp); + jfile->charnum++; + + switch (ch) { + case '\n': + jfile->linenum++; + continue; + case ' ': + case '\r': + case '\t': + continue; + case EOF: + token->type = token_eof; + return token->type; + case '{': + token->type = token_lbrace; + return token->type; + case '}': + token->type = token_rbrace; + return token->type; + case '[': + token->type = token_lbracket; + return token->type; + case ']': + token->type = token_rbracket; + return token->type; + case ':': + token->type = token_colon; + return token->type; + case ',': + token->type = token_comma; + return token->type; + case '"': + token->type = json_get_string(jfile, token); + return token->type; + case '0'...'9': + token->type = json_get_int(jfile, token); + return token->type; + case 'a'...'z': + fprintf(stderr, "json_parser: keywords not supported\n"); + token->type = token_error; + return token->type; + default: + token->type = token_error; + return token->type; + } + } + + /* Should never reach here */ + token->type = token_error; + return token->type; +} + +/* + * json_parse_error_where() + * very simple parser error message, report where in the file + * the parsing error occurred. + */ +void json_parse_error_where(json_file *jfile) +{ + if (jfile->error_reported == 0) + fprintf(stderr, "json_parser: aborted at line %d, char %d of file %s\n", + jfile->linenum, jfile->charnum, jfile->filename); + jfile->error_reported++; +} + +json_object *json_parse_object(json_file *jfile); + +/* + * json_parse_array() + * parse a json array + */ +json_object *json_parse_array(json_file *jfile) +{ + json_object *array_obj; + + array_obj = json_object_new_array(); + if (!array_obj) { + fprintf(stderr, "json_parser: out of memory allocating a json array object\n"); + json_parse_error_where(jfile); + return NULL; + } + + for (;;) { + json_object *obj; + json_token token; + + obj = json_parse_object(jfile); + if (!obj) { + json_parse_error_where(jfile); + free(array_obj); + return NULL; + } + json_object_array_add(array_obj, obj); + + switch (json_get_token(jfile, &token)) { + case token_rbracket: + return array_obj; + case token_comma: + continue; + default: + if (json_unget_token(jfile, &token) != 0) { + fprintf(stderr, "json_parser: failed to unget a token\n"); + free(array_obj); + return NULL; + } + break; + } + } + return array_obj; +} + +/* + * json_parse_object() + * parse a json object (simplified fwts json format only) + */ +json_object *json_parse_object(json_file *jfile) +{ + json_token token; + json_object *obj, *val_obj; + char *key = NULL; + + if (json_get_token(jfile, &token) != token_lbrace) { + fprintf(stderr, "json_parser: expecting '{', got %s instead\n", json_token_string(&token)); + return NULL; + } + + obj = json_object_new_object(); + if (!obj) + goto err_nomem; + + for (;;) { + switch (json_get_token(jfile, &token)) { + case token_rbrace: + return obj; + case token_string: + key = token.u.str; + if (!key) + goto err_nomem; + token.u.str = NULL; + break; + default: + fprintf(stderr, "json_parser: expecting } or key literal string, got %s instead\n", json_token_string(&token)); + goto err_free; + } + if (json_get_token(jfile, &token) != token_colon) { + fprintf(stderr, "json_parser: expecting ':', got %s instead\n", json_token_string(&token)); + goto err_free; + } + switch (json_get_token(jfile, &token)) { + case token_string: + val_obj = json_object_new_string(token.u.str); + if (!val_obj) + goto err_nomem; + json_object_object_add(obj, key, val_obj); + free(key); + break; + case token_int: + val_obj = json_object_new_int(token.u.intval); + if (!val_obj) + goto err_nomem; + json_object_object_add(obj, key, val_obj); + free(key); + break; + case token_lbracket: + val_obj = json_parse_array(jfile); + if (!val_obj) + goto err_nomem; + json_object_object_add(obj, key, val_obj); + break; + case token_lbrace: + fprintf(stderr, "json_parser: nested objects not supported\n"); + goto err_free; + case token_true: + case token_false: + case token_null: + fprintf(stderr, "json_parser: tokens %s not supported\n", json_token_string(&token)); + goto err_free; + default: + fprintf(stderr, "json_parser: unexpected token %s\n", json_token_string(&token)); + } + switch (json_get_token(jfile, &token)) { + case token_comma: + continue; + case token_rbrace: + return obj; + default: + fprintf(stderr, "json_parser: expected , or }, got %s instead\n", json_token_string(&token)); + goto err_free; + } + } + +err_nomem: + fprintf(stderr, "json_parser: out of memory allocating a json object\n"); + json_parse_error_where(jfile); +err_free: + free(obj); + return NULL; +} + +/* + * json_object_from_file() + * parse a simplified fwts json file and convert it into + * a json object, return NULL if parsing failed or ran + * out of memory + */ +json_object *json_object_from_file(const char *filename) +{ + json_object *obj; + json_file jfile; + + jfile.filename = filename; + jfile.linenum = 1; + jfile.charnum = 0; + jfile.error_reported = 0; + + jfile.fp = fopen(filename, "r"); + if (!jfile.fp) + return NULL; + + obj = json_parse_object(&jfile); + + fclose(jfile.fp); + return obj; +} + +/* + * json_object_new_object() + * return a new json object, NULL if failed + */ +json_object *json_object_new_object(void) +{ + json_object *obj; + + obj = calloc(1, sizeof(*obj)); + if (!obj) + return NULL; + obj->type = type_object; + obj->u.ptr = NULL; + + return obj; +} + +/* + * json_object_new_object() + * return a new json object, NULL if failed + */ +int json_object_array_length(json_object *obj) +{ + if (!obj) + return 0; + if (obj->type != type_array) + return 0; + return obj->length; +} + +/* + * json_object_new_int() + * return a new json integer object, NULL if failed + */ +json_object *json_object_new_int(int val) +{ + json_object *obj; + + obj = calloc(1, sizeof(*obj)); + if (!obj) + return NULL; + obj->type = type_int; + obj->u.intval = val; + + return obj; +} + +/* + * json_object_new_string() + * return a new json string object, NULL if failed + */ +json_object *json_object_new_string(const char *str) +{ + json_object *obj; + + obj = calloc(1, sizeof(*obj)); + if (!obj) + return NULL; + obj->type = type_string; + obj->u.ptr = strdup(str); + if (!obj->u.ptr) { + free(obj); + return NULL; + } + return obj; +} + +/* + * json_object_new_array() + * return a new json array object, NULL if failed + */ +json_object *json_object_new_array(void) +{ + json_object *obj; + + obj = calloc(1, sizeof(*obj)); + if (!obj) + return NULL; + obj->type = type_array; + obj->length = 0; + obj->u.ptr = NULL; + + return obj; +} + +/* + * json_object_array_add_item() + * add an object to another object, return 0 if succeeded, + * non-zero if failed + */ +static int json_object_array_add_item(json_object *obj, json_object *item) +{ + json_object **obj_ptr; + + if (obj->length < 0) + return -1; + obj_ptr = realloc(obj->u.ptr, sizeof(json_object *) * (obj->length + 1)); + if (!obj_ptr) + return -1; + obj->u.ptr = (void *)obj_ptr; + obj_ptr[obj->length] = item; + obj->length++; + return 0; +} + +/* + * json_object_array_add() + * add a object to a json array object, return NULL if failed + */ +int json_object_array_add(json_object *obj, json_object *item) +{ + if (!obj || !item) + return -1; + if (obj->type != type_array) + return -1; + return json_object_array_add_item(obj, item); +} + + +/* + * json_object_object_add() + * add a key/valyue object to a json object, return NULL if failed + */ +void json_object_object_add(json_object *obj, const char *key, json_object *value) +{ + if (!obj || !key || !value) + return; + if (obj->type != type_object) + return; + value->key = strdup(key); + if (!value->key) + return; + json_object_array_add_item(obj, value); +} + +/* + * json_object_array_get_idx() + * get an object at position index from a json array object, + * return NULL if failed + */ +json_object *json_object_array_get_idx(json_object *obj, int index) +{ + json_object **obj_array; + + if (!obj) + return NULL; + if (obj->type != type_array) + return NULL; + if (index >= obj->length) + return NULL; + obj_array = (json_object **)obj->u.ptr; + if (obj_array == NULL) + return NULL; + return obj_array[index]; +} + +/* + * json_object_get_string() + * return a C string from a json string object + */ +const char *json_object_get_string(json_object *obj) +{ + if (!obj) + return NULL; + if (obj->type != type_string) + return NULL; + return (const char *)obj->u.ptr; +} + +/* + * json_object_put() + * free a json object and all sub-objects + */ +void json_object_put(json_object *obj) +{ + int i; + json_object **obj_ptr; + + if (!obj) + return; + + if (obj->key) + free(obj->key); + + switch (obj->type) { + case type_array: + case type_object: + obj_ptr = (json_object **)obj->u.ptr; + + for (i = 0; i < obj->length; i++) { + json_object_put(obj_ptr[i]); + } + free(obj->u.ptr); + break; + case type_string: + free(obj->u.ptr); + break; + case type_null: + case type_int: + default: + break; + } + free(obj); +} + +/* + * str_append() + * append a string to a string, return NULL if failed + */ +static char *str_append(char *str, char *append) +{ + char *new_str; + size_t len; + + if (!append) + return NULL; + + if (str) { + len = strlen(append) + strlen(str) + 1; + new_str = realloc(str, len); + if (!new_str) { + free(str); + return NULL; + } + strcat(new_str, append); + } else { + len = strlen(append) + 1; + new_str = malloc(len); + if (!new_str) + return NULL; + strcpy(new_str, append); + } + return new_str; +} + +/* + * str_indent() + * add 2 spaces per indent level to a string, returns + * NULL if failed + */ +static char *str_indent(char *str, int indent) +{ + char buf[81]; + int i; + + indent = indent + indent; + if (indent > 80) + indent = 80; + + for (i = 0; i < indent; i++) + buf[i] = ' '; + buf[i] = '\0'; + + return str_append(str, buf); +} + +/* + * json_object_to_json_string_indent() + * turn a simplified fwts json object into a C string, returns + * the stringified object or NULL if failed. Will traverse object + * tree and add indentation based on recursion depth. + */ +static char *json_object_to_json_string_indent(json_object *obj, int indent) +{ + int i; + json_object **obj_ptr; + char *str = NULL; + char buf[64]; + + if (!obj) + return NULL; + + if (obj->type == type_object) { + str = str_indent(str, indent); + if (!str) + return NULL; + str = str_append(str, "{\n"); + if (!str) + return NULL; + } + + if (obj->key) { + str = str_indent(str, indent); + if (!str) + return NULL; + str = str_append(str, "\""); + if (!str) + return NULL; + str = str_append(str, obj->key); + if (!str) + return NULL; + str = str_append(str, "\":"); + if (!str) + return NULL; + } + + switch (obj->type) { + case type_array: + str = str_append(str, "[\n"); + if (!str) + return NULL; + + obj_ptr = (json_object **)obj->u.ptr; + if (obj_ptr) { + for (i = 0; i < obj->length; i++) { + char *obj_str = json_object_to_json_string_indent(obj_ptr[i], indent + 1); + + if (!obj_str) { + free(str); + return NULL; + } + str = str_append(str, obj_str); + if (!str) + return NULL; + } + } + str = str_append(str, "\n"); + if (!str) + return NULL; + str = str_indent(str, indent); + if (!str) + return NULL; + str = str_append(str, "]\n"); + if (!str) + return NULL; + break; + + case type_object: + obj_ptr = (json_object **)obj->u.ptr; + if (obj_ptr) { + for (i = 0; i < obj->length; i++) { + char *obj_str = json_object_to_json_string_indent(obj_ptr[i], indent + 1); + + if (!obj_str) + return NULL; + str = str_append(str, obj_str); + if (!str) + return NULL; + } + } + if (!str) + return NULL; + break; + + case type_string: + str = str_append(str, "\""); + if (!str) + return NULL; + str = str_append(str, (char *)obj->u.ptr); + if (!str) + return NULL; + str = str_append(str, "\",\n"); + if (!str) + return NULL; + break; + + case type_null: + str = str_append(str, "(null)\n"); + if (!str) + return NULL; + break; + + case type_int: + snprintf(buf, sizeof(buf), "%d,\n", obj->u.intval); + str = str_append(str, buf); + if (!str) + return NULL; + break; + default: + return NULL; + } + + if (obj->type == type_object) { + str = str_indent(str, indent); + if (!str) + return NULL; + str = str_append(str, "},\n"); + if (!str) + return NULL; + } + return str; +} + +/* + * json_object_to_json_string + * convert simplified fwts object to a C string, returns + * NULL if failed + */ +char *json_object_to_json_string(json_object *obj) +{ + return json_object_to_json_string_indent(obj, 0); +} + +/* + * json_object_object_get() + * return value from key/value pair from an object, returns + * NULL if it can't be found + */ +json_object *json_object_object_get(json_object *obj, const char *key) +{ + int i; + json_object **obj_ptr; + + if (!obj || !key) + return NULL; + if (obj->type != type_object) + return NULL; + + obj_ptr = (json_object **)obj->u.ptr; + for (i = 0; i < obj->length; i++) { + if (obj_ptr[i]->key && !strcmp(obj_ptr[i]->key, key)) + return obj_ptr[i]; + } + return NULL; +} + diff --git a/src/utilities/Makefile.am b/src/utilities/Makefile.am index 98d6ebaa..2ae86038 100644 --- a/src/utilities/Makefile.am +++ b/src/utilities/Makefile.am @@ -18,10 +18,11 @@ AM_CPPFLAGS = -Wall -Werror -Wextra -DDATAROOTDIR=\"$(datarootdir)\" \ `pkg-config --silence-errors --cflags json` \ - `pkg-config --silence-errors --cflags json-c` + `pkg-config --silence-errors --cflags json-c` \ + -I../lib/include bin_PROGRAMS = kernelscan -kernelscan_SOURCES = kernelscan.c +kernelscan_SOURCES = kernelscan.c ../lib/src/fwts_json.c -include $(top_srcdir)/git.mk diff --git a/src/utilities/kernelscan.c b/src/utilities/kernelscan.c index 2e68ef33..49ffe5c1 100644 --- a/src/utilities/kernelscan.c +++ b/src/utilities/kernelscan.c @@ -21,11 +21,12 @@ #include <stdbool.h> #include <stdlib.h> #include <string.h> +#include <stddef.h> #include <ctype.h> #include <unistd.h> #include <sys/types.h> #include <regex.h> -#include <json.h> +#include "fwts_json.h" #include "config.h"