Add tests for the simple json parser.
This commit is contained in:
6
MANIFEST
6
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
|
||||
|
@@ -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 $@
|
||||
|
@@ -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"));
|
||||
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);
|
||||
}
|
||||
|
50
lib/iolog/iolog_json.h
Normal file
50
lib/iolog/iolog_json.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2020 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
*
|
||||
* 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 */
|
271
lib/iolog/regress/iolog_json/check_iolog_json.c
Normal file
271
lib/iolog/regress/iolog_json/check_iolog_json.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2020 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
*
|
||||
* 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 <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif /* HAVE_STRING_H */
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif /* HAVE_STRINGS_H */
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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);
|
||||
}
|
34
lib/iolog/regress/iolog_json/test1.in
Normal file
34
lib/iolog/regress/iolog_json/test1.in
Normal file
@@ -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"
|
||||
}
|
28
lib/iolog/regress/iolog_json/test2.in
Normal file
28
lib/iolog/regress/iolog_json/test2.in
Normal file
@@ -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"
|
||||
}
|
34
lib/iolog/regress/iolog_json/test2.out.ok
Normal file
34
lib/iolog/regress/iolog_json/test2.out.ok
Normal file
@@ -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"
|
||||
}
|
22
lib/iolog/regress/iolog_json/test3.in
Normal file
22
lib/iolog/regress/iolog_json/test3.in
Normal file
@@ -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"
|
||||
]
|
||||
]
|
||||
}
|
@@ -274,9 +274,14 @@ sudo_json_open_array_v1(struct json_container *json, const char *name)
|
||||
|
||||
json_append_indent(json, json->indent_level);
|
||||
|
||||
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)
|
||||
{
|
||||
|
Reference in New Issue
Block a user