add service_validator support
authorJohn Crispin <blogic@openwrt.org>
Mon, 11 Nov 2013 18:28:13 +0000 (19:28 +0100)
committerJohn Crispin <blogic@openwrt.org>
Tue, 12 Nov 2013 09:32:32 +0000 (10:32 +0100)
Signed-off-by: John Crispin <blogic@openwrt.org>
CMakeLists.txt
service.c
service.h
service_validate.c [new file with mode: 0644]
state.c
validate.c

index 51626104c096481727365c72148b20b5330dff85..a521ea7c21c91300164c20763cee5ff3ee0bb92a 100644 (file)
@@ -10,7 +10,7 @@ IF(APPLE)
   LINK_DIRECTORIES(/opt/local/lib)
 ENDIF()
 
-SET(SOURCES main.c ubus.c service.c instance.c utils.c md5.c hotplug.c state.c mkdev.c early.c inittab.c preinit.c coldplug.c syslog.c log.c watchdog.c signal.c system.c debug.c rcS.c trigger.c measure.c)
+SET(SOURCES main.c ubus.c service.c service_validate.c instance.c utils.c md5.c hotplug.c state.c mkdev.c early.c inittab.c preinit.c coldplug.c syslog.c log.c watchdog.c signal.c system.c debug.c rcS.c trigger.c measure.c)
 
 find_library(json NAMES json-c json)
 SET(LIBS ubox ubus ${json} blobmsg_json json_script)
index 29acc57eef7ff20760864c4a06d07a3a000642cd..c5f5bf307bf30fb723df1b8cad6212a8113f88f6 100644 (file)
--- a/service.c
+++ b/service.c
@@ -77,6 +77,7 @@ service_alloc(const char *name)
        s->instances.keep_old = true;
        s->name = new_name;
        s->avl.key = s->name;
+       INIT_LIST_HEAD(&s->validators);
 
        return s;
 }
@@ -86,6 +87,7 @@ enum {
        SERVICE_SET_SCRIPT,
        SERVICE_SET_INSTANCES,
        SERVICE_SET_TRIGGER,
+       SERVICE_SET_VALIDATE,
        __SERVICE_SET_MAX
 };
 
@@ -94,6 +96,7 @@ static const struct blobmsg_policy service_set_attrs[__SERVICE_SET_MAX] = {
        [SERVICE_SET_SCRIPT] = { "script", BLOBMSG_TYPE_STRING },
        [SERVICE_SET_INSTANCES] = { "instances", BLOBMSG_TYPE_TABLE },
        [SERVICE_SET_TRIGGER] = { "triggers", BLOBMSG_TYPE_ARRAY },
+       [SERVICE_SET_VALIDATE] = { "validate", BLOBMSG_TYPE_ARRAY },
 };
 
 static int
@@ -108,6 +111,8 @@ service_update(struct service *s, struct blob_attr *config, struct blob_attr **t
                s->trigger = NULL;
        }
 
+       service_validate_del(s);
+
        if (tb[SERVICE_SET_TRIGGER] && blobmsg_data_len(tb[SERVICE_SET_TRIGGER])) {
                s->trigger = malloc(blob_pad_len(tb[SERVICE_SET_TRIGGER]));
                if (!s->trigger)
@@ -116,6 +121,11 @@ service_update(struct service *s, struct blob_attr *config, struct blob_attr **t
                trigger_add(s->trigger, s);
        }
 
+       if (tb[SERVICE_SET_VALIDATE] && blobmsg_data_len(tb[SERVICE_SET_VALIDATE])) {
+               blobmsg_for_each_attr(cur, tb[SERVICE_SET_VALIDATE], rem)
+                       service_validate_add(s, cur);
+       }
+
        if (tb[SERVICE_SET_INSTANCES]) {
                if (!add)
                        vlist_update(&s->instances);
@@ -140,6 +150,7 @@ service_delete(struct service *s)
        s->trigger = NULL;
        free(s->trigger);
        free(s);
+       service_validate_del(s);
 }
 
 enum {
@@ -182,6 +193,19 @@ static const struct blobmsg_policy event_policy[__EVENT_MAX] = {
        [EVENT_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
 };
 
+enum {
+       VALIDATE_PACKAGE,
+       VALIDATE_TYPE,
+       VALIDATE_SERVICE,
+       __VALIDATE_MAX
+};
+
+static const struct blobmsg_policy validate_policy[__VALIDATE_MAX] = {
+       [VALIDATE_PACKAGE] = { .name = "package", .type = BLOBMSG_TYPE_STRING },
+       [VALIDATE_TYPE] = { .name = "type", .type = BLOBMSG_TYPE_STRING },
+       [VALIDATE_SERVICE] = { .name = "service", .type = BLOBMSG_TYPE_STRING },
+};
+
 static int
 service_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
                   struct ubus_request_data *req, const char *method,
@@ -243,6 +267,8 @@ service_dump(struct service *s, int verbose)
        blobmsg_close_table(&b, i);
        if (verbose && s->trigger)
                blobmsg_add_blob(&b, s->trigger);
+       if (verbose && !list_empty(&s->validators))
+               service_validate_dump(&b, s);
        blobmsg_close_table(&b, c);
 }
 
@@ -350,6 +376,34 @@ service_handle_event(struct ubus_context *ctx, struct ubus_object *obj,
        return 0;
 }
 
+static int
+service_handle_validate(struct ubus_context *ctx, struct ubus_object *obj,
+                       struct ubus_request_data *req, const char *method,
+                       struct blob_attr *msg)
+{
+       struct blob_attr *tb[__VALIDATE_MAX];
+       char *p = NULL, *t = NULL;
+
+       if (!msg)
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
+       blobmsg_parse(validate_policy, __VALIDATE_MAX, tb, blob_data(msg), blob_len(msg));
+       if (tb[VALIDATE_SERVICE]) {
+               return 0;
+       }
+       if (tb[VALIDATE_PACKAGE])
+               p = blobmsg_get_string(tb[VALIDATE_PACKAGE]);
+
+       if (tb[VALIDATE_TYPE])
+               t = blobmsg_get_string(tb[VALIDATE_TYPE]);
+
+       blob_buf_init(&b, 0);
+       service_validate_dump_all(&b, p, t);
+       ubus_send_reply(ctx, req, b.head);
+
+       return 0;
+}
+
 static struct ubus_method main_object_methods[] = {
        UBUS_METHOD("set", service_handle_set, service_set_attrs),
        UBUS_METHOD("add", service_handle_set, service_set_attrs),
@@ -358,6 +412,7 @@ static struct ubus_method main_object_methods[] = {
        UBUS_METHOD("update_start", service_handle_update, service_attrs),
        UBUS_METHOD("update_complete", service_handle_update, service_attrs),
        UBUS_METHOD("event", service_handle_event, event_policy),
+       UBUS_METHOD("validate", service_handle_validate, validate_policy),
 };
 
 static struct ubus_object_type main_object_type =
@@ -372,6 +427,13 @@ static struct ubus_object main_object = {
 
 void ubus_init_service(struct ubus_context *ctx)
 {
-       avl_init(&services, avl_strcmp, false, NULL);
        ubus_add_object(ctx, &main_object);
 }
+
+void
+service_init(void)
+{
+       avl_init(&services, avl_strcmp, false, NULL);
+       service_validate_init();
+}
+
index 9d3c30894da30a10cb9d9b7146648c0f5cd24246..6448e5ecbaa54cf6eb48154839de895bc27c01ec 100644 (file)
--- a/service.h
+++ b/service.h
 
 #include <libubox/avl.h>
 #include <libubox/vlist.h>
+#include <libubox/list.h>
 
 extern struct avl_tree services;
 
+struct vrule {
+       struct avl_node avl;
+       char *option;
+       char *rule;
+};
+
+struct validate {
+       struct avl_node avl;
+       struct list_head list;
+
+       char *package;
+       char *type;
+
+       struct avl_tree rules;
+};
+
 struct service {
        struct avl_node avl;
        const char *name;
 
        struct blob_attr *trigger;
        struct vlist_tree instances;
+       struct list_head validators;
 };
 
+void service_validate_add(struct service *s, struct blob_attr *attr);
+void service_validate_dump(struct blob_buf *b, struct service *s);
+void service_validate_dump_all(struct blob_buf *b, char *p, char *s);
+void service_validate_del(struct service *s);
+void service_validate_init(void);
+void service_init(void);
+
 #endif
diff --git a/service_validate.c b/service_validate.c
new file mode 100644 (file)
index 0000000..3522cde
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <libubox/blobmsg_json.h>
+#include <libubox/avl-cmp.h>
+
+#include "procd.h"
+#include "service.h"
+
+enum {
+       SERVICE_VAL_PACKAGE,
+       SERVICE_VAL_TYPE,
+       SERVICE_VAL_DATA,
+       __SERVICE_VAL_MAX
+};
+
+static const struct blobmsg_policy service_validate_attrs[__SERVICE_VAL_MAX] = {
+       [SERVICE_VAL_PACKAGE] = { "package", BLOBMSG_TYPE_STRING },
+       [SERVICE_VAL_TYPE] = { "type", BLOBMSG_TYPE_STRING },
+       [SERVICE_VAL_DATA] = { "data", BLOBMSG_TYPE_TABLE },
+};
+
+static struct avl_tree validators;
+
+void
+service_validate_dump_all(struct blob_buf *b, char *p, char *s)
+{
+       struct json_object *r = json_object_new_object();
+       struct validate *v;
+
+       if (!r)
+               return;
+
+       avl_for_each_element(&validators, v, avl) {
+               struct json_object *o, *t;
+               struct vrule *vr;
+
+               if (p && strcmp(p, v->package))
+                       continue;
+
+               if (s && strcmp(s, v->type))
+                       continue;
+
+               o = json_object_object_get(r, v->package);
+               if (!o) {
+                       o = json_object_new_object();
+                       json_object_object_add(r, v->package, o);
+               }
+               t = json_object_object_get(o, v->type);
+               if (!t) {
+                       t = json_object_new_object();
+                       json_object_object_add(o, v->type, t);
+               }
+               avl_for_each_element(&v->rules, vr, avl)
+                       json_object_object_add(t, vr->option, json_object_new_string(vr->rule));
+       }
+       blobmsg_add_object(b, r);
+}
+
+void
+service_validate_dump(struct blob_buf *b, struct service *s)
+{
+       struct validate *v;
+       void *i = blobmsg_open_array(b, "validate");
+
+        list_for_each_entry(v, &s->validators, list) {
+               struct vrule *vr;
+               void *k, *j = blobmsg_open_table(b, "validate");
+
+               blobmsg_add_string(b, "package", v->package);
+                blobmsg_add_string(b, "type", v->type);
+               k = blobmsg_open_table(b, "rules");
+               avl_for_each_element(&v->rules, vr, avl)
+                       blobmsg_add_string(b, vr->option, vr->rule);
+               blobmsg_close_table(b, k);
+               blobmsg_close_table(b, j);
+       }
+       blobmsg_close_array(b, i);
+}
+
+void
+service_validate_del(struct service *s)
+{
+       struct validate *v, *n;
+
+       if (list_empty(&s->validators))
+               return;
+
+        list_for_each_entry_safe(v, n, &s->validators, list) {
+               struct vrule *vr, *a;
+
+               avl_for_each_element_safe(&v->rules, vr, avl, a) {
+                       avl_delete(&v->rules, &vr->avl);
+                       free(vr);
+               }
+               avl_delete(&validators, &v->avl);
+               list_del(&v->list);
+               free(v);
+       }
+}
+
+void
+service_validate_add(struct service *s, struct blob_attr *msg)
+{
+       struct blob_attr *tb[__SERVICE_VAL_MAX];
+       struct validate *v;
+       char *type, *package;
+       struct blob_attr *cur;
+       int rem;
+
+       blobmsg_parse(service_validate_attrs, __SERVICE_VAL_MAX, tb, blobmsg_data(msg), blobmsg_data_len(msg));
+       if (!tb[SERVICE_VAL_PACKAGE] || !tb[SERVICE_VAL_TYPE] || !tb[SERVICE_VAL_DATA])
+               return;
+
+       v = calloc_a(sizeof(*v), &package, blobmsg_data_len(tb[SERVICE_VAL_PACKAGE]) + 1,
+                        &type, blobmsg_data_len(tb[SERVICE_VAL_TYPE]) + 1);
+       if (!v)
+               return;
+
+       v->type = type;
+       v->avl.key = v->package = package;
+       strcpy(v->package, blobmsg_get_string(tb[SERVICE_VAL_PACKAGE]));
+       strcpy(v->type, blobmsg_get_string(tb[SERVICE_VAL_TYPE]));
+
+       list_add(&v->list, &s->validators);
+       if (avl_insert(&validators, &v->avl)) {
+               free(v);
+               return;
+       }
+       avl_init(&v->rules, avl_strcmp, false, NULL);
+
+       blobmsg_for_each_attr(cur, tb[SERVICE_VAL_DATA], rem) {
+               char *option;
+               char *rule;
+               struct vrule *vr = calloc_a(sizeof(*vr), &option, strlen(blobmsg_name(cur)) + 1,
+                       &rule, strlen(blobmsg_get_string(cur)) + 1);
+
+               vr->avl.key = vr->option = option;
+               vr->rule = rule;
+               strcpy(vr->option, blobmsg_name(cur));
+               strcpy(vr->rule, blobmsg_get_string(cur));
+               if (avl_insert(&v->rules, &vr->avl))
+                       free(vr);
+       }
+}
+
+void
+service_validate_init(void)
+{
+       avl_init(&validators, avl_strcmp, true, NULL);
+}
diff --git a/state.c b/state.c
index 90e883c50c5fb2dee1cd009b098c9247bf76620e..f83032ce9d4c2ad0c7080d7eea9392390409e5b1 100644 (file)
--- a/state.c
+++ b/state.c
@@ -20,6 +20,7 @@
 #include "syslog.h"
 #include "hotplug.h"
 #include "watchdog.h"
+#include "service.h"
 
 enum {
        STATE_NONE = 0,
@@ -51,6 +52,7 @@ static void state_enter(void)
                LOG("- init -\n");
                log_init();
                procd_connect_ubus();
+               service_init();
                procd_inittab();
                procd_inittab_run("respawn");
                procd_inittab_run("askconsole");
index 85b69aff74e283877ec52348ccc2240472076f01..f94a071e21100147f4de83df74b01474ae928a8c 100644 (file)
@@ -313,6 +313,12 @@ dt_type_bool(struct dt_state *s, int nargs)
        return false;
 }
 
+static bool
+dt_type_string(struct dt_state *s, int nargs)
+{
+       return true;
+}
+
 static bool
 dt_type_ip4addr(struct dt_state *s, int nargs)
 {
@@ -682,6 +688,7 @@ static struct dt_fun dt_types[] = {
        { "float",                      dt_type_float           },
        { "ufloat",                     dt_type_ufloat          },
        { "bool",                       dt_type_bool            },
+       { "string",                     dt_type_string          },
        { "ip4addr",            dt_type_ip4addr         },
        { "ip6addr",            dt_type_ip6addr         },
        { "ipaddr",                     dt_type_ipaddr          },