Line data Source code
1 : /*
2 : START OF LICENSE STUB
3 : DeDOS: Declarative Dispersion-Oriented Software
4 : Copyright (C) 2017 University of Pennsylvania, Georgetown University
5 :
6 : This program is free software: you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : END OF LICENSE STUB
19 : */
20 : /**
21 : * @file jsmn_parser.c
22 : *
23 : * General-purpose function to interact with JSMN library, and create
24 : * objects (potentially with circular references) from the parsed
25 : * json.
26 : */
27 :
28 : #include "jsmn.h"
29 : #include "jsmn_parser.h"
30 : #include "logging.h"
31 : #include <string.h>
32 : #include <strings.h>
33 :
34 : /** Destructively extracts an int from a jsmn token.
35 : * Sets the "end" char to \0, and converts converts the resulting
36 : * string to an integer
37 : *
38 : * @param tok - JSMN token to extract
39 : * @param j - original json string
40 : * @returns - integer
41 : */
42 0 : int tok_to_int(jsmntok_t *tok, char *j){
43 0 : j[tok->end] = '\0';
44 0 : return atoi(&j[tok->start]);
45 : }
46 :
47 : /** Destructively extracts an int from a jsmn token.
48 : * Sets the "end" char to \0, and converts converts the resulting
49 : * string to a long integer
50 : *
51 : * @param tok - JSMN token to extract
52 : * @param j - original json string
53 : * @returns - long
54 : */
55 0 : long tok_to_long(jsmntok_t *tok, char *j){
56 0 : j[tok->end] = '\0';
57 0 : return atol(&j[tok->start]);
58 : }
59 :
60 :
61 : /** Destructively extracts a c-string from a jsmn token.
62 : * Sets the "end" char to \0, and returns a pointer
63 : * to the start
64 : *
65 : * @param tok - JSMN token to extract
66 : * @param j - original json string
67 : * @returns - null-terminated char *
68 : */
69 0 : char *tok_to_str(jsmntok_t *tok, char *j){
70 0 : j[tok->end] = '\0';
71 0 : return &j[tok->start];
72 : }
73 :
74 : /**
75 : * Global key mapping, giving which function should be called for each JSMN key
76 : */
77 : static struct key_mapping *jsmn_key_map;
78 :
79 : /**
80 : * Retries tokens which have been deferred
81 : */
82 : static int retry_saved(struct json_state **saved, char *j);
83 :
84 : /**
85 : * The object which is pased into the JSON parser to be filled
86 : */
87 : static void * global_obj;
88 :
89 : /**
90 : * Returns the initial object passed into the JSON parser
91 : */
92 0 : void *get_root_jsmn_obj(){
93 0 : return global_obj;
94 : }
95 :
96 0 : int jsmn_ignore_list(jsmntok_t **tok, char *j, struct json_state *in, struct json_state **saved){
97 0 : int size = (*tok)->size;
98 : log(LOG_JSMN_PARSING, "Ignoring list of size %d", size);
99 0 : for (int i=0; i<size; i++){
100 0 : ++(*tok); // Move to the next value
101 0 : jsmn_ignore(tok, j, in, saved);
102 : }
103 0 : return 0;
104 : }
105 :
106 0 : int jsmn_ignore_obj(jsmntok_t **tok, char *j, struct json_state *in, struct json_state **saved){
107 0 : int size = (*tok)->size;
108 : log(LOG_JSMN_PARSING, "Ignoring object of size %d", size);
109 0 : for (int i=0; i<size; i++) {
110 0 : ++(*tok); // Ignore the key
111 0 : ++(*tok); // Move to the next value
112 0 : jsmn_ignore(tok, j, in, saved);
113 : }
114 0 : return 0;
115 : }
116 :
117 : /**
118 : * Ignores a JSMN value
119 : */
120 0 : int jsmn_ignore(jsmntok_t **tok, char *j, struct json_state *in, struct json_state **saved){
121 0 : switch ( (*tok)->type ) {
122 : case JSMN_OBJECT:
123 0 : jsmn_ignore_obj(tok, j, in, saved);
124 0 : break;
125 : case JSMN_ARRAY:
126 0 : jsmn_ignore_list(tok, j, in, saved);
127 0 : break;
128 : default:
129 0 : break;
130 : }
131 0 : return 0;
132 : }
133 :
134 :
135 0 : int parse_file_into_obj(const char *filename, void *obj, struct key_mapping *km){
136 : // Set the global key map
137 0 : jsmn_key_map = km;
138 :
139 0 : FILE *file = fopen(filename, "r");
140 :
141 0 : if (file == NULL) {
142 0 : log_perror("Error opening %s", filename);
143 0 : return -1;
144 : }
145 :
146 : // Get the size of the file
147 0 : fseek(file, 0, SEEK_END);
148 0 : long fsize = ftell(file);
149 0 : rewind(file);
150 :
151 : // Read the file
152 0 : char *contents = malloc(fsize + 1);
153 0 : size_t rtn = fread(contents, fsize, 1, file);
154 0 : if (rtn == 0) {
155 0 : log_error("Could not read from file");
156 0 : return -1;
157 : }
158 0 : contents[fsize] = '\0';
159 0 : fclose(file);
160 :
161 0 : rtn = parse_str_into_obj(contents, obj, km);
162 0 : free(contents);
163 0 : return rtn;
164 : }
165 :
166 : /** The maximum number of times that a #PARSE_FN can return `1` before
167 : * an error is raised */
168 : #define MAX_RETRIES 1024
169 :
170 :
171 0 : int parse_str_into_obj(char *contents, void *obj, struct key_mapping *km) {
172 :
173 : // Initialize the JSON parser
174 : jsmn_parser parser;
175 0 : jsmn_init(&parser);
176 : // Get the number of necessary tokens
177 0 : int n_tokens = jsmn_parse(&parser, contents, strlen(contents), NULL, 16384);
178 : log(LOG_JSMN_PARSING, "Allocating %d tokens", n_tokens);
179 0 : jsmntok_t *tokens = malloc(n_tokens * sizeof(*tokens));
180 0 : jsmntok_t *tok_orig = tokens;
181 : // Parse the JSON
182 0 : jsmn_init(&parser);
183 0 : n_tokens = jsmn_parse(&parser, contents, strlen(contents), tokens, n_tokens);
184 : log(LOG_JSMN_PARSING, "Allocated %d tokens", n_tokens);
185 :
186 : // Set the top-level object so it can be retrieved elsewhere
187 0 : global_obj = obj;
188 :
189 : // Set the initial state for the first object
190 : // (type of 0 should indicate the root object)
191 0 : struct json_state init_state = {
192 : .parent_type = 0,
193 : .data = obj
194 : };
195 0 : struct json_state *saved_state = NULL;
196 :
197 : // Parse the jsmn tokens (assumes top-level item is an object)
198 0 : int rtn = parse_jsmn_obj(&tokens, contents, &init_state, &saved_state);
199 :
200 0 : if (rtn < 0){
201 0 : log_error("Error parsing JSMN");
202 0 : return -1;
203 : }
204 :
205 : log(LOG_JSMN_PARSING, "Parsed %d top-level objects", rtn);
206 :
207 0 : int n_retries = 0;
208 : // Go back through and retry each of the tokens that couldn't be parsed
209 : // the first time around
210 0 : while (saved_state != NULL){
211 : log(LOG_JSMN_PARSING, "Retrying saved states");
212 0 : rtn = retry_saved(&saved_state, contents);
213 0 : if (rtn < 0){
214 0 : log_error("Failed to re-interpret saved JSMN tokens");
215 0 : free(tok_orig);
216 0 : return -1;
217 : }
218 0 : n_retries++;
219 0 : if (n_retries > 100) {
220 0 : log_error("Something is wrong. Retried too many times");
221 0 : free(tok_orig);
222 0 : return -1;
223 : }
224 : }
225 0 : free(tok_orig);
226 0 : return 0;
227 : }
228 :
229 : /** Returns the function to parse a given key.
230 : *
231 : * @param key - the JSON key to parse
232 : * @param parent_type - the type of object in which this key is located
233 : * @return reference to function for parsing this key
234 : */
235 0 : static jsmn_parsing_fn get_parse_fn(char *key, int parent_type){
236 :
237 0 : for (struct key_mapping *km = jsmn_key_map;
238 0 : km->parse != 0; km++){
239 0 : if (strncasecmp(key, km->key, strlen(km->key)) == 0 &&
240 0 : km->parent_type == parent_type){
241 0 : return km->parse;
242 : }
243 : }
244 0 : return NULL;
245 : }
246 :
247 : /**
248 : * Retries the states that failed to parse the first time
249 : * due to dependencies.
250 : *
251 : * @param saved_states - the states which failed to parse the first time
252 : * (returned 1)
253 : * @param j - the entire JSON string
254 : * @return 0 if at least one saved state was parsed, -1 otherwise
255 : */
256 0 : static int retry_saved(struct json_state **saved_states, char *j){
257 0 : struct json_state *prev = NULL;
258 0 : struct json_state *saved = *saved_states;
259 :
260 0 : int success = -1;
261 0 : char *key = NULL;
262 0 : int n_retries = 0;
263 0 : while (saved != NULL){
264 0 : jsmntok_t *tok = saved->tok;
265 0 : key = tok_to_str(tok, j);
266 : log(LOG_JSMN_PARSING, "Retrying key: %s", key);
267 :
268 0 : jsmn_parsing_fn parser = get_parse_fn(key, saved->parent_type);
269 0 : tok += 1;
270 0 : int rtn = parser(&tok, j, saved, saved_states);
271 :
272 0 : if (rtn < 0){
273 0 : log_error("Error retrying key %s", key);
274 0 : return -1;
275 0 : } else if (rtn == 0){
276 : log(LOG_JSMN_PARSING, "Successfully parsed retried key '%s'", key);
277 0 : success = 0;
278 0 : if (*saved_states == saved)
279 0 : *saved_states = saved->next;
280 0 : if (prev != NULL) {
281 0 : prev->next = saved->next;
282 : }
283 0 : struct json_state *tmp = saved;
284 0 : saved = saved->next;
285 0 : free(tmp);
286 0 : } else if (rtn == 1){
287 : log(LOG_JSMN_PARSING, "Will try again on the next pass...");
288 0 : prev = saved;
289 0 : saved = saved->next;
290 : } else {
291 0 : log_error("Unknown return code: %d", rtn);
292 0 : return -1;
293 : }
294 0 : if (n_retries++ > MAX_RETRIES) {
295 0 : log_error("Retried too many times. Something is wrong");
296 0 : return -1;
297 : }
298 : }
299 :
300 0 : if (success == -1 && key != NULL) {
301 0 : log_error("Failed while reprocessing key '%s' for missing dependencies", key);
302 0 : log_error("Value for improper key %s", key + (strlen(key) + 1));
303 : }
304 0 : return success;
305 : }
306 :
307 : /** Parses an array of JSMN objects.
308 : * Calls init() for each new object, then passes the returned json_state
309 : * to the next parsed object.
310 : *
311 : * @param tok - the current jsmn token to parse
312 : * @param j - the entire json string
313 : * @param state - the current state of parsing
314 : * @param saved - list of states to be re-interpreted laster
315 : * @param init - initialization function, returning a `struct json_state`
316 : * @return 0 on success, -1 on failure
317 : */
318 0 : int parse_jsmn_obj_list(jsmntok_t **tok, char *j, struct json_state *state,
319 : struct json_state **saved, jsmn_initializer init){
320 0 : ASSERT_JSMN_TYPE(*tok, JSMN_ARRAY, j);
321 0 : int size = (*tok)->size;
322 :
323 : // Advance to the object inside the list
324 0 : ++(*tok);
325 0 : for (int i=0; i<size; i++){
326 : log(LOG_JSMN_PARSING, "Parsing list item %d", i);
327 : // This is only for lists of OBJECTS.
328 0 : ASSERT_JSMN_TYPE(*tok, JSMN_OBJECT, j);
329 :
330 : // Construct the new state to be passed to the next object
331 0 : struct json_state new_state = init(state, i);
332 0 : new_state.tok = *tok;
333 0 : new_state.next = NULL;
334 :
335 : // Parse the next object in the list
336 0 : int n_tokens = parse_jsmn_obj(tok, j, &new_state, saved);
337 :
338 : // Advance to the subsequent object
339 0 : ++(*tok);
340 0 : if (n_tokens < 0){
341 0 : return -1;
342 : }
343 : }
344 : // parse_jsmn_obj handles advancing to the next object, so we
345 : // want to rewind here so as not to advance twice
346 0 : --(*tok);
347 0 : return 0;
348 : }
349 :
350 : /** Parses a single JSMN object utilizing the provided (global) key map.
351 : *
352 : * @param tok - pointer to current token to be parsed (advances automatically)
353 : * @param j - entire json string being parsed
354 : * @param state - curernt state of parsing, including the data structure being parsed into
355 : * @param saved - JSON entries on which parsing has been deferred for later
356 : *
357 : */
358 0 : int parse_jsmn_obj(jsmntok_t **tok, char *j, struct json_state *state,
359 : struct json_state **saved){
360 0 : ASSERT_JSMN_TYPE(*tok, JSMN_OBJECT, j);
361 :
362 0 : int size = (*tok)->size;
363 :
364 0 : ++(*tok);
365 : //tpok+=1;
366 0 : int parent_type = state->parent_type;
367 :
368 0 : for (int i=0; i<size; i++){
369 0 : ASSERT_JSMN_TYPE(*tok, JSMN_STRING, j);
370 :
371 0 : char *key = tok_to_str(*tok, j);
372 :
373 0 : jsmn_parsing_fn parse = get_parse_fn(key, parent_type);
374 0 : if (parse == NULL){
375 0 : log_error("No matching key %s for type %d",
376 : tok_to_str(*tok, j), parent_type);
377 0 : return -1;
378 : }
379 :
380 0 : jsmntok_t *pre_tok = *tok;
381 0 : void *pre_data = state->data;
382 0 : state->tok = ++(*tok);
383 0 : int rtn = parse(tok, j, state, saved);
384 :
385 0 : if (rtn < 0){
386 0 : log_error("Error parsing token at %s", &j[pre_tok->start]);
387 0 : return -1;
388 0 : } else if (rtn > 0){
389 : log(LOG_JSMN_PARSING, "Deferring parsing of %s", key);
390 0 : struct json_state *to_save = malloc(sizeof(*to_save));
391 0 : to_save->data = pre_data;
392 0 : to_save->parent_type = parent_type;
393 0 : to_save->next = *saved;
394 0 : to_save->tok = pre_tok;
395 0 : *saved = to_save;
396 : } else {
397 : log(LOG_JSMN_PARSING, "Successfully parsed %s", key);
398 : }
399 0 : ++(*tok);
400 : }
401 0 : --(*tok);
402 0 : return size;
403 : }
|