X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=iw.c;h=af55a9bfae493bfd90161798eeca22a53694d771;hb=d8880179da0a04fcbe6759497be5a3513582cb9f;hp=dca9089443687c2c0bed428dbab51733d699d8a8;hpb=4f0cae73f779525c32d5ef6a30c82fde56142c38;p=thirdparty%2Fiw.git diff --git a/iw.c b/iw.c index dca9089..af55a9b 100644 --- a/iw.c +++ b/iw.c @@ -13,19 +13,18 @@ #include #include #include - + #include #include -#include +#include #include #include #include "nl80211.h" #include "iw.h" -#ifndef CONFIG_LIBNL20 -/* libnl 2.0 compatibility code */ - +/* libnl 1.x compatibility code */ +#if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30) static inline struct nl_handle *nl_socket_alloc(void) { return nl_handle_alloc(); @@ -35,17 +34,7 @@ static inline void nl_socket_free(struct nl_sock *h) { nl_handle_destroy(h); } - -static inline int __genl_ctrl_alloc_cache(struct nl_sock *h, struct nl_cache **cache) -{ - struct nl_cache *tmp = genl_ctrl_alloc_cache(h); - if (!tmp) - return -ENOMEM; - *cache = tmp; - return 0; -} -#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache -#endif /* CONFIG_LIBNL20 */ +#endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */ int iw_debug = 0; @@ -65,23 +54,15 @@ static int nl80211_init(struct nl80211_state *state) goto out_handle_destroy; } - if (genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache)) { - fprintf(stderr, "Failed to allocate generic netlink cache.\n"); - err = -ENOMEM; - goto out_handle_destroy; - } - - state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211"); - if (!state->nl80211) { + state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211"); + if (state->nl80211_id < 0) { fprintf(stderr, "nl80211 not found.\n"); err = -ENOENT; - goto out_cache_free; + goto out_handle_destroy; } return 0; - out_cache_free: - nl_cache_free(state->nl_cache); out_handle_destroy: nl_socket_free(state->nl_sock); return err; @@ -89,65 +70,140 @@ static int nl80211_init(struct nl80211_state *state) static void nl80211_cleanup(struct nl80211_state *state) { - genl_family_put(state->nl80211); - nl_cache_free(state->nl_cache); nl_socket_free(state->nl_sock); } -__COMMAND(NULL, NULL, "", NULL, 0, 0, 0, CIB_NONE, NULL); -__COMMAND(NULL, NULL, "", NULL, 1, 0, 0, CIB_NONE, NULL); - static int cmd_size; -static void __usage_cmd(struct cmd *cmd, char *indent) +extern struct cmd __start___cmd; +extern struct cmd __stop___cmd; + +#define for_each_cmd(_cmd) \ + for (_cmd = &__start___cmd; _cmd < &__stop___cmd; \ + _cmd = (const struct cmd *)((char *)_cmd + cmd_size)) + + +static void __usage_cmd(const struct cmd *cmd, char *indent, bool full) { - fprintf(stderr, "%s", indent); + const char *start, *lend, *end; + + printf("%s", indent); switch (cmd->idby) { case CIB_NONE: break; case CIB_PHY: - fprintf(stderr, "phy "); + printf("phy "); break; case CIB_NETDEV: - fprintf(stderr, "dev "); + printf("dev "); + break; + case CIB_WDEV: + printf("wdev "); break; } - if (cmd->section) - fprintf(stderr, "%s ", cmd->section); - fprintf(stderr, "%s", cmd->name); + if (cmd->parent && cmd->parent->name) + printf("%s ", cmd->parent->name); + printf("%s", cmd->name); if (cmd->args) - fprintf(stderr, " %s", cmd->args); - fprintf(stderr, "\n"); + printf(" %s", cmd->args); + printf("\n"); + + if (!full || !cmd->help) + return; + + /* hack */ + if (strlen(indent)) + indent = "\t\t"; + else + printf("\n"); + + /* print line by line */ + start = cmd->help; + end = strchr(start, '\0'); + do { + lend = strchr(start, '\n'); + if (!lend) + lend = end; + printf("%s", indent); + printf("%.*s\n", (int)(lend - start), start); + start = lend + 1; + } while (end != lend); + + printf("\n"); } static void usage_options(void) { - fprintf(stderr, "Options:\n"); - fprintf(stderr, "\t--debug\t\tenable netlink debugging\n"); + printf("Options:\n"); + printf("\t--debug\t\tenable netlink debugging\n"); } -static void usage(const char *argv0) +static const char *argv0; + +static void usage(int argc, char **argv) { - struct cmd *cmd; + const struct cmd *section, *cmd; + bool full = argc >= 0; + const char *sect_filt = NULL; + const char *cmd_filt = NULL; + + if (argc > 0) + sect_filt = argv[0]; - fprintf(stderr, "Usage:\t%s [options] command\n", argv0); + if (argc > 1) + cmd_filt = argv[1]; + + printf("Usage:\t%s [options] command\n", argv0); usage_options(); - fprintf(stderr, "\t--version\tshow version\n"); - fprintf(stderr, "Commands:\n"); - fprintf(stderr, "\thelp\n"); - for (cmd = &__start___cmd; cmd < &__stop___cmd; - cmd = (struct cmd *)((char *)cmd + cmd_size)) { - if (!cmd->handler || cmd->hidden) + printf("\t--version\tshow version (%s)\n", iw_version); + printf("Commands:\n"); + for_each_cmd(section) { + if (section->parent) + continue; + + if (sect_filt && strcmp(section->name, sect_filt)) continue; - __usage_cmd(cmd, "\t"); + + if (section->handler && !section->hidden) + __usage_cmd(section, "\t", full); + + for_each_cmd(cmd) { + if (section != cmd->parent) + continue; + if (!cmd->handler || cmd->hidden) + continue; + if (cmd_filt && strcmp(cmd->name, cmd_filt)) + continue; + __usage_cmd(cmd, "\t", full); + } } + printf("\nCommands that use the netdev ('dev') can also be given the\n" + "'wdev' instead to identify the device.\n"); + printf("\nYou can omit the 'phy' or 'dev' if " + "the identification is unique,\n" + "e.g. \"iw wlan0 info\" or \"iw phy0 info\". " + "(Don't when scripting.)\n\n" + "Do NOT screenscrape this tool, we don't " + "consider its output stable.\n\n"); +} + +static int print_help(struct nl80211_state *state, + struct nl_cb *cb, + struct nl_msg *msg, + int argc, char **argv, + enum id_input id) +{ + exit(3); } +TOPLEVEL(help, "[command]", 0, 0, CIB_NONE, print_help, + "Print usage for all or a specific command, e.g.\n" + "\"help wowlan\" or \"help wowlan enable\"."); -static void usage_cmd(const char *argv0, struct cmd *cmd) +static void usage_cmd(const struct cmd *cmd) { - fprintf(stderr, "Usage:\t%s [options] ", argv0); - __usage_cmd(cmd, ""); + printf("Usage:\t%s [options] ", argv0); + __usage_cmd(cmd, "", true); usage_options(); } @@ -167,9 +223,12 @@ static int phy_lookup(char *name) if (fd < 0) return -1; pos = read(fd, buf, sizeof(buf) - 1); - if (pos < 0) + if (pos < 0) { + close(fd); return -1; + } buf[pos] = '\0'; + close(fd); return atoi(buf); } @@ -196,12 +255,13 @@ static int ack_handler(struct nl_msg *msg, void *arg) } static int __handle_cmd(struct nl80211_state *state, enum id_input idby, - int argc, char **argv, struct cmd **cmdout) + int argc, char **argv, const struct cmd **cmdout) { - struct cmd *cmd, *match = NULL; + const struct cmd *cmd, *match = NULL, *sectcmd; struct nl_cb *cb; + struct nl_cb *s_cb; struct nl_msg *msg; - int devidx = 0; + signed long long devidx = 0; int err, o_argc; const char *command, *section; char *tmp, **o_argv; @@ -236,6 +296,13 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, argc--; argv++; break; + case II_WDEV: + command_idby = CIB_WDEV; + devidx = strtoll(*argv, &tmp, 0); + if (*tmp != '\0') + return 1; + argc--; + argv++; default: break; } @@ -243,44 +310,74 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, if (devidx < 0) return -errno; - section = command = *argv; + section = *argv; argc--; argv++; - for (cmd = &__start___cmd; cmd < &__stop___cmd; - cmd = (struct cmd *)((char *)cmd + cmd_size)) { - if (!cmd->handler) + for_each_cmd(sectcmd) { + if (sectcmd->parent) continue; - if (cmd->idby != command_idby) + /* ok ... bit of a hack for the dupe 'info' section */ + if (match && sectcmd->idby != command_idby) continue; - if (cmd->section) { - if (strcmp(cmd->section, section)) + if (strcmp(sectcmd->name, section) == 0) + match = sectcmd; + } + + sectcmd = match; + match = NULL; + if (!sectcmd) + return 1; + + if (argc > 0) { + command = *argv; + + for_each_cmd(cmd) { + if (!cmd->handler) continue; - /* this is a bit icky ... */ - if (command == section) { - if (argc <= 0) { - if (match) - break; - return 1; - } - command = *argv; - argc--; - argv++; - } - } else if (section != command) - continue; - if (strcmp(cmd->name, command)) - continue; - if (argc && !cmd->args) - continue; + if (cmd->parent != sectcmd) + continue; + /* + * ignore mismatch id by, but allow WDEV + * in place of NETDEV + */ + if (cmd->idby != command_idby && + !(cmd->idby == CIB_NETDEV && + command_idby == CIB_WDEV)) + continue; + if (strcmp(cmd->name, command)) + continue; + if (argc > 1 && !cmd->args) + continue; + match = cmd; + break; + } - match = cmd; + if (match) { + argc--; + argv++; + } } - cmd = match; + if (match) + cmd = match; + else { + /* Use the section itself, if possible. */ + cmd = sectcmd; + if (argc && !cmd->args) + return 1; + if (cmd->idby != command_idby && + !(cmd->idby == CIB_NETDEV && command_idby == CIB_WDEV)) + return 1; + if (!cmd->handler) + return 1; + } - if (!cmd) - return 1; + if (cmd->selector) { + cmd = cmd->selector(argc, argv); + if (!cmd) + return 1; + } if (cmdout) *cmdout = cmd; @@ -288,7 +385,7 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, if (!cmd->cmd) { argc = o_argc; argv = o_argv; - return cmd->handler(state, NULL, NULL, argc, argv); + return cmd->handler(state, NULL, NULL, argc, argv, idby); } msg = nlmsg_alloc(); @@ -298,13 +395,14 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, } cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); - if (!cb) { + s_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); + if (!cb || !s_cb) { fprintf(stderr, "failed to allocate netlink callbacks\n"); err = 2; goto out_free_msg; } - genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, + genlmsg_put(msg, 0, 0, state->nl80211_id, 0, cmd->nl_msg_flags, cmd->cmd, 0); switch (command_idby) { @@ -314,14 +412,19 @@ static int __handle_cmd(struct nl80211_state *state, enum id_input idby, case CIB_NETDEV: NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); break; + case CIB_WDEV: + NLA_PUT_U64(msg, NL80211_ATTR_WDEV, devidx); + break; default: break; } - err = cmd->handler(state, cb, msg, argc, argv); + err = cmd->handler(state, cb, msg, argc, argv, idby); if (err) goto out; + nl_socket_set_cb(state->nl_sock, s_cb); + err = nl_send_auto_complete(state->nl_sock, msg); if (err < 0) goto out; @@ -354,12 +457,10 @@ int main(int argc, char **argv) { struct nl80211_state nlstate; int err; - const char *argv0; - struct cmd *cmd = NULL; + const struct cmd *cmd = NULL; /* calculate command size including padding */ - cmd_size = abs((long)&__cmd_NULL_NULL_1_CIB_NONE_0 - - (long)&__cmd_NULL_NULL_0_CIB_NONE_0); + cmd_size = abs((long)&__section_set - (long)&__section_get); /* strip off self */ argc--; argv0 = *argv++; @@ -375,8 +476,9 @@ int main(int argc, char **argv) return 0; } + /* need to treat "help" command specially so it works w/o nl80211 */ if (argc == 0 || strcmp(*argv, "help") == 0) { - usage(argv0); + usage(argc - 1, argv + 1); return 0; } @@ -396,15 +498,27 @@ int main(int argc, char **argv) } else if (*(*argv + 3) == '#') err = __handle_cmd(&nlstate, II_PHY_IDX, argc, argv, &cmd); else - err = 1; - } else - err = __handle_cmd(&nlstate, II_NONE, argc, argv, &cmd); + goto detect; + } else if (strcmp(*argv, "wdev") == 0 && argc > 1) { + argc--; + argv++; + err = __handle_cmd(&nlstate, II_WDEV, argc, argv, &cmd); + } else { + int idx; + enum id_input idby = II_NONE; + detect: + if ((idx = if_nametoindex(argv[0])) != 0) + idby = II_NETDEV; + else if ((idx = phy_lookup(argv[0])) >= 0) + idby = II_PHY_NAME; + err = __handle_cmd(&nlstate, idby, argc, argv, &cmd); + } if (err == 1) { if (cmd) - usage_cmd(argv0, cmd); + usage_cmd(cmd); else - usage(argv0); + usage(0, NULL); } else if (err < 0) fprintf(stderr, "command failed: %s (%d)\n", strerror(-err), err);