examples: add shunit2 tests for json_script
[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 }