X-Git-Url: http://git.archive.openwrt.org/?a=blobdiff_plain;f=luci2.c;h=276c60db1926cdca4dd34e50b7090aca8e777f2e;hb=059a6df1fd424c76ac6b1a7d54f187a95822f5f6;hp=283d7cfba926c4b14074684c2f099ad688da88a3;hpb=cc288ccea51f4fc6a318582466e807c4c33bc6e5;p=project%2Frpcd.git diff --git a/luci2.c b/luci2.c index 283d7cf..276c60d 100644 --- a/luci2.c +++ b/luci2.c @@ -1,5 +1,5 @@ /* - * luci-rpcd - LuCI UBUS RPC server + * rpcd - UBUS RPC server * * Copyright (C) 2013 Jo-Philipp Wich * @@ -21,13 +21,30 @@ #include #include #include +#include +#include #include #include +#include #include #include #include +#include +#include +#include +#include -#include "luci2.h" +#include "plugin.h" + +/* limit of log size buffer */ +#define RPC_LUCI2_MAX_LOGSIZE (128 * 1024) +#define RPC_LUCI2_DEF_LOGSIZE (16 * 1024) + +/* location of menu definitions */ +#define RPC_LUCI2_MENU_FILES "/usr/share/luci2/menu.d/*.json" /* */ + + +static const struct rpc_daemon_ops *ops; static struct blob_buf buf; static struct uci_context *cursor; @@ -54,6 +71,76 @@ static const struct blobmsg_policy rpc_init_policy[__RPC_I_MAX] = { [RPC_I_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_STRING }, }; +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 +}; + +static const struct blobmsg_policy rpc_sshkey_policy[__RPC_K_MAX] = { + [RPC_K_KEYS] = { .name = "keys", .type = BLOBMSG_TYPE_ARRAY }, +}; + +enum { + RPC_P_USER, + RPC_P_PASSWORD, + __RPC_P_MAX +}; + +static const struct blobmsg_policy rpc_password_policy[__RPC_P_MAX] = { + [RPC_P_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING }, + [RPC_P_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING }, +}; + +enum { + RPC_OM_LIMIT, + RPC_OM_OFFSET, + RPC_OM_PATTERN, + __RPC_OM_MAX +}; + +static const struct blobmsg_policy rpc_opkg_match_policy[__RPC_OM_MAX] = { + [RPC_OM_LIMIT] = { .name = "limit", .type = BLOBMSG_TYPE_INT32 }, + [RPC_OM_OFFSET] = { .name = "offset", .type = BLOBMSG_TYPE_INT32 }, + [RPC_OM_PATTERN] = { .name = "pattern", .type = BLOBMSG_TYPE_STRING }, +}; + +enum { + RPC_OP_PACKAGE, + __RPC_OP_MAX +}; + +static const struct blobmsg_policy rpc_opkg_package_policy[__RPC_OP_MAX] = { + [RPC_OP_PACKAGE] = { .name = "package", .type = BLOBMSG_TYPE_STRING }, +}; + +enum { + RPC_UPGRADE_KEEP, + __RPC_UPGRADE_MAX +}; + +static const struct blobmsg_policy rpc_upgrade_policy[__RPC_UPGRADE_MAX] = { + [RPC_UPGRADE_KEEP] = { .name = "keep", .type = BLOBMSG_TYPE_BOOL }, +}; + +enum { + RPC_MENU_SESSION, + __RPC_MENU_MAX +}; + +static const struct blobmsg_policy rpc_menu_policy[__RPC_MENU_MAX] = { + [RPC_MENU_SESSION] = { .name = "ubus_rpc_session", + .type = BLOBMSG_TYPE_STRING }, +}; + static int rpc_errno_status(void) @@ -206,6 +293,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) @@ -338,14 +458,14 @@ rpc_luci2_init_list(struct ubus_context *ctx, struct ubus_object *obj, while (fgets(path, sizeof(path) - 1, f)) { - p = strtok(path, "="); + p = strtok(path, "= \t"); - if (!strcmp(p, "START") && !!(p = strtok(NULL, " \t\n"))) + if (!strcmp(p, "START") && !!(p = strtok(NULL, "= \t\n"))) { n = atoi(p); blobmsg_add_u32(&buf, "start", n); } - else if (!strcmp(p, "STOP") && !!(p = strtok(NULL, " \t\n"))) + else if (!strcmp(p, "STOP") && !!(p = strtok(NULL, "= \t\n"))) { blobmsg_add_u32(&buf, "stop", atoi(p)); break; @@ -365,75 +485,733 @@ rpc_luci2_init_list(struct ubus_context *ctx, struct ubus_object *obj, blobmsg_add_u8(&buf, "enabled", 0); } - blobmsg_close_table(&buf, t); - -skip: - fclose(f); + blobmsg_close_table(&buf, t); + +skip: + fclose(f); + } + } + + closedir(d); + blobmsg_close_array(&buf, c); + + ubus_send_reply(ctx, req, buf.head); + return 0; +} + +static int +rpc_luci2_init_action(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + int fd; + pid_t pid; + struct stat s; + char path[PATH_MAX]; + const char *action; + struct blob_attr *tb[__RPC_I_MAX]; + + blobmsg_parse(rpc_init_policy, __RPC_I_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[RPC_I_NAME] || !tb[RPC_I_ACTION]) + return UBUS_STATUS_INVALID_ARGUMENT; + + action = blobmsg_data(tb[RPC_I_ACTION]); + + if (strcmp(action, "start") && strcmp(action, "stop") && + strcmp(action, "reload") && strcmp(action, "restart") && + strcmp(action, "enable") && strcmp(action, "disable")) + return UBUS_STATUS_INVALID_ARGUMENT; + + snprintf(path, sizeof(path) - 1, "/etc/init.d/%s", + (char *)blobmsg_data(tb[RPC_I_NAME])); + + if (stat(path, &s)) + return rpc_errno_status(); + + if (!(s.st_mode & S_IXUSR)) + return UBUS_STATUS_PERMISSION_DENIED; + + switch ((pid = fork())) + { + case -1: + return rpc_errno_status(); + + case 0: + uloop_done(); + + if ((fd = open("/dev/null", O_RDWR)) > -1) + { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + + close(fd); + } + + chdir("/"); + + if (execl(path, path, action, NULL)) + return rpc_errno_status(); + + default: + return 0; + } +} + +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) +{ + FILE *f; + void *c; + char *p, line[4096]; + + if (!(f = fopen("/etc/dropbear/authorized_keys", "r"))) + return rpc_errno_status(); + + blob_buf_init(&buf, 0); + c = blobmsg_open_array(&buf, "keys"); + + while (fgets(line, sizeof(line) - 1, f)) + { + for (p = line + strlen(line) - 1; (p > line) && isspace(*p); p--) + *p = 0; + + for (p = line; isspace(*p); p++) + *p = 0; + + if (*p) + blobmsg_add_string(&buf, NULL, p); + } + + blobmsg_close_array(&buf, c); + fclose(f); + + ubus_send_reply(ctx, req, buf.head); + return 0; +} + +static int +rpc_luci2_sshkeys_set(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + FILE *f; + int rem; + struct blob_attr *cur, *tb[__RPC_K_MAX]; + + blobmsg_parse(rpc_sshkey_policy, __RPC_K_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[RPC_K_KEYS]) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (!(f = fopen("/etc/dropbear/authorized_keys", "w"))) + return rpc_errno_status(); + + blobmsg_for_each_attr(cur, tb[RPC_K_KEYS], rem) + { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + fwrite(blobmsg_data(cur), blobmsg_data_len(cur) - 1, 1, f); + fwrite("\n", 1, 1, f); + } + + fclose(f); + return 0; +} + +static int +rpc_luci2_password_set(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + pid_t pid; + int fd, fds[2]; + struct stat s; + struct blob_attr *tb[__RPC_P_MAX]; + + blobmsg_parse(rpc_password_policy, __RPC_P_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[RPC_P_USER] || !tb[RPC_P_PASSWORD]) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (stat("/usr/bin/passwd", &s)) + return UBUS_STATUS_NOT_FOUND; + + if (!(s.st_mode & S_IXUSR)) + return UBUS_STATUS_PERMISSION_DENIED; + + if (pipe(fds)) + return rpc_errno_status(); + + switch ((pid = fork())) + { + case -1: + close(fds[0]); + close(fds[1]); + return rpc_errno_status(); + + case 0: + uloop_done(); + + dup2(fds[0], 0); + close(fds[0]); + close(fds[1]); + + if ((fd = open("/dev/null", O_RDWR)) > -1) + { + dup2(fd, 1); + dup2(fd, 2); + close(fd); + } + + chdir("/"); + + if (execl("/usr/bin/passwd", "/usr/bin/passwd", + blobmsg_data(tb[RPC_P_USER]), NULL)) + return rpc_errno_status(); + + default: + close(fds[0]); + + write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]), + blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1); + write(fds[1], "\n", 1); + + usleep(100 * 1000); + + write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]), + blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1); + write(fds[1], "\n", 1); + + close(fds[1]); + + waitpid(pid, NULL, 0); + + return 0; + } +} + +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 ops->exec(cmd, NULL, 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_clean(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + if (unlink("/tmp/firmware.bin")) + return rpc_errno_status(); + + return 0; +} + +static int +rpc_luci2_backup_restore(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", "--restore-backup", + "/tmp/backup.tar.gz", NULL }; + + return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req); +} + +static int +rpc_luci2_backup_clean(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + if (unlink("/tmp/backup.tar.gz")) + return rpc_errno_status(); + + return 0; +} + +static int +rpc_luci2_backup_config_get(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + FILE *f; + char conf[2048] = { 0 }; + + if (!(f = fopen("/etc/sysupgrade.conf", "r"))) + return rpc_errno_status(); + + fread(conf, sizeof(conf) - 1, 1, f); + fclose(f); + + blob_buf_init(&buf, 0); + blobmsg_add_string(&buf, "config", conf); + + ubus_send_reply(ctx, req, buf.head); + return 0; +} + +static int +rpc_luci2_backup_config_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]) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (blobmsg_data_len(tb[RPC_D_DATA]) >= 2048) + return UBUS_STATUS_NOT_SUPPORTED; + + if (!(f = fopen("/etc/sysupgrade.conf", "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; +} + +struct backup_state { + bool open; + void *array; +}; + +static int +backup_parse_list(struct blob_buf *blob, char *buf, int len, void *priv) +{ + struct backup_state *s = priv; + char *nl = strchr(buf, '\n'); + + if (!nl) + return 0; + + if (!s->open) + { + s->open = true; + s->array = blobmsg_open_array(blob, "files"); + } + + *nl = 0; + blobmsg_add_string(blob, NULL, buf); + + return (nl - buf + 1); +} + +static int +backup_finish_list(struct blob_buf *blob, int status, void *priv) +{ + struct backup_state *s = priv; + + if (!s->open) + return UBUS_STATUS_NO_DATA; + + blobmsg_close_array(blob, s->array); + + return UBUS_STATUS_OK; +} + +static int +rpc_luci2_backup_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct backup_state *state = NULL; + const char *cmd[3] = { "sysupgrade", "--list-backup", NULL }; + + state = malloc(sizeof(*state)); + + if (!state) + return UBUS_STATUS_UNKNOWN_ERROR; + + memset(state, 0, sizeof(*state)); + + return ops->exec(cmd, NULL, backup_parse_list, NULL, backup_finish_list, + state, ctx, req); +} + +static int +rpc_luci2_reset_test(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + FILE *mtd; + struct stat s; + char line[64] = { 0 }; + bool supported = false; + + if (!stat("/sbin/mtd", &s) && (s.st_mode & S_IXUSR)) + { + if ((mtd = fopen("/proc/mtd", "r")) != NULL) + { + while (fgets(line, sizeof(line) - 1, mtd)) + { + if (strstr(line, "\"rootfs_data\"")) + { + supported = true; + break; + } + } + + fclose(mtd); } } - closedir(d); - blobmsg_close_array(&buf, c); + blob_buf_init(&buf, 0); + blobmsg_add_u8(&buf, "supported", supported); ubus_send_reply(ctx, req, buf.head); + return 0; } static int -rpc_luci2_init_action(struct ubus_context *ctx, struct ubus_object *obj, +rpc_luci2_reset_start(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { - int fd; - pid_t pid; - struct stat s; - char path[PATH_MAX]; - const char *action; - struct blob_attr *tb[__RPC_I_MAX]; + switch (fork()) + { + case -1: + return rpc_errno_status(); - blobmsg_parse(rpc_init_policy, __RPC_I_MAX, tb, - blob_data(msg), blob_len(msg)); + case 0: + uloop_done(); - if (!tb[RPC_I_NAME] || !tb[RPC_I_ACTION]) - return UBUS_STATUS_INVALID_ARGUMENT; + chdir("/"); - action = blobmsg_data(tb[RPC_I_ACTION]); + close(0); + close(1); + close(2); - if (strcmp(action, "start") && strcmp(action, "stop") && - strcmp(action, "reload") && strcmp(action, "restart") && - strcmp(action, "enable") && strcmp(action, "disable")) - return UBUS_STATUS_INVALID_ARGUMENT; + sleep(1); - snprintf(path, sizeof(path) - 1, "/etc/init.d/%s", - (char *)blobmsg_data(tb[RPC_I_NAME])); + execl("/sbin/mtd", "/sbin/mtd", "-r", "erase", "rootfs_data", NULL); - if (stat(path, &s)) return rpc_errno_status(); - if (!(s.st_mode & S_IXUSR)) - return UBUS_STATUS_PERMISSION_DENIED; + default: + return 0; + } +} - switch ((pid = fork())) +static int +rpc_luci2_reboot(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + switch (fork()) { case -1: return rpc_errno_status(); case 0: - uloop_done(); + chdir("/"); - if ((fd = open("/dev/null", O_RDWR)) > -1) - { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); + close(0); + close(1); + close(2); - close(fd); - } + sleep(1); - chdir("/"); + execl("/sbin/reboot", "/sbin/reboot", NULL); - if (execl(path, path, action, NULL)) - return rpc_errno_status(); + return rpc_errno_status(); default: return 0; @@ -959,19 +1737,397 @@ rpc_luci2_network_routes6(struct ubus_context *ctx, struct ubus_object *obj, } -int rpc_luci2_api_init(struct ubus_context *ctx) +struct opkg_state { + int cur_offset; + int cur_count; + int req_offset; + int req_count; + int total; + bool open; + void *array; +}; + +static int +opkg_parse_list(struct blob_buf *blob, char *buf, int len, void *priv) +{ + struct opkg_state *s = priv; + + char *ptr, *last; + char *nl = strchr(buf, '\n'); + char *name = NULL, *vers = NULL, *desc = NULL; + void *c; + + if (!nl) + return 0; + + s->total++; + + if (s->cur_offset++ < s->req_offset) + goto skip; + + if (s->cur_count++ >= s->req_count) + goto skip; + + if (!s->open) + { + s->open = true; + s->array = blobmsg_open_array(blob, "packages"); + } + + for (ptr = buf, last = buf, *nl = 0; ptr <= nl; ptr++) + { + if (!*ptr || (*ptr == ' ' && *(ptr+1) == '-' && *(ptr+2) == ' ')) + { + if (!name) + { + name = last; + last = ptr + 3; + *ptr = 0; + ptr += 2; + } + else if (!vers) + { + vers = last; + desc = *ptr ? (ptr + 3) : NULL; + *ptr = 0; + break; + } + } + } + + if (name && vers) + { + c = blobmsg_open_array(blob, NULL); + + blobmsg_add_string(blob, NULL, name); + blobmsg_add_string(blob, NULL, vers); + + if (desc && *desc) + blobmsg_add_string(blob, NULL, desc); + + blobmsg_close_array(blob, c); + } + +skip: + return (nl - buf + 1); +} + +static int +opkg_finish_list(struct blob_buf *blob, int status, void *priv) +{ + struct opkg_state *s = priv; + + if (!s->open) + return UBUS_STATUS_NO_DATA; + + blobmsg_close_array(blob, s->array); + blobmsg_add_u32(blob, "total", s->total); + + return UBUS_STATUS_OK; +} + +static int +opkg_exec_list(const char *action, struct blob_attr *msg, + struct ubus_context *ctx, struct ubus_request_data *req) +{ + struct opkg_state *state = NULL; + struct blob_attr *tb[__RPC_OM_MAX]; + const char *cmd[5] = { "opkg", action, "-nocase", NULL, NULL }; + + blobmsg_parse(rpc_opkg_match_policy, __RPC_OM_MAX, tb, + blob_data(msg), blob_len(msg)); + + state = malloc(sizeof(*state)); + + if (!state) + return UBUS_STATUS_UNKNOWN_ERROR; + + memset(state, 0, sizeof(*state)); + + if (tb[RPC_OM_PATTERN]) + cmd[3] = blobmsg_data(tb[RPC_OM_PATTERN]); + + if (tb[RPC_OM_LIMIT]) + state->req_count = blobmsg_get_u32(tb[RPC_OM_LIMIT]); + + if (tb[RPC_OM_OFFSET]) + state->req_offset = blobmsg_get_u32(tb[RPC_OM_OFFSET]); + + if (state->req_offset < 0) + state->req_offset = 0; + + if (state->req_count <= 0 || state->req_count > 100) + state->req_count = 100; + + return ops->exec(cmd, NULL, opkg_parse_list, NULL, opkg_finish_list, + state, ctx, req); +} + + +static int +rpc_luci2_opkg_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return opkg_exec_list("list", msg, ctx, req); +} + +static int +rpc_luci2_opkg_list_installed(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return opkg_exec_list("list-installed", msg, ctx, req); +} + +static int +rpc_luci2_opkg_find(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return opkg_exec_list("find", msg, ctx, req); +} + +static int +rpc_luci2_opkg_update(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + const char *cmd[3] = { "opkg", "update", NULL }; + return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req); +} + +static int +rpc_luci2_opkg_install(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__RPC_OP_MAX]; + const char *cmd[5] = { "opkg", "--force-overwrite", + "install", NULL, NULL }; + + blobmsg_parse(rpc_opkg_package_policy, __RPC_OP_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[RPC_OP_PACKAGE]) + return UBUS_STATUS_INVALID_ARGUMENT; + + cmd[3] = blobmsg_data(tb[RPC_OP_PACKAGE]); + + return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req); +} + +static int +rpc_luci2_opkg_remove(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__RPC_OP_MAX]; + const char *cmd[5] = { "opkg", "--force-removal-of-dependent-packages", + "remove", NULL, NULL }; + + blobmsg_parse(rpc_opkg_package_policy, __RPC_OP_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[RPC_OP_PACKAGE]) + return UBUS_STATUS_INVALID_ARGUMENT; + + cmd[3] = blobmsg_data(tb[RPC_OP_PACKAGE]); + + return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req); +} + +static int +rpc_luci2_opkg_config_get(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + FILE *f; + char conf[2048] = { 0 }; + + if (!(f = fopen("/etc/opkg.conf", "r"))) + return rpc_errno_status(); + + fread(conf, sizeof(conf) - 1, 1, f); + fclose(f); + + blob_buf_init(&buf, 0); + blobmsg_add_string(&buf, "config", conf); + + ubus_send_reply(ctx, req, buf.head); + return 0; +} + +static int +rpc_luci2_opkg_config_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]) + return UBUS_STATUS_INVALID_ARGUMENT; + + 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_D_DATA]), + blobmsg_data_len(tb[RPC_D_DATA]) - 1, 1, f); + + fclose(f); + return 0; +} + + +static bool +menu_access(struct blob_attr *sid, struct blob_attr *acls, struct blob_buf *e) +{ + int rem; + struct blob_attr *acl; + bool rv = true; + void *c; + + c = blobmsg_open_table(e, "write"); + + blobmsg_for_each_attr(acl, acls, rem) + { + if (!ops->access(blobmsg_data(sid), "luci-ui", + blobmsg_data(acl), "read")) + { + rv = false; + break; + } + + blobmsg_add_u8(e, blobmsg_data(acl), + ops->access(blobmsg_data(sid), "luci-ui", + blobmsg_data(acl), "write")); + } + + blobmsg_close_table(e, c); + + return rv; +} + +static int +rpc_luci2_ui_menu(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + int i, rem, rem2; + glob_t gl; + struct blob_buf menu = { 0 }; + struct blob_buf item = { 0 }; + struct blob_attr *entry, *attr; + struct blob_attr *tb[__RPC_MENU_MAX]; + bool access; + void *c, *d; + + blobmsg_parse(rpc_menu_policy, __RPC_MENU_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[RPC_MENU_SESSION]) + return UBUS_STATUS_INVALID_ARGUMENT; + + + blob_buf_init(&buf, 0); + c = blobmsg_open_table(&buf, "menu"); + + if (!glob(RPC_LUCI2_MENU_FILES, 0, NULL, &gl)) + { + for (i = 0; i < gl.gl_pathc; i++) + { + blob_buf_init(&menu, 0); + + if (!blobmsg_add_json_from_file(&menu, gl.gl_pathv[i])) + goto skip; + + blob_for_each_attr(entry, menu.head, rem) + { + access = true; + + blob_buf_init(&item, 0); + d = blobmsg_open_table(&item, blobmsg_name(entry)); + + blobmsg_for_each_attr(attr, entry, rem2) + { + if (blob_id(attr) == BLOBMSG_TYPE_ARRAY && + !strcmp(blobmsg_name(attr), "acls")) + access = menu_access(tb[RPC_MENU_SESSION], attr, &item); + else + blobmsg_add_blob(&item, attr); + } + + blobmsg_close_table(&item, d); + + if (access) + blob_for_each_attr(attr, item.head, rem2) + blobmsg_add_blob(&buf, attr); + + blob_buf_free(&item); + } + +skip: + blob_buf_free(&menu); + } + + globfree(&gl); + } + + blobmsg_close_table(&buf, c); + + ubus_send_reply(ctx, req, buf.head); + return 0; +} + + +static int +rpc_luci2_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx) { int rv = 0; 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) + 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), + 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_clean", rpc_luci2_upgrade_clean), + UBUS_METHOD_NOARG("backup_restore", rpc_luci2_backup_restore), + UBUS_METHOD_NOARG("backup_clean", rpc_luci2_backup_clean), + UBUS_METHOD_NOARG("backup_config_get", rpc_luci2_backup_config_get), + UBUS_METHOD("backup_config_set", rpc_luci2_backup_config_set, + rpc_data_policy), + UBUS_METHOD_NOARG("backup_list", rpc_luci2_backup_list), + UBUS_METHOD_NOARG("reset_test", rpc_luci2_reset_test), + UBUS_METHOD_NOARG("reset_start", rpc_luci2_reset_start), + UBUS_METHOD_NOARG("reboot", rpc_luci2_reboot) }; static struct ubus_object_type luci2_system_type = @@ -1005,13 +2161,64 @@ int rpc_luci2_api_init(struct ubus_context *ctx) .n_methods = ARRAY_SIZE(luci2_network_methods), }; + + static const struct ubus_method luci2_opkg_methods[] = { + UBUS_METHOD("list", rpc_luci2_opkg_list, + rpc_opkg_match_policy), + UBUS_METHOD("list_installed", rpc_luci2_opkg_list_installed, + rpc_opkg_match_policy), + UBUS_METHOD("find", rpc_luci2_opkg_find, + rpc_opkg_match_policy), + UBUS_METHOD("install", rpc_luci2_opkg_install, + rpc_opkg_package_policy), + UBUS_METHOD("remove", rpc_luci2_opkg_remove, + rpc_opkg_package_policy), + 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_data_policy) + }; + + static struct ubus_object_type luci2_opkg_type = + UBUS_OBJECT_TYPE("luci-rpc-luci2-network", luci2_opkg_methods); + + static struct ubus_object opkg_obj = { + .name = "luci2.opkg", + .type = &luci2_opkg_type, + .methods = luci2_opkg_methods, + .n_methods = ARRAY_SIZE(luci2_opkg_methods), + }; + + + static const struct ubus_method luci2_ui_methods[] = { + UBUS_METHOD_NOARG("menu", rpc_luci2_ui_menu) + }; + + static struct ubus_object_type luci2_ui_type = + UBUS_OBJECT_TYPE("luci-rpc-luci2-ui", luci2_ui_methods); + + static struct ubus_object ui_obj = { + .name = "luci2.ui", + .type = &luci2_ui_type, + .methods = luci2_ui_methods, + .n_methods = ARRAY_SIZE(luci2_ui_methods), + }; + cursor = uci_alloc_context(); if (!cursor) return UBUS_STATUS_UNKNOWN_ERROR; + ops = o; + rv |= ubus_add_object(ctx, &system_obj); rv |= ubus_add_object(ctx, &network_obj); + rv |= ubus_add_object(ctx, &opkg_obj); + rv |= ubus_add_object(ctx, &ui_obj); return rv; } + +const struct rpc_plugin rpc_plugin = { + .init = rpc_luci2_api_init +};