X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=iw.c;h=40f8f6ea7d69ca2c92449de533cf1872cd15bb5b;hb=HEAD;hp=17b2d4c7b3f22941032c4d8011df25d031029a20;hpb=27c49ed655f433ef8b69d76625071c428c09748e;p=thirdparty%2Fiw.git diff --git a/iw.c b/iw.c index 17b2d4c..c99edb1 100644 --- a/iw.c +++ b/iw.c @@ -13,19 +13,19 @@ #include #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(); @@ -36,18 +36,14 @@ 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) +static inline int nl_socket_set_buffer_size(struct nl_sock *sk, + int rxbuf, int txbuf) { - struct nl_cache *tmp = genl_ctrl_alloc_cache(h); - if (!tmp) - return -ENOMEM; - *cache = tmp; - return 0; + return nl_set_buffer_size(sk, rxbuf, txbuf); } -#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache -#endif /* CONFIG_LIBNL20 */ +#endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */ -static int debug = 0; +int iw_debug = 0; static int nl80211_init(struct nl80211_state *state) { @@ -65,23 +61,22 @@ 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; - } + nl_socket_set_buffer_size(state->nl_sock, 8192, 8192); + + /* try to set NETLINK_EXT_ACK to 1, ignoring errors */ + err = 1; + setsockopt(nl_socket_get_fd(state->nl_sock), SOL_NETLINK, + NETLINK_EXT_ACK, &err, sizeof(err)); - 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,49 +84,170 @@ 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); +extern struct cmd *__start___cmd[]; +extern struct cmd *__stop___cmd; -static int cmd_size; +#define for_each_cmd(_cmd, i) \ + for (i = 0; i < &__stop___cmd - __start___cmd; i++) \ + if ((_cmd = __start___cmd[i])) -static void usage(const char *argv0) + +static void __usage_cmd(const struct cmd *cmd, char *indent, bool full) { - struct cmd *cmd; - - fprintf(stderr, "Usage:\t%s [options] command\n", argv0); - fprintf(stderr, "Options:\n"); - fprintf(stderr, "\t--debug\t\tenable netlink debugging\n"); - fprintf(stderr, "\t--version\tshow version\n"); - fprintf(stderr, "Commands:\n"); - fprintf(stderr, "\thelp\n"); - fprintf(stderr, "\tevent [-f]\n"); - for (cmd = &__start___cmd; cmd < &__stop___cmd; - cmd = (struct cmd *)((char *)cmd + cmd_size)) { - if (!cmd->handler || cmd->hidden) + const char *start, *lend, *end; + + printf("%s", indent); + + switch (cmd->idby) { + case CIB_NONE: + break; + case CIB_PHY: + printf("phy "); + break; + case CIB_NETDEV: + printf("dev "); + break; + case CIB_WDEV: + printf("wdev "); + break; + } + if (cmd->parent && cmd->parent->name) + printf("%s ", cmd->parent->name); + printf("%s", cmd->name); + + if (cmd->args) { + /* print line by line */ + start = cmd->args; + end = strchr(start, '\0'); + printf(" "); + do { + lend = strchr(start, '\n'); + if (!lend) + lend = end; + if (start != cmd->args) { + printf("\t"); + switch (cmd->idby) { + case CIB_NONE: + break; + case CIB_PHY: + printf("phy "); + break; + case CIB_NETDEV: + printf("dev "); + break; + case CIB_WDEV: + printf("wdev "); + break; + } + if (cmd->parent && cmd->parent->name) + printf("%s ", cmd->parent->name); + printf("%s ", cmd->name); + } + printf("%.*s\n", (int)(lend - start), start); + start = lend + 1; + } while (end != lend); + } else + 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) +{ + printf("Options:\n"); + printf("\t--debug\t\tenable netlink debugging\n"); +} + +static const char *argv0; + +static void usage(int argc, char **argv) +{ + const struct cmd *section, *cmd; + bool full = argc >= 0; + const char *sect_filt = NULL; + const char *cmd_filt = NULL; + unsigned int i, j; + + if (argc > 0) + sect_filt = argv[0]; + + if (argc > 1) + cmd_filt = argv[1]; + + printf("Usage:\t%s [options] command\n", argv0); + usage_options(); + printf("\t--version\tshow version (%s)\n", iw_version); + printf("Commands:\n"); + for_each_cmd(section, i) { + if (section->parent) continue; - switch (cmd->idby) { - case CIB_NONE: - fprintf(stderr, "\t"); - break; - case CIB_PHY: - fprintf(stderr, "\tphy "); - break; - case CIB_NETDEV: - fprintf(stderr, "\tdev "); - break; + + if (sect_filt && strcmp(section->name, sect_filt)) + continue; + + if (section->handler && !section->hidden) + __usage_cmd(section, "\t", full); + + for_each_cmd(cmd, j) { + 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); } - if (cmd->section) - fprintf(stderr, "%s ", cmd->section); - fprintf(stderr, "%s", cmd->name); - if (cmd->args) - fprintf(stderr, " %s", cmd->args); - fprintf(stderr, "\n"); } + 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_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 struct cmd *cmd) +{ + printf("Usage:\t%s [options] ", argv0); + __usage_cmd(cmd, "", true); + usage_options(); } static void version(void) @@ -150,17 +266,59 @@ 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); } static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { + struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1; + int len = nlh->nlmsg_len; + struct nlattr *attrs; + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; int *ret = arg; - *ret = err->error; + int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh); + + if (err->error > 0) { + /* + * This is illegal, per netlink(7), but not impossible (think + * "vendor commands"). Callers really expect negative error + * codes, so make that happen. + */ + fprintf(stderr, + "ERROR: received positive netlink error code %d\n", + err->error); + *ret = -EPROTO; + } else { + *ret = err->error; + } + + if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) + return NL_STOP; + + if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) + ack_len += err->msg.nlmsg_len - sizeof(*nlh); + + if (len <= ack_len) + return NL_STOP; + + attrs = (void *)((unsigned char *)nlh + ack_len); + len -= ack_len; + + nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL); + if (tb[NLMSGERR_ATTR_MSG]) { + len = strnlen((char *)nla_data(tb[NLMSGERR_ATTR_MSG]), + nla_len(tb[NLMSGERR_ATTR_MSG])); + fprintf(stderr, "kernel reports: %*s\n", len, + (char *)nla_data(tb[NLMSGERR_ATTR_MSG])); + } + return NL_STOP; } @@ -178,14 +336,32 @@ static int ack_handler(struct nl_msg *msg, void *arg) return NL_STOP; } -int handle_cmd(struct nl80211_state *state, enum id_input idby, - int argc, char **argv) +static int (*registered_handler)(struct nl_msg *, void *); +static void *registered_handler_data; + +void register_handler(int (*handler)(struct nl_msg *, void *), void *data) { - struct cmd *cmd, *match = NULL; + registered_handler = handler; + registered_handler_data = data; +} + +int valid_handler(struct nl_msg *msg, void *arg) +{ + if (registered_handler) + return registered_handler(msg, registered_handler_data); + + return NL_OK; +} + +static int __handle_cmd(struct nl80211_state *state, enum id_input idby, + int argc, char **argv, const struct cmd **cmdout) +{ + const struct cmd *cmd, *match = NULL, *sectcmd; struct nl_cb *cb; + struct nl_cb *s_cb; struct nl_msg *msg; - int devidx = 0; - int err, o_argc; + signed long long devidx = 0; + int err, o_argc, i; const char *command, *section; char *tmp, **o_argv; enum command_identify_by command_idby = CIB_NONE; @@ -219,6 +395,13 @@ 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; } @@ -226,49 +409,82 @@ 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, i) { + 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, i) { + 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; if (!cmd->cmd) { argc = o_argc; argv = o_argv; - return cmd->handler(state, NULL, NULL, argc, argv); + return cmd->handler(state, NULL, argc, argv, idby); } msg = nlmsg_alloc(); @@ -277,14 +493,15 @@ int handle_cmd(struct nl80211_state *state, enum id_input idby, return 2; } - cb = nl_cb_alloc(debug ? NL_CB_DEBUG : NL_CB_DEFAULT); - if (!cb) { + cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); + 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; + goto out; } - 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) { @@ -294,14 +511,19 @@ 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, 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; @@ -311,12 +533,13 @@ int handle_cmd(struct nl80211_state *state, enum id_input idby, nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, NULL); while (err > 0) nl_recvmsgs(state->nl_sock, cb); out: nl_cb_put(cb); - out_free_msg: + nl_cb_put(s_cb); nlmsg_free(msg); return err; nla_put_failure: @@ -324,285 +547,24 @@ int handle_cmd(struct nl80211_state *state, enum id_input idby, return 2; } -static int no_seq_check(struct nl_msg *msg, void *arg) -{ - return NL_OK; -} - -struct print_event_args { - bool frame; -}; - -static void print_frame(struct print_event_args *args, struct nlattr *attr, - bool reason) -{ - uint8_t *frame; - size_t len; - int i; - char macbuf[6*3]; - - if (!attr) - printf(" [no frame]"); - - frame = nla_data(attr); - len = nla_len(attr); - - if (len >= 16) { - mac_addr_n2a(macbuf, frame + 10); - printf(" %s -> ", macbuf); - } else - printf(" ??? -> "); - - if (len >= 20) { - mac_addr_n2a(macbuf, frame + 4); - printf("%s", macbuf); - } else - printf("???"); - - if (reason) { - if (len >= 26) { - uint16_t reason = (frame[25] << 8) + frame[24]; - printf(" reason %d: %s", - reason, get_reason_str(reason)); - } else - printf(" reason ?: ???"); - } - - if (!args->frame) - return; - - printf(" [frame:"); - for (i = 0; i < len; i++) - printf(" %.02x", frame[i]); - printf("]"); -} - -static int print_event(struct nl_msg *msg, void *arg) -{ - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct print_event_args *args = arg; - char ifname[100]; - char macbuf[6*3]; - __u8 reg_type; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb[NL80211_ATTR_IFINDEX] && tb[NL80211_ATTR_WIPHY]) { - if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname); - printf("%s (phy #%d): ", ifname, nla_get_u32(tb[NL80211_ATTR_WIPHY])); - } else if (tb[NL80211_ATTR_IFINDEX]) { - if_indextoname(nla_get_u32(tb[NL80211_ATTR_IFINDEX]), ifname); - printf("%s: ", ifname); - } else if (tb[NL80211_ATTR_WIPHY]) { - printf("phy #%d: ", nla_get_u32(tb[NL80211_ATTR_WIPHY])); - } - - switch (gnlh->cmd) { - case NL80211_CMD_NEW_WIPHY: - printf("renamed to %s\n", nla_get_string(tb[NL80211_ATTR_WIPHY_NAME])); - break; - case NL80211_CMD_NEW_SCAN_RESULTS: - printf("scan finished\n"); - break; - case NL80211_CMD_SCAN_ABORTED: - printf("scan aborted\n"); - break; - case NL80211_CMD_REG_CHANGE: - printf("regulatory domain change: "); - - reg_type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]); - - switch (reg_type) { - case NL80211_REGDOM_TYPE_COUNTRY: - printf("set to %s by %s request", - nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]), - reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]))); - if (tb[NL80211_ATTR_WIPHY]) - printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY])); - break; - case NL80211_REGDOM_TYPE_WORLD: - printf("set to world roaming by %s request", - reg_initiator_to_string(nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]))); - break; - case NL80211_REGDOM_TYPE_CUSTOM_WORLD: - printf("custom world roaming rules in place on phy%d by %s request", - nla_get_u32(tb[NL80211_ATTR_WIPHY]), - reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR]))); - break; - case NL80211_REGDOM_TYPE_INTERSECTION: - printf("intersection used due to a request made by %s", - reg_initiator_to_string(nla_get_u32(tb[NL80211_ATTR_REG_INITIATOR]))); - if (tb[NL80211_ATTR_WIPHY]) - printf(" on phy%d", nla_get_u32(tb[NL80211_ATTR_WIPHY])); - break; - default: - printf("unknown source (upgrade this utility)"); - break; - } - - printf("\n"); - break; - case NL80211_CMD_JOIN_IBSS: - mac_addr_n2a(macbuf, nla_data(tb[NL80211_ATTR_MAC])); - printf("IBSS %s joined\n", macbuf); - break; - case NL80211_CMD_AUTHENTICATE: - printf("auth"); - print_frame(args, tb[NL80211_ATTR_FRAME], false); - printf("\n"); - break; - case NL80211_CMD_ASSOCIATE: - printf("assoc"); - print_frame(args, tb[NL80211_ATTR_FRAME], false); - printf("\n"); - break; - case NL80211_CMD_DEAUTHENTICATE: - printf("deauth"); - print_frame(args, tb[NL80211_ATTR_FRAME], true); - printf("\n"); - break; - case NL80211_CMD_DISASSOCIATE: - printf("disassoc"); - print_frame(args, tb[NL80211_ATTR_FRAME], true); - printf("\n"); - break; - default: - printf("unknown event %d\n", gnlh->cmd); - break; - } - - return NL_SKIP; -} - -struct wait_event { - int n_cmds; - const __u32 *cmds; - __u32 cmd; -}; - -static int wait_event(struct nl_msg *msg, void *arg) -{ - struct wait_event *wait = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - int i; - - for (i = 0; i < wait->n_cmds; i++) { - if (gnlh->cmd == wait->cmds[i]) { - wait->cmd = gnlh->cmd; - } - } - - return NL_SKIP; -} - -static __u32 __listen_events(struct nl80211_state *state, - const int n_waits, const __u32 *waits, - struct print_event_args *args) -{ - int mcid, ret; - struct nl_cb *cb = nl_cb_alloc(debug ? NL_CB_DEBUG : NL_CB_DEFAULT); - struct wait_event wait_ev; - - if (!cb) { - fprintf(stderr, "failed to allocate netlink callbacks\n"); - return -ENOMEM; - } - - /* Configuration multicast group */ - mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "config"); - if (mcid < 0) - return mcid; - - ret = nl_socket_add_membership(state->nl_sock, mcid); - if (ret) - return ret; - - /* Scan multicast group */ - mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "scan"); - if (mcid >= 0) { - ret = nl_socket_add_membership(state->nl_sock, mcid); - if (ret) - return ret; - } - - /* Regulatory multicast group */ - mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "regulatory"); - if (mcid >= 0) { - ret = nl_socket_add_membership(state->nl_sock, mcid); - if (ret) - return ret; - } - - /* MLME multicast group */ - mcid = nl_get_multicast_id(state->nl_sock, "nl80211", "mlme"); - if (mcid >= 0) { - ret = nl_socket_add_membership(state->nl_sock, mcid); - if (ret) - return ret; - } - - /* no sequence checking for multicast messages */ - nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); - - if (n_waits && waits) { - wait_ev.cmds = waits; - wait_ev.n_cmds = n_waits; - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, wait_event, &wait_ev); - } else { - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_event, args); - } - - wait_ev.cmd = 0; - - while (!wait_ev.cmd) - nl_recvmsgs(state->nl_sock, cb); - - nl_cb_put(cb); - - return wait_ev.cmd; -} - -__u32 listen_events(struct nl80211_state *state, - const int n_waits, const __u32 *waits) +int handle_cmd(struct nl80211_state *state, enum id_input idby, + int argc, char **argv) { - return __listen_events(state, n_waits, waits, NULL); -} - -static int print_events(struct nl80211_state *state, int argc, char **argv) -{ - struct print_event_args args; - - memset(&args, 0, sizeof(args)); - - if (argc > 1) - return 1; - - if (argc > 0) { - if (strcmp(argv[0], "-f")) - return 1; - args.frame = true; - } - - return __listen_events(state, 0, NULL, &args); + return __handle_cmd(state, idby, argc, argv, NULL); } int main(int argc, char **argv) { struct nl80211_state nlstate; int err; - const char *argv0; + 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); /* strip off self */ argc--; argv0 = *argv++; if (argc > 0 && strcmp(*argv, "--debug") == 0) { - debug = 1; + iw_debug = 1; argc--; argv++; } @@ -612,8 +574,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; } @@ -621,29 +584,43 @@ int main(int argc, char **argv) if (err) return 1; - if (strcmp(*argv, "event") == 0) { + if (strcmp(*argv, "dev") == 0 && argc > 1) { argc--; argv++; - err = print_events(&nlstate, argc, argv); - } else if (strcmp(*argv, "dev") == 0 && argc > 1) { - argc--; - argv++; - err = handle_cmd(&nlstate, II_NETDEV, argc, argv); + err = __handle_cmd(&nlstate, II_NETDEV, argc, argv, &cmd); } else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) { if (strlen(*argv) == 3) { argc--; argv++; - err = handle_cmd(&nlstate, II_PHY_NAME, argc, argv); + err = __handle_cmd(&nlstate, II_PHY_NAME, argc, argv, &cmd); } else if (*(*argv + 3) == '#') - err = handle_cmd(&nlstate, II_PHY_IDX, argc, argv); + err = __handle_cmd(&nlstate, II_PHY_IDX, argc, argv, &cmd); else - err = 1; - } else - err = handle_cmd(&nlstate, II_NONE, argc, argv); + 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; + detect: + idby = II_NONE; + 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) - usage(argv0); - if (err < 0) + if (err == HANDLER_RET_USAGE) { + if (cmd) + usage_cmd(cmd); + else + usage(0, NULL); + } else if (err == HANDLER_RET_DONE) { + err = 0; + } else if (err < 0) fprintf(stderr, "command failed: %s (%d)\n", strerror(-err), err); nl80211_cleanup(&nlstate);