Path: blob/main/lib/eventlog/regress/parse_json/check_parse_json.c
1532 views
/*1* SPDX-License-Identifier: ISC2*3* Copyright (c) 2020 Todd C. Miller <[email protected]>4*5* Permission to use, copy, modify, and distribute this software for any6* purpose with or without fee is hereby granted, provided that the above7* copyright notice and this permission notice appear in all copies.8*9* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES10* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF11* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR12* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES13* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN14* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF15* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.16*/1718#include <config.h>1920#include <stdio.h>21#include <stdlib.h>22#include <string.h>23#include <limits.h>24#include <unistd.h>2526#define SUDO_ERROR_WRAP 02728#include <sudo_compat.h>29#include <sudo_eventlog.h>30#include <sudo_fatal.h>31#include <sudo_util.h>3233#include <parse_json.h>3435sudo_dso_public int main(int argc, char *argv[]);3637static bool38json_print_object(struct json_container *jsonc, struct eventlog_json_object *object)39{40struct json_item *item;41struct json_value json_value;42bool ret = false;4344TAILQ_FOREACH(item, &object->items, entries) {45switch (item->type) {46case JSON_STRING:47json_value.type = JSON_STRING;48json_value.u.string = item->u.string;49if (!sudo_json_add_value(jsonc, item->name, &json_value))50goto oom;51break;52case JSON_NUMBER:53json_value.type = JSON_NUMBER;54json_value.u.number = item->u.number;55if (!sudo_json_add_value(jsonc, item->name, &json_value))56goto oom;57break;58case JSON_OBJECT:59if (!sudo_json_open_object(jsonc, item->name))60goto oom;61if (!json_print_object(jsonc, &item->u.child))62goto done;63if (!sudo_json_close_object(jsonc))64goto oom;65break;66case JSON_ARRAY:67if (!sudo_json_open_array(jsonc, item->name))68goto oom;69if (!json_print_object(jsonc, &item->u.child))70goto done;71if (!sudo_json_close_array(jsonc))72goto oom;73break;74case JSON_BOOL:75json_value.type = JSON_BOOL;76json_value.u.boolean = item->u.boolean;77if (!sudo_json_add_value(jsonc, item->name, &json_value))78goto oom;79break;80case JSON_NULL:81json_value.type = JSON_NULL;82if (!sudo_json_add_value(jsonc, item->name, &json_value))83goto oom;84break;85default:86sudo_warnx("unsupported JSON type %d", item->type);87goto done;88}89}9091ret = true;92goto done;9394oom:95sudo_warnx("%s: %s", __func__, "unable to allocate memory");96done:97return ret;98}99100static bool101json_format(struct json_container *jsonc, struct eventlog_json_object *object)102{103struct json_item *item;104bool ret = false;105106/* First object holds all the actual data. */107item = TAILQ_FIRST(&object->items);108if (item->type != JSON_OBJECT) {109sudo_warnx("expected JSON_OBJECT, got %d", item->type);110goto done;111}112object = &item->u.child;113114if (!json_print_object(jsonc, object))115goto done;116117ret = true;118119done:120return ret;121}122123sudo_noreturn static void124usage(void)125{126fprintf(stderr, "usage: %s [-cv] input_file ...\n",127getprogname());128exit(EXIT_FAILURE);129}130131static bool132compare(FILE *fp, const char *infile, struct json_container *jsonc)133{134const char *cp;135unsigned int lineno = 0;136size_t linesize = 0;137char *line = NULL;138ssize_t len;139140cp = sudo_json_get_buf(jsonc);141142while ((len = getdelim(&line, &linesize, '\n', fp)) != -1) {143lineno++;144145/* skip open/close brace, not present in formatted output */146if (lineno == 1 && strcmp(line, "{\n") == 0)147continue;148if (*cp == '\0' && strcmp(line, "}\n") == 0)149continue;150151/* Ignore newlines in output to make comparison easier. */152if (*cp == '\n')153cp++;154if (line[len - 1] == '\n')155len--;156157if (strncmp(line, cp, (size_t)len) != 0) {158fprintf(stderr, "%s: mismatch on line %u\n", infile, lineno);159fprintf(stderr, "expected: %s", line);160fprintf(stderr, "got : %.*s\n", (int)len, cp);161return false;162}163cp += len;164}165free(line);166167return true;168}169170int171main(int argc, char *argv[])172{173int ch, i, ntests = 0, errors = 0;174bool cat = false;175176initprogname(argc > 0 ? argv[0] : "check_parse_json");177178while ((ch = getopt(argc, argv, "cv")) != -1) {179switch (ch) {180case 'c':181cat = true;182break;183case 'v':184/* ignored */185break;186default:187usage();188}189}190argc -= optind;191argv += optind;192193if (argc < 1)194usage();195196for (i = 0; i < argc; i++) {197struct eventlog_json_object *root;198struct json_container jsonc;199const char *infile = argv[i];200const char *outfile = argv[i];201const char *cp;202char pathbuf[PATH_MAX];203FILE *infp = NULL;204FILE *outfp = NULL;205206ntests++;207208if (!sudo_json_init(&jsonc, 4, false, true, true)) {209errors++;210continue;211}212213/* Parse input file. */214if ((infp = fopen(infile, "r")) == NULL) {215sudo_warn("%s", argv[i]);216errors++;217continue;218}219root = eventlog_json_read(infp, infile);220if (root == NULL) {221errors++;222goto next;223}224225/* Format as pretty-printed JSON */226if (!json_format(&jsonc, root)) {227errors++;228goto next;229}230231/* Check for a .out.ok file in the same location as the .in file. */232cp = strrchr(infile, '.');233if (cp != NULL && strcmp(cp, ".in") == 0) {234snprintf(pathbuf, sizeof(pathbuf), "%.*s.out.ok",235(int)(cp - infile), infile);236if ((outfp = fopen(pathbuf, "r")) != NULL)237outfile = pathbuf;238}239if (outfp == NULL)240outfp = infp;241242/* Compare output to expected output. */243rewind(outfp);244if (!compare(outfp, outfile, &jsonc))245errors++;246247/* Write the formatted output to stdout for -c (cat) */248if (cat) {249fprintf(stdout, "{%s\n}\n", sudo_json_get_buf(&jsonc));250fflush(stdout);251}252253next:254eventlog_json_free(root);255sudo_json_free(&jsonc);256if (infp != NULL)257fclose(infp);258if (outfp != NULL && outfp != infp)259fclose(outfp);260}261262if (ntests != 0) {263printf("%s: %d test%s run, %d errors, %d%% success rate\n",264getprogname(), ntests, ntests == 1 ? "" : "s", errors,265(ntests - errors) * 100 / ntests);266}267268return errors;269}270271272