uloop: revert signalfd support for now
[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 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 int ret;
338
339 json_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0);
340 if (!tb[1])
341 return -1;
342
343 ret = json_process_expr(call, tb[1]);
344 if (ret < 0)
345 return ret;
346 return !ret;
347 }
348
349 static int handle_expr_isdir(struct json_call *call, struct blob_attr *expr)
350 {
351 static struct blob_buf b;
352 struct blob_attr *tb[3];
353 const char *pattern, *path;
354 struct stat s;
355 int ret;
356
357 json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
358 if (!tb[1] || blobmsg_type(tb[1]) != BLOBMSG_TYPE_STRING)
359 return -1;
360 pattern = blobmsg_data(tb[1]);
361
362 blob_buf_init(&b, 0);
363 ret = eval_string(call, &b, NULL, pattern);
364 if (ret < 0)
365 return ret;
366 path = blobmsg_data(blob_data(b.head));
367 ret = stat(path, &s);
368 if (ret < 0)
369 return 0;
370 return S_ISDIR(s.st_mode);
371 }
372
373 static const struct json_handler expr[] = {
374 { "eq", handle_expr_eq },
375 { "regex", handle_expr_regex },
376 { "has", handle_expr_has },
377 { "and", handle_expr_and },
378 { "or", handle_expr_or },
379 { "not", handle_expr_not },
380 { "isdir", handle_expr_isdir },
381 };
382
383 static int
384 __json_process_type(struct json_call *call, struct blob_attr *cur,
385 const struct json_handler *h, int n, bool *found)
386 {
387 const char *name = blobmsg_data(blobmsg_data(cur));
388 int i;
389
390 for (i = 0; i < n; i++) {
391 if (strcmp(name, h[i].name) != 0)
392 continue;
393
394 *found = true;
395 return h[i].cb(call, cur);
396 }
397
398 *found = false;
399 return -1;
400 }
401
402 static int json_process_expr(struct json_call *call, struct blob_attr *cur)
403 {
404 struct json_script_ctx *ctx = call->ctx;
405 bool found;
406 int ret;
407
408 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
409 blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
410 ctx->handle_error(ctx, "Unexpected element type", cur);
411 return -1;
412 }
413
414 ret = __json_process_type(call, cur, expr, ARRAY_SIZE(expr), &found);
415 if (!found)
416 ctx->handle_error(ctx, "Unknown expression type", cur);
417
418 return ret;
419 }
420
421 static int eval_string(struct json_call *call, struct blob_buf *buf, const char *name, const char *pattern)
422 {
423 char *dest, *next, *str;
424 int len = 0;
425 bool var = false;
426 char c = '%';
427
428 dest = blobmsg_alloc_string_buffer(buf, name, 1);
429 next = alloca(strlen(pattern) + 1);
430 strcpy(next, pattern);
431
432 for (str = next; str; str = next) {
433 const char *cur;
434 char *end;
435 int cur_len = 0;
436 bool cur_var = var;
437
438 end = strchr(str, '%');
439 if (end) {
440 *end = 0;
441 next = end + 1;
442 var = !var;
443 } else {
444 end = str + strlen(str);
445 next = NULL;
446 }
447
448 if (cur_var) {
449 if (end > str) {
450 cur = msg_find_var(call, str);
451 if (!cur)
452 continue;
453
454 cur_len = strlen(cur);
455 } else {
456 cur = &c;
457 cur_len = 1;
458 }
459 } else {
460 if (str == end)
461 continue;
462
463 cur = str;
464 cur_len = end - str;
465 }
466
467 dest = blobmsg_realloc_string_buffer(buf, len + cur_len + 1);
468 memcpy(dest + len, cur, cur_len);
469 len += cur_len;
470 }
471
472 dest[len] = 0;
473 blobmsg_add_string_buffer(buf);
474
475 if (var)
476 return -1;
477
478 return 0;
479 }
480
481 static int cmd_add_string(struct json_call *call, const char *pattern)
482 {
483 return eval_string(call, &call->ctx->buf, NULL, pattern);
484 }
485
486 int json_script_eval_string(struct json_script_ctx *ctx, struct blob_attr *vars,
487 struct blob_buf *buf, const char *name,
488 const char *pattern)
489 {
490 struct json_call call = {
491 .ctx = ctx,
492 .vars = vars,
493 };
494
495 return eval_string(&call, buf, name, pattern);
496 }
497
498 static int cmd_process_strings(struct json_call *call, struct blob_attr *attr)
499 {
500 struct json_script_ctx *ctx = call->ctx;
501 struct blob_attr *cur;
502 int args = -1;
503 int rem, ret;
504 void *c;
505
506 blob_buf_init(&ctx->buf, 0);
507 c = blobmsg_open_array(&ctx->buf, NULL);
508 blobmsg_for_each_attr(cur, attr, rem) {
509 if (args++ < 0)
510 continue;
511
512 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
513 blobmsg_add_blob(&ctx->buf, cur);
514 continue;
515 }
516
517 ret = cmd_add_string(call, blobmsg_data(cur));
518 if (ret) {
519 ctx->handle_error(ctx, "Unterminated variable reference in string", attr);
520 return ret;
521 }
522 }
523
524 blobmsg_close_array(&ctx->buf, c);
525
526 return 0;
527 }
528
529 static int __json_process_cmd(struct json_call *call, struct blob_attr *cur)
530 {
531 struct json_script_ctx *ctx = call->ctx;
532 const char *name;
533 bool found;
534 int ret;
535
536 if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
537 blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
538 ctx->handle_error(ctx, "Unexpected element type", cur);
539 return -1;
540 }
541
542 ret = __json_process_type(call, cur, cmd, ARRAY_SIZE(cmd), &found);
543 if (found)
544 return ret;
545
546 name = blobmsg_data(blobmsg_data(cur));
547 ret = cmd_process_strings(call, cur);
548 if (ret)
549 return ret;
550
551 ctx->handle_command(ctx, name, blob_data(ctx->buf.head), call->vars);
552
553 return 0;
554 }
555
556 static int json_process_cmd(struct json_call *call, struct blob_attr *block)
557 {
558 struct json_script_ctx *ctx = call->ctx;
559 struct blob_attr *cur;
560 int rem;
561 int ret;
562 int i = 0;
563
564 if (blobmsg_type(block) != BLOBMSG_TYPE_ARRAY) {
565 ctx->handle_error(ctx, "Unexpected element type", block);
566 return -1;
567 }
568
569 blobmsg_for_each_attr(cur, block, rem) {
570 if (ctx->abort)
571 break;
572
573 switch(blobmsg_type(cur)) {
574 case BLOBMSG_TYPE_STRING:
575 if (!i)
576 return __json_process_cmd(call, block);
577 default:
578 ret = json_process_cmd(call, cur);
579 if (ret < -1)
580 return ret;
581 break;
582 }
583 i++;
584 }
585
586 return 0;
587 }
588
589 void json_script_run_file(struct json_script_ctx *ctx, struct json_script_file *file,
590 struct blob_attr *vars)
591 {
592 static unsigned int _seq = 0;
593 struct json_call call = {
594 .ctx = ctx,
595 .vars = vars,
596 .seq = ++_seq,
597 };
598
599 /* overflow */
600 if (!call.seq)
601 call.seq = ++_seq;
602
603 ctx->abort = false;
604
605 __json_script_run(&call, file, NULL);
606 }
607
608 void json_script_run(struct json_script_ctx *ctx, const char *name,
609 struct blob_attr *vars)
610 {
611 struct json_script_file *file;
612
613 file = json_script_get_file(ctx, name);
614 if (!file)
615 return;
616
617 json_script_run_file(ctx, file, vars);
618 }
619
620 static void __json_script_file_free(struct json_script_file *f)
621 {
622 struct json_script_file *next;
623
624 if (!f)
625 return;
626
627 next = f->next;
628 free(f);
629
630 __json_script_file_free(next);
631 }
632
633 void
634 json_script_free(struct json_script_ctx *ctx)
635 {
636 struct json_script_file *f, *next;
637
638 avl_remove_all_elements(&ctx->files, f, avl, next)
639 __json_script_file_free(f);
640
641 blob_buf_free(&ctx->buf);
642 }
643
644 static void
645 __default_handle_error(struct json_script_ctx *ctx, const char *msg,
646 struct blob_attr *context)
647 {
648 }
649
650 static const char *
651 __default_handle_var(struct json_script_ctx *ctx, const char *name,
652 struct blob_attr *vars)
653 {
654 return NULL;
655 }
656
657 static int
658 __default_handle_expr(struct json_script_ctx *ctx, const char *name,
659 struct blob_attr *expr, struct blob_attr *vars)
660 {
661 return -1;
662 }
663
664 static struct json_script_file *
665 __default_handle_file(struct json_script_ctx *ctx, const char *name)
666 {
667 return NULL;
668 }
669
670 void json_script_init(struct json_script_ctx *ctx)
671 {
672 avl_init(&ctx->files, avl_strcmp, false, NULL);
673
674 if (!ctx->handle_error)
675 ctx->handle_error = __default_handle_error;
676
677 if (!ctx->handle_var)
678 ctx->handle_var = __default_handle_var;
679
680 if (!ctx->handle_expr)
681 ctx->handle_expr = __default_handle_expr;
682
683 if (!ctx->handle_file)
684 ctx->handle_file = __default_handle_file;
685 }