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