From 0e1ddb8cc10fab53958d5a73329efefaa49b7b12 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 10 Feb 2013 23:52:46 +0100 Subject: [PATCH] hotplug: add parser for new hotplug event rulesets (work in progress, not integrated yet) Signed-off-by: Felix Fietkau --- CMakeLists.txt | 5 +- hotplug-rule.c | 504 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hotplug.c | 10 ++ hotplug.h | 25 +++ 4 files changed, 542 insertions(+), 2 deletions(-) create mode 100644 hotplug-rule.c create mode 100644 hotplug.c create mode 100644 hotplug.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ac88616..fc1ad3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,9 +10,10 @@ IF(APPLE) LINK_DIRECTORIES(/opt/local/lib) ENDIF() -SET(SOURCES main.c ubus.c service.c instance.c utils.c md5.c) +SET(SOURCES main.c ubus.c service.c instance.c utils.c md5.c hotplug-rule.c hotplug.c) -SET(LIBS ubox ubus) +find_library(json NAMES json-c json) +SET(LIBS ubox ubus ${json} blobmsg_json) IF(DEBUG) ADD_DEFINITIONS(-DDEBUG -g3) diff --git a/hotplug-rule.c b/hotplug-rule.c new file mode 100644 index 0000000..a2d8892 --- /dev/null +++ b/hotplug-rule.c @@ -0,0 +1,504 @@ +#include + +#include +#include + +#include +#include +#include + +#include "hotplug.h" + +static struct blob_buf b; + +static int rule_process_expr(struct blob_attr *cur, struct blob_attr *msg); +static int rule_process_cmd(struct blob_attr *cur, struct blob_attr *msg); + +static char *__msg_find_var(struct blob_attr *msg, const char *name) +{ + struct blob_attr *cur; + int rem; + + blob_for_each_attr(cur, msg, rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + if (strcmp(blobmsg_name(cur), name) != 0) + continue; + + return blobmsg_data(cur); + } + + return NULL; +} + +static char *msg_find_var(struct blob_attr *msg, const char *name) +{ + char *str; + + if (!strcmp(name, "DEVICENAME") || !strcmp(name, "DEVNAME")) { + str = __msg_find_var(msg, "DEVPATH"); + if (!str) + return NULL; + + return basename(str); + } + + return __msg_find_var(msg, name); +} + +static void +rule_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 blob_attr *expr, struct blob_attr *msg) +{ + 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 = rule_process_expr(tb[1], msg); + if (ret < 0) + return 0; + + if (ret) + return rule_process_cmd(tb[2], msg); + + if (!tb[3]) + return 0; + + return rule_process_cmd(tb[3], msg); +} + +static int handle_case(struct blob_attr *expr, struct blob_attr *msg) +{ + struct blob_attr *tb[3], *cur; + const char *var; + int rem; + + rule_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, BLOBMSG_TYPE_TABLE); + if (!tb[1] || !tb[2]) + return 0; + + var = msg_find_var(msg, blobmsg_data(tb[1])); + if (!var) + return 0; + + blobmsg_for_each_attr(cur, tb[2], rem) { + if (!strcmp(var, blobmsg_name(cur))) + return rule_process_cmd(cur, msg); + } + + return 0; +} + +static int handle_return(struct blob_attr *expr, struct blob_attr *msg) +{ + return -2; +} + +static int handle_include(struct blob_attr *expr, struct blob_attr *msg) +{ + struct blob_attr *tb[3]; + struct rule_file *r; + + rule_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); + if (!tb[1]) + return 0; + + r = rule_file_get(blobmsg_data(tb[1])); + if (!r) + return 0; + + return rule_process_cmd(r->data, msg); +} + +static const struct rule_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 blob_attr *expr, struct blob_attr *msg, bool regex) +{ + struct blob_attr *tb[3], *cur; + const char *var; + int rem; + + rule_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); + if (!tb[1] || !tb[2]) + return -1; + + var = msg_find_var(msg, 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) { + rule_error(cur, "Unexpected element type"); + return -1; + } + + if (eq_regex_cmp(var, blobmsg_data(cur), regex)) + return 1; + } + return 0; + default: + rule_error(tb[2], "Unexpected element type"); + return -1; + } +} + +static int handle_expr_eq(struct blob_attr *expr, struct blob_attr *msg) +{ + return expr_eq_regex(expr, msg, false); +} + +static int handle_expr_regex(struct blob_attr *expr, struct blob_attr *msg) +{ + return expr_eq_regex(expr, msg, true); +} + +static int handle_expr_has(struct blob_attr *expr, struct blob_attr *msg) +{ + struct blob_attr *tb[3], *cur; + int rem; + + rule_get_tuple(expr, tb, 0, 0); + if (!tb[1]) + return -1; + + switch(blobmsg_type(tb[1])) { + case BLOBMSG_TYPE_STRING: + return !!msg_find_var(msg, blobmsg_data(tb[1])); + case BLOBMSG_TYPE_ARRAY: + blobmsg_for_each_attr(cur, tb[1], rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { + rule_error(cur, "Unexpected element type"); + return -1; + } + + if (msg_find_var(msg, blobmsg_data(cur))) + return 1; + } + return 0; + default: + rule_error(tb[1], "Unexpected element type"); + return -1; + } +} + +static int expr_and_or(struct blob_attr *expr, struct blob_attr *msg, bool and) +{ + struct blob_attr *cur; + int ret, rem; + int i = 0; + + blobmsg_for_each_attr(cur, expr, rem) { + if (i++ < 1) + continue; + + ret = rule_process_expr(cur, msg); + if (ret < 0) + return ret; + + if (ret != and) + return ret; + } + + return and; +} + +static int handle_expr_and(struct blob_attr *expr, struct blob_attr *msg) +{ + return expr_and_or(expr, msg, 1); +} + +static int handle_expr_or(struct blob_attr *expr, struct blob_attr *msg) +{ + return expr_and_or(expr, msg, 0); +} + +static int handle_expr_not(struct blob_attr *expr, struct blob_attr *msg) +{ + struct blob_attr *tb[3]; + + rule_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0); + if (!tb[1]) + return -1; + + return rule_process_expr(tb[1], msg); +} + +static const struct rule_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 }, +}; + +static int +__rule_process_type(struct blob_attr *cur, struct blob_attr *msg, + const struct rule_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].handler(cur, msg); + } + + *found = false; + return -1; +} + +static int rule_process_expr(struct blob_attr *cur, struct blob_attr *msg) +{ + bool found; + int ret; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY) { + rule_error(cur, "Unexpected element type"); + return -1; + } + + ret = __rule_process_type(cur, msg, expr, ARRAY_SIZE(expr), &found); + if (!found) + rule_error(cur, "Unknown expression type"); + + return ret; +} + +static void cmd_add_string(const char *pattern, struct blob_attr *msg) +{ + char *dest, *next, *str; + int len = 0; + bool var = false; + + blobmsg_alloc_string_buffer(&b, NULL, 1); + str = alloca(strlen(pattern) + 1); + strcpy(str, pattern); + next = str; + + while (*str) { + const char *cur; + int cur_len = 0; + + next = strchr(str, '%'); + if (!next) + next = str + strlen(str); + + if (var) { + if (next > str) { + *next = 0; + cur = msg_find_var(msg, str); + if (cur) + cur_len = strlen(cur); + } else { + cur = str - 1; + cur_len = 1; + } + } else { + cur = str; + cur_len = next - str; + } + + if (cur_len) { + dest = blobmsg_realloc_string_buffer(&b, cur_len); + memcpy(dest + len, cur, cur_len); + len += cur_len; + } + + var = !var; + str = next + 1; + } + + dest[len] = 0; + blobmsg_add_string_buffer(&b); +} + +static int cmd_process_strings(struct blob_attr *attr, struct blob_attr *msg) +{ + struct blob_attr *cur; + int args = -1; + int rem; + void *c; + + blob_buf_init(&b, 0); + c = blobmsg_open_array(&b, NULL); + blobmsg_for_each_attr(cur, attr, rem) { + if (args++ < 0) + continue; + + cmd_add_string(blobmsg_data(cur), msg); + } + + blobmsg_close_array(&b, c); + + return 0; +} + +static int __rule_process_cmd(struct blob_attr *cur, struct blob_attr *msg) +{ + const char *name; + bool found; + int ret; + + ret = __rule_process_type(cur, msg, cmd, ARRAY_SIZE(cmd), &found); + if (found) + return ret; + + name = blobmsg_data(blobmsg_data(cur)); + cmd_process_strings(cur, msg); + rule_handle_command(name, blob_data(b.head)); + + return 0; +} + +static int rule_process_cmd(struct blob_attr *block, struct blob_attr *msg) +{ + struct blob_attr *cur; + int rem; + int ret; + int i = 0; + + if (blobmsg_type(block) != BLOBMSG_TYPE_ARRAY) { + rule_error(block, "Unexpected element type"); + return -1; + } + + blobmsg_for_each_attr(cur, block, rem) { + switch(blobmsg_type(cur)) { + case BLOBMSG_TYPE_STRING: + if (!i) + return __rule_process_cmd(block, msg); + default: + ret = rule_process_cmd(cur, msg); + if (ret < -1) + return ret; + break; + } + i++; + } + + return 0; +} + +void rule_process_msg(struct rule_file *f, struct blob_attr *msg) +{ + rule_process_cmd(f->data, msg); +} + +static struct rule_file * +rule_file_load(const char *filename) +{ + struct rule_file *r; + struct stat st; + + json_object *obj = NULL; + + blob_buf_init(&b, 0); + + if (stat(filename, &st)) + return NULL; + + obj = json_object_from_file((char *) filename); + if (!obj) + return NULL; + + if (!json_object_is_type(obj, json_type_array)) { + json_object_put(obj); + return NULL; + } + + blobmsg_add_json_element(&b, filename, obj); + json_object_put(obj); + + r = calloc(1, sizeof(*r) + blob_len(b.head)); + memcpy(r->data, blob_data(b.head), blob_len(b.head)); + r->avl.key = blobmsg_name(r->data); + + return r; +} + +static struct avl_tree rule_files; + +struct rule_file * +rule_file_get(const char *filename) +{ + struct rule_file *r; + + if (!rule_files.comp) + avl_init(&rule_files, avl_strcmp, false, NULL); + + r = avl_find_element(&rule_files, filename, r, avl); + if (r) + return r; + + r = rule_file_load(filename); + if (!r) + return NULL; + + avl_insert(&rule_files, &r->avl); + return r; +} + +void +rule_file_free_all(void) +{ + struct rule_file *r, *next; + + avl_remove_all_elements(&rule_files, r, avl, next) + free(r); + + blob_buf_free(&b); +} diff --git a/hotplug.c b/hotplug.c new file mode 100644 index 0000000..7e681f9 --- /dev/null +++ b/hotplug.c @@ -0,0 +1,10 @@ +#include "hotplug.h" + +void rule_handle_command(const char *name, struct blob_attr *data) +{ +} + +void rule_error(struct blob_attr *cur, const char *msg) +{ +} + diff --git a/hotplug.h b/hotplug.h new file mode 100644 index 0000000..2fa9cfe --- /dev/null +++ b/hotplug.h @@ -0,0 +1,25 @@ +#ifndef __PROCD_HOTPLUG_H +#define __PROCD_HOTPLUG_H + +#include +#include +#include +#include + +struct rule_file { + struct avl_node avl; + struct blob_attr data[]; +}; + +struct rule_handler { + const char *name; + int (*handler)(struct blob_attr *cur, struct blob_attr *msg); +}; + +struct rule_file *rule_file_get(const char *filename); +void rule_file_free_all(void); +void rule_error(struct blob_attr *cur, const char *msg); +void rule_process_msg(struct rule_file *f, struct blob_attr *msg); +void rule_handle_command(const char *name, struct blob_attr *data); + +#endif -- 2.11.0