]> git.ipfire.org Git - thirdparty/hostap.git/blame - hostapd/hostapd_cli.c
Add more STA information into the ctrl_iface STA command
[thirdparty/hostap.git] / hostapd / hostapd_cli.c
CommitLineData
6fc6879b
JM
1/*
2 * hostapd - command line interface for hostapd daemon
5ae6449c 3 * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
6fc6879b 4 *
0f3d578e
JM
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
6fc6879b
JM
7 */
8
9#include "includes.h"
10#include <dirent.h>
11
90973fb2 12#include "common/wpa_ctrl.h"
42838059
JM
13#include "utils/common.h"
14#include "utils/eloop.h"
15#include "utils/edit.h"
90973fb2 16#include "common/version.h"
6fc6879b
JM
17
18
19static const char *hostapd_cli_version =
20"hostapd_cli v" VERSION_STR "\n"
e056f93e 21"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
6fc6879b
JM
22
23
24static const char *hostapd_cli_license =
331f89ff
JM
25"This software may be distributed under the terms of the BSD license.\n"
26"See README for more details.\n";
6fc6879b
JM
27
28static const char *hostapd_cli_full_license =
331f89ff 29"This software may be distributed under the terms of the BSD license.\n"
6fc6879b
JM
30"\n"
31"Redistribution and use in source and binary forms, with or without\n"
32"modification, are permitted provided that the following conditions are\n"
33"met:\n"
34"\n"
35"1. Redistributions of source code must retain the above copyright\n"
36" notice, this list of conditions and the following disclaimer.\n"
37"\n"
38"2. Redistributions in binary form must reproduce the above copyright\n"
39" notice, this list of conditions and the following disclaimer in the\n"
40" documentation and/or other materials provided with the distribution.\n"
41"\n"
42"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
43" names of its contributors may be used to endorse or promote products\n"
44" derived from this software without specific prior written permission.\n"
45"\n"
46"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
47"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
48"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
49"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
50"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
51"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
52"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
53"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
54"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
55"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
56"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
57"\n";
58
59static const char *commands_help =
60"Commands:\n"
61" mib get MIB variables (dot1x, dot11, radius)\n"
62" sta <addr> get MIB variables for one station\n"
63" all_sta get MIB variables for all stations\n"
64" new_sta <addr> add a new station\n"
488d0934
JM
65" deauthenticate <addr> deauthenticate a station\n"
66" disassociate <addr> disassociate a station\n"
88b4b424
JM
67#ifdef CONFIG_IEEE80211W
68" sa_query <addr> send SA Query to a station\n"
69#endif /* CONFIG_IEEE80211W */
ad08c363 70#ifdef CONFIG_WPS
31fcea93 71" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n"
3981cb3c 72" wps_check_pin <PIN> verify PIN checksum\n"
ad08c363 73" wps_pbc indicate button pushed to initiate PBC\n"
1c79cdff 74" wps_cancel cancel the pending WPS operation\n"
bb45b6d7
JM
75#ifdef CONFIG_WPS_NFC
76" wps_nfc_tag_read <hexdump> report read NFC tag with WPS data\n"
3cf7a59d 77" wps_nfc_config_token <WPS/NDEF> build NFC configuration token\n"
ffdaa05a 78" wps_nfc_token <WPS/NDEF/enable/disable> manager NFC password token\n"
bb45b6d7 79#endif /* CONFIG_WPS_NFC */
5a1cc30f 80" wps_ap_pin <cmd> [params..] enable/disable AP PIN\n"
450eddcf 81" wps_config <SSID> <auth> <encr> <key> configure AP\n"
3351a384 82" wps_get_status show current WPS status\n"
ad08c363 83#endif /* CONFIG_WPS */
403b96fe 84" get_config show current configuration\n"
6fc6879b
JM
85" help show this usage help\n"
86" interface [ifname] show interfaces/select interface\n"
87" level <debug level> change debug level\n"
88" license show full hostapd_cli license\n"
89" quit exit hostapd_cli\n";
90
91static struct wpa_ctrl *ctrl_conn;
92static int hostapd_cli_quit = 0;
93static int hostapd_cli_attached = 0;
2d39a4d8
JJ
94
95#ifndef CONFIG_CTRL_IFACE_DIR
96#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
97#endif /* CONFIG_CTRL_IFACE_DIR */
98static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
99
6fc6879b 100static char *ctrl_ifname = NULL;
bae92174
GD
101static const char *pid_file = NULL;
102static const char *action_file = NULL;
1cc84c1c 103static int ping_interval = 5;
42838059 104static int interactive = 0;
6fc6879b
JM
105
106
107static void usage(void)
108{
109 fprintf(stderr, "%s\n", hostapd_cli_version);
bae92174
GD
110 fprintf(stderr,
111 "\n"
112 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
113 "[-a<path>] \\\n"
114 " [-G<ping interval>] [command..]\n"
6fc6879b
JM
115 "\n"
116 "Options:\n"
117 " -h help (show this usage text)\n"
118 " -v shown version information\n"
119 " -p<path> path to find control sockets (default: "
120 "/var/run/hostapd)\n"
bae92174
GD
121 " -a<file> run in daemon mode executing the action file "
122 "based on events\n"
123 " from hostapd\n"
124 " -B run a daemon in the background\n"
6fc6879b
JM
125 " -i<ifname> Interface to listen on (default: first "
126 "interface found in the\n"
127 " socket path)\n\n"
128 "%s",
129 commands_help);
130}
131
132
133static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
134{
135 char *cfile;
136 int flen;
137
138 if (ifname == NULL)
139 return NULL;
140
141 flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
142 cfile = malloc(flen);
143 if (cfile == NULL)
144 return NULL;
145 snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
146
147 ctrl_conn = wpa_ctrl_open(cfile);
148 free(cfile);
149 return ctrl_conn;
150}
151
152
153static void hostapd_cli_close_connection(void)
154{
155 if (ctrl_conn == NULL)
156 return;
157
158 if (hostapd_cli_attached) {
159 wpa_ctrl_detach(ctrl_conn);
160 hostapd_cli_attached = 0;
161 }
162 wpa_ctrl_close(ctrl_conn);
163 ctrl_conn = NULL;
164}
165
166
167static void hostapd_cli_msg_cb(char *msg, size_t len)
168{
169 printf("%s\n", msg);
170}
171
172
173static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
174{
175 char buf[4096];
176 size_t len;
177 int ret;
178
179 if (ctrl_conn == NULL) {
180 printf("Not connected to hostapd - command dropped.\n");
181 return -1;
182 }
183 len = sizeof(buf) - 1;
184 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
185 hostapd_cli_msg_cb);
186 if (ret == -2) {
187 printf("'%s' command timed out.\n", cmd);
188 return -2;
189 } else if (ret < 0) {
190 printf("'%s' command failed.\n", cmd);
191 return -1;
192 }
193 if (print) {
194 buf[len] = '\0';
195 printf("%s", buf);
196 }
197 return 0;
198}
199
200
201static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
202{
203 return _wpa_ctrl_command(ctrl, cmd, 1);
204}
205
206
207static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
208{
209 return wpa_ctrl_command(ctrl, "PING");
210}
211
212
b41a47c0
BG
213static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
214{
215 return wpa_ctrl_command(ctrl, "RELOG");
216}
217
218
5ae6449c
JM
219static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
220{
221 return wpa_ctrl_command(ctrl, "STATUS");
222}
223
224
6fc6879b
JM
225static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
226{
227 return wpa_ctrl_command(ctrl, "MIB");
228}
229
230
bae92174
GD
231static int hostapd_cli_exec(const char *program, const char *arg1,
232 const char *arg2)
233{
234 char *cmd;
235 size_t len;
236 int res;
237 int ret = 0;
238
239 len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
240 cmd = os_malloc(len);
241 if (cmd == NULL)
242 return -1;
243 res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
244 if (res < 0 || (size_t) res >= len) {
245 os_free(cmd);
246 return -1;
247 }
248 cmd[len - 1] = '\0';
249#ifndef _WIN32_WCE
250 if (system(cmd) < 0)
251 ret = -1;
252#endif /* _WIN32_WCE */
253 os_free(cmd);
254
255 return ret;
256}
257
258
259static void hostapd_cli_action_process(char *msg, size_t len)
260{
261 const char *pos;
262
263 pos = msg;
264 if (*pos == '<') {
265 pos = os_strchr(pos, '>');
266 if (pos)
267 pos++;
268 else
269 pos = msg;
270 }
271
272 hostapd_cli_exec(action_file, ctrl_ifname, pos);
273}
274
275
6fc6879b
JM
276static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
277{
278 char buf[64];
279 if (argc != 1) {
280 printf("Invalid 'sta' command - exactly one argument, STA "
281 "address, is required.\n");
282 return -1;
283 }
284 snprintf(buf, sizeof(buf), "STA %s", argv[0]);
285 return wpa_ctrl_command(ctrl, buf);
286}
287
288
289static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
290 char *argv[])
291{
292 char buf[64];
293 if (argc != 1) {
294 printf("Invalid 'new_sta' command - exactly one argument, STA "
295 "address, is required.\n");
296 return -1;
297 }
298 snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
299 return wpa_ctrl_command(ctrl, buf);
300}
301
302
90a3206a
JM
303static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
304 char *argv[])
305{
306 char buf[64];
b91ab76e 307 if (argc < 1) {
90a3206a
JM
308 printf("Invalid 'deauthenticate' command - exactly one "
309 "argument, STA address, is required.\n");
310 return -1;
311 }
b91ab76e
JM
312 if (argc > 1)
313 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
314 argv[0], argv[1]);
315 else
316 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
90a3206a
JM
317 return wpa_ctrl_command(ctrl, buf);
318}
319
320
321static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
322 char *argv[])
323{
324 char buf[64];
b91ab76e 325 if (argc < 1) {
90a3206a
JM
326 printf("Invalid 'disassociate' command - exactly one "
327 "argument, STA address, is required.\n");
328 return -1;
329 }
b91ab76e
JM
330 if (argc > 1)
331 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
332 argv[0], argv[1]);
333 else
334 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
90a3206a
JM
335 return wpa_ctrl_command(ctrl, buf);
336}
337
338
88b4b424
JM
339#ifdef CONFIG_IEEE80211W
340static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
341 char *argv[])
342{
343 char buf[64];
344 if (argc != 1) {
345 printf("Invalid 'sa_query' command - exactly one argument, "
346 "STA address, is required.\n");
347 return -1;
348 }
349 snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
350 return wpa_ctrl_command(ctrl, buf);
351}
352#endif /* CONFIG_IEEE80211W */
353
354
ad08c363
JM
355#ifdef CONFIG_WPS
356static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
357 char *argv[])
358{
31fcea93 359 char buf[256];
077a781f
JM
360 if (argc < 2) {
361 printf("Invalid 'wps_pin' command - at least two arguments, "
ad08c363
JM
362 "UUID and PIN, are required.\n");
363 return -1;
364 }
31fcea93
JM
365 if (argc > 3)
366 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
367 argv[0], argv[1], argv[2], argv[3]);
368 else if (argc > 2)
077a781f
JM
369 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
370 argv[0], argv[1], argv[2]);
371 else
372 snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
ad08c363
JM
373 return wpa_ctrl_command(ctrl, buf);
374}
375
376
3981cb3c
JM
377static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
378 char *argv[])
379{
380 char cmd[256];
381 int res;
382
383 if (argc != 1 && argc != 2) {
384 printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
385 "- PIN to be verified\n");
386 return -1;
387 }
388
389 if (argc == 2)
390 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
391 argv[0], argv[1]);
392 else
393 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
394 argv[0]);
395 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
396 printf("Too long WPS_CHECK_PIN command.\n");
397 return -1;
398 }
399 return wpa_ctrl_command(ctrl, cmd);
400}
401
402
ad08c363
JM
403static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
404 char *argv[])
405{
406 return wpa_ctrl_command(ctrl, "WPS_PBC");
407}
46bdb83a
MH
408
409
4c374cde
AS
410static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
411 char *argv[])
412{
413 return wpa_ctrl_command(ctrl, "WPS_CANCEL");
414}
415
416
bb45b6d7
JM
417#ifdef CONFIG_WPS_NFC
418static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
419 char *argv[])
420{
421 int ret;
422 char *buf;
423 size_t buflen;
424
425 if (argc != 1) {
426 printf("Invalid 'wps_nfc_tag_read' command - one argument "
427 "is required.\n");
428 return -1;
429 }
430
431 buflen = 18 + os_strlen(argv[0]);
432 buf = os_malloc(buflen);
433 if (buf == NULL)
434 return -1;
435 os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
436
437 ret = wpa_ctrl_command(ctrl, buf);
438 os_free(buf);
439
440 return ret;
441}
3cf7a59d
JM
442
443
444static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
445 int argc, char *argv[])
446{
447 char cmd[64];
448 int res;
449
450 if (argc != 1) {
451 printf("Invalid 'wps_nfc_config_token' command - one argument "
452 "is required.\n");
453 return -1;
454 }
455
456 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
457 argv[0]);
458 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
459 printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
460 return -1;
461 }
462 return wpa_ctrl_command(ctrl, cmd);
463}
ffdaa05a
JM
464
465
466static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
467 int argc, char *argv[])
468{
469 char cmd[64];
470 int res;
471
472 if (argc != 1) {
473 printf("Invalid 'wps_nfc_token' command - one argument is "
474 "required.\n");
475 return -1;
476 }
477
478 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
479 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
480 printf("Too long WPS_NFC_TOKEN command.\n");
481 return -1;
482 }
483 return wpa_ctrl_command(ctrl, cmd);
484}
6772a90a
JM
485
486
487static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
488 int argc, char *argv[])
489{
490 char cmd[64];
491 int res;
492
493 if (argc != 2) {
494 printf("Invalid 'nfc_get_handover_sel' command - two arguments "
495 "are required.\n");
496 return -1;
497 }
498
499 res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
500 argv[0], argv[1]);
501 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
502 printf("Too long NFC_GET_HANDOVER_SEL command.\n");
503 return -1;
504 }
505 return wpa_ctrl_command(ctrl, cmd);
506}
507
bb45b6d7
JM
508#endif /* CONFIG_WPS_NFC */
509
510
5a1cc30f
JM
511static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
512 char *argv[])
513{
514 char buf[64];
515 if (argc < 1) {
516 printf("Invalid 'wps_ap_pin' command - at least one argument "
517 "is required.\n");
518 return -1;
519 }
520 if (argc > 2)
521 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
522 argv[0], argv[1], argv[2]);
523 else if (argc > 1)
524 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
525 argv[0], argv[1]);
526 else
527 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
528 return wpa_ctrl_command(ctrl, buf);
529}
450eddcf
JM
530
531
3351a384
JM
532static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
533 char *argv[])
534{
535 return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
536}
537
538
450eddcf
JM
539static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
540 char *argv[])
541{
542 char buf[256];
543 char ssid_hex[2 * 32 + 1];
544 char key_hex[2 * 64 + 1];
545 int i;
546
547 if (argc < 1) {
548 printf("Invalid 'wps_config' command - at least two arguments "
549 "are required.\n");
550 return -1;
551 }
552
553 ssid_hex[0] = '\0';
554 for (i = 0; i < 32; i++) {
555 if (argv[0][i] == '\0')
556 break;
557 os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
558 }
559
560 key_hex[0] = '\0';
561 if (argc > 3) {
562 for (i = 0; i < 64; i++) {
563 if (argv[3][i] == '\0')
564 break;
565 os_snprintf(&key_hex[i * 2], 3, "%02x",
566 argv[3][i]);
567 }
568 }
569
570 if (argc > 3)
571 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
572 ssid_hex, argv[1], argv[2], key_hex);
573 else if (argc > 2)
574 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
575 ssid_hex, argv[1], argv[2]);
576 else
577 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
578 ssid_hex, argv[1]);
579 return wpa_ctrl_command(ctrl, buf);
580}
ad08c363
JM
581#endif /* CONFIG_WPS */
582
583
2049a875
JM
584static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
585 char *argv[])
586{
587 char buf[300];
588 int res;
589
590 if (argc < 2) {
591 printf("Invalid 'disassoc_imminent' command - two arguments "
592 "(STA addr and Disassociation Timer) are needed\n");
593 return -1;
594 }
595
596 res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
597 argv[0], argv[1]);
598 if (res < 0 || res >= (int) sizeof(buf))
599 return -1;
600 return wpa_ctrl_command(ctrl, buf);
601}
602
603
71269b37
JM
604static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
605 char *argv[])
606{
607 char buf[300];
608 int res;
609
d5b559b6
KP
610 if (argc < 3) {
611 printf("Invalid 'ess_disassoc' command - three arguments (STA "
612 "addr, disassoc timer, and URL) are needed\n");
71269b37
JM
613 return -1;
614 }
615
d5b559b6
KP
616 res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
617 argv[0], argv[1], argv[2]);
71269b37
JM
618 if (res < 0 || res >= (int) sizeof(buf))
619 return -1;
620 return wpa_ctrl_command(ctrl, buf);
621}
622
623
403b96fe
JM
624static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
625 char *argv[])
626{
627 return wpa_ctrl_command(ctrl, "GET_CONFIG");
628}
629
630
6fc6879b
JM
631static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
632 char *addr, size_t addr_len)
633{
634 char buf[4096], *pos;
635 size_t len;
636 int ret;
637
638 if (ctrl_conn == NULL) {
639 printf("Not connected to hostapd - command dropped.\n");
640 return -1;
641 }
642 len = sizeof(buf) - 1;
643 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
644 hostapd_cli_msg_cb);
645 if (ret == -2) {
646 printf("'%s' command timed out.\n", cmd);
647 return -2;
648 } else if (ret < 0) {
649 printf("'%s' command failed.\n", cmd);
650 return -1;
651 }
652
653 buf[len] = '\0';
654 if (memcmp(buf, "FAIL", 4) == 0)
655 return -1;
656 printf("%s", buf);
657
658 pos = buf;
659 while (*pos != '\0' && *pos != '\n')
660 pos++;
661 *pos = '\0';
662 os_strlcpy(addr, buf, addr_len);
663 return 0;
664}
665
666
667static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
668 char *argv[])
669{
670 char addr[32], cmd[64];
671
672 if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
673 return 0;
674 do {
675 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
676 } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
677
678 return -1;
679}
680
681
682static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
683{
684 printf("%s", commands_help);
685 return 0;
686}
687
688
689static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
690 char *argv[])
691{
692 printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
693 return 0;
694}
695
696
c551700f
KP
697static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
698 int argc, char *argv[])
699{
700 char buf[200];
701 int res;
702
703 if (argc != 1) {
704 printf("Invalid 'set_qos_map_set' command - "
705 "one argument (comma delimited QoS map set) "
706 "is needed\n");
707 return -1;
708 }
709
710 res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
711 if (res < 0 || res >= (int) sizeof(buf))
712 return -1;
713 return wpa_ctrl_command(ctrl, buf);
714}
715
716
717static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
718 int argc, char *argv[])
719{
720 char buf[50];
721 int res;
722
723 if (argc != 1) {
724 printf("Invalid 'send_qos_map_conf' command - "
725 "one argument (STA addr) is needed\n");
726 return -1;
727 }
728
729 res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
730 if (res < 0 || res >= (int) sizeof(buf))
731 return -1;
732 return wpa_ctrl_command(ctrl, buf);
733}
734
735
6fc6879b
JM
736static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
737{
738 hostapd_cli_quit = 1;
42838059
JM
739 if (interactive)
740 eloop_terminate();
6fc6879b
JM
741 return 0;
742}
743
744
745static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
746{
747 char cmd[256];
748 if (argc != 1) {
749 printf("Invalid LEVEL command: needs one argument (debug "
750 "level)\n");
751 return 0;
752 }
753 snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
754 return wpa_ctrl_command(ctrl, cmd);
755}
756
757
758static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
759{
760 struct dirent *dent;
761 DIR *dir;
762
763 dir = opendir(ctrl_iface_dir);
764 if (dir == NULL) {
765 printf("Control interface directory '%s' could not be "
766 "openned.\n", ctrl_iface_dir);
767 return;
768 }
769
770 printf("Available interfaces:\n");
771 while ((dent = readdir(dir))) {
772 if (strcmp(dent->d_name, ".") == 0 ||
773 strcmp(dent->d_name, "..") == 0)
774 continue;
775 printf("%s\n", dent->d_name);
776 }
777 closedir(dir);
778}
779
780
781static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
782 char *argv[])
783{
784 if (argc < 1) {
785 hostapd_cli_list_interfaces(ctrl);
786 return 0;
787 }
788
789 hostapd_cli_close_connection();
790 free(ctrl_ifname);
791 ctrl_ifname = strdup(argv[0]);
792
793 if (hostapd_cli_open_connection(ctrl_ifname)) {
794 printf("Connected to interface '%s.\n", ctrl_ifname);
795 if (wpa_ctrl_attach(ctrl_conn) == 0) {
796 hostapd_cli_attached = 1;
797 } else {
798 printf("Warning: Failed to attach to "
799 "hostapd.\n");
800 }
801 } else {
802 printf("Could not connect to interface '%s' - re-trying\n",
803 ctrl_ifname);
804 }
805 return 0;
806}
807
808
b4e34f2f
JM
809static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
810{
811 char cmd[256];
812 int res;
813
814 if (argc != 2) {
815 printf("Invalid SET command: needs two arguments (variable "
816 "name and value)\n");
817 return -1;
818 }
819
820 res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
821 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
822 printf("Too long SET command.\n");
823 return -1;
824 }
825 return wpa_ctrl_command(ctrl, cmd);
826}
827
828
acec8d32
JM
829static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
830{
831 char cmd[256];
832 int res;
833
834 if (argc != 1) {
835 printf("Invalid GET command: needs one argument (variable "
836 "name)\n");
837 return -1;
838 }
839
840 res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
841 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
842 printf("Too long GET command.\n");
843 return -1;
844 }
845 return wpa_ctrl_command(ctrl, cmd);
846}
847
848
334bf36a
AO
849static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
850 int argc, char *argv[])
851{
852 char cmd[256];
853 int res;
854 int i;
855 char *tmp;
856 int total;
857
858 if (argc < 2) {
859 printf("Invalid chan_switch command: needs at least two "
860 "arguments (count and freq)\n"
861 "usage: <cs_count> <freq> [sec_channel_offset=] "
862 "[center_freq1=] [center_freq2=] [bandwidth=] "
863 "[blocktx] [ht|vht]\n");
864 return -1;
865 }
866
867 res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
868 argv[0], argv[1]);
869 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
870 printf("Too long CHAN_SWITCH command.\n");
871 return -1;
872 }
873
874 total = res;
875 for (i = 2; i < argc; i++) {
876 tmp = cmd + total;
877 res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
878 if (res < 0 || (size_t) res >= sizeof(cmd) - total - 1) {
879 printf("Too long CHAN_SWITCH command.\n");
880 return -1;
881 }
882 total += res;
883 }
884 return wpa_ctrl_command(ctrl, cmd);
885}
886
887
6fc6879b
JM
888struct hostapd_cli_cmd {
889 const char *cmd;
890 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
891};
892
893static struct hostapd_cli_cmd hostapd_cli_commands[] = {
894 { "ping", hostapd_cli_cmd_ping },
895 { "mib", hostapd_cli_cmd_mib },
b41a47c0 896 { "relog", hostapd_cli_cmd_relog },
5ae6449c 897 { "status", hostapd_cli_cmd_status },
6fc6879b
JM
898 { "sta", hostapd_cli_cmd_sta },
899 { "all_sta", hostapd_cli_cmd_all_sta },
900 { "new_sta", hostapd_cli_cmd_new_sta },
90a3206a
JM
901 { "deauthenticate", hostapd_cli_cmd_deauthenticate },
902 { "disassociate", hostapd_cli_cmd_disassociate },
88b4b424
JM
903#ifdef CONFIG_IEEE80211W
904 { "sa_query", hostapd_cli_cmd_sa_query },
905#endif /* CONFIG_IEEE80211W */
ad08c363
JM
906#ifdef CONFIG_WPS
907 { "wps_pin", hostapd_cli_cmd_wps_pin },
3981cb3c 908 { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
ad08c363 909 { "wps_pbc", hostapd_cli_cmd_wps_pbc },
4c374cde 910 { "wps_cancel", hostapd_cli_cmd_wps_cancel },
bb45b6d7
JM
911#ifdef CONFIG_WPS_NFC
912 { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
3cf7a59d 913 { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
ffdaa05a 914 { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
6772a90a 915 { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
bb45b6d7 916#endif /* CONFIG_WPS_NFC */
5a1cc30f 917 { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
450eddcf 918 { "wps_config", hostapd_cli_cmd_wps_config },
3351a384 919 { "wps_get_status", hostapd_cli_cmd_wps_get_status },
ad08c363 920#endif /* CONFIG_WPS */
2049a875 921 { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
71269b37 922 { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
403b96fe 923 { "get_config", hostapd_cli_cmd_get_config },
6fc6879b
JM
924 { "help", hostapd_cli_cmd_help },
925 { "interface", hostapd_cli_cmd_interface },
926 { "level", hostapd_cli_cmd_level },
927 { "license", hostapd_cli_cmd_license },
928 { "quit", hostapd_cli_cmd_quit },
b4e34f2f 929 { "set", hostapd_cli_cmd_set },
acec8d32 930 { "get", hostapd_cli_cmd_get },
c551700f
KP
931 { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
932 { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
334bf36a 933 { "chan_switch", hostapd_cli_cmd_chan_switch },
6fc6879b
JM
934 { NULL, NULL }
935};
936
937
938static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
939{
940 struct hostapd_cli_cmd *cmd, *match = NULL;
941 int count;
942
943 count = 0;
944 cmd = hostapd_cli_commands;
945 while (cmd->cmd) {
946 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
947 match = cmd;
acec8d32
JM
948 if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
949 /* we have an exact match */
950 count = 1;
951 break;
952 }
6fc6879b
JM
953 count++;
954 }
955 cmd++;
956 }
957
958 if (count > 1) {
959 printf("Ambiguous command '%s'; possible commands:", argv[0]);
960 cmd = hostapd_cli_commands;
961 while (cmd->cmd) {
962 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
963 0) {
964 printf(" %s", cmd->cmd);
965 }
966 cmd++;
967 }
968 printf("\n");
969 } else if (count == 0) {
970 printf("Unknown command '%s'\n", argv[0]);
971 } else {
972 match->handler(ctrl, argc - 1, &argv[1]);
973 }
974}
975
976
bae92174
GD
977static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
978 int action_monitor)
6fc6879b
JM
979{
980 int first = 1;
981 if (ctrl_conn == NULL)
982 return;
983 while (wpa_ctrl_pending(ctrl)) {
984 char buf[256];
985 size_t len = sizeof(buf) - 1;
986 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
987 buf[len] = '\0';
bae92174
GD
988 if (action_monitor)
989 hostapd_cli_action_process(buf, len);
990 else {
991 if (in_read && first)
992 printf("\n");
993 first = 0;
994 printf("%s\n", buf);
995 }
6fc6879b
JM
996 } else {
997 printf("Could not read pending message.\n");
998 break;
999 }
1000 }
1001}
1002
1003
42838059 1004#define max_args 10
6fc6879b 1005
42838059
JM
1006static int tokenize_cmd(char *cmd, char *argv[])
1007{
1008 char *pos;
1009 int argc = 0;
6fc6879b 1010
42838059
JM
1011 pos = cmd;
1012 for (;;) {
1013 while (*pos == ' ')
6fc6879b 1014 pos++;
42838059
JM
1015 if (*pos == '\0')
1016 break;
1017 argv[argc] = pos;
1018 argc++;
1019 if (argc == max_args)
1020 break;
1021 if (*pos == '"') {
1022 char *pos2 = os_strrchr(pos, '"');
1023 if (pos2)
1024 pos = pos2 + 1;
6fc6879b 1025 }
42838059
JM
1026 while (*pos != '\0' && *pos != ' ')
1027 pos++;
1028 if (*pos == ' ')
1029 *pos++ = '\0';
1030 }
bae92174 1031
42838059 1032 return argc;
6fc6879b
JM
1033}
1034
1035
42838059 1036static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
6fc6879b
JM
1037{
1038 if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
1039 printf("Connection to hostapd lost - trying to reconnect\n");
1040 hostapd_cli_close_connection();
1041 }
1042 if (!ctrl_conn) {
1043 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1044 if (ctrl_conn) {
1045 printf("Connection to hostapd re-established\n");
1046 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1047 hostapd_cli_attached = 1;
1048 } else {
1049 printf("Warning: Failed to attach to "
1050 "hostapd.\n");
1051 }
1052 }
1053 }
1054 if (ctrl_conn)
bae92174 1055 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
42838059
JM
1056 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1057}
1058
1059
1060static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
1061{
1062 eloop_terminate();
1063}
1064
1065
1066static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
1067{
1068 char *argv[max_args];
1069 int argc;
1070 argc = tokenize_cmd(cmd, argv);
1071 if (argc)
1072 wpa_request(ctrl_conn, argc, argv);
1073}
1074
1075
1076static void hostapd_cli_edit_eof_cb(void *ctx)
1077{
1078 eloop_terminate();
1079}
1080
1081
1082static void hostapd_cli_interactive(void)
1083{
1084 printf("\nInteractive mode\n\n");
1085
1086 eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
1087 edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
2d2398a1 1088 NULL, NULL, NULL, NULL);
42838059
JM
1089 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1090
1091 eloop_run();
1092
1093 edit_deinit(NULL, NULL);
1094 eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
1095}
1096
1097
1098static void hostapd_cli_cleanup(void)
1099{
1100 hostapd_cli_close_connection();
1101 if (pid_file)
1102 os_daemonize_terminate(pid_file);
1103
1104 os_program_deinit();
6fc6879b
JM
1105}
1106
1107
bae92174
GD
1108static void hostapd_cli_action(struct wpa_ctrl *ctrl)
1109{
1110 fd_set rfds;
1111 int fd, res;
1112 struct timeval tv;
1113 char buf[256];
1114 size_t len;
1115
1116 fd = wpa_ctrl_get_fd(ctrl);
1117
1118 while (!hostapd_cli_quit) {
1119 FD_ZERO(&rfds);
1120 FD_SET(fd, &rfds);
1121 tv.tv_sec = ping_interval;
1122 tv.tv_usec = 0;
1123 res = select(fd + 1, &rfds, NULL, NULL, &tv);
1124 if (res < 0 && errno != EINTR) {
1125 perror("select");
1126 break;
1127 }
1128
1129 if (FD_ISSET(fd, &rfds))
1130 hostapd_cli_recv_pending(ctrl, 0, 1);
1131 else {
1132 len = sizeof(buf) - 1;
1133 if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1134 hostapd_cli_action_process) < 0 ||
1135 len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1136 printf("hostapd did not reply to PING "
1137 "command - exiting\n");
1138 break;
1139 }
1140 }
1141 }
1142}
1143
1144
6fc6879b
JM
1145int main(int argc, char *argv[])
1146{
6fc6879b
JM
1147 int warning_displayed = 0;
1148 int c;
bae92174 1149 int daemonize = 0;
6fc6879b 1150
3433ed8c
JM
1151 if (os_program_init())
1152 return -1;
1153
6fc6879b 1154 for (;;) {
bae92174 1155 c = getopt(argc, argv, "a:BhG:i:p:v");
6fc6879b
JM
1156 if (c < 0)
1157 break;
1158 switch (c) {
bae92174
GD
1159 case 'a':
1160 action_file = optarg;
1161 break;
1162 case 'B':
1163 daemonize = 1;
1164 break;
1cc84c1c
JM
1165 case 'G':
1166 ping_interval = atoi(optarg);
1167 break;
6fc6879b
JM
1168 case 'h':
1169 usage();
1170 return 0;
1171 case 'v':
1172 printf("%s\n", hostapd_cli_version);
1173 return 0;
1174 case 'i':
b242d398
JM
1175 os_free(ctrl_ifname);
1176 ctrl_ifname = os_strdup(optarg);
6fc6879b
JM
1177 break;
1178 case 'p':
1179 ctrl_iface_dir = optarg;
1180 break;
1181 default:
1182 usage();
1183 return -1;
1184 }
1185 }
1186
bae92174 1187 interactive = (argc == optind) && (action_file == NULL);
6fc6879b
JM
1188
1189 if (interactive) {
1190 printf("%s\n\n%s\n\n", hostapd_cli_version,
1191 hostapd_cli_license);
1192 }
1193
42838059
JM
1194 if (eloop_init())
1195 return -1;
1196
6fc6879b
JM
1197 for (;;) {
1198 if (ctrl_ifname == NULL) {
1199 struct dirent *dent;
1200 DIR *dir = opendir(ctrl_iface_dir);
1201 if (dir) {
1202 while ((dent = readdir(dir))) {
b242d398
JM
1203 if (os_strcmp(dent->d_name, ".") == 0
1204 ||
1205 os_strcmp(dent->d_name, "..") == 0)
6fc6879b
JM
1206 continue;
1207 printf("Selected interface '%s'\n",
1208 dent->d_name);
b242d398 1209 ctrl_ifname = os_strdup(dent->d_name);
6fc6879b
JM
1210 break;
1211 }
1212 closedir(dir);
1213 }
1214 }
1215 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1216 if (ctrl_conn) {
1217 if (warning_displayed)
1218 printf("Connection established.\n");
1219 break;
1220 }
1221
1222 if (!interactive) {
1223 perror("Failed to connect to hostapd - "
1224 "wpa_ctrl_open");
1225 return -1;
1226 }
1227
1228 if (!warning_displayed) {
1229 printf("Could not connect to hostapd - re-trying\n");
1230 warning_displayed = 1;
1231 }
b242d398 1232 os_sleep(1, 0);
6fc6879b
JM
1233 continue;
1234 }
1235
bae92174 1236 if (interactive || action_file) {
6fc6879b
JM
1237 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1238 hostapd_cli_attached = 1;
1239 } else {
1240 printf("Warning: Failed to attach to hostapd.\n");
bae92174
GD
1241 if (action_file)
1242 return -1;
6fc6879b 1243 }
bae92174
GD
1244 }
1245
1246 if (daemonize && os_daemonize(pid_file))
1247 return -1;
1248
1249 if (interactive)
6fc6879b 1250 hostapd_cli_interactive();
bae92174
GD
1251 else if (action_file)
1252 hostapd_cli_action(ctrl_conn);
1253 else
6fc6879b
JM
1254 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1255
bae92174 1256 os_free(ctrl_ifname);
42838059 1257 eloop_destroy();
bae92174 1258 hostapd_cli_cleanup();
6fc6879b
JM
1259 return 0;
1260}