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