uloop: ignore SIGPIPE by default
[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                 switch(blobmsg_type(cur)) {
545                 case BLOBMSG_TYPE_STRING:
546                         if (!i)
547                                 return __json_process_cmd(call, block);
548                 default:
549                         ret = json_process_cmd(call, cur);
550                         if (ret < -1)
551                                 return ret;
552                         break;
553                 }
554                 i++;
555         }
556
557         return 0;
558 }
559
560 void json_script_run_file(struct json_script_ctx *ctx, struct json_script_file *file,
561                           struct blob_attr *vars)
562 {
563         static unsigned int _seq = 0;
564         struct json_call call = {
565                 .ctx = ctx,
566                 .vars = vars,
567                 .seq = ++_seq,
568         };
569
570         /* overflow */
571         if (!call.seq)
572                 call.seq = ++_seq;
573
574         __json_script_run(&call, file, NULL);
575 }
576
577 void json_script_run(struct json_script_ctx *ctx, const char *name,
578                      struct blob_attr *vars)
579 {
580         struct json_script_file *file;
581
582         file = json_script_get_file(ctx, name);
583         if (!file)
584                 return;
585
586         json_script_run_file(ctx, file, vars);
587 }
588
589 static void __json_script_file_free(struct json_script_file *f)
590 {
591         struct json_script_file *next;
592
593         if (!f)
594                 return;
595
596         next = f->next;
597         free(f);
598
599         __json_script_file_free(next);
600 }
601
602 void
603 json_script_free(struct json_script_ctx *ctx)
604 {
605         struct json_script_file *f, *next;
606
607         avl_remove_all_elements(&ctx->files, f, avl, next)
608                 __json_script_file_free(f);
609
610         blob_buf_free(&ctx->buf);
611 }
612
613 static void
614 __default_handle_error(struct json_script_ctx *ctx, const char *msg,
615                        struct blob_attr *context)
616 {
617 }
618
619 static const char *
620 __default_handle_var(struct json_script_ctx *ctx, const char *name,
621                      struct blob_attr *vars)
622 {
623         return NULL;
624 }
625
626 static int
627 __default_handle_expr(struct json_script_ctx *ctx, const char *name,
628                       struct blob_attr *expr, struct blob_attr *vars)
629 {
630         return -1;
631 }
632
633 static struct json_script_file *
634 __default_handle_file(struct json_script_ctx *ctx, const char *name)
635 {
636         return NULL;
637 }
638
639 void json_script_init(struct json_script_ctx *ctx)
640 {
641         avl_init(&ctx->files, avl_strcmp, false, NULL);
642
643         if (!ctx->handle_error)
644                 ctx->handle_error = __default_handle_error;
645
646         if (!ctx->handle_var)
647                 ctx->handle_var = __default_handle_var;
648
649         if (!ctx->handle_expr)
650                 ctx->handle_expr = __default_handle_expr;
651
652         if (!ctx->handle_file)
653                 ctx->handle_file = __default_handle_file;
654 }