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/hostcheck.c
|
||||||
lib/iolog/iolog_fileio.c
|
lib/iolog/iolog_fileio.c
|
||||||
lib/iolog/iolog_json.c
|
lib/iolog/iolog_json.c
|
||||||
|
lib/iolog/iolog_json.h
|
||||||
lib/iolog/iolog_path.c
|
lib/iolog/iolog_path.c
|
||||||
lib/iolog/iolog_util.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/check_iolog_path.c
|
||||||
lib/iolog/regress/iolog_path/data
|
lib/iolog/regress/iolog_path/data
|
||||||
lib/iolog/regress/iolog_util/check_iolog_util.c
|
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)
|
PVS_LOG_OPTS = -a 'GA:1,2' -e -t errorfile -d $(PVS_IGNORE)
|
||||||
|
|
||||||
# Regression tests
|
# 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_LIBS = @LIBS@
|
||||||
TEST_LDFLAGS = @LDFLAGS@
|
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_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
|
all: libsudo_iolog.la
|
||||||
|
|
||||||
pvs-log-files: $(POBJS)
|
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
|
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)
|
$(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:
|
pre-install:
|
||||||
|
|
||||||
install:
|
install:
|
||||||
@@ -159,6 +164,7 @@ check: $(TEST_PROGS)
|
|||||||
LC_ALL=C; export LC_ALL; \
|
LC_ALL=C; export LC_ALL; \
|
||||||
unset LANG || LANG=; \
|
unset LANG || LANG=; \
|
||||||
rval=0; \
|
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_path $(srcdir)/regress/iolog_path/data || rval=`expr $$rval + $$?`; \
|
||||||
./check_iolog_util || rval=`expr $$rval + $$?`; \
|
./check_iolog_util || rval=`expr $$rval + $$?`; \
|
||||||
exit $$rval; \
|
exit $$rval; \
|
||||||
@@ -182,6 +188,20 @@ realclean: distclean
|
|||||||
cleandir: realclean
|
cleandir: realclean
|
||||||
|
|
||||||
# Autogenerated dependencies, do not modify
|
# 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 \
|
check_iolog_path.lo: $(srcdir)/regress/iolog_path/check_iolog_path.c \
|
||||||
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
|
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
|
||||||
$(incdir)/sudo_fatal.h $(incdir)/sudo_iolog.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_fatal.h $(incdir)/sudo_gettext.h \
|
||||||
$(incdir)/sudo_iolog.h $(incdir)/sudo_json.h \
|
$(incdir)/sudo_iolog.h $(incdir)/sudo_json.h \
|
||||||
$(incdir)/sudo_queue.h $(incdir)/sudo_util.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
|
$(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 \
|
iolog_json.i: $(srcdir)/iolog_json.c $(incdir)/compat/stdbool.h \
|
||||||
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
|
||||||
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
|
||||||
$(incdir)/sudo_iolog.h $(incdir)/sudo_json.h \
|
$(incdir)/sudo_iolog.h $(incdir)/sudo_json.h \
|
||||||
$(incdir)/sudo_queue.h $(incdir)/sudo_util.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) $<
|
$(CC) -E -o $@ $(CPPFLAGS) $<
|
||||||
iolog_json.plog: iolog_json.i
|
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 $@
|
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_compat.h"
|
||||||
#include "sudo_fatal.h"
|
#include "sudo_fatal.h"
|
||||||
#include "sudo_debug.h"
|
#include "sudo_debug.h"
|
||||||
#include "sudo_queue.h"
|
|
||||||
#include "sudo_json.h"
|
|
||||||
#include "sudo_util.h"
|
#include "sudo_util.h"
|
||||||
#include "sudo_iolog.h"
|
#include "sudo_iolog.h"
|
||||||
|
|
||||||
TAILQ_HEAD(json_item_list, json_item);
|
#include "iolog_json.h"
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct json_stack {
|
struct json_stack {
|
||||||
unsigned int depth;
|
unsigned int depth;
|
||||||
@@ -128,7 +107,7 @@ json_store_lines(struct json_item *item, struct iolog_info *li)
|
|||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char **
|
char **
|
||||||
json_array_to_strvec(struct json_object *array)
|
json_array_to_strvec(struct json_object *array)
|
||||||
{
|
{
|
||||||
struct json_item *item;
|
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);
|
debug_decl(json_array_to_strvec, SUDO_DEBUG_UTIL);
|
||||||
|
|
||||||
TAILQ_FOREACH(item, &array->items, entries) {
|
TAILQ_FOREACH(item, &array->items, entries) {
|
||||||
|
/* Can only convert arrays of string. */
|
||||||
if (item->type != JSON_STRING) {
|
if (item->type != JSON_STRING) {
|
||||||
sudo_warnx(U_("expected JSON_STRING, got %d"), item->type);
|
sudo_warnx(U_("expected JSON_STRING, got %d"), item->type);
|
||||||
debug_return_ptr(NULL);
|
debug_return_ptr(NULL);
|
||||||
@@ -380,7 +360,7 @@ json_parse_string(char **strp)
|
|||||||
debug_return_str(ret);
|
debug_return_str(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
free_json_items(struct json_item_list *items)
|
free_json_items(struct json_item_list *items)
|
||||||
{
|
{
|
||||||
struct json_item *item;
|
struct json_item *item;
|
||||||
@@ -498,6 +478,19 @@ json_insert_bool(struct json_item_list *items, char *name, bool value,
|
|||||||
debug_return_bool(true);
|
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
|
static bool
|
||||||
json_insert_num(struct json_item_list *items, char *name, long long value,
|
json_insert_num(struct json_item_list *items, char *name, long long value,
|
||||||
unsigned int lineno)
|
unsigned int lineno)
|
||||||
@@ -557,10 +550,9 @@ json_stack_push(struct json_stack *stack, struct json_item_list *items,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
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_object *curarray = NULL;
|
||||||
struct json_stack objstack = JSON_STACK_INTIALIZER(objstack);
|
struct json_stack objstack = JSON_STACK_INTIALIZER(objstack);
|
||||||
struct json_stack arrstack = JSON_STACK_INTIALIZER(arrstack);
|
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;
|
char *buf = NULL;
|
||||||
size_t bufsize = 0;
|
size_t bufsize = 0;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
long long num;
|
|
||||||
bool ret = false;
|
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) {
|
while ((len = getdelim(&buf, &bufsize, '\n', fp)) != -1) {
|
||||||
char *cp = buf;
|
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"));
|
sudo_warnx(U_("unexpected array"));
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
curarray = json_stack_push(&arrstack, &curobj->items, curarray,
|
if (curarray != NULL) {
|
||||||
JSON_ARRAY, name, lineno);
|
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)
|
if (curarray == NULL)
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
name = NULL;
|
name = NULL;
|
||||||
@@ -666,13 +667,15 @@ iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
if (name == NULL) {
|
if (name == NULL && curarray == NULL) {
|
||||||
sudo_warnx(U_("unexpected boolean"));
|
sudo_warnx(U_("unexpected boolean"));
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
if (strcmp(cp, "true") != 0)
|
if (strncmp(cp, "true", sizeof("true") - 1) != 0)
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
cp += sizeof("true") - 1;
|
cp += sizeof("true") - 1;
|
||||||
|
if (*cp != ',' && !isspace((unsigned char)*cp) && *cp != '\0')
|
||||||
|
goto parse_error;
|
||||||
|
|
||||||
if (curarray != NULL) {
|
if (curarray != NULL) {
|
||||||
if (!json_insert_bool(&curarray->items, NULL, true, lineno))
|
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;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
if (name == NULL) {
|
if (name == NULL && curarray == NULL) {
|
||||||
sudo_warnx(U_("unexpected boolean"));
|
sudo_warnx(U_("unexpected boolean"));
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
if (strcmp(cp, "false") != 0)
|
if (strncmp(cp, "false", sizeof("false") - 1) != 0)
|
||||||
goto parse_error;
|
goto parse_error;
|
||||||
cp += sizeof("false") - 1;
|
cp += sizeof("false") - 1;
|
||||||
|
if (*cp != ',' && !isspace((unsigned char)*cp) && *cp != '\0')
|
||||||
|
goto parse_error;
|
||||||
|
|
||||||
if (curarray != NULL) {
|
if (curarray != NULL) {
|
||||||
if (!json_insert_bool(&curarray->items, NULL, false, lineno))
|
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;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
if (strcmp(cp, "null") == 0)
|
if (name == NULL && curarray == NULL) {
|
||||||
sudo_warnx(U_("null not allowed"));
|
sudo_warnx(U_("unexpected boolean"));
|
||||||
goto parse_error;
|
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 '+': case '-': case '0': case '1': case '2': case '3':
|
||||||
case '4': case '5': case '6': case '7': case '8': case '9':
|
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"));
|
sudo_warnx(U_("unexpected number"));
|
||||||
goto parse_error;
|
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';
|
cp[len] = '\0';
|
||||||
num = sudo_strtonum(cp, LLONG_MIN, LLONG_MAX, &errstr);
|
num = sudo_strtonum(cp, LLONG_MIN, LLONG_MAX, &errstr);
|
||||||
if (errstr != NULL) {
|
if (errstr != NULL) {
|
||||||
@@ -719,6 +742,7 @@ iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct iolog_info *li)
|
|||||||
goto parse_error;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
cp += len;
|
cp += len;
|
||||||
|
*cp = ch;
|
||||||
|
|
||||||
if (curarray != NULL) {
|
if (curarray != NULL) {
|
||||||
if (!json_insert_num(&curarray->items, NULL, num, lineno))
|
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;
|
goto parse_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Walk the stack and parse entries. */
|
ret = true;
|
||||||
ret = iolog_parse_json_object(&root, li);
|
|
||||||
|
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
parse_error:
|
parse_error:
|
||||||
sudo_warnx(U_("%s/%s:%u unable to parse \"%s\""), iolog_dir,
|
sudo_warnx(U_("%s:%u unable to parse \"%s\""), filename, lineno, buf);
|
||||||
"log.json", lineno, buf);
|
|
||||||
done:
|
done:
|
||||||
free(buf);
|
free(buf);
|
||||||
free(name);
|
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);
|
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);
|
json_append_indent(json, json->indent_level);
|
||||||
|
|
||||||
json_append_string(json, name);
|
if (name != NULL) {
|
||||||
if (!json_append_buf(json, ": ["))
|
json_append_string(json, name);
|
||||||
debug_return_bool(false);
|
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->indent_level += json->indent_increment;
|
||||||
json->need_comma = false;
|
json->need_comma = false;
|
||||||
@@ -300,7 +305,7 @@ sudo_json_close_array_v1(struct json_container *json)
|
|||||||
debug_return_bool(true);
|
debug_return_bool(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
static bool
|
||||||
sudo_json_add_value_int(struct json_container *json, const char *name,
|
sudo_json_add_value_int(struct json_container *json, const char *name,
|
||||||
struct json_value *value, bool as_object)
|
struct json_value *value, bool as_object)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user