X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fluci2%2Fui.git;a=blobdiff_plain;f=luci2%2Fsrc%2Frpcd%2Fluci2.c;h=c8ef9bf370d22be3b65ddd406cf5fed6d8a75ef3;hp=1661424d72244eeb6ce42e6830f2b275d63659b3;hb=3d591dfe4abf79c1e43d0126556f65774fe0c80b;hpb=65e6c5916f1cdf89dd597a21cf56f060ddd396b7 diff --git a/luci2/src/rpcd/luci2.c b/luci2/src/rpcd/luci2.c index 1661424..c8ef9bf 100644 --- a/luci2/src/rpcd/luci2.c +++ b/luci2/src/rpcd/luci2.c @@ -144,6 +144,15 @@ static const struct blobmsg_policy rpc_menu_policy[__RPC_MENU_MAX] = { .type = BLOBMSG_TYPE_STRING }, }; +enum { + RPC_SWITCH_NAME, + __RPC_SWITCH_MAX +}; + +static const struct blobmsg_policy rpc_switch_policy[__RPC_SWITCH_MAX] = { + [RPC_SWITCH_NAME] = { .name = "switch", .type = BLOBMSG_TYPE_STRING }, +}; + static int rpc_errno_status(void) @@ -1740,6 +1749,553 @@ rpc_luci2_network_routes6(struct ubus_context *ctx, struct ubus_object *obj, } +struct swconfig_state { + bool open; + void *array; + bool open2; + void *array2; + int port; +}; + +static int +swconfig_parse_list(struct blob_buf *blob, char *buf, int len, void *priv) +{ + char *p; + char *nl = strchr(buf, '\n'); + struct swconfig_state *s = priv; + + if (!nl) + return 0; + + if (!s->open) + { + s->open = true; + s->array = blobmsg_open_array(blob, "switches"); + } + + strtok(buf, "-"); + p = strtok(NULL, " \n"); + + if (p) + blobmsg_add_string(blob, NULL, p); + + return (nl - buf + 1); +} + +static int +swconfig_finish_list(struct blob_buf *blob, int status, void *priv) +{ + struct swconfig_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_network_sw_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct swconfig_state *state = NULL; + const char *cmd[3] = { "swconfig", "list", NULL }; + + state = malloc(sizeof(*state)); + + if (!state) + return UBUS_STATUS_UNKNOWN_ERROR; + + memset(state, 0, sizeof(*state)); + + return ops->exec(cmd, NULL, swconfig_parse_list, NULL, swconfig_finish_list, + state, ctx, req); +} + + +static int +swconfig_parse_help(struct blob_buf *blob, char *buf, int len, void *priv) +{ + void *c; + char *p; + char *nl = strchr(buf, '\n'); + struct swconfig_state *s = priv; + + if (!nl) + return 0; + + if (!s->open) + { + s->open = true; + s->array = blobmsg_open_table(blob, "info"); + } + + switch (*buf) + { + case ' ': + strtok(buf, "-"); + p = strtok(NULL, "-\n"); + + if (p) + { + if (s->open2) + blobmsg_close_array(blob, s->array2); + + s->array2 = blobmsg_open_array(blob, p); + s->open2 = true; + } + + break; + + case '\t': + c = blobmsg_open_table(blob, NULL); + + strtok(buf, "("); + p = strtok(NULL, ")"); + + if (p) + blobmsg_add_string(blob, "type", p); + + p = strtok(NULL, ":( "); + + if (p) + blobmsg_add_string(blob, "name", p); + + p = strtok(NULL, "\n"); + *(nl - 1) = 0; + + if (p) + blobmsg_add_string(blob, "description", p + 1); + + blobmsg_close_table(blob, c); + break; + + default: + strtok(buf, "("); + p = strtok(NULL, ")"); + + if (p) + blobmsg_add_string(blob, "model", p); + + strtok(NULL, ":"); + p = strtok(NULL, "("); + + if (p) + blobmsg_add_u32(blob, "num_ports", atoi(p)); + + strtok(NULL, "@"); + p = strtok(NULL, ")"); + + if (p) + blobmsg_add_u32(blob, "cpu_port", atoi(p)); + + strtok(NULL, ":"); + p = strtok(NULL, "\n"); + + if (p) + blobmsg_add_u32(blob, "num_vlans", atoi(p)); + + break; + } + + return (nl - buf + 1); +} + +static int +swconfig_finish_help(struct blob_buf *blob, int status, void *priv) +{ + struct swconfig_state *s = priv; + + if (!s->open) + return UBUS_STATUS_NO_DATA; + + if (s->open2) + blobmsg_close_array(blob, s->array2); + + blobmsg_close_table(blob, s->array); + + return UBUS_STATUS_OK; +} + +static int +rpc_luci2_network_sw_info(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct swconfig_state *state = NULL; + struct blob_attr *tb[__RPC_SWITCH_MAX]; + const char *cmd[5] = { "swconfig", "dev", NULL, "help", NULL }; + + blobmsg_parse(rpc_switch_policy, __RPC_SWITCH_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[RPC_SWITCH_NAME]) + return UBUS_STATUS_INVALID_ARGUMENT; + + state = malloc(sizeof(*state)); + + if (!state) + return UBUS_STATUS_UNKNOWN_ERROR; + + memset(state, 0, sizeof(*state)); + + cmd[2] = blobmsg_get_string(tb[RPC_SWITCH_NAME]); + + return ops->exec(cmd, NULL, swconfig_parse_help, NULL, swconfig_finish_help, + state, ctx, req); +} + + +static void +swconfig_parse_link(struct blob_buf *blob, char *val) +{ + char *p; + + int speed = 0; + + bool rxflow = false; + bool txflow = false; + bool duplex = false; + bool aneg = false; + bool up = false; + + for (p = strtok(val, " "); p; p = strtok(NULL, " ")) + { + if (!strncmp(p, "speed:", 6)) + speed = atoi(p + 6); + else if (!strcmp(p, "link:up")) + up = true; + else if (!strcmp(p, "txflow")) + txflow = true; + else if (!strcmp(p, "rxflow")) + rxflow = true; + else if (!strcmp(p, "full-duplex")) + duplex = true; + else if (!strcmp(p, "auto")) + aneg = true; + } + + blobmsg_add_u8(blob, "link", up); + blobmsg_add_u8(blob, "rx_flow_control", rxflow); + blobmsg_add_u8(blob, "tx_flow_control", txflow); + blobmsg_add_u8(blob, "full_duplex", duplex); + blobmsg_add_u8(blob, "auto_negotiation", aneg); + blobmsg_add_u32(blob, "speed", speed); +} + +static int +swconfig_parse_stat(struct blob_buf *blob, char *buf, int len, void *priv) +{ + char *p, *v; + char *nl = strchr(buf, '\n'); + struct swconfig_state *s = priv; + + if (!nl) + return 0; + + if (nl == buf) + return 1; + + if (!s->open) + { + s->open = true; + s->array = blobmsg_open_array(blob, "ports"); + } + + p = strtok(buf, " :\t"); + + if (p) + { + if (!strcmp(p, "Port")) + { + if (s->open2) + blobmsg_close_table(blob, s->array2); + + s->array2 = blobmsg_open_table(blob, NULL); + s->open2 = true; + } + else if (s->open2) + { + v = strtok(NULL, "\n"); + + if (v) + { + if (!strcmp(p, "link")) + swconfig_parse_link(blob, v); + } + } + } + + return (nl - buf + 1); +} + +static int +swconfig_finish_stat(struct blob_buf *blob, int status, void *priv) +{ + struct swconfig_state *s = priv; + + if (!s->open) + return UBUS_STATUS_NO_DATA; + + if (s->open2) + blobmsg_close_table(blob, s->array2); + + blobmsg_close_array(blob, s->array); + + return UBUS_STATUS_OK; +} + +static int +rpc_luci2_network_sw_status(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct swconfig_state *state = NULL; + struct blob_attr *tb[__RPC_SWITCH_MAX]; + const char *cmd[5] = { "swconfig", "dev", NULL, "show", NULL }; + + blobmsg_parse(rpc_switch_policy, __RPC_SWITCH_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (!tb[RPC_SWITCH_NAME]) + return UBUS_STATUS_INVALID_ARGUMENT; + + state = malloc(sizeof(*state)); + + if (!state) + return UBUS_STATUS_UNKNOWN_ERROR; + + memset(state, 0, sizeof(*state)); + + cmd[2] = blobmsg_get_string(tb[RPC_SWITCH_NAME]); + + return ops->exec(cmd, NULL, swconfig_parse_stat, NULL, swconfig_finish_stat, + state, ctx, req); +} + +enum { + NETWORK_CMD_PING, + NETWORK_CMD_PING6, + NETWORK_CMD_TRACEROUTE, + NETWORK_CMD_TRACEROUTE6, + NETWORK_CMD_NSLOOKUP +}; + +static int +network_cmd(struct ubus_context *ctx, struct ubus_request_data *req, + struct blob_attr *msg, int which) +{ + char *arg; + 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; + + arg = blobmsg_get_string(tb[RPC_D_DATA]); + + const char *cmds[][8] = { + [NETWORK_CMD_PING] = { + "ping", "-c", "5", "-W", "1", arg + }, + [NETWORK_CMD_PING6] = { + "ping6", "-c", "5", "-W", "1", arg + }, + [NETWORK_CMD_TRACEROUTE] = { + "traceroute", "-q", "1", "-w", "1", "-n", arg + }, + [NETWORK_CMD_TRACEROUTE6] = { + "traceroute6", "-q", "1", "-w", "2", "-n", arg + }, + [NETWORK_CMD_NSLOOKUP] = { + "nslookup", arg + } + }; + + return ops->exec(cmds[which], NULL, NULL, NULL, NULL, NULL, ctx, req); +} + +static int +rpc_luci2_network_ping(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return network_cmd(ctx, req, msg, NETWORK_CMD_PING); +} + +static int +rpc_luci2_network_ping6(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return network_cmd(ctx, req, msg, NETWORK_CMD_PING6); +} + +static int +rpc_luci2_network_traceroute(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return network_cmd(ctx, req, msg, NETWORK_CMD_TRACEROUTE); +} + +static int +rpc_luci2_network_traceroute6(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return network_cmd(ctx, req, msg, NETWORK_CMD_TRACEROUTE6); +} + +static int +rpc_luci2_network_nslookup(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return network_cmd(ctx, req, msg, NETWORK_CMD_NSLOOKUP); +} + + +static int +network_ifupdown(struct ubus_context *ctx, struct ubus_request_data *req, + struct blob_attr *msg, bool up) +{ + const char *cmd[3] = { NULL }; + 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; + + cmd[0] = up ? "/sbin/ifup" : "/sbin/ifdown"; + cmd[1] = blobmsg_get_string(tb[RPC_D_DATA]); + + return ops->exec(cmd, NULL, NULL, NULL, NULL, NULL, ctx, req); +} + +static int +rpc_luci2_network_ifup(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return network_ifupdown(ctx, req, msg, true); +} + +static int +rpc_luci2_network_ifdown(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + return network_ifupdown(ctx, req, msg, false); +} + +static int +rpc_luci2_network_dev_list(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + char path[PATH_MAX]; + struct dirent *e; + struct stat s; + void *c, *t; + bool wireless, bridge, tuntap; + int type, flags; + DIR *d; + FILE *f; + + if (!(d = opendir("/sys/class/net"))) + return rpc_errno_status(); + + blob_buf_init(&buf, 0); + c = blobmsg_open_array(&buf, "devices"); + + while ((e = readdir(d)) != NULL) + { + snprintf(path, sizeof(path) - 1, "/sys/class/net/%s/type", e->d_name); + + if (stat(path, &s) || !S_ISREG(s.st_mode) || !(f = fopen(path, "r"))) + continue; + + type = 1; + memset(path, 0, sizeof(path)); + + if (fread(path, 1, sizeof(path) - 1, f) > 0) + type = atoi(path); + + fclose(f); + + snprintf(path, sizeof(path) - 1, "/sys/class/net/%s/flags", e->d_name); + + if (stat(path, &s) || !S_ISREG(s.st_mode) || !(f = fopen(path, "r"))) + continue; + + flags = 0; + memset(path, 0, sizeof(path)); + + if (fread(path, 1, sizeof(path) - 1, f) > 0) + flags = strtoul(path, NULL, 16); + + fclose(f); + + snprintf(path, sizeof(path) - 1, + "/sys/class/net/%s/wireless", e->d_name); + + wireless = (!stat(path, &s) && S_ISDIR(s.st_mode)); + + snprintf(path, sizeof(path) - 1, + "/sys/class/net/%s/phy80211", e->d_name); + + wireless = (wireless || (!stat(path, &s) && S_ISLNK(s.st_mode))); + + snprintf(path, sizeof(path) - 1, + "/sys/class/net/%s/bridge", e->d_name); + + bridge = (!stat(path, &s) && S_ISDIR(s.st_mode)); + + snprintf(path, sizeof(path) - 1, + "/sys/class/net/%s/tun_flags", e->d_name); + + tuntap = (!stat(path, &s) && S_ISREG(s.st_mode)); + + t = blobmsg_open_table(&buf, NULL); + + blobmsg_add_string(&buf, "device", e->d_name); + blobmsg_add_u32(&buf, "type", type); + blobmsg_add_u8(&buf, "is_up", flags & 1); + blobmsg_add_u8(&buf, "is_bridge", bridge); + blobmsg_add_u8(&buf, "is_tuntap", tuntap); + blobmsg_add_u8(&buf, "is_wireless", wireless); + + blobmsg_close_table(&buf, t); + } + + blobmsg_close_array(&buf, c); + + closedir(d); + + ubus_send_reply(ctx, req, buf.head); + return 0; +} + +static int +rpc_luci2_network_eap_support(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + blob_buf_init(&buf, 0); + blobmsg_add_u8(&buf, "master", !system("/usr/sbin/hostapd -veap")); + blobmsg_add_u8(&buf, "client", !system("/usr/sbin/wpa_supplicant -veap")); + ubus_send_reply(ctx, req, buf.head); + return 0; +} + + struct opkg_state { int cur_offset; int cur_count; @@ -2018,6 +2574,30 @@ menu_access(struct blob_attr *sid, struct blob_attr *acls, struct blob_buf *e) return rv; } +static bool +menu_files(struct blob_attr *files) +{ + int rem; + bool empty = true; + struct stat s; + struct blob_attr *file; + + blobmsg_for_each_attr(file, files, rem) + { + empty = false; + + if (blobmsg_type(file) != BLOBMSG_TYPE_STRING) + continue; + + if (stat(blobmsg_get_string(file), &s) || !S_ISREG(s.st_mode)) + continue; + + return true; + } + + return empty; +} + static int rpc_luci2_ui_menu(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, @@ -2029,7 +2609,7 @@ rpc_luci2_ui_menu(struct ubus_context *ctx, struct ubus_object *obj, struct blob_buf item = { 0 }; struct blob_attr *entry, *attr; struct blob_attr *tb[__RPC_MENU_MAX]; - bool access; + bool access, files; void *c, *d; blobmsg_parse(rpc_menu_policy, __RPC_MENU_MAX, tb, @@ -2053,7 +2633,7 @@ rpc_luci2_ui_menu(struct ubus_context *ctx, struct ubus_object *obj, blob_for_each_attr(entry, menu.head, rem) { - access = true; + access = files = true; blob_buf_init(&item, 0); d = blobmsg_open_table(&item, blobmsg_name(entry)); @@ -2063,13 +2643,16 @@ rpc_luci2_ui_menu(struct ubus_context *ctx, struct ubus_object *obj, if (blob_id(attr) == BLOBMSG_TYPE_ARRAY && !strcmp(blobmsg_name(attr), "acls")) access = menu_access(tb[RPC_MENU_SESSION], attr, &item); + else if (blob_id(attr) == BLOBMSG_TYPE_ARRAY && + !strcmp(blobmsg_name(attr), "files")) + files = menu_files(attr); else blobmsg_add_blob(&item, attr); } blobmsg_close_table(&item, d); - if (access) + if (access && files) blob_for_each_attr(attr, item.head, rem2) blobmsg_add_blob(&buf, attr); @@ -2224,6 +2807,27 @@ rpc_luci2_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx) UBUS_METHOD_NOARG("dhcp6_leases", rpc_luci2_network_leases6), UBUS_METHOD_NOARG("routes", rpc_luci2_network_routes), UBUS_METHOD_NOARG("routes6", rpc_luci2_network_routes6), + UBUS_METHOD_NOARG("switch_list", rpc_luci2_network_sw_list), + UBUS_METHOD("switch_info", rpc_luci2_network_sw_info, + rpc_switch_policy), + UBUS_METHOD("switch_status", rpc_luci2_network_sw_status, + rpc_switch_policy), + UBUS_METHOD("ping", rpc_luci2_network_ping, + rpc_data_policy), + UBUS_METHOD("ping6", rpc_luci2_network_ping6, + rpc_data_policy), + UBUS_METHOD("traceroute", rpc_luci2_network_traceroute, + rpc_data_policy), + UBUS_METHOD("traceroute6", rpc_luci2_network_traceroute6, + rpc_data_policy), + UBUS_METHOD("nslookup", rpc_luci2_network_nslookup, + rpc_data_policy), + UBUS_METHOD("ifup", rpc_luci2_network_ifup, + rpc_data_policy), + UBUS_METHOD("ifdown", rpc_luci2_network_ifdown, + rpc_data_policy), + UBUS_METHOD_NOARG("device_list", rpc_luci2_network_dev_list), + UBUS_METHOD_NOARG("eap_support", rpc_luci2_network_eap_support) }; static struct ubus_object_type luci2_network_type = @@ -2297,6 +2901,6 @@ rpc_luci2_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx) return rv; } -const struct rpc_plugin rpc_plugin = { +struct rpc_plugin rpc_plugin = { .init = rpc_luci2_api_init };