add json_script, a minimalistic JSON based script interpreter
[project/libubox.git] / json_script.c
1 /*
2 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 #include <sys/stat.h>
17 #include <regex.h>
18
19 #include <libubox/avl-cmp.h>
20
21 #include "json_script.h"
22
23 struct json_call {
24 struct json_script_ctx *ctx;
25 struct blob_attr *vars;
26 unsigned int seq;
27 };
28
29 struct json_handler {
30 const char *name;
31 int (*cb)(struct json_call *call, struct blob_attr *cur);
32 };
33
34 static int json_process_expr(struct json_call *call, struct blob_attr *cur);
35 static int json_process_cmd(struct json_call *call, struct blob_attr *cur);
36
37 struct json_script_file *
38 json_script_file_from_blobmsg(const char *name, void *data, int len)
39 {
40 struct json_script_file *f;
41 char *new_name;
42 int name_len = 0;
43
44 if (name)
45 name_len = strlen(name) + 1;
46
47 f = calloc_a(sizeof(*f) + len, &new_name, name_len);
48 memcpy(f->data, data, len);
49 if (name)
50 f->avl.key = strcpy(new_name, name);
51
52 return f;
53 }
54
55 static struct json_script_file *
56 json_script_get_file(struct json_script_ctx *ctx, const char *filename)
57 {
58 struct json_script_file *f;
59
60 f = avl_find_element(&ctx->files, filename, f, avl);
61 if (f)
62 return f;
63
64 f = ctx->handle_file(ctx, filename);
65 if (!f)
66 return NULL;
67
68 avl_insert(&ctx->files, &f->avl);
69 return f;
70 }
71
72 static void __json_script_run(struct json_call *call, struct json_script_file *file,
73 struct blob_attr *context)
74 {
75 struct json_script_ctx *ctx = call->ctx;
76
77 if (file->seq == call->seq) {
78 if (context)
79 ctx->handle_error(ctx, "Recursive include", context);
80
81 return;
82 }
83
84 file->seq = call->seq;
85 while (file) {
86 json_process_cmd(call, file->data);
87 file = file->next;
88 }
89 }
90
91 const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars,
92 const char *name)
93 {
94 struct blob_attr *cur;
95 int rem;
96
97 blobmsg_for_each_attr(cur, vars, rem) {
98 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
99 continue;
100
101 if (strcmp(blobmsg_name(cur), name) != 0)
102 continue;
103
104 return blobmsg_data(cur);
105 }
106
107 return ctx->handle_var(ctx, name, vars);
108 }
109
110 static const char *
111 msg_find_var(struct json_call *call, const char *name)
112 {
113 return json_script_find_var(call->ctx, call->vars, name);
114 }
115
116 static void
117 json_get_tuple(struct blob_attr *cur, struct blob_attr **tb, int t1, int t2)
118 {
119 static struct blobmsg_policy expr_tuple[3] = {
120 { .type = BLOBMSG_TYPE_STRING },
121 {},
122 {},
123 };
124
125 expr_tuple[1].type = t1;
126 expr_tuple[2].type = t2;
127 blobmsg_parse_array(expr_tuple, 3, tb, blobmsg_data(cur), blobmsg_data_len(cur));
128 }
129
130 static int handle_if(struct json_call *call, struct blob_attr *expr)
131 {
132 struct blob_attr *tb[4];
133 int ret;
134
135 static const struct blobmsg_policy if_tuple[4] = {
136 { .type = BLOBMSG_TYPE_STRING },
137 { .type = BLOBMSG_TYPE_ARRAY },
138 { .type = BLOBMSG_TYPE_ARRAY },
139 { .type = BLOBMSG_TYPE_ARRAY },
140 };
141
142 blobmsg_parse_array(if_tuple, 4, tb, blobmsg_data(expr), blobmsg_data_len(expr));
143
144 if (!tb[1] || !tb[2])
145 return 0;
146
147 ret = json_process_expr(call, tb[1]);
148 if (ret < 0)
149 return 0;
150
151 if (ret)
152 return json_process_cmd(call, tb[2]);
153
154 if (!tb[3])
155 return 0;
156
157 return json_process_cmd(call, tb[3]);
158 }
159
160 static int handle_case(struct json_call *call, struct blob_attr *expr)
161 {
162 struct blob_attr *tb[3], *cur;
163 const char *var;
164 int rem;
165
166 json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, BLOBMSG_TYPE_TABLE);
167 if (!tb[1] || !tb[2])
168 return 0;
169
170 var = msg_find_var(call, blobmsg_data(tb[1]));
171 if (!var)
172 return 0;
173
174 blobmsg_for_each_attr(cur, tb[2], rem) {
175 if (!strcmp(var, blobmsg_name(cur)))
176 return json_process_cmd(call, cur);
177 }
178
179 return 0;
180 }
181
182 static int handle_return(struct json_call *call, struct blob_attr *expr)
183 {
184 return -2;
185 }
186
187 static int handle_include(struct json_call *call, struct blob_attr *expr)
188 {
189 struct blob_attr *tb[3];
190 struct json_script_file *f;
191
192 json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
193 if (!tb[1])
194 return 0;
195
196 f = json_script_get_file(call->ctx, blobmsg_data(tb[1]));
197 if (!f)
198 return 0;
199
200 __json_script_run(call, f, expr);
201 return 0;
202 }
203
204 static const struct json_handler cmd[] = {
205 { "if", handle_if },
206 { "case", handle_case },
207 { "return", handle_return },
208 { "include", handle_include },
209 };
210
211 static int eq_regex_cmp(const char *str, const char *pattern, bool regex)
212 {
213 regex_t reg;
214 int ret;
215
216 if (!regex)
217 return !strcmp(str, pattern);
218
219 if (regcomp(&reg, pattern, REG_EXTENDED | REG_NOSUB))
220 return 0;
221
222 ret = !regexec(&reg, str, 0, NULL, 0);
223 regfree(&reg);
224
225 return ret;
226 }
227
228 static int expr_eq_regex(struct json_call *call, struct blob_attr *expr, bool regex)
229 {
230 struct json_script_ctx *ctx = call->ctx;
231 struct blob_attr *tb[3], *cur;
232 const char *var;
233 int rem;
234
235 json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
236 if (!tb[1] || !tb[2])
237 return -1;
238
239 var = msg_find_var(call, blobmsg_data(tb[1]));
240 if (!var)
241 return 0;
242
243 switch(blobmsg_type(tb[2])) {
244 case BLOBMSG_TYPE_STRING:
245 return eq_regex_cmp(var, blobmsg_data(tb[2]), regex);
246 case BLOBMSG_TYPE_ARRAY:
247 blobmsg_for_each_attr(cur, tb[2], rem) {
248 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
249 ctx->handle_error(ctx, "Unexpected element type", cur);
250 return -1;
251 }
252
253 if (eq_regex_cmp(var, blobmsg_data(cur), regex))
254 return 1;
255 }
256 return 0;
257 default:
258 ctx->handle_error(ctx, "Unexpected element type", tb[2]);
259 return -1;
260 }
261 }
262
263 static int handle_expr_eq(struct json_call *call, struct blob_attr *expr)
264 {
265 return expr_eq_regex(call, expr, false);
266 }
267
268 static int handle_expr_regex(struct json_call *call, struct blob_attr *expr)
269 {
270 return expr_eq_regex(call, expr, true);
271 }
272
273 static int handle_expr_has(struct json_call *call, struct blob_attr *expr)
274 {
275 struct json_script_ctx *ctx = call->ctx;
276 struct blob_attr *tb[3], *cur;
277 int rem;
278
279 json_get_tuple(expr, tb, 0, 0);
280 if (!tb[1])
281 return -1;
282
283 switch(blobmsg_type(tb[1])) {
284 case BLOBMSG_TYPE_STRING:
285 return !!msg_find_var(call, blobmsg_data(tb[1]));
286 case BLOBMSG_TYPE_ARRAY:
287 blobmsg_for_each_attr(cur, tb[1], rem) {
288 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
289 ctx->handle_error(ctx, "Unexpected element type", cur);
290 return -1;
291 }
292
293 if (msg_find_var(call, blobmsg_data(cur)))
294 return 1;
295 }
296 return 0;
297 default:
298 ctx->handle_error(ctx, "Unexpected element type", tb[1]);
299 return -1;
300 }
301 }
302
303 static int expr_and_or(struct json_call *call, struct blob_attr *expr, bool and)
304 {
305 struct blob_attr *cur;
306 int ret, rem;
307 int i = 0;
308
309 blobmsg_for_each_attr(cur, expr, rem) {
310 if (i++ < 1)
311 continue;
312
313 ret = json_process_expr(call, cur);
314 if (ret < 0)
315 return ret;
316
317 if (ret != and)
318 return ret;
319 }
320
321 return and;
322 }
323
324 static int handle_expr_and(struct json_call *call, struct blob_attr *expr)
325 {
326 return expr_and_or(call, expr, 1);
327 }
328
329 static int handle_expr_or(struct json_call *call, struct blob_attr *expr)
330 {
331 return expr_and_or(call, expr, 0);
332 }
333
334 static int handle_expr_not(struct json_call *call, struct blob_attr *expr)
335 {
336 struct blob_attr *tb[3];
337
338 json_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0);
339 if (!tb[1])
340 return -1;
341
342 return json_process_expr(call, tb[1]);
343 }
344
345 static const struct json_handler expr[] = {
346 { "eq", handle_expr_eq },
347 { "regex", handle_expr_regex },
348 { "has", handle_expr_has },
349 { "and", handle_expr_and },
350 { "or", handle_expr_or },
351 { "not", handle_expr_not },
352 };
353
354 static int
355 __json_process_type(struct json_call *call, struct blob_attr *cur,
356 const struct json_handler *h, int n, bool *found)
357 {
358 const char *name = blobmsg_data(blobmsg_data(cur));
359 int i;
360
361 for (i = 0; i < n; i++) {
362 if (strcmp(name, h[i].name) != 0)
363 continue;
364
365 *found = true;
366 return h[i].cb(call, cur);
367 }
368
369 *found = false;
370 return -1;
371 }
372
373 static int json_process_expr(struct json_call *call, struct blob_attr *cur)
374 {
375 struct json_script_ctx *ctx = call->ctx;
376 bool found;
377 int ret;
378
379 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
380 blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
381 ctx->handle_error(ctx, "Unexpected element type", cur);
382 return -1;
383 }
384
385 ret = __json_process_type(call, cur, expr, ARRAY_SIZE(expr), &found);
386 if (!found)
387 ctx->handle_error(ctx, "Unknown expression type", cur);
388
389 return ret;
390 }
391
392 static int cmd_add_string(struct json_call *call, const char *pattern)
393 {
394 struct json_script_ctx *ctx = call->ctx;
395 char *dest, *next, *str;
396 int len = 0;
397 bool var = false;
398 char c = '%';
399
400 dest = blobmsg_alloc_string_buffer(&ctx->buf, NULL, 1);
401 next = alloca(strlen(pattern) + 1);
402 strcpy(next, pattern);
403
404 for (str = next; str; str = next) {
405 const char *cur;
406 char *end;
407 int cur_len = 0;
408 bool cur_var = var;
409
410 end = strchr(str, '%');
411 if (end) {
412 *end = 0;
413 next = end + 1;
414 var = !var;
415 } else {
416 end = str + strlen(str);
417 next = NULL;
418 }
419
420 if (cur_var) {
421 if (next > str) {
422 cur = msg_find_var(call, str);
423 if (!cur)
424 continue;
425
426 cur_len = strlen(cur);
427 } else {
428 cur = &c;
429 cur_len = 1;
430 }
431 } else {
432 if (str == end)
433 continue;
434
435 cur = str;
436 cur_len = end - str;
437 }
438
439 dest = blobmsg_realloc_string_buffer(&ctx->buf, cur_len + 1);
440 memcpy(dest + len, cur, cur_len);
441 len += cur_len;
442 }
443
444 if (var)
445 return -1;
446
447 dest[len] = 0;
448 blobmsg_add_string_buffer(&ctx->buf);
449 return 0;
450 }
451
452 static int cmd_process_strings(struct json_call *call, struct blob_attr *attr)
453 {
454 struct json_script_ctx *ctx = call->ctx;
455 struct blob_attr *cur;
456 int args = -1;
457 int rem, ret;
458 void *c;
459
460 blob_buf_init(&ctx->buf, 0);
461 c = blobmsg_open_array(&ctx->buf, NULL);
462 blobmsg_for_each_attr(cur, attr, rem) {
463 if (args++ < 0)
464 continue;
465
466 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
467 ctx->handle_error(ctx, "Invalid argument in command", attr);
468 return -1;
469 }
470
471 ret = cmd_add_string(call, blobmsg_data(cur));
472 if (ret) {
473 ctx->handle_error(ctx, "Unterminated variable reference in string", attr);
474 return ret;
475 }
476 }
477
478 blobmsg_close_array(&ctx->buf, c);
479
480 return 0;
481 }
482
483 static int __json_process_cmd(struct json_call *call, struct blob_attr *cur)
484 {
485 struct json_script_ctx *ctx = call->ctx;
486 const char *name;
487 bool found;
488 int ret;
489
490 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
491 blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
492 ctx->handle_error(ctx, "Unexpected element type", cur);
493 return -1;
494 }
495
496 ret = __json_process_type(call, cur, cmd, ARRAY_SIZE(cmd), &found);
497 if (found)
498 return ret;
499
500 name = blobmsg_data(blobmsg_data(cur));
501 ret = cmd_process_strings(call, cur);
502 if (ret)
503 return ret;
504
505 ctx->handle_command(ctx, name, blob_data(ctx->buf.head), call->vars);
506
507 return 0;
508 }
509
510 static int json_process_cmd(struct json_call *call, struct blob_attr *block)
511 {
512 struct json_script_ctx *ctx = call->ctx;
513 struct blob_attr *cur;
514 int rem;
515 int ret;
516 int i = 0;
517
518 if (blobmsg_type(block) != BLOBMSG_TYPE_ARRAY) {
519 ctx->handle_error(ctx, "Unexpected element type", block);
520 return -1;
521 }
522
523 blobmsg_for_each_attr(cur, block, rem) {
524 switch(blobmsg_type(cur)) {
525 case BLOBMSG_TYPE_STRING:
526 if (!i)
527 return __json_process_cmd(call, block);
528 default:
529 ret = json_process_cmd(call, cur);
530 if (ret < -1)
531 return ret;
532 break;
533 }
534 i++;
535 }
536
537 return 0;
538 }
539
540 void json_script_run(struct json_script_ctx *ctx, const char *name,
541 struct blob_attr *vars)
542 {
543 struct json_script_file *file;
544 static unsigned int _seq = 0;
545 struct json_call call = {
546 .ctx = ctx,
547 .vars = vars,
548 .seq = ++_seq,
549 };
550
551 /* overflow */
552 if (!call.seq)
553 call.seq = ++_seq;
554
555 file = json_script_get_file(ctx, name);
556 if (!file)
557 return;
558
559 __json_script_run(&call, file, NULL);
560 }
561
562 static void __json_script_file_free(struct json_script_ctx *ctx, struct json_script_file *f)
563 {
564 struct json_script_file *next;
565
566 for (next = f->next; f; f = next, next = f->next)
567 free(f);
568 }
569
570 void
571 json_script_free(struct json_script_ctx *ctx)
572 {
573 struct json_script_file *f, *next;
574
575 avl_remove_all_elements(&ctx->files, f, avl, next)
576 __json_script_file_free(ctx, f);
577
578 blob_buf_free(&ctx->buf);
579 }
580
581 static void
582 __default_handle_error(struct json_script_ctx *ctx, const char *msg,
583 struct blob_attr *context)
584 {
585 }
586
587 static const char *
588 __default_handle_var(struct json_script_ctx *ctx, const char *name,
589 struct blob_attr *vars)
590 {
591 return NULL;
592 }
593
594 static int
595 __default_handle_expr(struct json_script_ctx *ctx, const char *name,
596 struct blob_attr *expr, struct blob_attr *vars)
597 {
598 return -1;
599 }
600
601 static struct json_script_file *
602 __default_handle_file(struct json_script_ctx *ctx, const char *name)
603 {
604 return NULL;
605 }
606
607 void json_script_init(struct json_script_ctx *ctx)
608 {
609 avl_init(&ctx->files, avl_strcmp, false, NULL);
610
611 if (!ctx->handle_error)
612 ctx->handle_error = __default_handle_error;
613
614 if (!ctx->handle_var)
615 ctx->handle_var = __default_handle_var;
616
617 if (!ctx->handle_expr)
618 ctx->handle_expr = __default_handle_expr;
619
620 if (!ctx->handle_file)
621 ctx->handle_file = __default_handle_file;
622 }