blob: aed981c4304687b12ff0b6a51d355bc37d795297 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * hostapd - command line interface for hostapd daemon
Dmitry Shmidt04949592012-07-19 12:16:46 -07003 * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "includes.h"
10#include <dirent.h>
11
12#include "common/wpa_ctrl.h"
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080013#include "utils/common.h"
14#include "utils/eloop.h"
15#include "utils/edit.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070016#include "common/version.h"
17
18
19static const char *hostapd_cli_version =
20"hostapd_cli v" VERSION_STR "\n"
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -080021"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors";
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070022
23
24static const char *hostapd_cli_license =
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -080025"This software may be distributed under the terms of the BSD license.\n"
26"See README for more details.\n";
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070027
28static const char *hostapd_cli_full_license =
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -080029"This software may be distributed under the terms of the BSD license.\n"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070030"\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"
65" deauthenticate <addr> deauthenticate a station\n"
66" disassociate <addr> disassociate a station\n"
67#ifdef CONFIG_IEEE80211W
68" sa_query <addr> send SA Query to a station\n"
69#endif /* CONFIG_IEEE80211W */
70#ifdef CONFIG_WPS
71" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n"
72" wps_check_pin <PIN> verify PIN checksum\n"
73" wps_pbc indicate button pushed to initiate PBC\n"
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070074" wps_cancel cancel the pending WPS operation\n"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070075#ifdef CONFIG_WPS_OOB
76" wps_oob <type> <path> <method> use WPS with out-of-band (UFD)\n"
77#endif /* CONFIG_WPS_OOB */
Dmitry Shmidt04949592012-07-19 12:16:46 -070078#ifdef CONFIG_WPS_NFC
79" wps_nfc_tag_read <hexdump> report read NFC tag with WPS data\n"
80" wps_nfc_config_token <WPS/NDEF> build NFC configuration token\n"
81" wps_nfc_token <WPS/NDEF/enable/disable> manager NFC password token\n"
82#endif /* CONFIG_WPS_NFC */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070083" wps_ap_pin <cmd> [params..] enable/disable AP PIN\n"
84" wps_config <SSID> <auth> <encr> <key> configure AP\n"
85#endif /* CONFIG_WPS */
86" get_config show current configuration\n"
87" help show this usage help\n"
88" interface [ifname] show interfaces/select interface\n"
89" level <debug level> change debug level\n"
90" license show full hostapd_cli license\n"
91" quit exit hostapd_cli\n";
92
93static struct wpa_ctrl *ctrl_conn;
94static int hostapd_cli_quit = 0;
95static int hostapd_cli_attached = 0;
96static const char *ctrl_iface_dir = "/var/run/hostapd";
97static char *ctrl_ifname = NULL;
98static const char *pid_file = NULL;
99static const char *action_file = NULL;
100static int ping_interval = 5;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800101static int interactive = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700102
103
104static void usage(void)
105{
106 fprintf(stderr, "%s\n", hostapd_cli_version);
107 fprintf(stderr,
108 "\n"
109 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
110 "[-a<path>] \\\n"
111 " [-G<ping interval>] [command..]\n"
112 "\n"
113 "Options:\n"
114 " -h help (show this usage text)\n"
115 " -v shown version information\n"
116 " -p<path> path to find control sockets (default: "
117 "/var/run/hostapd)\n"
118 " -a<file> run in daemon mode executing the action file "
119 "based on events\n"
120 " from hostapd\n"
121 " -B run a daemon in the background\n"
122 " -i<ifname> Interface to listen on (default: first "
123 "interface found in the\n"
124 " socket path)\n\n"
125 "%s",
126 commands_help);
127}
128
129
130static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
131{
132 char *cfile;
133 int flen;
134
135 if (ifname == NULL)
136 return NULL;
137
138 flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
139 cfile = malloc(flen);
140 if (cfile == NULL)
141 return NULL;
142 snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
143
144 ctrl_conn = wpa_ctrl_open(cfile);
145 free(cfile);
146 return ctrl_conn;
147}
148
149
150static void hostapd_cli_close_connection(void)
151{
152 if (ctrl_conn == NULL)
153 return;
154
155 if (hostapd_cli_attached) {
156 wpa_ctrl_detach(ctrl_conn);
157 hostapd_cli_attached = 0;
158 }
159 wpa_ctrl_close(ctrl_conn);
160 ctrl_conn = NULL;
161}
162
163
164static void hostapd_cli_msg_cb(char *msg, size_t len)
165{
166 printf("%s\n", msg);
167}
168
169
170static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
171{
172 char buf[4096];
173 size_t len;
174 int ret;
175
176 if (ctrl_conn == NULL) {
177 printf("Not connected to hostapd - command dropped.\n");
178 return -1;
179 }
180 len = sizeof(buf) - 1;
181 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
182 hostapd_cli_msg_cb);
183 if (ret == -2) {
184 printf("'%s' command timed out.\n", cmd);
185 return -2;
186 } else if (ret < 0) {
187 printf("'%s' command failed.\n", cmd);
188 return -1;
189 }
190 if (print) {
191 buf[len] = '\0';
192 printf("%s", buf);
193 }
194 return 0;
195}
196
197
198static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
199{
200 return _wpa_ctrl_command(ctrl, cmd, 1);
201}
202
203
204static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
205{
206 return wpa_ctrl_command(ctrl, "PING");
207}
208
209
210static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
211{
212 return wpa_ctrl_command(ctrl, "RELOG");
213}
214
215
216static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
217{
218 return wpa_ctrl_command(ctrl, "MIB");
219}
220
221
222static int hostapd_cli_exec(const char *program, const char *arg1,
223 const char *arg2)
224{
225 char *cmd;
226 size_t len;
227 int res;
228 int ret = 0;
229
230 len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
231 cmd = os_malloc(len);
232 if (cmd == NULL)
233 return -1;
234 res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
235 if (res < 0 || (size_t) res >= len) {
236 os_free(cmd);
237 return -1;
238 }
239 cmd[len - 1] = '\0';
240#ifndef _WIN32_WCE
241 if (system(cmd) < 0)
242 ret = -1;
243#endif /* _WIN32_WCE */
244 os_free(cmd);
245
246 return ret;
247}
248
249
250static void hostapd_cli_action_process(char *msg, size_t len)
251{
252 const char *pos;
253
254 pos = msg;
255 if (*pos == '<') {
256 pos = os_strchr(pos, '>');
257 if (pos)
258 pos++;
259 else
260 pos = msg;
261 }
262
263 hostapd_cli_exec(action_file, ctrl_ifname, pos);
264}
265
266
267static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
268{
269 char buf[64];
270 if (argc != 1) {
271 printf("Invalid 'sta' command - exactly one argument, STA "
272 "address, is required.\n");
273 return -1;
274 }
275 snprintf(buf, sizeof(buf), "STA %s", argv[0]);
276 return wpa_ctrl_command(ctrl, buf);
277}
278
279
280static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
281 char *argv[])
282{
283 char buf[64];
284 if (argc != 1) {
285 printf("Invalid 'new_sta' command - exactly one argument, STA "
286 "address, is required.\n");
287 return -1;
288 }
289 snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
290 return wpa_ctrl_command(ctrl, buf);
291}
292
293
294static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
295 char *argv[])
296{
297 char buf[64];
298 if (argc < 1) {
299 printf("Invalid 'deauthenticate' command - exactly one "
300 "argument, STA address, is required.\n");
301 return -1;
302 }
303 if (argc > 1)
304 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
305 argv[0], argv[1]);
306 else
307 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
308 return wpa_ctrl_command(ctrl, buf);
309}
310
311
312static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
313 char *argv[])
314{
315 char buf[64];
316 if (argc < 1) {
317 printf("Invalid 'disassociate' command - exactly one "
318 "argument, STA address, is required.\n");
319 return -1;
320 }
321 if (argc > 1)
322 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
323 argv[0], argv[1]);
324 else
325 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
326 return wpa_ctrl_command(ctrl, buf);
327}
328
329
330#ifdef CONFIG_IEEE80211W
331static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
332 char *argv[])
333{
334 char buf[64];
335 if (argc != 1) {
336 printf("Invalid 'sa_query' command - exactly one argument, "
337 "STA address, is required.\n");
338 return -1;
339 }
340 snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
341 return wpa_ctrl_command(ctrl, buf);
342}
343#endif /* CONFIG_IEEE80211W */
344
345
346#ifdef CONFIG_WPS
347static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
348 char *argv[])
349{
350 char buf[256];
351 if (argc < 2) {
352 printf("Invalid 'wps_pin' command - at least two arguments, "
353 "UUID and PIN, are required.\n");
354 return -1;
355 }
356 if (argc > 3)
357 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
358 argv[0], argv[1], argv[2], argv[3]);
359 else if (argc > 2)
360 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
361 argv[0], argv[1], argv[2]);
362 else
363 snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
364 return wpa_ctrl_command(ctrl, buf);
365}
366
367
368static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
369 char *argv[])
370{
371 char cmd[256];
372 int res;
373
374 if (argc != 1 && argc != 2) {
375 printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
376 "- PIN to be verified\n");
377 return -1;
378 }
379
380 if (argc == 2)
381 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
382 argv[0], argv[1]);
383 else
384 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
385 argv[0]);
386 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
387 printf("Too long WPS_CHECK_PIN command.\n");
388 return -1;
389 }
390 return wpa_ctrl_command(ctrl, cmd);
391}
392
393
394static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
395 char *argv[])
396{
397 return wpa_ctrl_command(ctrl, "WPS_PBC");
398}
399
400
Dmitry Shmidt04949592012-07-19 12:16:46 -0700401static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
402 char *argv[])
403{
404 return wpa_ctrl_command(ctrl, "WPS_CANCEL");
405}
406
407
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700408#ifdef CONFIG_WPS_OOB
409static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
410 char *argv[])
411{
412 char cmd[256];
413 int res;
414
415 if (argc != 3 && argc != 4) {
416 printf("Invalid WPS_OOB command: need three or four "
417 "arguments:\n"
418 "- DEV_TYPE: use 'ufd' or 'nfc'\n"
419 "- PATH: path of OOB device like '/mnt'\n"
420 "- METHOD: OOB method 'pin-e' or 'pin-r', "
421 "'cred'\n"
422 "- DEV_NAME: (only for NFC) device name like "
423 "'pn531'\n");
424 return -1;
425 }
426
427 if (argc == 3)
428 res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
429 argv[0], argv[1], argv[2]);
430 else
431 res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
432 argv[0], argv[1], argv[2], argv[3]);
433 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
434 printf("Too long WPS_OOB command.\n");
435 return -1;
436 }
437 return wpa_ctrl_command(ctrl, cmd);
438}
439#endif /* CONFIG_WPS_OOB */
440
441
Dmitry Shmidt04949592012-07-19 12:16:46 -0700442#ifdef CONFIG_WPS_NFC
443static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
444 char *argv[])
445{
446 int ret;
447 char *buf;
448 size_t buflen;
449
450 if (argc != 1) {
451 printf("Invalid 'wps_nfc_tag_read' command - one argument "
452 "is required.\n");
453 return -1;
454 }
455
456 buflen = 18 + os_strlen(argv[0]);
457 buf = os_malloc(buflen);
458 if (buf == NULL)
459 return -1;
460 os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
461
462 ret = wpa_ctrl_command(ctrl, buf);
463 os_free(buf);
464
465 return ret;
466}
467
468
469static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
470 int argc, char *argv[])
471{
472 char cmd[64];
473 int res;
474
475 if (argc != 1) {
476 printf("Invalid 'wps_nfc_config_token' command - one argument "
477 "is required.\n");
478 return -1;
479 }
480
481 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
482 argv[0]);
483 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
484 printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
485 return -1;
486 }
487 return wpa_ctrl_command(ctrl, cmd);
488}
489
490
491static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
492 int argc, char *argv[])
493{
494 char cmd[64];
495 int res;
496
497 if (argc != 1) {
498 printf("Invalid 'wps_nfc_token' command - one argument is "
499 "required.\n");
500 return -1;
501 }
502
503 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
504 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
505 printf("Too long WPS_NFC_TOKEN command.\n");
506 return -1;
507 }
508 return wpa_ctrl_command(ctrl, cmd);
509}
510#endif /* CONFIG_WPS_NFC */
511
512
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700513static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
514 char *argv[])
515{
516 char buf[64];
517 if (argc < 1) {
518 printf("Invalid 'wps_ap_pin' command - at least one argument "
519 "is required.\n");
520 return -1;
521 }
522 if (argc > 2)
523 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
524 argv[0], argv[1], argv[2]);
525 else if (argc > 1)
526 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
527 argv[0], argv[1]);
528 else
529 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
530 return wpa_ctrl_command(ctrl, buf);
531}
532
533
534static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
535 char *argv[])
536{
537 char buf[256];
538 char ssid_hex[2 * 32 + 1];
539 char key_hex[2 * 64 + 1];
540 int i;
541
542 if (argc < 1) {
543 printf("Invalid 'wps_config' command - at least two arguments "
544 "are required.\n");
545 return -1;
546 }
547
548 ssid_hex[0] = '\0';
549 for (i = 0; i < 32; i++) {
550 if (argv[0][i] == '\0')
551 break;
552 os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
553 }
554
555 key_hex[0] = '\0';
556 if (argc > 3) {
557 for (i = 0; i < 64; i++) {
558 if (argv[3][i] == '\0')
559 break;
560 os_snprintf(&key_hex[i * 2], 3, "%02x",
561 argv[3][i]);
562 }
563 }
564
565 if (argc > 3)
566 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
567 ssid_hex, argv[1], argv[2], key_hex);
568 else if (argc > 2)
569 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
570 ssid_hex, argv[1], argv[2]);
571 else
572 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
573 ssid_hex, argv[1]);
574 return wpa_ctrl_command(ctrl, buf);
575}
576#endif /* CONFIG_WPS */
577
578
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800579static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
580 char *argv[])
581{
582 char buf[300];
583 int res;
584
585 if (argc < 2) {
586 printf("Invalid 'ess_disassoc' command - two arguments (STA "
587 "addr and URL) are needed\n");
588 return -1;
589 }
590
591 res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s",
592 argv[0], argv[1]);
593 if (res < 0 || res >= (int) sizeof(buf))
594 return -1;
595 return wpa_ctrl_command(ctrl, buf);
596}
597
598
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700599static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
600 char *argv[])
601{
602 return wpa_ctrl_command(ctrl, "GET_CONFIG");
603}
604
605
606static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
607 char *addr, size_t addr_len)
608{
609 char buf[4096], *pos;
610 size_t len;
611 int ret;
612
613 if (ctrl_conn == NULL) {
614 printf("Not connected to hostapd - command dropped.\n");
615 return -1;
616 }
617 len = sizeof(buf) - 1;
618 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
619 hostapd_cli_msg_cb);
620 if (ret == -2) {
621 printf("'%s' command timed out.\n", cmd);
622 return -2;
623 } else if (ret < 0) {
624 printf("'%s' command failed.\n", cmd);
625 return -1;
626 }
627
628 buf[len] = '\0';
629 if (memcmp(buf, "FAIL", 4) == 0)
630 return -1;
631 printf("%s", buf);
632
633 pos = buf;
634 while (*pos != '\0' && *pos != '\n')
635 pos++;
636 *pos = '\0';
637 os_strlcpy(addr, buf, addr_len);
638 return 0;
639}
640
641
642static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
643 char *argv[])
644{
645 char addr[32], cmd[64];
646
647 if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
648 return 0;
649 do {
650 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
651 } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
652
653 return -1;
654}
655
656
657static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
658{
659 printf("%s", commands_help);
660 return 0;
661}
662
663
664static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
665 char *argv[])
666{
667 printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
668 return 0;
669}
670
671
672static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
673{
674 hostapd_cli_quit = 1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800675 if (interactive)
676 eloop_terminate();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700677 return 0;
678}
679
680
681static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
682{
683 char cmd[256];
684 if (argc != 1) {
685 printf("Invalid LEVEL command: needs one argument (debug "
686 "level)\n");
687 return 0;
688 }
689 snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
690 return wpa_ctrl_command(ctrl, cmd);
691}
692
693
694static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
695{
696 struct dirent *dent;
697 DIR *dir;
698
699 dir = opendir(ctrl_iface_dir);
700 if (dir == NULL) {
701 printf("Control interface directory '%s' could not be "
702 "openned.\n", ctrl_iface_dir);
703 return;
704 }
705
706 printf("Available interfaces:\n");
707 while ((dent = readdir(dir))) {
708 if (strcmp(dent->d_name, ".") == 0 ||
709 strcmp(dent->d_name, "..") == 0)
710 continue;
711 printf("%s\n", dent->d_name);
712 }
713 closedir(dir);
714}
715
716
717static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
718 char *argv[])
719{
720 if (argc < 1) {
721 hostapd_cli_list_interfaces(ctrl);
722 return 0;
723 }
724
725 hostapd_cli_close_connection();
726 free(ctrl_ifname);
727 ctrl_ifname = strdup(argv[0]);
728
729 if (hostapd_cli_open_connection(ctrl_ifname)) {
730 printf("Connected to interface '%s.\n", ctrl_ifname);
731 if (wpa_ctrl_attach(ctrl_conn) == 0) {
732 hostapd_cli_attached = 1;
733 } else {
734 printf("Warning: Failed to attach to "
735 "hostapd.\n");
736 }
737 } else {
738 printf("Could not connect to interface '%s' - re-trying\n",
739 ctrl_ifname);
740 }
741 return 0;
742}
743
744
745static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
746{
747 char cmd[256];
748 int res;
749
750 if (argc != 2) {
751 printf("Invalid SET command: needs two arguments (variable "
752 "name and value)\n");
753 return -1;
754 }
755
756 res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
757 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
758 printf("Too long SET command.\n");
759 return -1;
760 }
761 return wpa_ctrl_command(ctrl, cmd);
762}
763
764
765static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
766{
767 char cmd[256];
768 int res;
769
770 if (argc != 1) {
771 printf("Invalid GET command: needs one argument (variable "
772 "name)\n");
773 return -1;
774 }
775
776 res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
777 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
778 printf("Too long GET command.\n");
779 return -1;
780 }
781 return wpa_ctrl_command(ctrl, cmd);
782}
783
784
785struct hostapd_cli_cmd {
786 const char *cmd;
787 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
788};
789
790static struct hostapd_cli_cmd hostapd_cli_commands[] = {
791 { "ping", hostapd_cli_cmd_ping },
792 { "mib", hostapd_cli_cmd_mib },
793 { "relog", hostapd_cli_cmd_relog },
794 { "sta", hostapd_cli_cmd_sta },
795 { "all_sta", hostapd_cli_cmd_all_sta },
796 { "new_sta", hostapd_cli_cmd_new_sta },
797 { "deauthenticate", hostapd_cli_cmd_deauthenticate },
798 { "disassociate", hostapd_cli_cmd_disassociate },
799#ifdef CONFIG_IEEE80211W
800 { "sa_query", hostapd_cli_cmd_sa_query },
801#endif /* CONFIG_IEEE80211W */
802#ifdef CONFIG_WPS
803 { "wps_pin", hostapd_cli_cmd_wps_pin },
804 { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
805 { "wps_pbc", hostapd_cli_cmd_wps_pbc },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700806 { "wps_cancel", hostapd_cli_cmd_wps_cancel },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700807#ifdef CONFIG_WPS_OOB
808 { "wps_oob", hostapd_cli_cmd_wps_oob },
809#endif /* CONFIG_WPS_OOB */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700810#ifdef CONFIG_WPS_NFC
811 { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
812 { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
813 { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
814#endif /* CONFIG_WPS_NFC */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700815 { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
816 { "wps_config", hostapd_cli_cmd_wps_config },
817#endif /* CONFIG_WPS */
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800818 { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700819 { "get_config", hostapd_cli_cmd_get_config },
820 { "help", hostapd_cli_cmd_help },
821 { "interface", hostapd_cli_cmd_interface },
822 { "level", hostapd_cli_cmd_level },
823 { "license", hostapd_cli_cmd_license },
824 { "quit", hostapd_cli_cmd_quit },
825 { "set", hostapd_cli_cmd_set },
826 { "get", hostapd_cli_cmd_get },
827 { NULL, NULL }
828};
829
830
831static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
832{
833 struct hostapd_cli_cmd *cmd, *match = NULL;
834 int count;
835
836 count = 0;
837 cmd = hostapd_cli_commands;
838 while (cmd->cmd) {
839 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
840 match = cmd;
841 if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
842 /* we have an exact match */
843 count = 1;
844 break;
845 }
846 count++;
847 }
848 cmd++;
849 }
850
851 if (count > 1) {
852 printf("Ambiguous command '%s'; possible commands:", argv[0]);
853 cmd = hostapd_cli_commands;
854 while (cmd->cmd) {
855 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
856 0) {
857 printf(" %s", cmd->cmd);
858 }
859 cmd++;
860 }
861 printf("\n");
862 } else if (count == 0) {
863 printf("Unknown command '%s'\n", argv[0]);
864 } else {
865 match->handler(ctrl, argc - 1, &argv[1]);
866 }
867}
868
869
870static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
871 int action_monitor)
872{
873 int first = 1;
874 if (ctrl_conn == NULL)
875 return;
876 while (wpa_ctrl_pending(ctrl)) {
877 char buf[256];
878 size_t len = sizeof(buf) - 1;
879 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
880 buf[len] = '\0';
881 if (action_monitor)
882 hostapd_cli_action_process(buf, len);
883 else {
884 if (in_read && first)
885 printf("\n");
886 first = 0;
887 printf("%s\n", buf);
888 }
889 } else {
890 printf("Could not read pending message.\n");
891 break;
892 }
893 }
894}
895
896
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800897#define max_args 10
898
899static int tokenize_cmd(char *cmd, char *argv[])
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700900{
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800901 char *pos;
902 int argc = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700903
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800904 pos = cmd;
905 for (;;) {
906 while (*pos == ' ')
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700907 pos++;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800908 if (*pos == '\0')
909 break;
910 argv[argc] = pos;
911 argc++;
912 if (argc == max_args)
913 break;
914 if (*pos == '"') {
915 char *pos2 = os_strrchr(pos, '"');
916 if (pos2)
917 pos = pos2 + 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700918 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800919 while (*pos != '\0' && *pos != ' ')
920 pos++;
921 if (*pos == ' ')
922 *pos++ = '\0';
923 }
924
925 return argc;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700926}
927
928
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800929static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700930{
931 if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
932 printf("Connection to hostapd lost - trying to reconnect\n");
933 hostapd_cli_close_connection();
934 }
935 if (!ctrl_conn) {
936 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
937 if (ctrl_conn) {
938 printf("Connection to hostapd re-established\n");
939 if (wpa_ctrl_attach(ctrl_conn) == 0) {
940 hostapd_cli_attached = 1;
941 } else {
942 printf("Warning: Failed to attach to "
943 "hostapd.\n");
944 }
945 }
946 }
947 if (ctrl_conn)
948 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800949 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
950}
951
952
953static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
954{
955 eloop_terminate();
956}
957
958
959static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
960{
961 char *argv[max_args];
962 int argc;
963 argc = tokenize_cmd(cmd, argv);
964 if (argc)
965 wpa_request(ctrl_conn, argc, argv);
966}
967
968
969static void hostapd_cli_edit_eof_cb(void *ctx)
970{
971 eloop_terminate();
972}
973
974
975static void hostapd_cli_interactive(void)
976{
977 printf("\nInteractive mode\n\n");
978
979 eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
980 edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -0700981 NULL, NULL, NULL, NULL);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800982 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
983
984 eloop_run();
985
986 edit_deinit(NULL, NULL);
987 eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
988}
989
990
991static void hostapd_cli_cleanup(void)
992{
993 hostapd_cli_close_connection();
994 if (pid_file)
995 os_daemonize_terminate(pid_file);
996
997 os_program_deinit();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700998}
999
1000
1001static void hostapd_cli_action(struct wpa_ctrl *ctrl)
1002{
1003 fd_set rfds;
1004 int fd, res;
1005 struct timeval tv;
1006 char buf[256];
1007 size_t len;
1008
1009 fd = wpa_ctrl_get_fd(ctrl);
1010
1011 while (!hostapd_cli_quit) {
1012 FD_ZERO(&rfds);
1013 FD_SET(fd, &rfds);
1014 tv.tv_sec = ping_interval;
1015 tv.tv_usec = 0;
1016 res = select(fd + 1, &rfds, NULL, NULL, &tv);
1017 if (res < 0 && errno != EINTR) {
1018 perror("select");
1019 break;
1020 }
1021
1022 if (FD_ISSET(fd, &rfds))
1023 hostapd_cli_recv_pending(ctrl, 0, 1);
1024 else {
1025 len = sizeof(buf) - 1;
1026 if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1027 hostapd_cli_action_process) < 0 ||
1028 len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1029 printf("hostapd did not reply to PING "
1030 "command - exiting\n");
1031 break;
1032 }
1033 }
1034 }
1035}
1036
1037
1038int main(int argc, char *argv[])
1039{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001040 int warning_displayed = 0;
1041 int c;
1042 int daemonize = 0;
1043
1044 if (os_program_init())
1045 return -1;
1046
1047 for (;;) {
1048 c = getopt(argc, argv, "a:BhG:i:p:v");
1049 if (c < 0)
1050 break;
1051 switch (c) {
1052 case 'a':
1053 action_file = optarg;
1054 break;
1055 case 'B':
1056 daemonize = 1;
1057 break;
1058 case 'G':
1059 ping_interval = atoi(optarg);
1060 break;
1061 case 'h':
1062 usage();
1063 return 0;
1064 case 'v':
1065 printf("%s\n", hostapd_cli_version);
1066 return 0;
1067 case 'i':
1068 os_free(ctrl_ifname);
1069 ctrl_ifname = os_strdup(optarg);
1070 break;
1071 case 'p':
1072 ctrl_iface_dir = optarg;
1073 break;
1074 default:
1075 usage();
1076 return -1;
1077 }
1078 }
1079
1080 interactive = (argc == optind) && (action_file == NULL);
1081
1082 if (interactive) {
1083 printf("%s\n\n%s\n\n", hostapd_cli_version,
1084 hostapd_cli_license);
1085 }
1086
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001087 if (eloop_init())
1088 return -1;
1089
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001090 for (;;) {
1091 if (ctrl_ifname == NULL) {
1092 struct dirent *dent;
1093 DIR *dir = opendir(ctrl_iface_dir);
1094 if (dir) {
1095 while ((dent = readdir(dir))) {
1096 if (os_strcmp(dent->d_name, ".") == 0
1097 ||
1098 os_strcmp(dent->d_name, "..") == 0)
1099 continue;
1100 printf("Selected interface '%s'\n",
1101 dent->d_name);
1102 ctrl_ifname = os_strdup(dent->d_name);
1103 break;
1104 }
1105 closedir(dir);
1106 }
1107 }
1108 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1109 if (ctrl_conn) {
1110 if (warning_displayed)
1111 printf("Connection established.\n");
1112 break;
1113 }
1114
1115 if (!interactive) {
1116 perror("Failed to connect to hostapd - "
1117 "wpa_ctrl_open");
1118 return -1;
1119 }
1120
1121 if (!warning_displayed) {
1122 printf("Could not connect to hostapd - re-trying\n");
1123 warning_displayed = 1;
1124 }
1125 os_sleep(1, 0);
1126 continue;
1127 }
1128
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001129 if (interactive || action_file) {
1130 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1131 hostapd_cli_attached = 1;
1132 } else {
1133 printf("Warning: Failed to attach to hostapd.\n");
1134 if (action_file)
1135 return -1;
1136 }
1137 }
1138
1139 if (daemonize && os_daemonize(pid_file))
1140 return -1;
1141
1142 if (interactive)
1143 hostapd_cli_interactive();
1144 else if (action_file)
1145 hostapd_cli_action(ctrl_conn);
1146 else
1147 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1148
1149 os_free(ctrl_ifname);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001150 eloop_destroy();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001151 hostapd_cli_cleanup();
1152 return 0;
1153}