libubox: tests: add more blobmsg/json test cases
[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 "avl-cmp.h"
20 #include "json_script.h"
21
22 struct json_call {
23 struct json_script_ctx *ctx;
24 struct blob_attr *vars;
25 unsigned int seq;
26 };
27
28 struct json_handler {
29 const char *name;
30 int (*cb)(struct json_call *call, struct blob_attr *cur);
31 };
32
33 static int json_process_expr(struct json_call *call, struct blob_attr *cur);
34 static int json_process_cmd(struct json_call *call, struct blob_attr *cur);
35 static int eval_string(struct json_call *call, struct blob_buf *buf, const char *name, const char *pattern);
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 if (!f)
49 return NULL;
50
51 memcpy(f->data, data, len);
52 if (name)
53 f->avl.key = strcpy(new_name, name);
54
55 return f;
56 }
57
58 static struct json_script_file *
59 json_script_get_file(struct json_script_ctx *ctx, const char *filename)
60 {
61 struct json_script_file *f;
62
63 f = avl_find_element(&ctx->files, filename, f, avl);
64 if (f)
65 return f;
66
67 f = ctx->handle_file(ctx, filename);
68 if (!f)
69 return NULL;
70
71 avl_insert(&ctx->files, &f->avl);
72 return f;
73 }
74
75 static void __json_script_run(struct json_call *call, struct json_script_file *file,
76 struct blob_attr *context)
77 {
78 struct json_script_ctx *ctx = call->ctx;
79
80 if (file->seq == call->seq) {
81 if (context)
82 ctx->handle_error(ctx, "Recursive include", context);
83
84 return;
85 }
86
87 file->seq = call->seq;
88 while (file) {
89 json_process_cmd(call, file->data);
90 file = file->next;
91 }
92 }
93
94 const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars,
95 const char *name)
96 {
97 struct blob_attr *cur;
98 size_t rem;
99
100 blobmsg_for_each_attr(cur, vars, rem) {
101 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
102 continue;
103
104 if (strcmp(blobmsg_name(cur), name) != 0)
105 continue;
106
107 return blobmsg_data(cur);
108 }
109
110 return ctx->handle_var(ctx, name, vars);
111 }
112
113 static const char *
114 msg_find_var(struct json_call *call, const char *name)
115 {
116 return json_script_find_var(call->ctx, call->vars, name);
117 }
118
119 static void
120 json_get_tuple(struct blob_attr *cur, struct blob_attr **tb, int t1, int t2)
121 {
122 static struct blobmsg_policy expr_tuple[3] = {
123 { .type = BLOBMSG_TYPE_STRING },
124 {},
125 {},
126 };
127
128 expr_tuple[1].type = t1;
129 expr_tuple[2].type = t2;
130 blobmsg_parse_array(expr_tuple, 3, tb, blobmsg_data(cur), blobmsg_data_len(cur));
131 }
132
133 static int handle_if(struct json_call *call, struct blob_attr *expr)
134 {
135 struct blob_attr *tb[4];
136 int ret;
137
138 static const struct blobmsg_policy if_tuple[4] = {
139 { .type = BLOBMSG_TYPE_STRING },
140 { .type = BLOBMSG_TYPE_ARRAY },
141 { .type = BLOBMSG_TYPE_ARRAY },
142 { .type = BLOBMSG_TYPE_ARRAY },
143 };
144
145 blobmsg_parse_array(if_tuple, 4, tb, blobmsg_data(expr), blobmsg_data_len(expr));
146
147 if (!tb[1] || !tb[2])
148 return 0;
149
150 ret = json_process_expr(call, tb[1]);
151 if (ret < 0)
152 return 0;
153
154 if (ret)
155 return json_process_cmd(call, tb[2]);
156
157 if (!tb[3])
158 return 0;
159
160 return json_process_cmd(call, tb[3]);
161 }
162
163 static int handle_case(struct json_call *call, struct blob_attr *expr)
164 {
165 struct blob_attr *tb[3], *cur;
166 const char *var;
167 size_t rem;
168
169 json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, BLOBMSG_TYPE_TABLE);
170 if (!tb[1] || !tb[2])
171 return 0;
172
173 var = msg_find_var(call, blobmsg_data(tb[1]));
174 if (!var)
175 return 0;
176
177 blobmsg_for_each_attr(cur, tb[2], rem) {
178 if (!strcmp(var, blobmsg_name(cur)))
179 return json_process_cmd(call, cur);
180 }
181
182 return 0;
183 }
184
185 static int handle_return(struct json_call *call, struct blob_attr *expr)
186 {
187 return -2;
188 }
189
190 static int handle_include(struct json_call *call, struct blob_attr *expr)
191 {
192 struct blob_attr *tb[3];
193 struct json_script_file *f;
194
195 json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
196 if (!tb[1])
197 return 0;
198
199 f = json_script_get_file(call->ctx, blobmsg_data(tb[1]));
200 if (!f)
201 return 0;
202
203 __json_script_run(call, f, expr);
204 return 0;
205 }
206
207 static const struct json_handler cmd[] = {
208 { "if", handle_if },
209 { "case", handle_case },
210 { "return", handle_return },
211 { "include", handle_include },
212 };
213
214 static int eq_regex_cmp(const char *str, const char *pattern, bool regex)
215 {
216 regex_t reg;
217 int ret;
218
219 if (!regex)
220 return !strcmp(str, pattern);
221
222 if (regcomp(&reg, pattern, REG_EXTENDED | REG_NOSUB))
223 return 0;
224
225 ret = !regexec(&reg, str, 0, NULL, 0);
226 regfree(&reg);
227
228 return ret;
229 }
230
231 static int expr_eq_regex(struct json_call *call, struct blob_attr *expr, bool regex)
232 {
233 struct json_script_ctx *ctx = call->ctx;
234 struct blob_attr *tb[3], *cur;
235 const char *var;
236 size_t rem;
237
238 json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
239 if (!tb[1] || !tb[2])
240 return -1;
241
242 var = msg_find_var(call, blobmsg_data(tb[1]));
243 if (!var)
244 return 0;
245
246 switch(blobmsg_type(tb[2])) {
247 case BLOBMSG_TYPE_STRING:
248 return eq_regex_cmp(var, blobmsg_data(tb[2]), regex);
249 case BLOBMSG_TYPE_ARRAY:
250 blobmsg_for_each_attr(cur, tb[2], rem) {
251 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
252 ctx->handle_error(ctx, "Unexpected element type", cur);
253 return -1;
254 }
255
256 if (eq_regex_cmp(var, blobmsg_data(cur), regex))
257 return 1;
258 }
259 return 0;
260 default:
261 ctx->handle_error(ctx, "Unexpected element type", tb[2]);
262 return -1;
263 }
264 }
265
266 static int handle_expr_eq(struct json_call *call, struct blob_attr *expr)
267 {
268 return expr_eq_regex(call, expr, false);
269 }
270
271 static int handle_expr_regex(struct json_call *call, struct blob_attr *expr)
272 {
273 return expr_eq_regex(call, expr, true);
274 }
275
276 static int handle_expr_has(struct json_call *call, struct blob_attr *expr)
277 {
278 struct json_script_ctx *ctx = call->ctx;
279 struct blob_attr *tb[3], *cur;
280 size_t rem;
281
282 json_get_tuple(expr, tb, 0, 0);
283 if (!tb[1])
284 return -1;
285
286 switch(blobmsg_type(tb[1])) {
287 case BLOBMSG_TYPE_STRING:
288 return !!msg_find_var(call, blobmsg_data(tb[1]));
289 case BLOBMSG_TYPE_ARRAY:
290 blobmsg_for_each_attr(cur, tb[1], rem) {
291 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
292 ctx->handle_error(ctx, "Unexpected element type", cur);
293 return -1;
294 }
295
296 if (msg_find_var(call, blobmsg_data(cur)))
297 return 1;
298 }
299 return 0;
300 default:
301 ctx->handle_error(ctx, "Unexpected element type", tb[1]);
302 return -1;
303 }
304 }
305
306 static int expr_and_or(struct json_call *call, struct blob_attr *expr, bool and)
307 {
308 struct blob_attr *cur;
309 int ret;
310 size_t rem;
311 int i = 0;
312
313 blobmsg_for_each_attr(cur, expr, rem) {
314 if (i++ < 1)
315 continue;
316
317 ret = json_process_expr(call, cur);
318 if (ret < 0)
319 return ret;
320
321 if (ret != and)
322 return ret;
323 }
324
325 return and;
326 }
327
328 static int handle_expr_and(struct json_call *call, struct blob_attr *expr)
329 {
330 return expr_and_or(call, expr, 1);
331 }
332
333 static int handle_expr_or(struct json_call *call, struct blob_attr *expr)
334 {
335 return expr_and_or(call, expr, 0);
336 }
337
338 static int handle_expr_not(struct json_call *call, struct blob_attr *expr)
339 {
340 struct blob_attr *tb[3];
341 int ret;
342
343 json_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0);
344 if (!tb[1])
345 return -1;
346
347 ret = json_process_expr(call, tb[1]);
348 if (ret < 0)
349 return ret;
350 return !ret;
351 }
352
353 static int handle_expr_isdir(struct json_call *call, struct blob_attr *expr)
354 {
355 static struct blob_buf b;
356 struct blob_attr *tb[3];
357 const char *pattern, *path;
358 struct stat s;
359 int ret;
360
361 json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
362 if (!tb[1] || blobmsg_type(tb[1]) != BLOBMSG_TYPE_STRING)
363 return -1;
364 pattern = blobmsg_data(tb[1]);
365
366 blob_buf_init(&b, 0);
367 ret = eval_string(call, &b, NULL, pattern);
368 if (ret < 0)
369 return ret;
370 path = blobmsg_data(blob_data(b.head));
371 ret = stat(path, &s);
372 if (ret < 0)
373 return 0;
374 return S_ISDIR(s.st_mode);
375 }
376
377 static const struct json_handler expr[] = {
378 { "eq", handle_expr_eq },
379 { "regex", handle_expr_regex },
380 { "has", handle_expr_has },
381 { "and", handle_expr_and },
382 { "or", handle_expr_or },
383 { "not", handle_expr_not },
384 { "isdir", handle_expr_isdir },
385 };
386
387 static int
388 __json_process_type(struct json_call *call, struct blob_attr *cur,
389 const struct json_handler *h, int n, bool *found)
390 {
391 const char *name = blobmsg_data(blobmsg_data(cur));
392 int i;
393
394 for (i = 0; i < n; i++) {
395 if (strcmp(name, h[i].name) != 0)
396 continue;
397
398 *found = true;
399 return h[i].cb(call, cur);
400 }
401
402 *found = false;
403 return -1;
404 }
405
406 static int json_process_expr(struct json_call *call, struct blob_attr *cur)
407 {
408 struct json_script_ctx *ctx = call->ctx;
409 bool found;
410 int ret;
411
412 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
413 blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
414 ctx->handle_error(ctx, "Unexpected element type", cur);
415 return -1;
416 }
417
418 ret = __json_process_type(call, cur, expr, ARRAY_SIZE(expr), &found);
419 if (!found) {
420 const char *name = blobmsg_data(blobmsg_data(cur));
421 ctx->handle_expr(ctx, name, cur, call->vars);
422 }
423
424 return ret;
425 }
426
427 static int eval_string(struct json_call *call, struct blob_buf *buf, const char *name, const char *pattern)
428 {
429 char *dest, *next, *str;
430 int len = 0;
431 bool var = false;
432 char c = '%';
433
434 dest = blobmsg_alloc_string_buffer(buf, name, 1);
435 if (!dest)
436 return -1;
437
438 next = alloca(strlen(pattern) + 1);
439 strcpy(next, pattern);
440
441 for (str = next; str; str = next) {
442 const char *cur;
443 char *end, *new_buf;
444 int cur_len = 0;
445 bool cur_var = var;
446
447 end = strchr(str, '%');
448 if (end) {
449 *end = 0;
450 next = end + 1;
451 var = !var;
452 } else {
453 end = str + strlen(str);
454 next = NULL;
455 }
456
457 if (cur_var) {
458 if (end > str) {
459 cur = msg_find_var(call, str);
460 if (!cur)
461 continue;
462
463 cur_len = strlen(cur);
464 } else {
465 cur = &c;
466 cur_len = 1;
467 }
468 } else {
469 if (str == end)
470 continue;
471
472 cur = str;
473 cur_len = end - str;
474 }
475
476 new_buf = blobmsg_realloc_string_buffer(buf, len + cur_len + 1);
477 if (!new_buf) {
478 /* Make eval_string return -1 */
479 var = true;
480 break;
481 }
482
483 dest = new_buf;
484 memcpy(dest + len, cur, cur_len);
485 len += cur_len;
486 }
487
488 dest[len] = 0;
489 blobmsg_add_string_buffer(buf);
490
491 if (var)
492 return -1;
493
494 return 0;
495 }
496
497 static int cmd_add_string(struct json_call *call, const char *pattern)
498 {
499 return eval_string(call, &call->ctx->buf, NULL, pattern);
500 }
501
502 int json_script_eval_string(struct json_script_ctx *ctx, struct blob_attr *vars,
503 struct blob_buf *buf, const char *name,
504 const char *pattern)
505 {
506 struct json_call call = {
507 .ctx = ctx,
508 .vars = vars,
509 };
510
511 return eval_string(&call, buf, name, pattern);
512 }
513
514 static int cmd_process_strings(struct json_call *call, struct blob_attr *attr)
515 {
516 struct json_script_ctx *ctx = call->ctx;
517 struct blob_attr *cur;
518 int args = -1;
519 int ret;
520 size_t rem;
521 void *c;
522
523 blob_buf_init(&ctx->buf, 0);
524 c = blobmsg_open_array(&ctx->buf, NULL);
525 blobmsg_for_each_attr(cur, attr, rem) {
526 if (args++ < 0)
527 continue;
528
529 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
530 blobmsg_add_blob(&ctx->buf, cur);
531 continue;
532 }
533
534 ret = cmd_add_string(call, blobmsg_data(cur));
535 if (ret) {
536 ctx->handle_error(ctx, "Unterminated variable reference in string", attr);
537 return ret;
538 }
539 }
540
541 blobmsg_close_array(&ctx->buf, c);
542
543 return 0;
544 }
545
546 static int __json_process_cmd(struct json_call *call, struct blob_attr *cur)
547 {
548 struct json_script_ctx *ctx = call->ctx;
549 const char *name;
550 bool found;
551 int ret;
552
553 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
554 blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
555 ctx->handle_error(ctx, "Unexpected element type", cur);
556 return -1;
557 }
558
559 ret = __json_process_type(call, cur, cmd, ARRAY_SIZE(cmd), &found);
560 if (found)
561 return ret;
562
563 name = blobmsg_data(blobmsg_data(cur));
564 ret = cmd_process_strings(call, cur);
565 if (ret)
566 return ret;
567
568 ctx->handle_command(ctx, name, blob_data(ctx->buf.head), call->vars);
569
570 return 0;
571 }
572
573 static int json_process_cmd(struct json_call *call, struct blob_attr *block)
574 {
575 struct json_script_ctx *ctx = call->ctx;
576 struct blob_attr *cur;
577 size_t rem;
578 int ret;
579 int i = 0;
580
581 if (blobmsg_type(block) != BLOBMSG_TYPE_ARRAY) {
582 ctx->handle_error(ctx, "Unexpected element type", block);
583 return -1;
584 }
585
586 blobmsg_for_each_attr(cur, block, rem) {
587 if (ctx->abort)
588 break;
589
590 switch(blobmsg_type(cur)) {
591 case BLOBMSG_TYPE_STRING:
592 if (!i)
593 return __json_process_cmd(call, block);
594 /* fall through */
595 default:
596 ret = json_process_cmd(call, cur);
597 if (ret < -1)
598 return ret;
599 break;
600 }
601 i++;
602 }
603
604 return 0;
605 }
606
607 void json_script_run_file(struct json_script_ctx *ctx, struct json_script_file *file,
608 struct blob_attr *vars)
609 {
610 static unsigned int _seq = 0;
611 struct json_call call = {
612 .ctx = ctx,
613 .vars = vars,
614 .seq = ++_seq,
615 };
616
617 /* overflow */
618 if (!call.seq)
619 call.seq = ++_seq;
620
621 ctx->abort = false;
622
623 __json_script_run(&call, file, NULL);
624 }
625
626 void json_script_run(struct json_script_ctx *ctx, const char *name,
627 struct blob_attr *vars)
628 {
629 struct json_script_file *file;
630
631 file = json_script_get_file(ctx, name);
632 if (!file)
633 return;
634
635 json_script_run_file(ctx, file, vars);
636 }
637
638 static void __json_script_file_free(struct json_script_file *f)
639 {
640 struct json_script_file *next;
641
642 if (!f)
643 return;
644
645 next = f->next;
646 free(f);
647
648 __json_script_file_free(next);
649 }
650
651 void
652 json_script_free(struct json_script_ctx *ctx)
653 {
654 struct json_script_file *f, *next;
655
656 avl_remove_all_elements(&ctx->files, f, avl, next)
657 __json_script_file_free(f);
658
659 blob_buf_free(&ctx->buf);
660 }
661
662 static void
663 __default_handle_error(struct json_script_ctx *ctx, const char *msg,
664 struct blob_attr *context)
665 {
666 }
667
668 static const char *
669 __default_handle_var(struct json_script_ctx *ctx, const char *name,
670 struct blob_attr *vars)
671 {
672 return NULL;
673 }
674
675 static int
676 __default_handle_expr(struct json_script_ctx *ctx, const char *name,
677 struct blob_attr *expr, struct blob_attr *vars)
678 {
679 ctx->handle_error(ctx, "Unknown expression type", expr);
680 return -1;
681 }
682
683 static struct json_script_file *
684 __default_handle_file(struct json_script_ctx *ctx, const char *name)
685 {
686 return NULL;
687 }
688
689 void json_script_init(struct json_script_ctx *ctx)
690 {
691 avl_init(&ctx->files, avl_strcmp, false, NULL);
692
693 if (!ctx->handle_error)
694 ctx->handle_error = __default_handle_error;
695
696 if (!ctx->handle_var)
697 ctx->handle_var = __default_handle_var;
698
699 if (!ctx->handle_expr)
700 ctx->handle_expr = __default_handle_expr;
701
702 if (!ctx->handle_file)
703 ctx->handle_file = __default_handle_file;
704 }