make uci commit calls trigger config.change events
[project/rpcd.git] / uci.c
diff --git a/uci.c b/uci.c
index 0409a07..f5bb076 100644 (file)
--- a/uci.c
+++ b/uci.c
@@ -1,5 +1,5 @@
 /*
- * luci-rpcd - LuCI UBUS RPC server
+ * rpcd - UBUS RPC server
  *
  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
  *
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "uci.h"
-#include "session.h"
+#include <libubox/blobmsg.h>
+#include <libubox/blobmsg_json.h>
+
+#include <rpcd/uci.h>
+#include <rpcd/session.h>
 
 static struct blob_buf buf;
 static struct uci_context *cursor;
@@ -169,6 +172,28 @@ rpc_uci_status(void)
 }
 
 /*
+ * Setup per-session delta save directory. If the passed "sid" blob attribute
+ * pointer is NULL then the precedure was not invoked through the ubus-rpc so
+ * we do not perform session isolation and use the default save directory.
+ */
+static void
+rpc_uci_set_savedir(struct blob_attr *sid)
+{
+       char path[PATH_MAX];
+
+       if (!sid)
+       {
+               uci_set_savedir(cursor, "/tmp/.uci");
+               return;
+       }
+
+       snprintf(path, sizeof(path) - 1,
+                RPC_UCI_SAVEDIR_PREFIX "%s", blobmsg_get_string(sid));
+
+       uci_set_savedir(cursor, path);
+}
+
+/*
  * Test read access to given config. If the passed "sid" blob attribute pointer
  * is NULL then the precedure was not invoked through the ubus-rpc so we do not
  * perform access control and always assume true.
@@ -176,6 +201,8 @@ rpc_uci_status(void)
 static bool
 rpc_uci_read_access(struct blob_attr *sid, struct blob_attr *config)
 {
+       rpc_uci_set_savedir(sid);
+
        if (!sid)
                return true;
 
@@ -191,6 +218,8 @@ rpc_uci_read_access(struct blob_attr *sid, struct blob_attr *config)
 static bool
 rpc_uci_write_access(struct blob_attr *sid, struct blob_attr *config)
 {
+       rpc_uci_set_savedir(sid);
+
        if (!sid)
                return true;
 
@@ -1004,8 +1033,28 @@ out:
        return rpc_uci_status();
 }
 
+static void
+rpc_uci_trigger_event(struct ubus_context *ctx, const char *config)
+{
+       char *pkg = strdup(config);
+       static struct blob_buf b;
+       uint32_t id;
+
+       if (!ubus_lookup_id(ctx, "service", &id)) {
+               void *c;
+
+               blob_buf_init(&b, 0);
+               blobmsg_add_string(&b, "type", "config.change");
+               c = blobmsg_open_table(&b, "data");
+               blobmsg_add_string(&b, "package", pkg);
+               blobmsg_close_table(&b, c);
+               ubus_invoke(ctx, id, "event", b.head, NULL, 0, 1000);
+       }
+       free(pkg);
+}
+
 static int
-rpc_uci_revert_commit(struct blob_attr *msg, bool commit)
+rpc_uci_revert_commit(struct ubus_context *ctx, struct blob_attr *msg, bool commit)
 {
        struct blob_attr *tb[__RPC_C_MAX];
        struct uci_package *p = NULL;
@@ -1021,19 +1070,23 @@ rpc_uci_revert_commit(struct blob_attr *msg, bool commit)
                return UBUS_STATUS_PERMISSION_DENIED;
 
        ptr.package = blobmsg_data(tb[RPC_C_CONFIG]);
-       uci_load(cursor, ptr.package, &p);
-
-       if (!p || uci_lookup_ptr(cursor, &ptr, NULL, true) || !ptr.p)
-               goto out;
 
        if (commit)
-               uci_commit(cursor, &p, false);
-       else
-               uci_revert(cursor, &ptr);
+       {
+               uci_load(cursor, ptr.package, &p);
 
-out:
-       if (p)
-               uci_unload(cursor, p);
+               if (p)
+               {
+                       uci_commit(cursor, &p, false);
+                       uci_unload(cursor, p);
+               }
+               rpc_uci_trigger_event(ctx, blobmsg_get_string(tb[RPC_C_CONFIG]));
+       }
+       else
+       {
+               if (!uci_lookup_ptr(cursor, &ptr, NULL, true) && ptr.p)
+                       uci_revert(cursor, &ptr);
+       }
 
        return rpc_uci_status();
 }
@@ -1043,7 +1096,7 @@ rpc_uci_revert(struct ubus_context *ctx, struct ubus_object *obj,
                struct ubus_request_data *req, const char *method,
                struct blob_attr *msg)
 {
-       return rpc_uci_revert_commit(msg, false);
+       return rpc_uci_revert_commit(ctx, msg, false);
 }
 
 static int
@@ -1051,7 +1104,7 @@ rpc_uci_commit(struct ubus_context *ctx, struct ubus_object *obj,
                struct ubus_request_data *req, const char *method,
                struct blob_attr *msg)
 {
-       return rpc_uci_revert_commit(msg, true);
+       return rpc_uci_revert_commit(ctx, msg, true);
 }
 
 static int
@@ -1082,6 +1135,68 @@ out:
 }
 
 
+/*
+ * Remove given delta save directory (if any).
+ */
+static void
+rpc_uci_purge_dir(const char *path)
+{
+       DIR *d;
+       struct stat s;
+       struct dirent *e;
+       char file[PATH_MAX];
+
+       if (stat(path, &s) || !S_ISDIR(s.st_mode))
+               return;
+
+       if ((d = opendir(path)) != NULL)
+       {
+               while ((e = readdir(d)) != NULL)
+               {
+                       snprintf(file, sizeof(file) - 1, "%s/%s", path, e->d_name);
+
+                       if (stat(file, &s) || !S_ISREG(s.st_mode))
+                               continue;
+
+                       unlink(file);
+               }
+
+               closedir(d);
+
+               rmdir(path);
+       }
+}
+
+/*
+ * Session destroy callback to purge associated delta directory.
+ */
+static void
+rpc_uci_purge_savedir_cb(struct rpc_session *ses, void *priv)
+{
+       char path[PATH_MAX];
+
+       snprintf(path, sizeof(path) - 1, RPC_UCI_SAVEDIR_PREFIX "%s", ses->id);
+       rpc_uci_purge_dir(path);
+}
+
+/*
+ * Removes all delta directories which match the RPC_UCI_SAVEDIR_PREFIX.
+ * This is used to clean up garbage when starting rpcd.
+ */
+void rpc_uci_purge_savedirs(void)
+{
+       int i;
+       glob_t gl;
+
+       if (!glob(RPC_UCI_SAVEDIR_PREFIX "*", 0, NULL, &gl))
+       {
+               for (i = 0; i < gl.gl_pathc; i++)
+                       rpc_uci_purge_dir(gl.gl_pathv[i]);
+
+               globfree(&gl);
+       }
+}
+
 int rpc_uci_api_init(struct ubus_context *ctx)
 {
        static const struct ubus_method uci_methods[] = {
@@ -1107,10 +1222,16 @@ int rpc_uci_api_init(struct ubus_context *ctx)
                .n_methods = ARRAY_SIZE(uci_methods),
        };
 
+       static struct rpc_session_cb cb = {
+               .cb = rpc_uci_purge_savedir_cb
+       };
+
        cursor = uci_alloc_context();
 
        if (!cursor)
                return UBUS_STATUS_UNKNOWN_ERROR;
 
+       rpc_session_destroy_cb(&cb);
+
        return ubus_add_object(ctx, &obj);
 }