luci2: add upgrade_test, upgrade_start and upgrade_abort calls
[project/rpcd.git] / luci2.c
diff --git a/luci2.c b/luci2.c
index ba91533..ae36e42 100644 (file)
--- a/luci2.c
+++ b/luci2.c
@@ -25,6 +25,7 @@
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/statvfs.h>
 #include <dirent.h>
 #include <arpa/inet.h>
 #include <signal.h>
@@ -58,6 +59,15 @@ static const struct blobmsg_policy rpc_init_policy[__RPC_I_MAX] = {
 };
 
 enum {
+       RPC_D_DATA,
+       __RPC_D_MAX
+};
+
+static const struct blobmsg_policy rpc_data_policy[__RPC_D_MAX] = {
+       [RPC_D_DATA]   = { .name = "data",  .type = BLOBMSG_TYPE_STRING },
+};
+
+enum {
        RPC_K_KEYS,
        __RPC_K_MAX
 };
@@ -100,12 +110,12 @@ static const struct blobmsg_policy rpc_opkg_package_policy[__RPC_OP_MAX] = {
 };
 
 enum {
-       RPC_OC_CONFIG,
-       __RPC_OC_MAX
+       RPC_UPGRADE_KEEP,
+       __RPC_UPGRADE_MAX
 };
 
-static const struct blobmsg_policy rpc_opkg_config_policy[__RPC_OC_MAX] = {
-       [RPC_OC_CONFIG]     = { .name = "config", .type = BLOBMSG_TYPE_STRING },
+static const struct blobmsg_policy rpc_upgrade_policy[__RPC_UPGRADE_MAX] = {
+       [RPC_UPGRADE_KEEP] = { .name = "keep",    .type = BLOBMSG_TYPE_BOOL },
 };
 
 
@@ -260,6 +270,39 @@ rpc_luci2_system_dmesg(struct ubus_context *ctx, struct ubus_object *obj,
 }
 
 static int
+rpc_luci2_system_diskfree(struct ubus_context *ctx, struct ubus_object *obj,
+                          struct ubus_request_data *req, const char *method,
+                          struct blob_attr *msg)
+{
+       int i;
+       void *c;
+       struct statvfs s;
+       const char *fslist[] = {
+               "/",    "root",
+               "/tmp", "tmp",
+       };
+
+       blob_buf_init(&buf, 0);
+
+       for (i = 0; i < sizeof(fslist) / sizeof(fslist[0]); i += 2)
+       {
+               if (statvfs(fslist[i], &s))
+                       continue;
+
+               c = blobmsg_open_table(&buf, fslist[i+1]);
+
+               blobmsg_add_u32(&buf, "total", s.f_blocks * s.f_frsize);
+               blobmsg_add_u32(&buf, "free",  s.f_bfree  * s.f_frsize);
+               blobmsg_add_u32(&buf, "used", (s.f_blocks - s.f_bfree) * s.f_frsize);
+
+               blobmsg_close_table(&buf, c);
+       }
+
+       ubus_send_reply(ctx, req, buf.head);
+       return 0;
+}
+
+static int
 rpc_luci2_process_list(struct ubus_context *ctx, struct ubus_object *obj,
                        struct ubus_request_data *req, const char *method,
                        struct blob_attr *msg)
@@ -495,6 +538,100 @@ rpc_luci2_init_action(struct ubus_context *ctx, struct ubus_object *obj,
 }
 
 static int
+rpc_luci2_rclocal_get(struct ubus_context *ctx, struct ubus_object *obj,
+                      struct ubus_request_data *req, const char *method,
+                      struct blob_attr *msg)
+{
+       FILE *f;
+       char data[4096] = { 0 };
+
+       if (!(f = fopen("/etc/rc.local", "r")))
+               return rpc_errno_status();
+
+       fread(data, sizeof(data) - 1, 1, f);
+       fclose(f);
+
+       blob_buf_init(&buf, 0);
+       blobmsg_add_string(&buf, "data", data);
+
+       ubus_send_reply(ctx, req, buf.head);
+       return 0;
+}
+
+static int
+rpc_luci2_rclocal_set(struct ubus_context *ctx, struct ubus_object *obj,
+                      struct ubus_request_data *req, const char *method,
+                      struct blob_attr *msg)
+{
+       FILE *f;
+       struct blob_attr *tb[__RPC_D_MAX];
+
+       blobmsg_parse(rpc_data_policy, __RPC_D_MAX, tb,
+                     blob_data(msg), blob_len(msg));
+
+       if (!tb[RPC_D_DATA] || blobmsg_data_len(tb[RPC_D_DATA]) >= 4096)
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
+       if (!(f = fopen("/etc/rc.local", "w")))
+               return rpc_errno_status();
+
+       fwrite(blobmsg_data(tb[RPC_D_DATA]),
+              blobmsg_data_len(tb[RPC_D_DATA]) - 1, 1, f);
+
+       fclose(f);
+       return 0;
+}
+
+static int
+rpc_luci2_crontab_get(struct ubus_context *ctx, struct ubus_object *obj,
+                      struct ubus_request_data *req, const char *method,
+                      struct blob_attr *msg)
+{
+       FILE *f;
+       char data[4096] = { 0 };
+
+       if (!(f = fopen("/etc/crontabs/root", "r")))
+               return rpc_errno_status();
+
+       fread(data, sizeof(data) - 1, 1, f);
+       fclose(f);
+
+       blob_buf_init(&buf, 0);
+       blobmsg_add_string(&buf, "data", data);
+
+       ubus_send_reply(ctx, req, buf.head);
+       return 0;
+}
+
+static int
+rpc_luci2_crontab_set(struct ubus_context *ctx, struct ubus_object *obj,
+                      struct ubus_request_data *req, const char *method,
+                      struct blob_attr *msg)
+{
+       FILE *f;
+       struct stat s;
+       struct blob_attr *tb[__RPC_D_MAX];
+
+       blobmsg_parse(rpc_data_policy, __RPC_D_MAX, tb,
+                     blob_data(msg), blob_len(msg));
+
+       if (!tb[RPC_D_DATA] || blobmsg_data_len(tb[RPC_D_DATA]) >= 4096)
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
+       if (stat("/etc/crontabs", &s) && mkdir("/etc/crontabs", 0755))
+               return rpc_errno_status();
+
+       if (!(f = fopen("/etc/crontabs/root", "w")))
+               return rpc_errno_status();
+
+       fwrite(blobmsg_data(tb[RPC_D_DATA]),
+              blobmsg_data_len(tb[RPC_D_DATA]) - 1, 1, f);
+
+       fclose(f);
+       return 0;
+}
+
+static int
 rpc_luci2_sshkeys_get(struct ubus_context *ctx, struct ubus_object *obj,
                       struct ubus_request_data *req, const char *method,
                       struct blob_attr *msg)
@@ -632,6 +769,209 @@ rpc_luci2_password_set(struct ubus_context *ctx, struct ubus_object *obj,
        }
 }
 
+static int
+rpc_luci2_led_list(struct ubus_context *ctx, struct ubus_object *obj,
+                   struct ubus_request_data *req, const char *method,
+                   struct blob_attr *msg)
+{
+       DIR *d;
+       FILE *f;
+       void *list, *led, *trigger;
+       char *p, *active_trigger, line[512];
+       struct dirent *e;
+
+       if (!(d = opendir("/sys/class/leds")))
+               return rpc_errno_status();
+
+       blob_buf_init(&buf, 0);
+       list = blobmsg_open_array(&buf, "leds");
+
+       while ((e = readdir(d)) != NULL)
+       {
+               snprintf(line, sizeof(line) - 1, "/sys/class/leds/%s/trigger",
+                        e->d_name);
+
+               if (!(f = fopen(line, "r")))
+                       continue;
+
+               led = blobmsg_open_table(&buf, NULL);
+
+               blobmsg_add_string(&buf, "name", e->d_name);
+
+               if (fgets(line, sizeof(line) - 1, f))
+               {
+                       trigger = blobmsg_open_array(&buf, "triggers");
+
+                       for (p = strtok(line, " \n"), active_trigger = NULL;
+                            p != NULL;
+                            p = strtok(NULL, " \n"))
+                       {
+                               if (*p == '[')
+                               {
+                                       *(p + strlen(p) - 1) = 0;
+                                       *p++ = 0;
+                                       active_trigger = p;
+                               }
+
+                               blobmsg_add_string(&buf, NULL, p);
+                       }
+
+                       blobmsg_close_array(&buf, trigger);
+
+                       if (active_trigger)
+                               blobmsg_add_string(&buf, "active_trigger", active_trigger);
+               }
+
+               fclose(f);
+
+               snprintf(line, sizeof(line) - 1, "/sys/class/leds/%s/brightness",
+                        e->d_name);
+
+               if ((f = fopen(line, "r")) != NULL)
+               {
+                       if (fgets(line, sizeof(line) - 1, f))
+                               blobmsg_add_u32(&buf, "brightness", atoi(line));
+
+                       fclose(f);
+               }
+
+               snprintf(line, sizeof(line) - 1, "/sys/class/leds/%s/max_brightness",
+                        e->d_name);
+
+               if ((f = fopen(line, "r")) != NULL)
+               {
+                       if (fgets(line, sizeof(line) - 1, f))
+                               blobmsg_add_u32(&buf, "max_brightness", atoi(line));
+
+                       fclose(f);
+               }
+
+               blobmsg_close_table(&buf, led);
+       }
+
+       closedir(d);
+
+       blobmsg_close_array(&buf, list);
+       ubus_send_reply(ctx, req, buf.head);
+
+       return 0;
+}
+
+static int
+rpc_luci2_usb_list(struct ubus_context *ctx, struct ubus_object *obj,
+                   struct ubus_request_data *req, const char *method,
+                   struct blob_attr *msg)
+{
+       DIR *d;
+       FILE *f;
+       int i;
+       void *list, *device;
+       char *p, line[512];
+       struct stat s;
+       struct dirent *e;
+
+       const char *attributes[] = {
+               "manufacturer", "vendor_name",  "s",
+               "product",      "product_name", "s",
+               "idVendor",     "vendor_id",    "x",
+               "idProduct",    "product_id",   "x",
+               "serial",       "serial",       "s",
+               "speed",        "speed",        "d",
+       };
+
+       if (!(d = opendir("/sys/bus/usb/devices")))
+               return rpc_errno_status();
+
+       blob_buf_init(&buf, 0);
+       list = blobmsg_open_array(&buf, "devices");
+
+       while ((e = readdir(d)) != NULL)
+       {
+               if (e->d_name[0] < '0' || e->d_name[0] > '9')
+                       continue;
+
+               snprintf(line, sizeof(line) - 1,
+                        "/sys/bus/usb/devices/%s/%s", e->d_name, attributes[0]);
+
+               if (stat(line, &s))
+                       continue;
+
+               device = blobmsg_open_table(&buf, NULL);
+
+               blobmsg_add_string(&buf, "name", e->d_name);
+
+               for (i = 0; i < sizeof(attributes) / sizeof(attributes[0]); i += 3)
+               {
+                       snprintf(line, sizeof(line) - 1,
+                                        "/sys/bus/usb/devices/%s/%s", e->d_name, attributes[i]);
+
+                       if (!(f = fopen(line, "r")))
+                               continue;
+
+                       if (fgets(line, sizeof(line) - 1, f))
+                       {
+                               switch (*attributes[i+2])
+                               {
+                               case 'x':
+                                       blobmsg_add_u32(&buf, attributes[i+1],
+                                                       strtoul(line, NULL, 16));
+                                       break;
+
+                               case 'd':
+                                       blobmsg_add_u32(&buf, attributes[i+1],
+                                                       strtoul(line, NULL, 10));
+                                       break;
+
+                               default:
+                                       if ((p = strchr(line, '\n')) != NULL)
+                                               while (p > line && isspace(*p))
+                                                       *p-- = 0;
+
+                                       blobmsg_add_string(&buf, attributes[i+1], line);
+                                       break;
+                               }
+                       }
+
+                       fclose(f);
+               }
+
+               blobmsg_close_table(&buf, device);
+       }
+
+       closedir(d);
+
+       blobmsg_close_array(&buf, list);
+       ubus_send_reply(ctx, req, buf.head);
+
+       return 0;
+}
+
+static int
+rpc_luci2_upgrade_test(struct ubus_context *ctx, struct ubus_object *obj,
+                       struct ubus_request_data *req, const char *method,
+                       struct blob_attr *msg)
+{
+       const char *cmd[4] = { "sysupgrade", "--test", "/tmp/firmware.bin", NULL };
+       return rpc_exec(cmd, NULL, NULL, NULL, NULL, ctx, req);
+}
+
+static int
+rpc_luci2_upgrade_start(struct ubus_context *ctx, struct ubus_object *obj,
+                        struct ubus_request_data *req, const char *method,
+                        struct blob_attr *msg)
+{
+       return 0;
+}
+
+static int
+rpc_luci2_upgrade_abort(struct ubus_context *ctx, struct ubus_object *obj,
+                        struct ubus_request_data *req, const char *method,
+                        struct blob_attr *msg)
+{
+       unlink("/tmp/firmware.bin");
+       return 0;
+}
+
 
 static FILE *
 dnsmasq_leasefile(void)
@@ -1376,25 +1716,22 @@ rpc_luci2_opkg_config_set(struct ubus_context *ctx, struct ubus_object *obj,
                           struct blob_attr *msg)
 {
        FILE *f;
-       struct blob_attr *tb[__RPC_OC_MAX];
+       struct blob_attr *tb[__RPC_D_MAX];
 
-       blobmsg_parse(rpc_opkg_package_policy, __RPC_OC_MAX, tb,
+       blobmsg_parse(rpc_data_policy, __RPC_D_MAX, tb,
                      blob_data(msg), blob_len(msg));
 
-       if (!tb[RPC_OC_CONFIG])
-               return UBUS_STATUS_INVALID_ARGUMENT;
-
-       if (blobmsg_type(tb[RPC_OC_CONFIG]) != BLOBMSG_TYPE_STRING)
+       if (!tb[RPC_D_DATA])
                return UBUS_STATUS_INVALID_ARGUMENT;
 
-       if (blobmsg_data_len(tb[RPC_OC_CONFIG]) >= 2048)
+       if (blobmsg_data_len(tb[RPC_D_DATA]) >= 2048)
                return UBUS_STATUS_NOT_SUPPORTED;
 
        if (!(f = fopen("/etc/opkg.conf", "w")))
                return rpc_errno_status();
 
-       fwrite(blobmsg_data(tb[RPC_OC_CONFIG]),
-              blobmsg_data_len(tb[RPC_OC_CONFIG]), 1, f);
+       fwrite(blobmsg_data(tb[RPC_D_DATA]),
+              blobmsg_data_len(tb[RPC_D_DATA]), 1, f);
 
        fclose(f);
        return 0;
@@ -1408,17 +1745,30 @@ int rpc_luci2_api_init(struct ubus_context *ctx)
        static const struct ubus_method luci2_system_methods[] = {
                UBUS_METHOD_NOARG("syslog",       rpc_luci2_system_log),
                UBUS_METHOD_NOARG("dmesg",        rpc_luci2_system_dmesg),
+               UBUS_METHOD_NOARG("diskfree",     rpc_luci2_system_diskfree),
                UBUS_METHOD_NOARG("process_list", rpc_luci2_process_list),
                UBUS_METHOD("process_signal",     rpc_luci2_process_signal,
                                                  rpc_signal_policy),
                UBUS_METHOD_NOARG("init_list",    rpc_luci2_init_list),
                UBUS_METHOD("init_action",        rpc_luci2_init_action,
                                                  rpc_init_policy),
+               UBUS_METHOD_NOARG("rclocal_get",  rpc_luci2_rclocal_get),
+               UBUS_METHOD("rclocal_set",        rpc_luci2_rclocal_set,
+                                                 rpc_data_policy),
+               UBUS_METHOD_NOARG("crontab_get",  rpc_luci2_crontab_get),
+               UBUS_METHOD("crontab_set",        rpc_luci2_crontab_set,
+                                                 rpc_data_policy),
                UBUS_METHOD_NOARG("sshkeys_get",  rpc_luci2_sshkeys_get),
                UBUS_METHOD("sshkeys_set",        rpc_luci2_sshkeys_set,
                                                  rpc_sshkey_policy),
                UBUS_METHOD("password_set",       rpc_luci2_password_set,
-                                                 rpc_password_policy)
+                                                 rpc_password_policy),
+               UBUS_METHOD_NOARG("led_list",     rpc_luci2_led_list),
+               UBUS_METHOD_NOARG("usb_list",     rpc_luci2_usb_list),
+               UBUS_METHOD_NOARG("upgrade_test", rpc_luci2_upgrade_test),
+               UBUS_METHOD("upgrade_start",      rpc_luci2_upgrade_start,
+                                                 rpc_upgrade_policy),
+               UBUS_METHOD_NOARG("upgrade_abort", rpc_luci2_upgrade_abort)
        };
 
        static struct ubus_object_type luci2_system_type =
@@ -1467,7 +1817,7 @@ int rpc_luci2_api_init(struct ubus_context *ctx)
                UBUS_METHOD_NOARG("update",          rpc_luci2_opkg_update),
                UBUS_METHOD_NOARG("config_get",      rpc_luci2_opkg_config_get),
                UBUS_METHOD("config_set",            rpc_luci2_opkg_config_set,
-                                                    rpc_opkg_config_policy)
+                                                    rpc_data_policy)
        };
 
        static struct ubus_object_type luci2_opkg_type =