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