]>
Commit | Line | Data |
---|---|---|
cad53b3f JB |
1 | /* |
2 | * nl80211 userspace tool | |
3 | * | |
2a1fced2 | 4 | * Copyright 2007, 2008 Johannes Berg <johannes@sipsolutions.net> |
cad53b3f JB |
5 | */ |
6 | ||
7 | #include <errno.h> | |
8 | #include <stdio.h> | |
d5ac8ad3 | 9 | #include <string.h> |
bd396f2a JB |
10 | #include <net/if.h> |
11 | #include <sys/types.h> | |
12 | #include <sys/stat.h> | |
13 | #include <fcntl.h> | |
14 | #include <unistd.h> | |
27c49ed6 | 15 | #include <stdbool.h> |
c551449a | 16 | |
cad53b3f JB |
17 | #include <netlink/genl/genl.h> |
18 | #include <netlink/genl/family.h> | |
c551449a | 19 | #include <netlink/genl/ctrl.h> |
cad53b3f JB |
20 | #include <netlink/msg.h> |
21 | #include <netlink/attr.h> | |
cad53b3f | 22 | |
f408e01b | 23 | #include "nl80211.h" |
cad53b3f JB |
24 | #include "iw.h" |
25 | ||
ded667b0 YY |
26 | /* libnl 1.x compatibility code */ |
27 | #if !defined(CONFIG_LIBNL20) && !defined(CONFIG_LIBNL30) | |
dfd13ee5 PE |
28 | static inline struct nl_handle *nl_socket_alloc(void) |
29 | { | |
30 | return nl_handle_alloc(); | |
31 | } | |
32 | ||
57077d64 | 33 | static inline void nl_socket_free(struct nl_sock *h) |
dfd13ee5 PE |
34 | { |
35 | nl_handle_destroy(h); | |
36 | } | |
b9be8936 JB |
37 | |
38 | static inline int nl_socket_set_buffer_size(struct nl_sock *sk, | |
39 | int rxbuf, int txbuf) | |
40 | { | |
41 | return nl_set_buffer_size(sk, rxbuf, txbuf); | |
42 | } | |
ded667b0 | 43 | #endif /* CONFIG_LIBNL20 && CONFIG_LIBNL30 */ |
dfd13ee5 | 44 | |
957a0f07 | 45 | int iw_debug = 0; |
cad53b3f JB |
46 | |
47 | static int nl80211_init(struct nl80211_state *state) | |
48 | { | |
49 | int err; | |
50 | ||
57077d64 PE |
51 | state->nl_sock = nl_socket_alloc(); |
52 | if (!state->nl_sock) { | |
53 | fprintf(stderr, "Failed to allocate netlink socket.\n"); | |
cad53b3f JB |
54 | return -ENOMEM; |
55 | } | |
56 | ||
57077d64 | 57 | if (genl_connect(state->nl_sock)) { |
cad53b3f JB |
58 | fprintf(stderr, "Failed to connect to generic netlink.\n"); |
59 | err = -ENOLINK; | |
60 | goto out_handle_destroy; | |
61 | } | |
62 | ||
7687d596 TG |
63 | nl_socket_set_buffer_size(state->nl_sock, 8192, 8192); |
64 | ||
3a807325 JB |
65 | /* try to set NETLINK_EXT_ACK to 1, ignoring errors */ |
66 | err = 1; | |
67 | setsockopt(nl_socket_get_fd(state->nl_sock), SOL_NETLINK, | |
68 | NETLINK_EXT_ACK, &err, sizeof(err)); | |
69 | ||
f09cee6d JB |
70 | state->nl80211_id = genl_ctrl_resolve(state->nl_sock, "nl80211"); |
71 | if (state->nl80211_id < 0) { | |
cad53b3f JB |
72 | fprintf(stderr, "nl80211 not found.\n"); |
73 | err = -ENOENT; | |
f09cee6d | 74 | goto out_handle_destroy; |
cad53b3f JB |
75 | } |
76 | ||
77 | return 0; | |
78 | ||
cad53b3f | 79 | out_handle_destroy: |
57077d64 | 80 | nl_socket_free(state->nl_sock); |
cad53b3f JB |
81 | return err; |
82 | } | |
83 | ||
84 | static void nl80211_cleanup(struct nl80211_state *state) | |
85 | { | |
57077d64 | 86 | nl_socket_free(state->nl_sock); |
cad53b3f JB |
87 | } |
88 | ||
403b9c83 JB |
89 | static int cmd_size; |
90 | ||
4698bfc2 JB |
91 | extern struct cmd __start___cmd; |
92 | extern struct cmd __stop___cmd; | |
93 | ||
94 | #define for_each_cmd(_cmd) \ | |
95 | for (_cmd = &__start___cmd; _cmd < &__stop___cmd; \ | |
96 | _cmd = (const struct cmd *)((char *)_cmd + cmd_size)) | |
97 | ||
98 | ||
99 | static void __usage_cmd(const struct cmd *cmd, char *indent, bool full) | |
3bb116da | 100 | { |
01ae06f9 JB |
101 | const char *start, *lend, *end; |
102 | ||
bd663893 | 103 | printf("%s", indent); |
4f0cae73 | 104 | |
3bb116da JB |
105 | switch (cmd->idby) { |
106 | case CIB_NONE: | |
3bb116da JB |
107 | break; |
108 | case CIB_PHY: | |
bd663893 | 109 | printf("phy <phyname> "); |
3bb116da JB |
110 | break; |
111 | case CIB_NETDEV: | |
bd663893 | 112 | printf("dev <devname> "); |
3bb116da | 113 | break; |
290a3dd4 JB |
114 | case CIB_WDEV: |
115 | printf("wdev <idx> "); | |
116 | break; | |
3bb116da | 117 | } |
4698bfc2 JB |
118 | if (cmd->parent && cmd->parent->name) |
119 | printf("%s ", cmd->parent->name); | |
bd663893 | 120 | printf("%s", cmd->name); |
eb795501 JB |
121 | |
122 | if (cmd->args) { | |
123 | /* print line by line */ | |
124 | start = cmd->args; | |
125 | end = strchr(start, '\0'); | |
126 | printf(" "); | |
127 | do { | |
128 | lend = strchr(start, '\n'); | |
129 | if (!lend) | |
130 | lend = end; | |
131 | if (start != cmd->args) { | |
132 | printf("\t"); | |
133 | switch (cmd->idby) { | |
134 | case CIB_NONE: | |
135 | break; | |
136 | case CIB_PHY: | |
137 | printf("phy <phyname> "); | |
138 | break; | |
139 | case CIB_NETDEV: | |
140 | printf("dev <devname> "); | |
141 | break; | |
142 | case CIB_WDEV: | |
143 | printf("wdev <idx> "); | |
144 | break; | |
145 | } | |
146 | if (cmd->parent && cmd->parent->name) | |
147 | printf("%s ", cmd->parent->name); | |
148 | printf("%s ", cmd->name); | |
149 | } | |
150 | printf("%.*s\n", (int)(lend - start), start); | |
151 | start = lend + 1; | |
152 | } while (end != lend); | |
153 | } else | |
154 | printf("\n"); | |
01ae06f9 JB |
155 | |
156 | if (!full || !cmd->help) | |
157 | return; | |
158 | ||
159 | /* hack */ | |
160 | if (strlen(indent)) | |
161 | indent = "\t\t"; | |
162 | else | |
bd663893 | 163 | printf("\n"); |
01ae06f9 JB |
164 | |
165 | /* print line by line */ | |
166 | start = cmd->help; | |
167 | end = strchr(start, '\0'); | |
168 | do { | |
169 | lend = strchr(start, '\n'); | |
170 | if (!lend) | |
171 | lend = end; | |
bd663893 HS |
172 | printf("%s", indent); |
173 | printf("%.*s\n", (int)(lend - start), start); | |
01ae06f9 JB |
174 | start = lend + 1; |
175 | } while (end != lend); | |
176 | ||
bd663893 | 177 | printf("\n"); |
3bb116da JB |
178 | } |
179 | ||
4f0cae73 JB |
180 | static void usage_options(void) |
181 | { | |
bd663893 HS |
182 | printf("Options:\n"); |
183 | printf("\t--debug\t\tenable netlink debugging\n"); | |
4f0cae73 JB |
184 | } |
185 | ||
01ae06f9 JB |
186 | static const char *argv0; |
187 | ||
f3ac8bf1 | 188 | static void usage(int argc, char **argv) |
bd396f2a | 189 | { |
4698bfc2 | 190 | const struct cmd *section, *cmd; |
f3ac8bf1 JB |
191 | bool full = argc >= 0; |
192 | const char *sect_filt = NULL; | |
193 | const char *cmd_filt = NULL; | |
194 | ||
195 | if (argc > 0) | |
196 | sect_filt = argv[0]; | |
197 | ||
198 | if (argc > 1) | |
199 | cmd_filt = argv[1]; | |
bd396f2a | 200 | |
bd663893 | 201 | printf("Usage:\t%s [options] command\n", argv0); |
4f0cae73 | 202 | usage_options(); |
bd663893 HS |
203 | printf("\t--version\tshow version (%s)\n", iw_version); |
204 | printf("Commands:\n"); | |
4698bfc2 JB |
205 | for_each_cmd(section) { |
206 | if (section->parent) | |
403b9c83 | 207 | continue; |
4698bfc2 | 208 | |
f3ac8bf1 JB |
209 | if (sect_filt && strcmp(section->name, sect_filt)) |
210 | continue; | |
211 | ||
4698bfc2 JB |
212 | if (section->handler && !section->hidden) |
213 | __usage_cmd(section, "\t", full); | |
214 | ||
215 | for_each_cmd(cmd) { | |
216 | if (section != cmd->parent) | |
217 | continue; | |
218 | if (!cmd->handler || cmd->hidden) | |
219 | continue; | |
f3ac8bf1 JB |
220 | if (cmd_filt && strcmp(cmd->name, cmd_filt)) |
221 | continue; | |
4698bfc2 JB |
222 | __usage_cmd(cmd, "\t", full); |
223 | } | |
bd396f2a | 224 | } |
75f4204c JB |
225 | printf("\nCommands that use the netdev ('dev') can also be given the\n" |
226 | "'wdev' instead to identify the device.\n"); | |
bd663893 | 227 | printf("\nYou can omit the 'phy' or 'dev' if " |
f4ec76d0 | 228 | "the identification is unique,\n" |
8aefee9a | 229 | "e.g. \"iw wlan0 info\" or \"iw phy0 info\". " |
fbdb8d05 JB |
230 | "(Don't when scripting.)\n\n" |
231 | "Do NOT screenscrape this tool, we don't " | |
232 | "consider its output stable.\n\n"); | |
bd396f2a JB |
233 | } |
234 | ||
01ae06f9 | 235 | static int print_help(struct nl80211_state *state, |
01ae06f9 | 236 | struct nl_msg *msg, |
05514f95 JB |
237 | int argc, char **argv, |
238 | enum id_input id) | |
01ae06f9 JB |
239 | { |
240 | exit(3); | |
241 | } | |
f3ac8bf1 JB |
242 | TOPLEVEL(help, "[command]", 0, 0, CIB_NONE, print_help, |
243 | "Print usage for all or a specific command, e.g.\n" | |
244 | "\"help wowlan\" or \"help wowlan enable\"."); | |
01ae06f9 | 245 | |
4698bfc2 | 246 | static void usage_cmd(const struct cmd *cmd) |
4f0cae73 | 247 | { |
bd663893 | 248 | printf("Usage:\t%s [options] ", argv0); |
01ae06f9 | 249 | __usage_cmd(cmd, "", true); |
4f0cae73 JB |
250 | usage_options(); |
251 | } | |
252 | ||
d711f013 JB |
253 | static void version(void) |
254 | { | |
133b069f | 255 | printf("iw version %s\n", iw_version); |
d711f013 JB |
256 | } |
257 | ||
bd396f2a JB |
258 | static int phy_lookup(char *name) |
259 | { | |
260 | char buf[200]; | |
261 | int fd, pos; | |
262 | ||
263 | snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name); | |
264 | ||
265 | fd = open(buf, O_RDONLY); | |
989e97c2 JB |
266 | if (fd < 0) |
267 | return -1; | |
bd396f2a | 268 | pos = read(fd, buf, sizeof(buf) - 1); |
8f253ee2 ES |
269 | if (pos < 0) { |
270 | close(fd); | |
bd396f2a | 271 | return -1; |
8f253ee2 | 272 | } |
bd396f2a | 273 | buf[pos] = '\0'; |
8f253ee2 | 274 | close(fd); |
bd396f2a JB |
275 | return atoi(buf); |
276 | } | |
277 | ||
70391ccf JB |
278 | static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, |
279 | void *arg) | |
280 | { | |
3a807325 JB |
281 | struct nlmsghdr *nlh = (struct nlmsghdr *)err - 1; |
282 | int len = nlh->nlmsg_len; | |
283 | struct nlattr *attrs; | |
284 | struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; | |
70391ccf | 285 | int *ret = arg; |
3a807325 JB |
286 | int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh); |
287 | ||
70391ccf | 288 | *ret = err->error; |
3a807325 JB |
289 | |
290 | if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) | |
291 | return NL_STOP; | |
292 | ||
293 | if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) | |
294 | ack_len += err->msg.nlmsg_len - sizeof(*nlh); | |
295 | ||
296 | if (len <= ack_len) | |
297 | return NL_STOP; | |
298 | ||
299 | attrs = (void *)((unsigned char *)nlh + ack_len); | |
300 | len -= ack_len; | |
301 | ||
302 | nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL); | |
303 | if (tb[NLMSGERR_ATTR_MSG]) { | |
304 | len = strnlen((char *)nla_data(tb[NLMSGERR_ATTR_MSG]), | |
305 | nla_len(tb[NLMSGERR_ATTR_MSG])); | |
306 | fprintf(stderr, "kernel reports: %*s\n", len, | |
307 | (char *)nla_data(tb[NLMSGERR_ATTR_MSG])); | |
308 | } | |
309 | ||
70391ccf JB |
310 | return NL_STOP; |
311 | } | |
312 | ||
561c5b7e JB |
313 | static int finish_handler(struct nl_msg *msg, void *arg) |
314 | { | |
59c418c0 JB |
315 | int *ret = arg; |
316 | *ret = 0; | |
561c5b7e JB |
317 | return NL_SKIP; |
318 | } | |
319 | ||
320 | static int ack_handler(struct nl_msg *msg, void *arg) | |
70391ccf JB |
321 | { |
322 | int *ret = arg; | |
323 | *ret = 0; | |
324 | return NL_STOP; | |
325 | } | |
326 | ||
34b23014 JB |
327 | static int (*registered_handler)(struct nl_msg *, void *); |
328 | static void *registered_handler_data; | |
329 | ||
330 | void register_handler(int (*handler)(struct nl_msg *, void *), void *data) | |
331 | { | |
332 | registered_handler = handler; | |
333 | registered_handler_data = data; | |
334 | } | |
335 | ||
336 | int valid_handler(struct nl_msg *msg, void *arg) | |
337 | { | |
338 | if (registered_handler) | |
339 | return registered_handler(msg, registered_handler_data); | |
340 | ||
341 | return NL_OK; | |
342 | } | |
343 | ||
4f0cae73 | 344 | static int __handle_cmd(struct nl80211_state *state, enum id_input idby, |
4698bfc2 | 345 | int argc, char **argv, const struct cmd **cmdout) |
45c7212c | 346 | { |
4698bfc2 | 347 | const struct cmd *cmd, *match = NULL, *sectcmd; |
7c37a24d | 348 | struct nl_cb *cb; |
5cb60f91 | 349 | struct nl_cb *s_cb; |
bd396f2a | 350 | struct nl_msg *msg; |
290a3dd4 | 351 | signed long long devidx = 0; |
bb60b4ae | 352 | int err, o_argc; |
bd396f2a | 353 | const char *command, *section; |
bb60b4ae | 354 | char *tmp, **o_argv; |
9927363c | 355 | enum command_identify_by command_idby = CIB_NONE; |
45c7212c | 356 | |
9927363c | 357 | if (argc <= 1 && idby != II_NONE) |
5e75fd04 | 358 | return 1; |
45c7212c | 359 | |
bb60b4ae JB |
360 | o_argc = argc; |
361 | o_argv = argv; | |
362 | ||
bd396f2a | 363 | switch (idby) { |
9927363c JB |
364 | case II_PHY_IDX: |
365 | command_idby = CIB_PHY; | |
366 | devidx = strtoul(*argv + 4, &tmp, 0); | |
367 | if (*tmp != '\0') | |
368 | return 1; | |
369 | argc--; | |
370 | argv++; | |
371 | break; | |
372 | case II_PHY_NAME: | |
373 | command_idby = CIB_PHY; | |
bd396f2a JB |
374 | devidx = phy_lookup(*argv); |
375 | argc--; | |
376 | argv++; | |
377 | break; | |
9927363c JB |
378 | case II_NETDEV: |
379 | command_idby = CIB_NETDEV; | |
bd396f2a | 380 | devidx = if_nametoindex(*argv); |
989e97c2 JB |
381 | if (devidx == 0) |
382 | devidx = -1; | |
bd396f2a JB |
383 | argc--; |
384 | argv++; | |
385 | break; | |
290a3dd4 JB |
386 | case II_WDEV: |
387 | command_idby = CIB_WDEV; | |
388 | devidx = strtoll(*argv, &tmp, 0); | |
389 | if (*tmp != '\0') | |
390 | return 1; | |
391 | argc--; | |
392 | argv++; | |
bd396f2a JB |
393 | default: |
394 | break; | |
395 | } | |
396 | ||
989e97c2 JB |
397 | if (devidx < 0) |
398 | return -errno; | |
399 | ||
4698bfc2 | 400 | section = *argv; |
bd396f2a JB |
401 | argc--; |
402 | argv++; | |
403 | ||
4698bfc2 JB |
404 | for_each_cmd(sectcmd) { |
405 | if (sectcmd->parent) | |
403b9c83 | 406 | continue; |
4698bfc2 JB |
407 | /* ok ... bit of a hack for the dupe 'info' section */ |
408 | if (match && sectcmd->idby != command_idby) | |
bd396f2a | 409 | continue; |
4698bfc2 JB |
410 | if (strcmp(sectcmd->name, section) == 0) |
411 | match = sectcmd; | |
bd396f2a | 412 | } |
45c7212c | 413 | |
4698bfc2 JB |
414 | sectcmd = match; |
415 | match = NULL; | |
416 | if (!sectcmd) | |
5e75fd04 | 417 | return 1; |
45c7212c | 418 | |
4698bfc2 JB |
419 | if (argc > 0) { |
420 | command = *argv; | |
421 | ||
422 | for_each_cmd(cmd) { | |
423 | if (!cmd->handler) | |
424 | continue; | |
425 | if (cmd->parent != sectcmd) | |
426 | continue; | |
75f4204c JB |
427 | /* |
428 | * ignore mismatch id by, but allow WDEV | |
429 | * in place of NETDEV | |
430 | */ | |
431 | if (cmd->idby != command_idby && | |
432 | !(cmd->idby == CIB_NETDEV && | |
433 | command_idby == CIB_WDEV)) | |
4698bfc2 JB |
434 | continue; |
435 | if (strcmp(cmd->name, command)) | |
436 | continue; | |
437 | if (argc > 1 && !cmd->args) | |
438 | continue; | |
439 | match = cmd; | |
440 | break; | |
441 | } | |
442 | ||
443 | if (match) { | |
444 | argc--; | |
445 | argv++; | |
446 | } | |
447 | } | |
448 | ||
449 | if (match) | |
450 | cmd = match; | |
451 | else { | |
452 | /* Use the section itself, if possible. */ | |
453 | cmd = sectcmd; | |
454 | if (argc && !cmd->args) | |
455 | return 1; | |
75f4204c JB |
456 | if (cmd->idby != command_idby && |
457 | !(cmd->idby == CIB_NETDEV && command_idby == CIB_WDEV)) | |
4698bfc2 JB |
458 | return 1; |
459 | if (!cmd->handler) | |
460 | return 1; | |
461 | } | |
462 | ||
1633ddf7 JB |
463 | if (cmd->selector) { |
464 | cmd = cmd->selector(argc, argv); | |
465 | if (!cmd) | |
466 | return 1; | |
467 | } | |
468 | ||
4f0cae73 JB |
469 | if (cmdout) |
470 | *cmdout = cmd; | |
471 | ||
bb60b4ae JB |
472 | if (!cmd->cmd) { |
473 | argc = o_argc; | |
474 | argv = o_argv; | |
34b23014 | 475 | return cmd->handler(state, NULL, argc, argv, idby); |
bb60b4ae JB |
476 | } |
477 | ||
bd396f2a JB |
478 | msg = nlmsg_alloc(); |
479 | if (!msg) { | |
70391ccf JB |
480 | fprintf(stderr, "failed to allocate netlink message\n"); |
481 | return 2; | |
482 | } | |
483 | ||
957a0f07 | 484 | cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); |
5cb60f91 SR |
485 | s_cb = nl_cb_alloc(iw_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); |
486 | if (!cb || !s_cb) { | |
70391ccf JB |
487 | fprintf(stderr, "failed to allocate netlink callbacks\n"); |
488 | err = 2; | |
7faa1ba1 | 489 | goto out; |
bd396f2a | 490 | } |
45c7212c | 491 | |
f09cee6d | 492 | genlmsg_put(msg, 0, 0, state->nl80211_id, 0, |
bd396f2a JB |
493 | cmd->nl_msg_flags, cmd->cmd, 0); |
494 | ||
9927363c | 495 | switch (command_idby) { |
bd396f2a JB |
496 | case CIB_PHY: |
497 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx); | |
498 | break; | |
499 | case CIB_NETDEV: | |
500 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); | |
501 | break; | |
290a3dd4 JB |
502 | case CIB_WDEV: |
503 | NLA_PUT_U64(msg, NL80211_ATTR_WDEV, devidx); | |
504 | break; | |
bd396f2a JB |
505 | default: |
506 | break; | |
507 | } | |
508 | ||
34b23014 | 509 | err = cmd->handler(state, msg, argc, argv, idby); |
70391ccf JB |
510 | if (err) |
511 | goto out; | |
512 | ||
5cb60f91 SR |
513 | nl_socket_set_cb(state->nl_sock, s_cb); |
514 | ||
57077d64 | 515 | err = nl_send_auto_complete(state->nl_sock, msg); |
70391ccf JB |
516 | if (err < 0) |
517 | goto out; | |
518 | ||
c5c4471a JB |
519 | err = 1; |
520 | ||
70391ccf | 521 | nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); |
59c418c0 | 522 | nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); |
561c5b7e | 523 | nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); |
34b23014 | 524 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, valid_handler, NULL); |
70391ccf | 525 | |
c5c4471a | 526 | while (err > 0) |
57077d64 | 527 | nl_recvmsgs(state->nl_sock, cb); |
70391ccf JB |
528 | out: |
529 | nl_cb_put(cb); | |
7faa1ba1 | 530 | nl_cb_put(s_cb); |
70391ccf JB |
531 | nlmsg_free(msg); |
532 | return err; | |
bd396f2a JB |
533 | nla_put_failure: |
534 | fprintf(stderr, "building message failed\n"); | |
70391ccf | 535 | return 2; |
45c7212c JB |
536 | } |
537 | ||
4f0cae73 JB |
538 | int handle_cmd(struct nl80211_state *state, enum id_input idby, |
539 | int argc, char **argv) | |
540 | { | |
541 | return __handle_cmd(state, idby, argc, argv, NULL); | |
542 | } | |
543 | ||
cad53b3f JB |
544 | int main(int argc, char **argv) |
545 | { | |
546 | struct nl80211_state nlstate; | |
bd396f2a | 547 | int err; |
4698bfc2 | 548 | const struct cmd *cmd = NULL; |
cad53b3f | 549 | |
f408e01b | 550 | /* calculate command size including padding */ |
8ccc4795 | 551 | cmd_size = labs((long)&__section_set - (long)&__section_get); |
45c7212c JB |
552 | /* strip off self */ |
553 | argc--; | |
1cdd9016 MK |
554 | argv0 = *argv++; |
555 | ||
59c49f09 | 556 | if (argc > 0 && strcmp(*argv, "--debug") == 0) { |
957a0f07 | 557 | iw_debug = 1; |
59c49f09 JB |
558 | argc--; |
559 | argv++; | |
560 | } | |
561 | ||
d711f013 JB |
562 | if (argc > 0 && strcmp(*argv, "--version") == 0) { |
563 | version(); | |
564 | return 0; | |
565 | } | |
566 | ||
01ae06f9 | 567 | /* need to treat "help" command specially so it works w/o nl80211 */ |
bd396f2a | 568 | if (argc == 0 || strcmp(*argv, "help") == 0) { |
f3ac8bf1 | 569 | usage(argc - 1, argv + 1); |
4a972f80 | 570 | return 0; |
1cdd9016 | 571 | } |
45c7212c | 572 | |
2bdb6bd1 JB |
573 | err = nl80211_init(&nlstate); |
574 | if (err) | |
575 | return 1; | |
576 | ||
957a0f07 | 577 | if (strcmp(*argv, "dev") == 0 && argc > 1) { |
14a0380d LR |
578 | argc--; |
579 | argv++; | |
4f0cae73 | 580 | err = __handle_cmd(&nlstate, II_NETDEV, argc, argv, &cmd); |
811ec68f | 581 | } else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) { |
9927363c JB |
582 | if (strlen(*argv) == 3) { |
583 | argc--; | |
584 | argv++; | |
4f0cae73 | 585 | err = __handle_cmd(&nlstate, II_PHY_NAME, argc, argv, &cmd); |
9927363c | 586 | } else if (*(*argv + 3) == '#') |
4f0cae73 | 587 | err = __handle_cmd(&nlstate, II_PHY_IDX, argc, argv, &cmd); |
9927363c | 588 | else |
f4ec76d0 | 589 | goto detect; |
290a3dd4 JB |
590 | } else if (strcmp(*argv, "wdev") == 0 && argc > 1) { |
591 | argc--; | |
592 | argv++; | |
593 | err = __handle_cmd(&nlstate, II_WDEV, argc, argv, &cmd); | |
f4ec76d0 JB |
594 | } else { |
595 | int idx; | |
596 | enum id_input idby = II_NONE; | |
597 | detect: | |
598 | if ((idx = if_nametoindex(argv[0])) != 0) | |
599 | idby = II_NETDEV; | |
66f8ca45 | 600 | else if ((idx = phy_lookup(argv[0])) >= 0) |
f4ec76d0 JB |
601 | idby = II_PHY_NAME; |
602 | err = __handle_cmd(&nlstate, idby, argc, argv, &cmd); | |
603 | } | |
45c7212c | 604 | |
b2c4bf45 | 605 | if (err == HANDLER_RET_USAGE) { |
4f0cae73 | 606 | if (cmd) |
01ae06f9 | 607 | usage_cmd(cmd); |
4f0cae73 | 608 | else |
f3ac8bf1 | 609 | usage(0, NULL); |
94af668b JB |
610 | } else if (err == HANDLER_RET_DONE) { |
611 | err = 0; | |
4f0cae73 | 612 | } else if (err < 0) |
b49be3e1 | 613 | fprintf(stderr, "command failed: %s (%d)\n", strerror(-err), err); |
cad53b3f JB |
614 | |
615 | nl80211_cleanup(&nlstate); | |
616 | ||
45c7212c | 617 | return err; |
cad53b3f | 618 | } |