Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/lib/eventlog/regress/parse_json/check_parse_json.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2020 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include <config.h>
20
21
#include <stdio.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <limits.h>
25
#include <unistd.h>
26
27
#define SUDO_ERROR_WRAP 0
28
29
#include <sudo_compat.h>
30
#include <sudo_eventlog.h>
31
#include <sudo_fatal.h>
32
#include <sudo_util.h>
33
34
#include <parse_json.h>
35
36
sudo_dso_public int main(int argc, char *argv[]);
37
38
static bool
39
json_print_object(struct json_container *jsonc, struct eventlog_json_object *object)
40
{
41
struct json_item *item;
42
struct json_value json_value;
43
bool ret = false;
44
45
TAILQ_FOREACH(item, &object->items, entries) {
46
switch (item->type) {
47
case JSON_STRING:
48
json_value.type = JSON_STRING;
49
json_value.u.string = item->u.string;
50
if (!sudo_json_add_value(jsonc, item->name, &json_value))
51
goto oom;
52
break;
53
case JSON_NUMBER:
54
json_value.type = JSON_NUMBER;
55
json_value.u.number = item->u.number;
56
if (!sudo_json_add_value(jsonc, item->name, &json_value))
57
goto oom;
58
break;
59
case JSON_OBJECT:
60
if (!sudo_json_open_object(jsonc, item->name))
61
goto oom;
62
if (!json_print_object(jsonc, &item->u.child))
63
goto done;
64
if (!sudo_json_close_object(jsonc))
65
goto oom;
66
break;
67
case JSON_ARRAY:
68
if (!sudo_json_open_array(jsonc, item->name))
69
goto oom;
70
if (!json_print_object(jsonc, &item->u.child))
71
goto done;
72
if (!sudo_json_close_array(jsonc))
73
goto oom;
74
break;
75
case JSON_BOOL:
76
json_value.type = JSON_BOOL;
77
json_value.u.boolean = item->u.boolean;
78
if (!sudo_json_add_value(jsonc, item->name, &json_value))
79
goto oom;
80
break;
81
case JSON_NULL:
82
json_value.type = JSON_NULL;
83
if (!sudo_json_add_value(jsonc, item->name, &json_value))
84
goto oom;
85
break;
86
default:
87
sudo_warnx("unsupported JSON type %d", item->type);
88
goto done;
89
}
90
}
91
92
ret = true;
93
goto done;
94
95
oom:
96
sudo_warnx("%s: %s", __func__, "unable to allocate memory");
97
done:
98
return ret;
99
}
100
101
static bool
102
json_format(struct json_container *jsonc, struct eventlog_json_object *object)
103
{
104
struct json_item *item;
105
bool ret = false;
106
107
/* First object holds all the actual data. */
108
item = TAILQ_FIRST(&object->items);
109
if (item->type != JSON_OBJECT) {
110
sudo_warnx("expected JSON_OBJECT, got %d", item->type);
111
goto done;
112
}
113
object = &item->u.child;
114
115
if (!json_print_object(jsonc, object))
116
goto done;
117
118
ret = true;
119
120
done:
121
return ret;
122
}
123
124
sudo_noreturn static void
125
usage(void)
126
{
127
fprintf(stderr, "usage: %s [-cv] input_file ...\n",
128
getprogname());
129
exit(EXIT_FAILURE);
130
}
131
132
static bool
133
compare(FILE *fp, const char *infile, struct json_container *jsonc)
134
{
135
const char *cp;
136
unsigned int lineno = 0;
137
size_t linesize = 0;
138
char *line = NULL;
139
ssize_t len;
140
141
cp = sudo_json_get_buf(jsonc);
142
143
while ((len = getdelim(&line, &linesize, '\n', fp)) != -1) {
144
lineno++;
145
146
/* skip open/close brace, not present in formatted output */
147
if (lineno == 1 && strcmp(line, "{\n") == 0)
148
continue;
149
if (*cp == '\0' && strcmp(line, "}\n") == 0)
150
continue;
151
152
/* Ignore newlines in output to make comparison easier. */
153
if (*cp == '\n')
154
cp++;
155
if (line[len - 1] == '\n')
156
len--;
157
158
if (strncmp(line, cp, (size_t)len) != 0) {
159
fprintf(stderr, "%s: mismatch on line %u\n", infile, lineno);
160
fprintf(stderr, "expected: %s", line);
161
fprintf(stderr, "got : %.*s\n", (int)len, cp);
162
return false;
163
}
164
cp += len;
165
}
166
free(line);
167
168
return true;
169
}
170
171
int
172
main(int argc, char *argv[])
173
{
174
int ch, i, ntests = 0, errors = 0;
175
bool cat = false;
176
177
initprogname(argc > 0 ? argv[0] : "check_parse_json");
178
179
while ((ch = getopt(argc, argv, "cv")) != -1) {
180
switch (ch) {
181
case 'c':
182
cat = true;
183
break;
184
case 'v':
185
/* ignored */
186
break;
187
default:
188
usage();
189
}
190
}
191
argc -= optind;
192
argv += optind;
193
194
if (argc < 1)
195
usage();
196
197
for (i = 0; i < argc; i++) {
198
struct eventlog_json_object *root;
199
struct json_container jsonc;
200
const char *infile = argv[i];
201
const char *outfile = argv[i];
202
const char *cp;
203
char pathbuf[PATH_MAX];
204
FILE *infp = NULL;
205
FILE *outfp = NULL;
206
207
ntests++;
208
209
if (!sudo_json_init(&jsonc, 4, false, true, true)) {
210
errors++;
211
continue;
212
}
213
214
/* Parse input file. */
215
if ((infp = fopen(infile, "r")) == NULL) {
216
sudo_warn("%s", argv[i]);
217
errors++;
218
continue;
219
}
220
root = eventlog_json_read(infp, infile);
221
if (root == NULL) {
222
errors++;
223
goto next;
224
}
225
226
/* Format as pretty-printed JSON */
227
if (!json_format(&jsonc, root)) {
228
errors++;
229
goto next;
230
}
231
232
/* Check for a .out.ok file in the same location as the .in file. */
233
cp = strrchr(infile, '.');
234
if (cp != NULL && strcmp(cp, ".in") == 0) {
235
snprintf(pathbuf, sizeof(pathbuf), "%.*s.out.ok",
236
(int)(cp - infile), infile);
237
if ((outfp = fopen(pathbuf, "r")) != NULL)
238
outfile = pathbuf;
239
}
240
if (outfp == NULL)
241
outfp = infp;
242
243
/* Compare output to expected output. */
244
rewind(outfp);
245
if (!compare(outfp, outfile, &jsonc))
246
errors++;
247
248
/* Write the formatted output to stdout for -c (cat) */
249
if (cat) {
250
fprintf(stdout, "{%s\n}\n", sudo_json_get_buf(&jsonc));
251
fflush(stdout);
252
}
253
254
next:
255
eventlog_json_free(root);
256
sudo_json_free(&jsonc);
257
if (infp != NULL)
258
fclose(infp);
259
if (outfp != NULL && outfp != infp)
260
fclose(outfp);
261
}
262
263
if (ntests != 0) {
264
printf("%s: %d test%s run, %d errors, %d%% success rate\n",
265
getprogname(), ntests, ntests == 1 ? "" : "s", errors,
266
(ntests - errors) * 100 / ntests);
267
}
268
269
return errors;
270
}
271
272