add json_script, a minimalistic JSON based script interpreter
[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 <libubox/avl-cmp.h>
20
21 #include "json_script.h"
22
23 struct json_call {
24         struct json_script_ctx *ctx;
25         struct blob_attr *vars;
26         unsigned int seq;
27 };
28
29 struct json_handler {
30         const char *name;
31         int (*cb)(struct json_call *call, struct blob_attr *cur);
32 };
33
34 static int json_process_expr(struct json_call *call, struct blob_attr *cur);
35 static int json_process_cmd(struct json_call *call, struct blob_attr *cur);
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
338         json_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0);
339         if (!tb[1])
340                 return -1;
341
342         return json_process_expr(call, tb[1]);
343 }
344
345 static const struct json_handler expr[] = {
346         { "eq", handle_expr_eq },
347         { "regex", handle_expr_regex },
348         { "has", handle_expr_has },
349         { "and", handle_expr_and },
350         { "or", handle_expr_or },
351         { "not", handle_expr_not },
352 };
353
354 static int
355 __json_process_type(struct json_call *call, struct blob_attr *cur,
356                     const struct json_handler *h, int n, bool *found)
357 {
358         const char *name = blobmsg_data(blobmsg_data(cur));
359         int i;
360
361         for (i = 0; i < n; i++) {
362                 if (strcmp(name, h[i].name) != 0)
363                         continue;
364
365                 *found = true;
366                 return h[i].cb(call, cur);
367         }
368
369         *found = false;
370         return -1;
371 }
372
373 static int json_process_expr(struct json_call *call, struct blob_attr *cur)
374 {
375         struct json_script_ctx *ctx = call->ctx;
376         bool found;
377         int ret;
378
379         if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
380             blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
381                 ctx->handle_error(ctx, "Unexpected element type", cur);
382                 return -1;
383         }
384
385         ret = __json_process_type(call, cur, expr, ARRAY_SIZE(expr), &found);
386         if (!found)
387                 ctx->handle_error(ctx, "Unknown expression type", cur);
388
389         return ret;
390 }
391
392 static int cmd_add_string(struct json_call *call, const char *pattern)
393 {
394         struct json_script_ctx *ctx = call->ctx;
395         char *dest, *next, *str;
396         int len = 0;
397         bool var = false;
398         char c = '%';
399
400         dest = blobmsg_alloc_string_buffer(&ctx->buf, NULL, 1);
401         next = alloca(strlen(pattern) + 1);
402         strcpy(next, pattern);
403
404         for (str = next; str; str = next) {
405                 const char *cur;
406                 char *end;
407                 int cur_len = 0;
408                 bool cur_var = var;
409
410                 end = strchr(str, '%');
411                 if (end) {
412                         *end = 0;
413                         next = end + 1;
414                         var = !var;
415                 } else {
416                         end = str + strlen(str);
417                         next = NULL;
418                 }
419
420                 if (cur_var) {
421                         if (next > str) {
422                                 cur = msg_find_var(call, str);
423                                 if (!cur)
424                                         continue;
425
426                                 cur_len = strlen(cur);
427                         } else {
428                                 cur = &c;
429                                 cur_len = 1;
430                         }
431                 } else {
432                         if (str == end)
433                                 continue;
434
435                         cur = str;
436                         cur_len = end - str;
437                 }
438
439                 dest = blobmsg_realloc_string_buffer(&ctx->buf, cur_len + 1);
440                 memcpy(dest + len, cur, cur_len);
441                 len += cur_len;
442         }
443
444         if (var)
445                 return -1;
446
447         dest[len] = 0;
448         blobmsg_add_string_buffer(&ctx->buf);
449         return 0;
450 }
451
452 static int cmd_process_strings(struct json_call *call, struct blob_attr *attr)
453 {
454         struct json_script_ctx *ctx = call->ctx;
455         struct blob_attr *cur;
456         int args = -1;
457         int rem, ret;
458         void *c;
459
460         blob_buf_init(&ctx->buf, 0);
461         c = blobmsg_open_array(&ctx->buf, NULL);
462         blobmsg_for_each_attr(cur, attr, rem) {
463                 if (args++ < 0)
464                         continue;
465
466                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
467                         ctx->handle_error(ctx, "Invalid argument in command", attr);
468                         return -1;
469                 }
470
471                 ret = cmd_add_string(call, blobmsg_data(cur));
472                 if (ret) {
473                         ctx->handle_error(ctx, "Unterminated variable reference in string", attr);
474                         return ret;
475                 }
476         }
477
478         blobmsg_close_array(&ctx->buf, c);
479
480         return 0;
481 }
482
483 static int __json_process_cmd(struct json_call *call, struct blob_attr *cur)
484 {
485         struct json_script_ctx *ctx = call->ctx;
486         const char *name;
487         bool found;
488         int ret;
489
490         if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
491             blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
492                 ctx->handle_error(ctx, "Unexpected element type", cur);
493                 return -1;
494         }
495
496         ret = __json_process_type(call, cur, cmd, ARRAY_SIZE(cmd), &found);
497         if (found)
498                 return ret;
499
500         name = blobmsg_data(blobmsg_data(cur));
501         ret = cmd_process_strings(call, cur);
502         if (ret)
503                 return ret;
504
505         ctx->handle_command(ctx, name, blob_data(ctx->buf.head), call->vars);
506
507         return 0;
508 }
509
510 static int json_process_cmd(struct json_call *call, struct blob_attr *block)
511 {
512         struct json_script_ctx *ctx = call->ctx;
513         struct blob_attr *cur;
514         int rem;
515         int ret;
516         int i = 0;
517
518         if (blobmsg_type(block) != BLOBMSG_TYPE_ARRAY) {
519                 ctx->handle_error(ctx, "Unexpected element type", block);
520                 return -1;
521         }
522
523         blobmsg_for_each_attr(cur, block, rem) {
524                 switch(blobmsg_type(cur)) {
525                 case BLOBMSG_TYPE_STRING:
526                         if (!i)
527                                 return __json_process_cmd(call, block);
528                 default:
529                         ret = json_process_cmd(call, cur);
530                         if (ret < -1)
531                                 return ret;
532                         break;
533                 }
534                 i++;
535         }
536
537         return 0;
538 }
539
540 void json_script_run(struct json_script_ctx *ctx, const char *name,
541                      struct blob_attr *vars)
542 {
543         struct json_script_file *file;
544         static unsigned int _seq = 0;
545         struct json_call call = {
546                 .ctx = ctx,
547                 .vars = vars,
548                 .seq = ++_seq,
549         };
550
551         /* overflow */
552         if (!call.seq)
553                 call.seq = ++_seq;
554
555         file = json_script_get_file(ctx, name);
556         if (!file)
557                 return;
558
559         __json_script_run(&call, file, NULL);
560 }
561
562 static void __json_script_file_free(struct json_script_ctx *ctx, struct json_script_file *f)
563 {
564         struct json_script_file *next;
565
566         for (next = f->next; f; f = next, next = f->next)
567                 free(f);
568 }
569
570 void
571 json_script_free(struct json_script_ctx *ctx)
572 {
573         struct json_script_file *f, *next;
574
575         avl_remove_all_elements(&ctx->files, f, avl, next)
576                 __json_script_file_free(ctx, f);
577
578         blob_buf_free(&ctx->buf);
579 }
580
581 static void
582 __default_handle_error(struct json_script_ctx *ctx, const char *msg,
583                        struct blob_attr *context)
584 {
585 }
586
587 static const char *
588 __default_handle_var(struct json_script_ctx *ctx, const char *name,
589                      struct blob_attr *vars)
590 {
591         return NULL;
592 }
593
594 static int
595 __default_handle_expr(struct json_script_ctx *ctx, const char *name,
596                       struct blob_attr *expr, struct blob_attr *vars)
597 {
598         return -1;
599 }
600
601 static struct json_script_file *
602 __default_handle_file(struct json_script_ctx *ctx, const char *name)
603 {
604         return NULL;
605 }
606
607 void json_script_init(struct json_script_ctx *ctx)
608 {
609         avl_init(&ctx->files, avl_strcmp, false, NULL);
610
611         if (!ctx->handle_error)
612                 ctx->handle_error = __default_handle_error;
613
614         if (!ctx->handle_var)
615                 ctx->handle_var = __default_handle_var;
616
617         if (!ctx->handle_expr)
618                 ctx->handle_expr = __default_handle_expr;
619
620         if (!ctx->handle_file)
621                 ctx->handle_file = __default_handle_file;
622 }