diff --git a/MANIFEST b/MANIFEST index 419e5ee45..f3b952e08 100644 --- a/MANIFEST +++ b/MANIFEST @@ -100,8 +100,14 @@ lib/iolog/Makefile.in lib/iolog/hostcheck.c lib/iolog/iolog_fileio.c lib/iolog/iolog_json.c +lib/iolog/iolog_json.h lib/iolog/iolog_path.c lib/iolog/iolog_util.c +lib/iolog/regress/iolog_json/check_iolog_json.c +lib/iolog/regress/iolog_json/test1.in +lib/iolog/regress/iolog_json/test2.in +lib/iolog/regress/iolog_json/test2.out.ok +lib/iolog/regress/iolog_json/test3.in lib/iolog/regress/iolog_path/check_iolog_path.c lib/iolog/regress/iolog_path/data lib/iolog/regress/iolog_util/check_iolog_util.c diff --git a/lib/iolog/Makefile.in b/lib/iolog/Makefile.in index 9c458b4ed..105332020 100644 --- a/lib/iolog/Makefile.in +++ b/lib/iolog/Makefile.in @@ -75,7 +75,7 @@ PVS_IGNORE = 'V707,V011,V002,V536' PVS_LOG_OPTS = -a 'GA:1,2' -e -t errorfile -d $(PVS_IGNORE) # Regression tests -TEST_PROGS = check_iolog_path check_iolog_util +TEST_PROGS = check_iolog_json check_iolog_path check_iolog_util TEST_LIBS = @LIBS@ TEST_LDFLAGS = @LDFLAGS@ @@ -97,6 +97,8 @@ CHECK_IOLOG_PATH_OBJS = check_iolog_path.lo iolog_path.lo CHECK_IOLOG_UTIL_OBJS = check_iolog_util.lo iolog_json.lo iolog_util.lo +CHECK_IOLOG_JSON_OBJS = check_iolog_json.lo iolog_json.lo + all: libsudo_iolog.la pvs-log-files: $(POBJS) @@ -132,6 +134,9 @@ check_iolog_path: $(CHECK_IOLOG_PATH_OBJS) libsudo_iolog.la check_iolog_util: $(CHECK_IOLOG_UTIL_OBJS) libsudo_iolog.la $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_UTIL_OBJS) libsudo_iolog.la $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS) +check_iolog_json: $(CHECK_IOLOG_JSON_OBJS) libsudo_iolog.la + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_IOLOG_JSON_OBJS) libsudo_iolog.la $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(TEST_LDFLAGS) $(TEST_LIBS) + pre-install: install: @@ -159,6 +164,7 @@ check: $(TEST_PROGS) LC_ALL=C; export LC_ALL; \ unset LANG || LANG=; \ rval=0; \ + ./check_iolog_json $(srcdir)/regress/iolog_json/*.in || rval=`expr $$rval + $$?`; \ ./check_iolog_path $(srcdir)/regress/iolog_path/data || rval=`expr $$rval + $$?`; \ ./check_iolog_util || rval=`expr $$rval + $$?`; \ exit $$rval; \ @@ -182,6 +188,20 @@ realclean: distclean cleandir: realclean # Autogenerated dependencies, do not modify +check_iolog_json.lo: $(srcdir)/regress/iolog_json/check_iolog_json.c \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_json.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/iolog_json.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/iolog_json/check_iolog_json.c +check_iolog_json.i: $(srcdir)/regress/iolog_json/check_iolog_json.c \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_json.h \ + $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ + $(srcdir)/iolog_json.h $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +check_iolog_json.plog: check_iolog_json.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/iolog_json/check_iolog_json.c --i-file $< --output-file $@ check_iolog_path.lo: $(srcdir)/regress/iolog_path/check_iolog_path.c \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_iolog.h \ @@ -241,14 +261,14 @@ iolog_json.lo: $(srcdir)/iolog_json.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ $(incdir)/sudo_iolog.h $(incdir)/sudo_json.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(top_builddir)/config.h + $(srcdir)/iolog_json.h $(top_builddir)/config.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_json.c iolog_json.i: $(srcdir)/iolog_json.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ $(incdir)/sudo_iolog.h $(incdir)/sudo_json.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(top_builddir)/config.h + $(srcdir)/iolog_json.h $(top_builddir)/config.h $(CC) -E -o $@ $(CPPFLAGS) $< iolog_json.plog: iolog_json.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_json.c --i-file $< --output-file $@ diff --git a/lib/iolog/iolog_json.c b/lib/iolog/iolog_json.c index 4372d0b20..021073be6 100644 --- a/lib/iolog/iolog_json.c +++ b/lib/iolog/iolog_json.c @@ -49,31 +49,10 @@ #include "sudo_compat.h" #include "sudo_fatal.h" #include "sudo_debug.h" -#include "sudo_queue.h" -#include "sudo_json.h" #include "sudo_util.h" #include "sudo_iolog.h" -TAILQ_HEAD(json_item_list, json_item); - -struct json_object { - struct json_item *parent; - struct json_item_list items; -}; - -struct json_item { - TAILQ_ENTRY(json_item) entries; - char *name; /* may be NULL for first brace */ - unsigned int lineno; - enum json_value_type type; - union { - struct json_object child; - char *string; - long long number; - id_t id; - bool boolean; - } u; -}; +#include "iolog_json.h" struct json_stack { unsigned int depth; @@ -128,7 +107,7 @@ json_store_lines(struct json_item *item, struct iolog_info *li) debug_return_bool(true); } -static char ** +char ** json_array_to_strvec(struct json_object *array) { struct json_item *item; @@ -137,6 +116,7 @@ json_array_to_strvec(struct json_object *array) debug_decl(json_array_to_strvec, SUDO_DEBUG_UTIL); TAILQ_FOREACH(item, &array->items, entries) { + /* Can only convert arrays of string. */ if (item->type != JSON_STRING) { sudo_warnx(U_("expected JSON_STRING, got %d"), item->type); debug_return_ptr(NULL); @@ -380,7 +360,7 @@ json_parse_string(char **strp) debug_return_str(ret); } -static void +void free_json_items(struct json_item_list *items) { struct json_item *item; @@ -498,6 +478,19 @@ json_insert_bool(struct json_item_list *items, char *name, bool value, debug_return_bool(true); } +static bool +json_insert_null(struct json_item_list *items, char *name, unsigned int lineno) +{ + struct json_item *item; + debug_decl(json_insert_null, SUDO_DEBUG_UTIL); + + if ((item = new_json_item(JSON_NULL, name, lineno)) == NULL) + debug_return_bool(false); + TAILQ_INSERT_TAIL(items, item, entries); + + debug_return_bool(true); +} + static bool json_insert_num(struct json_item_list *items, char *name, long long value, unsigned int lineno) @@ -557,10 +550,9 @@ json_stack_push(struct json_stack *stack, struct json_item_list *items, } bool -iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li) +iolog_parse_json(FILE *fp, const char *filename, struct json_object *root) { - struct json_object root = { NULL, TAILQ_HEAD_INITIALIZER(root.items) }; - struct json_object *curobj = &root; + struct json_object *curobj = root; struct json_object *curarray = NULL; struct json_stack objstack = JSON_STACK_INTIALIZER(objstack); struct json_stack arrstack = JSON_STACK_INTIALIZER(arrstack); @@ -569,9 +561,13 @@ iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li) char *buf = NULL; size_t bufsize = 0; ssize_t len; - long long num; bool ret = false; - debug_decl(iolog_parse_loginfo_json, SUDO_DEBUG_UTIL); + long long num; + char ch; + debug_decl(iolog_parse_json, SUDO_DEBUG_UTIL); + + root->parent = NULL; + TAILQ_INIT(&root->items); while ((len = getdelim(&buf, &bufsize, '\n', fp)) != -1) { char *cp = buf; @@ -625,8 +621,13 @@ iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li) sudo_warnx(U_("unexpected array")); goto parse_error; } - curarray = json_stack_push(&arrstack, &curobj->items, curarray, - JSON_ARRAY, name, lineno); + if (curarray != NULL) { + curarray = json_stack_push(&arrstack, &curarray->items, + curarray, JSON_ARRAY, name, lineno); + } else { + curarray = json_stack_push(&arrstack, &curobj->items, + NULL, JSON_ARRAY, name, lineno); + } if (curarray == NULL) goto parse_error; name = NULL; @@ -666,13 +667,15 @@ iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li) } break; case 't': - if (name == NULL) { + if (name == NULL && curarray == NULL) { sudo_warnx(U_("unexpected boolean")); goto parse_error; } - if (strcmp(cp, "true") != 0) + if (strncmp(cp, "true", sizeof("true") - 1) != 0) goto parse_error; cp += sizeof("true") - 1; + if (*cp != ',' && !isspace((unsigned char)*cp) && *cp != '\0') + goto parse_error; if (curarray != NULL) { if (!json_insert_bool(&curarray->items, NULL, true, lineno)) @@ -684,13 +687,15 @@ iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li) } break; case 'f': - if (name == NULL) { + if (name == NULL && curarray == NULL) { sudo_warnx(U_("unexpected boolean")); goto parse_error; } - if (strcmp(cp, "false") != 0) + if (strncmp(cp, "false", sizeof("false") - 1) != 0) goto parse_error; cp += sizeof("false") - 1; + if (*cp != ',' && !isspace((unsigned char)*cp) && *cp != '\0') + goto parse_error; if (curarray != NULL) { if (!json_insert_bool(&curarray->items, NULL, false, lineno)) @@ -702,16 +707,34 @@ iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li) } break; case 'n': - if (strcmp(cp, "null") == 0) - sudo_warnx(U_("null not allowed")); - goto parse_error; + if (name == NULL && curarray == NULL) { + sudo_warnx(U_("unexpected boolean")); + goto parse_error; + } + if (strncmp(cp, "null", sizeof("null") - 1) != 0) + goto parse_error; + cp += sizeof("null") - 1; + if (*cp != ',' && !isspace((unsigned char)*cp) && *cp != '\0') + goto parse_error; + + if (curarray != NULL) { + if (!json_insert_null(&curarray->items, NULL, lineno)) + goto parse_error; + } else { + if (!json_insert_null(&curobj->items, name, lineno)) + goto parse_error; + name = NULL; + } + break; case '+': case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - if (name == NULL) { + if (name == NULL && curarray == NULL) { sudo_warnx(U_("unexpected number")); goto parse_error; } - len = strcspn(cp, " \t\r\n,"); + /* XXX - strtonumx() would be simpler here. */ + len = strcspn(cp, " \f\n\r\t\v,"); + ch = cp[len]; cp[len] = '\0'; num = sudo_strtonum(cp, LLONG_MIN, LLONG_MAX, &errstr); if (errstr != NULL) { @@ -719,6 +742,7 @@ iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li) goto parse_error; } cp += len; + *cp = ch; if (curarray != NULL) { if (!json_insert_num(&curarray->items, NULL, num, lineno)) @@ -743,18 +767,34 @@ iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li) goto parse_error; } - /* Walk the stack and parse entries. */ - ret = iolog_parse_json_object(&root, li); - + ret = true; goto done; parse_error: - sudo_warnx(U_("%s/%s:%u unable to parse \"%s\""), iolog_dir, - "log.json", lineno, buf); + sudo_warnx(U_("%s:%u unable to parse \"%s\""), filename, lineno, buf); done: free(buf); free(name); - free_json_items(&root.items); + if (!ret) + free_json_items(&root->items); + + debug_return_bool(ret); +} + +bool +iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li) +{ + struct json_object root; + bool ret = false; + debug_decl(iolog_parse_loginfo_json, SUDO_DEBUG_UTIL); + + if (iolog_parse_json(fp, iolog_dir, &root)) { + /* Walk the stack and parse entries. */ + ret = iolog_parse_json_object(&root, li); + + /* Cleanup. */ + free_json_items(&root.items); + } debug_return_bool(ret); } diff --git a/lib/iolog/iolog_json.h b/lib/iolog/iolog_json.h new file mode 100644 index 000000000..bc28dfa3e --- /dev/null +++ b/lib/iolog/iolog_json.h @@ -0,0 +1,50 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2020 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IOLOG_JSON_H +#define IOLOG_JSON_H + +#include "sudo_json.h" +#include "sudo_queue.h" + +TAILQ_HEAD(json_item_list, json_item); + +struct json_object { + struct json_item *parent; + struct json_item_list items; +}; + +struct json_item { + TAILQ_ENTRY(json_item) entries; + char *name; /* may be NULL for first brace */ + unsigned int lineno; + enum json_value_type type; + union { + struct json_object child; + char *string; + long long number; + id_t id; + bool boolean; + } u; +}; + +void free_json_items(struct json_item_list *items); +bool iolog_parse_json(FILE *fp, const char *filename, struct json_object *root); +char **json_array_to_strvec(struct json_object *array); + +#endif /* IOLOG_JSON_H */ diff --git a/lib/iolog/regress/iolog_json/check_iolog_json.c b/lib/iolog/regress/iolog_json/check_iolog_json.c new file mode 100644 index 000000000..b7673ecbb --- /dev/null +++ b/lib/iolog/regress/iolog_json/check_iolog_json.c @@ -0,0 +1,271 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2020 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ +#include +#include + +#define SUDO_ERROR_WRAP 0 + +#include "sudo_compat.h" +#include "sudo_util.h" +#include "sudo_fatal.h" + +#include "iolog_json.h" + +__dso_public int main(int argc, char *argv[]); + +bool +json_print_object(struct json_container *json, struct json_object *object) +{ + struct json_item *item; + struct json_value json_value; + bool ret = false; + + TAILQ_FOREACH(item, &object->items, entries) { + switch (item->type) { + case JSON_STRING: + json_value.type = JSON_STRING; + json_value.u.string = item->u.string; + if (!sudo_json_add_value(json, item->name, &json_value)) + goto oom; + break; + case JSON_NUMBER: + json_value.type = JSON_NUMBER; + json_value.u.number = item->u.number; + if (!sudo_json_add_value(json, item->name, &json_value)) + goto oom; + break; + case JSON_OBJECT: + if (!sudo_json_open_object(json, item->name)) + goto oom; + if (!json_print_object(json, &item->u.child)) + goto done; + if (!sudo_json_close_object(json)) + goto oom; + break; + case JSON_ARRAY: + if (!sudo_json_open_array(json, item->name)) + goto oom; + if (!json_print_object(json, &item->u.child)) + goto done; + if (!sudo_json_close_array(json)) + goto oom; + break; + case JSON_BOOL: + json_value.type = JSON_BOOL; + json_value.u.boolean = item->u.boolean; + if (!sudo_json_add_value(json, item->name, &json_value)) + goto oom; + break; + case JSON_NULL: + json_value.type = JSON_NULL; + if (!sudo_json_add_value(json, item->name, &json_value)) + goto oom; + break; + default: + sudo_warnx("unsupported JSON type %d", item->type); + goto done; + } + } + + ret = true; + goto done; + +oom: + sudo_warnx("%s: %s", __func__, "unable to allocate memory"); +done: + return ret; +} + +static bool +json_format(struct json_container *json, struct json_object *object) +{ + struct json_item *item; + bool ret = false; + + /* First object holds all the actual data. */ + item = TAILQ_FIRST(&object->items); + if (item->type != JSON_OBJECT) { + sudo_warnx("expected JSON_OBJECT, got %d", item->type); + goto done; + } + object = &item->u.child; + + if (!json_print_object(json, object)) + goto done; + + ret = true; + +done: + return ret; +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-c] input_file ...\n", + getprogname()); + exit(EXIT_FAILURE); +} + +static bool +compare(FILE *fp, const char *infile, struct json_container *json) +{ + const char *cp; + unsigned int lineno = 0; + size_t linesize = 0; + char *line = NULL; + ssize_t len; + + cp = sudo_json_get_buf(json); + + while ((len = getdelim(&line, &linesize, '\n', fp)) != -1) { + lineno++; + + /* skip open/close brace, not present in formatted output */ + if (lineno == 1 && strcmp(line, "{\n") == 0) + continue; + if (*cp == '\0' && strcmp(line, "}\n") == 0) + continue; + + /* Ignore newlines in output to make comparison easier. */ + if (*cp == '\n') + cp++; + if (line[len - 1] == '\n') + len--; + + if (strncmp(line, cp, len) != 0) { + fprintf(stderr, "%s: mismatch on line %u\n", infile, lineno); + fprintf(stderr, "expected: %s", line); + fprintf(stderr, "got : %.*s\n", (int)len, cp); + return false; + } + cp += len; + } + free(line); + + return true; +} + +int +main(int argc, char *argv[]) +{ + struct json_object root; + int ch, i, tests = 0, errors = 0; + bool cat = false; + + initprogname(argc > 0 ? argv[0] : "check_iolog_json"); + + while ((ch = getopt(argc, argv, "c")) != -1) { + switch (ch) { + case 'c': + cat = true; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + for (i = 0; i < argc; i++) { + struct json_container json; + const char *infile = argv[i]; + const char *outfile = argv[i]; + const char *cp; + char pathbuf[PATH_MAX]; + FILE *infp = NULL; + FILE *outfp = NULL; + + tests++; + + if (!sudo_json_init(&json, 4, false, true)) { + errors++; + continue; + } + + /* Parse input file. */ + if ((infp = fopen(infile, "r")) == NULL) { + sudo_warn("%s", argv[1]); + errors++; + goto next; + } + if (!iolog_parse_json(infp, infile, &root)) { + errors++; + goto next; + } + + /* Format as pretty-printed JSON */ + if (!json_format(&json, &root)) { + errors++; + goto next; + } + + /* Check for a .out.ok file in the same location as the .in file. */ + cp = strrchr(infile, '.'); + if (cp != NULL && strcmp(cp, ".in") == 0) { + snprintf(pathbuf, sizeof(pathbuf), "%.*s.out.ok", + (int)(cp - infile), infile); + if ((outfp = fopen(pathbuf, "r")) != NULL) + outfile = pathbuf; + } + if (outfp == NULL) + outfp = infp; + + /* Compare output to expected output. */ + rewind(outfp); + if (!compare(outfp, outfile, &json)) + errors++; + + /* Write the formatted output to stdout for -c (cat) */ + if (cat) { + fprintf(stdout, "{%s\n}\n", sudo_json_get_buf(&json)); + fflush(stdout); + } + +next: + free_json_items(&root.items); + sudo_json_free(&json); + if (infp != NULL) + fclose(infp); + if (outfp != NULL && outfp != infp) + fclose(outfp); + } + + if (tests != 0) { + printf("iolog_json: %d test%s run, %d errors, %d%% success rate\n", + tests, tests == 1 ? "" : "s", errors, + (tests - errors) * 100 / tests); + } + + exit(errors); +} diff --git a/lib/iolog/regress/iolog_json/test1.in b/lib/iolog/regress/iolog_json/test1.in new file mode 100644 index 000000000..8ad36894e --- /dev/null +++ b/lib/iolog/regress/iolog_json/test1.in @@ -0,0 +1,34 @@ +{ + "timestamp": { + "seconds": 1584993067, + "nanoseconds": 880288287 + }, + "columns": 80, + "command": "/usr/bin/make", + "lines": 24, + "runargv": [ + "make", + "test" + ], + "runenv": [ + "LANG=en_US.UTF-8", + "PATH=/bin:/sbin:/usr/games:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin", + "TERM=vt100", + "MAIL=/var/mail/root", + "LOGNAME=root", + "USER=root", + "HOME=/root", + "SHELL=/bin/ksh", + "SUDO_COMMAND=/usr/bin/make test", + "SUDO_USER=millert", + "SUDO_UID=8036", + "SUDO_GID=20", + "A__z=\"*SHLVL" + ], + "runuid": 0, + "runuser": "root", + "submitcwd": "/home/test", + "submithost": "sudo.ws", + "submituser": "millert", + "ttyname": "/dev/console" +} diff --git a/lib/iolog/regress/iolog_json/test2.in b/lib/iolog/regress/iolog_json/test2.in new file mode 100644 index 000000000..df7170f1c --- /dev/null +++ b/lib/iolog/regress/iolog_json/test2.in @@ -0,0 +1,28 @@ +{ + "timestamp": { "seconds": 1584993067, "nanoseconds": 880288287 }, + "columns": 80, + "command": "/usr/bin/make", + "lines": 24, + "runargv": [ "make", "test" ], + "runenv": [ + "LANG=en_US.UTF-8", + "PATH=/bin:/sbin:/usr/games:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin", + "TERM=vt100", + "MAIL=/var/mail/root", + "LOGNAME=root", + "USER=root", + "HOME=/root", + "SHELL=/bin/ksh", + "SUDO_COMMAND=/usr/bin/make test", + "SUDO_USER=millert", + "SUDO_UID=8036", + "SUDO_GID=20", + "A__z=\"*SHLVL" + ], + "runuid": 0, + "runuser": "root", + "submitcwd": "/home/test", + "submithost": "sudo.ws", + "submituser": "millert", + "ttyname": "/dev/console" +} diff --git a/lib/iolog/regress/iolog_json/test2.out.ok b/lib/iolog/regress/iolog_json/test2.out.ok new file mode 100644 index 000000000..8ad36894e --- /dev/null +++ b/lib/iolog/regress/iolog_json/test2.out.ok @@ -0,0 +1,34 @@ +{ + "timestamp": { + "seconds": 1584993067, + "nanoseconds": 880288287 + }, + "columns": 80, + "command": "/usr/bin/make", + "lines": 24, + "runargv": [ + "make", + "test" + ], + "runenv": [ + "LANG=en_US.UTF-8", + "PATH=/bin:/sbin:/usr/games:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin", + "TERM=vt100", + "MAIL=/var/mail/root", + "LOGNAME=root", + "USER=root", + "HOME=/root", + "SHELL=/bin/ksh", + "SUDO_COMMAND=/usr/bin/make test", + "SUDO_USER=millert", + "SUDO_UID=8036", + "SUDO_GID=20", + "A__z=\"*SHLVL" + ], + "runuid": 0, + "runuser": "root", + "submitcwd": "/home/test", + "submithost": "sudo.ws", + "submituser": "millert", + "ttyname": "/dev/console" +} diff --git a/lib/iolog/regress/iolog_json/test3.in b/lib/iolog/regress/iolog_json/test3.in new file mode 100644 index 000000000..ea2df89b8 --- /dev/null +++ b/lib/iolog/regress/iolog_json/test3.in @@ -0,0 +1,22 @@ +{ + "true": false, + "false": true, + "number": 1234567890, + "null": null, + "string": "nonsense", + "scope": { + "a": "b", + "bah": null + }, + "array1": [ + "foo", + "bar", + [ + 123, + null, + false, + "fizz", + "buzz" + ] + ] +} diff --git a/lib/util/json.c b/lib/util/json.c index 1bc46572b..34deb828d 100644 --- a/lib/util/json.c +++ b/lib/util/json.c @@ -274,9 +274,14 @@ sudo_json_open_array_v1(struct json_container *json, const char *name) json_append_indent(json, json->indent_level); - json_append_string(json, name); - if (!json_append_buf(json, ": [")) - debug_return_bool(false); + if (name != NULL) { + json_append_string(json, name); + if (!json_append_buf(json, ": [")) + debug_return_bool(false); + } else { + if (!json_append_buf(json, "[")) + debug_return_bool(false); + } json->indent_level += json->indent_increment; json->need_comma = false; @@ -300,7 +305,7 @@ sudo_json_close_array_v1(struct json_container *json) debug_return_bool(true); } -bool +static bool sudo_json_add_value_int(struct json_container *json, const char *name, struct json_value *value, bool as_object) {