123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- /*
- * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <sys/stat.h>
- #include <regex.h>
- #include "avl-cmp.h"
- #include "json_script.h"
- struct json_call {
- struct json_script_ctx *ctx;
- struct blob_attr *vars;
- unsigned int seq;
- };
- struct json_handler {
- const char *name;
- int (*cb)(struct json_call *call, struct blob_attr *cur);
- };
- static int json_process_expr(struct json_call *call, struct blob_attr *cur);
- static int json_process_cmd(struct json_call *call, struct blob_attr *cur);
- static int eval_string(struct json_call *call, struct blob_buf *buf, const char *name, const char *pattern);
- struct json_script_file *
- json_script_file_from_blobmsg(const char *name, void *data, int len)
- {
- struct json_script_file *f;
- char *new_name;
- int name_len = 0;
- if (name)
- name_len = strlen(name) + 1;
- f = calloc_a(sizeof(*f) + len, &new_name, name_len);
- if (!f)
- return NULL;
- memcpy(f->data, data, len);
- if (name)
- f->avl.key = strcpy(new_name, name);
- return f;
- }
- static struct json_script_file *
- json_script_get_file(struct json_script_ctx *ctx, const char *filename)
- {
- struct json_script_file *f;
- f = avl_find_element(&ctx->files, filename, f, avl);
- if (f)
- return f;
- f = ctx->handle_file(ctx, filename);
- if (!f)
- return NULL;
- avl_insert(&ctx->files, &f->avl);
- return f;
- }
- static void __json_script_run(struct json_call *call, struct json_script_file *file,
- struct blob_attr *context)
- {
- struct json_script_ctx *ctx = call->ctx;
- if (file->seq == call->seq) {
- if (context)
- ctx->handle_error(ctx, "Recursive include", context);
- return;
- }
- file->seq = call->seq;
- while (file) {
- json_process_cmd(call, file->data);
- file = file->next;
- }
- }
- const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars,
- const char *name)
- {
- struct blob_attr *cur;
- size_t rem;
- blobmsg_for_each_attr(cur, vars, rem) {
- if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
- continue;
- if (strcmp(blobmsg_name(cur), name) != 0)
- continue;
- return blobmsg_data(cur);
- }
- return ctx->handle_var(ctx, name, vars);
- }
- static const char *
- msg_find_var(struct json_call *call, const char *name)
- {
- return json_script_find_var(call->ctx, call->vars, name);
- }
- static void
- json_get_tuple(struct blob_attr *cur, struct blob_attr **tb, int t1, int t2)
- {
- static struct blobmsg_policy expr_tuple[3] = {
- { .type = BLOBMSG_TYPE_STRING },
- {},
- {},
- };
- expr_tuple[1].type = t1;
- expr_tuple[2].type = t2;
- blobmsg_parse_array(expr_tuple, 3, tb, blobmsg_data(cur), blobmsg_data_len(cur));
- }
- static int handle_if(struct json_call *call, struct blob_attr *expr)
- {
- struct blob_attr *tb[4];
- int ret;
- static const struct blobmsg_policy if_tuple[4] = {
- { .type = BLOBMSG_TYPE_STRING },
- { .type = BLOBMSG_TYPE_ARRAY },
- { .type = BLOBMSG_TYPE_ARRAY },
- { .type = BLOBMSG_TYPE_ARRAY },
- };
- blobmsg_parse_array(if_tuple, 4, tb, blobmsg_data(expr), blobmsg_data_len(expr));
- if (!tb[1] || !tb[2])
- return 0;
- ret = json_process_expr(call, tb[1]);
- if (ret < 0)
- return 0;
- if (ret)
- return json_process_cmd(call, tb[2]);
- if (!tb[3])
- return 0;
- return json_process_cmd(call, tb[3]);
- }
- static int handle_case(struct json_call *call, struct blob_attr *expr)
- {
- struct blob_attr *tb[3], *cur;
- const char *var;
- size_t rem;
- json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, BLOBMSG_TYPE_TABLE);
- if (!tb[1] || !tb[2])
- return 0;
- var = msg_find_var(call, blobmsg_data(tb[1]));
- if (!var)
- return 0;
- blobmsg_for_each_attr(cur, tb[2], rem) {
- if (!strcmp(var, blobmsg_name(cur)))
- return json_process_cmd(call, cur);
- }
- return 0;
- }
- static int handle_return(struct json_call *call, struct blob_attr *expr)
- {
- return -2;
- }
- static int handle_include(struct json_call *call, struct blob_attr *expr)
- {
- struct blob_attr *tb[3];
- struct json_script_file *f;
- json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
- if (!tb[1])
- return 0;
- f = json_script_get_file(call->ctx, blobmsg_data(tb[1]));
- if (!f)
- return 0;
- __json_script_run(call, f, expr);
- return 0;
- }
- static const struct json_handler cmd[] = {
- { "if", handle_if },
- { "case", handle_case },
- { "return", handle_return },
- { "include", handle_include },
- };
- static int eq_regex_cmp(const char *str, const char *pattern, bool regex)
- {
- regex_t reg;
- int ret;
- if (!regex)
- return !strcmp(str, pattern);
- if (regcomp(®, pattern, REG_EXTENDED | REG_NOSUB))
- return 0;
- ret = !regexec(®, str, 0, NULL, 0);
- regfree(®);
- return ret;
- }
- static int expr_eq_regex(struct json_call *call, struct blob_attr *expr, bool regex)
- {
- struct json_script_ctx *ctx = call->ctx;
- struct blob_attr *tb[3], *cur;
- const char *var;
- size_t rem;
- json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
- if (!tb[1] || !tb[2])
- return -1;
- var = msg_find_var(call, blobmsg_data(tb[1]));
- if (!var)
- return 0;
- switch(blobmsg_type(tb[2])) {
- case BLOBMSG_TYPE_STRING:
- return eq_regex_cmp(var, blobmsg_data(tb[2]), regex);
- case BLOBMSG_TYPE_ARRAY:
- blobmsg_for_each_attr(cur, tb[2], rem) {
- if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
- ctx->handle_error(ctx, "Unexpected element type", cur);
- return -1;
- }
- if (eq_regex_cmp(var, blobmsg_data(cur), regex))
- return 1;
- }
- return 0;
- default:
- ctx->handle_error(ctx, "Unexpected element type", tb[2]);
- return -1;
- }
- }
- static int handle_expr_eq(struct json_call *call, struct blob_attr *expr)
- {
- return expr_eq_regex(call, expr, false);
- }
- static int handle_expr_regex(struct json_call *call, struct blob_attr *expr)
- {
- return expr_eq_regex(call, expr, true);
- }
- static int handle_expr_has(struct json_call *call, struct blob_attr *expr)
- {
- struct json_script_ctx *ctx = call->ctx;
- struct blob_attr *tb[3], *cur;
- size_t rem;
- json_get_tuple(expr, tb, 0, 0);
- if (!tb[1])
- return -1;
- switch(blobmsg_type(tb[1])) {
- case BLOBMSG_TYPE_STRING:
- return !!msg_find_var(call, blobmsg_data(tb[1]));
- case BLOBMSG_TYPE_ARRAY:
- blobmsg_for_each_attr(cur, tb[1], rem) {
- if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
- ctx->handle_error(ctx, "Unexpected element type", cur);
- return -1;
- }
- if (msg_find_var(call, blobmsg_data(cur)))
- return 1;
- }
- return 0;
- default:
- ctx->handle_error(ctx, "Unexpected element type", tb[1]);
- return -1;
- }
- }
- static int expr_and_or(struct json_call *call, struct blob_attr *expr, bool and)
- {
- struct blob_attr *cur;
- int ret;
- size_t rem;
- int i = 0;
- blobmsg_for_each_attr(cur, expr, rem) {
- if (i++ < 1)
- continue;
- ret = json_process_expr(call, cur);
- if (ret < 0)
- return ret;
- if (ret != and)
- return ret;
- }
- return and;
- }
- static int handle_expr_and(struct json_call *call, struct blob_attr *expr)
- {
- return expr_and_or(call, expr, 1);
- }
- static int handle_expr_or(struct json_call *call, struct blob_attr *expr)
- {
- return expr_and_or(call, expr, 0);
- }
- static int handle_expr_not(struct json_call *call, struct blob_attr *expr)
- {
- struct blob_attr *tb[3];
- int ret;
- json_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0);
- if (!tb[1])
- return -1;
- ret = json_process_expr(call, tb[1]);
- if (ret < 0)
- return ret;
- return !ret;
- }
- static int handle_expr_isdir(struct json_call *call, struct blob_attr *expr)
- {
- static struct blob_buf b;
- struct blob_attr *tb[3];
- const char *pattern, *path;
- struct stat s;
- int ret;
- json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
- if (!tb[1] || blobmsg_type(tb[1]) != BLOBMSG_TYPE_STRING)
- return -1;
- pattern = blobmsg_data(tb[1]);
- blob_buf_init(&b, 0);
- ret = eval_string(call, &b, NULL, pattern);
- if (ret < 0)
- return ret;
- path = blobmsg_data(blob_data(b.head));
- ret = stat(path, &s);
- if (ret < 0)
- return 0;
- return S_ISDIR(s.st_mode);
- }
- static const struct json_handler expr[] = {
- { "eq", handle_expr_eq },
- { "regex", handle_expr_regex },
- { "has", handle_expr_has },
- { "and", handle_expr_and },
- { "or", handle_expr_or },
- { "not", handle_expr_not },
- { "isdir", handle_expr_isdir },
- };
- static int
- __json_process_type(struct json_call *call, struct blob_attr *cur,
- const struct json_handler *h, int n, bool *found)
- {
- const char *name = blobmsg_data(blobmsg_data(cur));
- int i;
- for (i = 0; i < n; i++) {
- if (strcmp(name, h[i].name) != 0)
- continue;
- *found = true;
- return h[i].cb(call, cur);
- }
- *found = false;
- return -1;
- }
- static int json_process_expr(struct json_call *call, struct blob_attr *cur)
- {
- struct json_script_ctx *ctx = call->ctx;
- bool found;
- int ret;
- if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
- blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
- ctx->handle_error(ctx, "Unexpected element type", cur);
- return -1;
- }
- ret = __json_process_type(call, cur, expr, ARRAY_SIZE(expr), &found);
- if (!found) {
- const char *name = blobmsg_data(blobmsg_data(cur));
- ctx->handle_expr(ctx, name, cur, call->vars);
- }
- return ret;
- }
- static int eval_string(struct json_call *call, struct blob_buf *buf, const char *name, const char *pattern)
- {
- char *dest, *next, *str;
- int len = 0;
- bool var = false;
- char c = '%';
- dest = blobmsg_alloc_string_buffer(buf, name, 0);
- if (!dest)
- return -1;
- next = alloca(strlen(pattern) + 1);
- strcpy(next, pattern);
- for (str = next; str; str = next) {
- const char *cur;
- char *end, *new_buf;
- int cur_len = 0;
- bool cur_var = var;
- end = strchr(str, '%');
- if (end) {
- *end = 0;
- next = end + 1;
- var = !var;
- } else {
- end = str + strlen(str);
- next = NULL;
- }
- if (cur_var) {
- if (end > str) {
- cur = msg_find_var(call, str);
- if (!cur)
- continue;
- cur_len = strlen(cur);
- } else {
- cur = &c;
- cur_len = 1;
- }
- } else {
- if (str == end)
- continue;
- cur = str;
- cur_len = end - str;
- }
- new_buf = blobmsg_realloc_string_buffer(buf, len + cur_len);
- if (!new_buf) {
- /* Make eval_string return -1 */
- var = true;
- break;
- }
- dest = new_buf;
- memcpy(dest + len, cur, cur_len);
- len += cur_len;
- }
- dest[len] = 0;
- blobmsg_add_string_buffer(buf);
- if (var)
- return -1;
- return 0;
- }
- static int cmd_add_string(struct json_call *call, const char *pattern)
- {
- return eval_string(call, &call->ctx->buf, NULL, pattern);
- }
- int json_script_eval_string(struct json_script_ctx *ctx, struct blob_attr *vars,
- struct blob_buf *buf, const char *name,
- const char *pattern)
- {
- struct json_call call = {
- .ctx = ctx,
- .vars = vars,
- };
- return eval_string(&call, buf, name, pattern);
- }
- static int cmd_process_strings(struct json_call *call, struct blob_attr *attr)
- {
- struct json_script_ctx *ctx = call->ctx;
- struct blob_attr *cur;
- int args = -1;
- int ret;
- size_t rem;
- void *c;
- blob_buf_init(&ctx->buf, 0);
- c = blobmsg_open_array(&ctx->buf, NULL);
- blobmsg_for_each_attr(cur, attr, rem) {
- if (args++ < 0)
- continue;
- if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
- blobmsg_add_blob(&ctx->buf, cur);
- continue;
- }
- ret = cmd_add_string(call, blobmsg_data(cur));
- if (ret) {
- ctx->handle_error(ctx, "Unterminated variable reference in string", attr);
- return ret;
- }
- }
- blobmsg_close_array(&ctx->buf, c);
- return 0;
- }
- static int __json_process_cmd(struct json_call *call, struct blob_attr *cur)
- {
- struct json_script_ctx *ctx = call->ctx;
- const char *name;
- bool found;
- int ret;
- if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
- blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
- ctx->handle_error(ctx, "Unexpected element type", cur);
- return -1;
- }
- ret = __json_process_type(call, cur, cmd, ARRAY_SIZE(cmd), &found);
- if (found)
- return ret;
- name = blobmsg_data(blobmsg_data(cur));
- ret = cmd_process_strings(call, cur);
- if (ret)
- return ret;
- ctx->handle_command(ctx, name, blob_data(ctx->buf.head), call->vars);
- return 0;
- }
- static int json_process_cmd(struct json_call *call, struct blob_attr *block)
- {
- struct json_script_ctx *ctx = call->ctx;
- struct blob_attr *cur;
- size_t rem;
- int ret;
- int i = 0;
- if (blobmsg_type(block) != BLOBMSG_TYPE_ARRAY) {
- ctx->handle_error(ctx, "Unexpected element type", block);
- return -1;
- }
- blobmsg_for_each_attr(cur, block, rem) {
- if (ctx->abort)
- break;
- switch(blobmsg_type(cur)) {
- case BLOBMSG_TYPE_STRING:
- if (!i)
- return __json_process_cmd(call, block);
- fallthrough;
- default:
- ret = json_process_cmd(call, cur);
- if (ret < -1)
- return ret;
- break;
- }
- i++;
- }
- return 0;
- }
- void json_script_run_file(struct json_script_ctx *ctx, struct json_script_file *file,
- struct blob_attr *vars)
- {
- static unsigned int _seq = 0;
- struct json_call call = {
- .ctx = ctx,
- .vars = vars,
- .seq = ++_seq,
- };
- /* overflow */
- if (!call.seq)
- call.seq = ++_seq;
- ctx->abort = false;
- __json_script_run(&call, file, NULL);
- }
- void json_script_run(struct json_script_ctx *ctx, const char *name,
- struct blob_attr *vars)
- {
- struct json_script_file *file;
- file = json_script_get_file(ctx, name);
- if (!file)
- return;
- json_script_run_file(ctx, file, vars);
- }
- static void __json_script_file_free(struct json_script_file *f)
- {
- struct json_script_file *next;
- if (!f)
- return;
- next = f->next;
- free(f);
- __json_script_file_free(next);
- }
- void
- json_script_free(struct json_script_ctx *ctx)
- {
- struct json_script_file *f, *next;
- avl_remove_all_elements(&ctx->files, f, avl, next)
- __json_script_file_free(f);
- blob_buf_free(&ctx->buf);
- }
- static void
- __default_handle_error(struct json_script_ctx *ctx, const char *msg,
- struct blob_attr *context)
- {
- }
- static const char *
- __default_handle_var(struct json_script_ctx *ctx, const char *name,
- struct blob_attr *vars)
- {
- return NULL;
- }
- static int
- __default_handle_expr(struct json_script_ctx *ctx, const char *name,
- struct blob_attr *expr, struct blob_attr *vars)
- {
- ctx->handle_error(ctx, "Unknown expression type", expr);
- return -1;
- }
- static struct json_script_file *
- __default_handle_file(struct json_script_ctx *ctx, const char *name)
- {
- return NULL;
- }
- void json_script_init(struct json_script_ctx *ctx)
- {
- avl_init(&ctx->files, avl_strcmp, false, NULL);
- if (!ctx->handle_error)
- ctx->handle_error = __default_handle_error;
- if (!ctx->handle_var)
- ctx->handle_var = __default_handle_var;
- if (!ctx->handle_expr)
- ctx->handle_expr = __default_handle_expr;
- if (!ctx->handle_file)
- ctx->handle_file = __default_handle_file;
- }
|