hotplug: add parser for new hotplug event rulesets (work in progress, not integrated...
authorFelix Fietkau <nbd@openwrt.org>
Sun, 10 Feb 2013 22:52:46 +0000 (23:52 +0100)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 10 Feb 2013 22:52:46 +0000 (23:52 +0100)
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
CMakeLists.txt
hotplug-rule.c [new file with mode: 0644]
hotplug.c [new file with mode: 0644]
hotplug.h [new file with mode: 0644]

index ac88616..fc1ad3a 100644 (file)
@@ -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 (file)
index 0000000..a2d8892
--- /dev/null
@@ -0,0 +1,504 @@
+#include <sys/stat.h>
+
+#include <libgen.h>
+#include <regex.h>
+
+#include <json/json.h>
+#include <libubox/avl-cmp.h>
+#include <libubox/blobmsg_json.h>
+
+#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(&reg, pattern, REG_EXTENDED | REG_NOSUB))
+               return 0;
+
+       ret = !regexec(&reg, str, 0, NULL, 0);
+       regfree(&reg);
+
+       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 (file)
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 (file)
index 0000000..2fa9cfe
--- /dev/null
+++ b/hotplug.h
@@ -0,0 +1,25 @@
+#ifndef __PROCD_HOTPLUG_H
+#define __PROCD_HOTPLUG_H
+
+#include <libubox/avl.h>
+#include <libubox/blob.h>
+#include <libubox/blobmsg.h>
+#include <libubox/utils.h>
+
+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