blob: 7187abcfebb3d122347edb58309103bf72013c37 [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"
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -070082" wps_get_status show current WPS status\n"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070083#endif /* CONFIG_WPS */
84" get_config show current configuration\n"
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;
Jeff Johnson205f2142012-09-03 22:12:17 -070094
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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700100static char *ctrl_ifname = NULL;
101static const char *pid_file = NULL;
102static const char *action_file = NULL;
103static int ping_interval = 5;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800104static int interactive = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700105
106
107static void usage(void)
108{
109 fprintf(stderr, "%s\n", hostapd_cli_version);
110 fprintf(stderr,
111 "\n"
112 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
113 "[-a<path>] \\\n"
114 " [-G<ping interval>] [command..]\n"
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"
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"
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
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
219static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
220{
221 return wpa_ctrl_command(ctrl, "MIB");
222}
223
224
225static int hostapd_cli_exec(const char *program, const char *arg1,
226 const char *arg2)
227{
228 char *cmd;
229 size_t len;
230 int res;
231 int ret = 0;
232
233 len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
234 cmd = os_malloc(len);
235 if (cmd == NULL)
236 return -1;
237 res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
238 if (res < 0 || (size_t) res >= len) {
239 os_free(cmd);
240 return -1;
241 }
242 cmd[len - 1] = '\0';
243#ifndef _WIN32_WCE
244 if (system(cmd) < 0)
245 ret = -1;
246#endif /* _WIN32_WCE */
247 os_free(cmd);
248
249 return ret;
250}
251
252
253static void hostapd_cli_action_process(char *msg, size_t len)
254{
255 const char *pos;
256
257 pos = msg;
258 if (*pos == '<') {
259 pos = os_strchr(pos, '>');
260 if (pos)
261 pos++;
262 else
263 pos = msg;
264 }
265
266 hostapd_cli_exec(action_file, ctrl_ifname, pos);
267}
268
269
270static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
271{
272 char buf[64];
273 if (argc != 1) {
274 printf("Invalid 'sta' command - exactly one argument, STA "
275 "address, is required.\n");
276 return -1;
277 }
278 snprintf(buf, sizeof(buf), "STA %s", argv[0]);
279 return wpa_ctrl_command(ctrl, buf);
280}
281
282
283static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
284 char *argv[])
285{
286 char buf[64];
287 if (argc != 1) {
288 printf("Invalid 'new_sta' command - exactly one argument, STA "
289 "address, is required.\n");
290 return -1;
291 }
292 snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
293 return wpa_ctrl_command(ctrl, buf);
294}
295
296
297static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
298 char *argv[])
299{
300 char buf[64];
301 if (argc < 1) {
302 printf("Invalid 'deauthenticate' command - exactly one "
303 "argument, STA address, is required.\n");
304 return -1;
305 }
306 if (argc > 1)
307 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
308 argv[0], argv[1]);
309 else
310 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
311 return wpa_ctrl_command(ctrl, buf);
312}
313
314
315static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
316 char *argv[])
317{
318 char buf[64];
319 if (argc < 1) {
320 printf("Invalid 'disassociate' command - exactly one "
321 "argument, STA address, is required.\n");
322 return -1;
323 }
324 if (argc > 1)
325 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
326 argv[0], argv[1]);
327 else
328 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
329 return wpa_ctrl_command(ctrl, buf);
330}
331
332
333#ifdef CONFIG_IEEE80211W
334static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
335 char *argv[])
336{
337 char buf[64];
338 if (argc != 1) {
339 printf("Invalid 'sa_query' command - exactly one argument, "
340 "STA address, is required.\n");
341 return -1;
342 }
343 snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
344 return wpa_ctrl_command(ctrl, buf);
345}
346#endif /* CONFIG_IEEE80211W */
347
348
349#ifdef CONFIG_WPS
350static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
351 char *argv[])
352{
353 char buf[256];
354 if (argc < 2) {
355 printf("Invalid 'wps_pin' command - at least two arguments, "
356 "UUID and PIN, are required.\n");
357 return -1;
358 }
359 if (argc > 3)
360 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
361 argv[0], argv[1], argv[2], argv[3]);
362 else if (argc > 2)
363 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
364 argv[0], argv[1], argv[2]);
365 else
366 snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
367 return wpa_ctrl_command(ctrl, buf);
368}
369
370
371static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
372 char *argv[])
373{
374 char cmd[256];
375 int res;
376
377 if (argc != 1 && argc != 2) {
378 printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
379 "- PIN to be verified\n");
380 return -1;
381 }
382
383 if (argc == 2)
384 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
385 argv[0], argv[1]);
386 else
387 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
388 argv[0]);
389 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
390 printf("Too long WPS_CHECK_PIN command.\n");
391 return -1;
392 }
393 return wpa_ctrl_command(ctrl, cmd);
394}
395
396
397static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
398 char *argv[])
399{
400 return wpa_ctrl_command(ctrl, "WPS_PBC");
401}
402
403
Dmitry Shmidt04949592012-07-19 12:16:46 -0700404static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
405 char *argv[])
406{
407 return wpa_ctrl_command(ctrl, "WPS_CANCEL");
408}
409
410
Dmitry Shmidt04949592012-07-19 12:16:46 -0700411#ifdef CONFIG_WPS_NFC
412static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
413 char *argv[])
414{
415 int ret;
416 char *buf;
417 size_t buflen;
418
419 if (argc != 1) {
420 printf("Invalid 'wps_nfc_tag_read' command - one argument "
421 "is required.\n");
422 return -1;
423 }
424
425 buflen = 18 + os_strlen(argv[0]);
426 buf = os_malloc(buflen);
427 if (buf == NULL)
428 return -1;
429 os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
430
431 ret = wpa_ctrl_command(ctrl, buf);
432 os_free(buf);
433
434 return ret;
435}
436
437
438static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
439 int argc, char *argv[])
440{
441 char cmd[64];
442 int res;
443
444 if (argc != 1) {
445 printf("Invalid 'wps_nfc_config_token' command - one argument "
446 "is required.\n");
447 return -1;
448 }
449
450 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
451 argv[0]);
452 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
453 printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
454 return -1;
455 }
456 return wpa_ctrl_command(ctrl, cmd);
457}
458
459
460static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
461 int argc, char *argv[])
462{
463 char cmd[64];
464 int res;
465
466 if (argc != 1) {
467 printf("Invalid 'wps_nfc_token' command - one argument is "
468 "required.\n");
469 return -1;
470 }
471
472 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
473 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
474 printf("Too long WPS_NFC_TOKEN command.\n");
475 return -1;
476 }
477 return wpa_ctrl_command(ctrl, cmd);
478}
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800479
480
481static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
482 int argc, char *argv[])
483{
484 char cmd[64];
485 int res;
486
487 if (argc != 2) {
488 printf("Invalid 'nfc_get_handover_sel' command - two arguments "
489 "are required.\n");
490 return -1;
491 }
492
493 res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
494 argv[0], argv[1]);
495 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
496 printf("Too long NFC_GET_HANDOVER_SEL command.\n");
497 return -1;
498 }
499 return wpa_ctrl_command(ctrl, cmd);
500}
501
Dmitry Shmidt04949592012-07-19 12:16:46 -0700502#endif /* CONFIG_WPS_NFC */
503
504
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700505static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
506 char *argv[])
507{
508 char buf[64];
509 if (argc < 1) {
510 printf("Invalid 'wps_ap_pin' command - at least one argument "
511 "is required.\n");
512 return -1;
513 }
514 if (argc > 2)
515 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
516 argv[0], argv[1], argv[2]);
517 else if (argc > 1)
518 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
519 argv[0], argv[1]);
520 else
521 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
522 return wpa_ctrl_command(ctrl, buf);
523}
524
525
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700526static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
527 char *argv[])
528{
529 return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
530}
531
532
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700533static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
534 char *argv[])
535{
536 char buf[256];
537 char ssid_hex[2 * 32 + 1];
538 char key_hex[2 * 64 + 1];
539 int i;
540
541 if (argc < 1) {
542 printf("Invalid 'wps_config' command - at least two arguments "
543 "are required.\n");
544 return -1;
545 }
546
547 ssid_hex[0] = '\0';
548 for (i = 0; i < 32; i++) {
549 if (argv[0][i] == '\0')
550 break;
551 os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
552 }
553
554 key_hex[0] = '\0';
555 if (argc > 3) {
556 for (i = 0; i < 64; i++) {
557 if (argv[3][i] == '\0')
558 break;
559 os_snprintf(&key_hex[i * 2], 3, "%02x",
560 argv[3][i]);
561 }
562 }
563
564 if (argc > 3)
565 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
566 ssid_hex, argv[1], argv[2], key_hex);
567 else if (argc > 2)
568 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
569 ssid_hex, argv[1], argv[2]);
570 else
571 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
572 ssid_hex, argv[1]);
573 return wpa_ctrl_command(ctrl, buf);
574}
575#endif /* CONFIG_WPS */
576
577
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800578static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
579 char *argv[])
580{
581 char buf[300];
582 int res;
583
584 if (argc < 2) {
585 printf("Invalid 'disassoc_imminent' command - two arguments "
586 "(STA addr and Disassociation Timer) are needed\n");
587 return -1;
588 }
589
590 res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
591 argv[0], argv[1]);
592 if (res < 0 || res >= (int) sizeof(buf))
593 return -1;
594 return wpa_ctrl_command(ctrl, buf);
595}
596
597
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800598static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
599 char *argv[])
600{
601 char buf[300];
602 int res;
603
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700604 if (argc < 3) {
605 printf("Invalid 'ess_disassoc' command - three arguments (STA "
606 "addr, disassoc timer, and URL) are needed\n");
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800607 return -1;
608 }
609
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700610 res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
611 argv[0], argv[1], argv[2]);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800612 if (res < 0 || res >= (int) sizeof(buf))
613 return -1;
614 return wpa_ctrl_command(ctrl, buf);
615}
616
617
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700618static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
619 char *argv[])
620{
621 return wpa_ctrl_command(ctrl, "GET_CONFIG");
622}
623
624
625static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
626 char *addr, size_t addr_len)
627{
628 char buf[4096], *pos;
629 size_t len;
630 int ret;
631
632 if (ctrl_conn == NULL) {
633 printf("Not connected to hostapd - command dropped.\n");
634 return -1;
635 }
636 len = sizeof(buf) - 1;
637 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
638 hostapd_cli_msg_cb);
639 if (ret == -2) {
640 printf("'%s' command timed out.\n", cmd);
641 return -2;
642 } else if (ret < 0) {
643 printf("'%s' command failed.\n", cmd);
644 return -1;
645 }
646
647 buf[len] = '\0';
648 if (memcmp(buf, "FAIL", 4) == 0)
649 return -1;
650 printf("%s", buf);
651
652 pos = buf;
653 while (*pos != '\0' && *pos != '\n')
654 pos++;
655 *pos = '\0';
656 os_strlcpy(addr, buf, addr_len);
657 return 0;
658}
659
660
661static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
662 char *argv[])
663{
664 char addr[32], cmd[64];
665
666 if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
667 return 0;
668 do {
669 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
670 } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
671
672 return -1;
673}
674
675
676static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
677{
678 printf("%s", commands_help);
679 return 0;
680}
681
682
683static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
684 char *argv[])
685{
686 printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
687 return 0;
688}
689
690
691static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
692{
693 hostapd_cli_quit = 1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800694 if (interactive)
695 eloop_terminate();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700696 return 0;
697}
698
699
700static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
701{
702 char cmd[256];
703 if (argc != 1) {
704 printf("Invalid LEVEL command: needs one argument (debug "
705 "level)\n");
706 return 0;
707 }
708 snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
709 return wpa_ctrl_command(ctrl, cmd);
710}
711
712
713static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
714{
715 struct dirent *dent;
716 DIR *dir;
717
718 dir = opendir(ctrl_iface_dir);
719 if (dir == NULL) {
720 printf("Control interface directory '%s' could not be "
721 "openned.\n", ctrl_iface_dir);
722 return;
723 }
724
725 printf("Available interfaces:\n");
726 while ((dent = readdir(dir))) {
727 if (strcmp(dent->d_name, ".") == 0 ||
728 strcmp(dent->d_name, "..") == 0)
729 continue;
730 printf("%s\n", dent->d_name);
731 }
732 closedir(dir);
733}
734
735
736static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
737 char *argv[])
738{
739 if (argc < 1) {
740 hostapd_cli_list_interfaces(ctrl);
741 return 0;
742 }
743
744 hostapd_cli_close_connection();
745 free(ctrl_ifname);
746 ctrl_ifname = strdup(argv[0]);
747
748 if (hostapd_cli_open_connection(ctrl_ifname)) {
749 printf("Connected to interface '%s.\n", ctrl_ifname);
750 if (wpa_ctrl_attach(ctrl_conn) == 0) {
751 hostapd_cli_attached = 1;
752 } else {
753 printf("Warning: Failed to attach to "
754 "hostapd.\n");
755 }
756 } else {
757 printf("Could not connect to interface '%s' - re-trying\n",
758 ctrl_ifname);
759 }
760 return 0;
761}
762
763
764static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
765{
766 char cmd[256];
767 int res;
768
769 if (argc != 2) {
770 printf("Invalid SET command: needs two arguments (variable "
771 "name and value)\n");
772 return -1;
773 }
774
775 res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
776 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
777 printf("Too long SET command.\n");
778 return -1;
779 }
780 return wpa_ctrl_command(ctrl, cmd);
781}
782
783
784static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
785{
786 char cmd[256];
787 int res;
788
789 if (argc != 1) {
790 printf("Invalid GET command: needs one argument (variable "
791 "name)\n");
792 return -1;
793 }
794
795 res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
796 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
797 printf("Too long GET command.\n");
798 return -1;
799 }
800 return wpa_ctrl_command(ctrl, cmd);
801}
802
803
804struct hostapd_cli_cmd {
805 const char *cmd;
806 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
807};
808
809static struct hostapd_cli_cmd hostapd_cli_commands[] = {
810 { "ping", hostapd_cli_cmd_ping },
811 { "mib", hostapd_cli_cmd_mib },
812 { "relog", hostapd_cli_cmd_relog },
813 { "sta", hostapd_cli_cmd_sta },
814 { "all_sta", hostapd_cli_cmd_all_sta },
815 { "new_sta", hostapd_cli_cmd_new_sta },
816 { "deauthenticate", hostapd_cli_cmd_deauthenticate },
817 { "disassociate", hostapd_cli_cmd_disassociate },
818#ifdef CONFIG_IEEE80211W
819 { "sa_query", hostapd_cli_cmd_sa_query },
820#endif /* CONFIG_IEEE80211W */
821#ifdef CONFIG_WPS
822 { "wps_pin", hostapd_cli_cmd_wps_pin },
823 { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
824 { "wps_pbc", hostapd_cli_cmd_wps_pbc },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700825 { "wps_cancel", hostapd_cli_cmd_wps_cancel },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700826#ifdef CONFIG_WPS_NFC
827 { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
828 { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
829 { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800830 { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700831#endif /* CONFIG_WPS_NFC */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700832 { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
833 { "wps_config", hostapd_cli_cmd_wps_config },
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700834 { "wps_get_status", hostapd_cli_cmd_wps_get_status },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700835#endif /* CONFIG_WPS */
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800836 { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800837 { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700838 { "get_config", hostapd_cli_cmd_get_config },
839 { "help", hostapd_cli_cmd_help },
840 { "interface", hostapd_cli_cmd_interface },
841 { "level", hostapd_cli_cmd_level },
842 { "license", hostapd_cli_cmd_license },
843 { "quit", hostapd_cli_cmd_quit },
844 { "set", hostapd_cli_cmd_set },
845 { "get", hostapd_cli_cmd_get },
846 { NULL, NULL }
847};
848
849
850static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
851{
852 struct hostapd_cli_cmd *cmd, *match = NULL;
853 int count;
854
855 count = 0;
856 cmd = hostapd_cli_commands;
857 while (cmd->cmd) {
858 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
859 match = cmd;
860 if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
861 /* we have an exact match */
862 count = 1;
863 break;
864 }
865 count++;
866 }
867 cmd++;
868 }
869
870 if (count > 1) {
871 printf("Ambiguous command '%s'; possible commands:", argv[0]);
872 cmd = hostapd_cli_commands;
873 while (cmd->cmd) {
874 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
875 0) {
876 printf(" %s", cmd->cmd);
877 }
878 cmd++;
879 }
880 printf("\n");
881 } else if (count == 0) {
882 printf("Unknown command '%s'\n", argv[0]);
883 } else {
884 match->handler(ctrl, argc - 1, &argv[1]);
885 }
886}
887
888
889static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
890 int action_monitor)
891{
892 int first = 1;
893 if (ctrl_conn == NULL)
894 return;
895 while (wpa_ctrl_pending(ctrl)) {
896 char buf[256];
897 size_t len = sizeof(buf) - 1;
898 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
899 buf[len] = '\0';
900 if (action_monitor)
901 hostapd_cli_action_process(buf, len);
902 else {
903 if (in_read && first)
904 printf("\n");
905 first = 0;
906 printf("%s\n", buf);
907 }
908 } else {
909 printf("Could not read pending message.\n");
910 break;
911 }
912 }
913}
914
915
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800916#define max_args 10
917
918static int tokenize_cmd(char *cmd, char *argv[])
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700919{
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800920 char *pos;
921 int argc = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700922
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800923 pos = cmd;
924 for (;;) {
925 while (*pos == ' ')
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700926 pos++;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800927 if (*pos == '\0')
928 break;
929 argv[argc] = pos;
930 argc++;
931 if (argc == max_args)
932 break;
933 if (*pos == '"') {
934 char *pos2 = os_strrchr(pos, '"');
935 if (pos2)
936 pos = pos2 + 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700937 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800938 while (*pos != '\0' && *pos != ' ')
939 pos++;
940 if (*pos == ' ')
941 *pos++ = '\0';
942 }
943
944 return argc;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700945}
946
947
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800948static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700949{
950 if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
951 printf("Connection to hostapd lost - trying to reconnect\n");
952 hostapd_cli_close_connection();
953 }
954 if (!ctrl_conn) {
955 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
956 if (ctrl_conn) {
957 printf("Connection to hostapd re-established\n");
958 if (wpa_ctrl_attach(ctrl_conn) == 0) {
959 hostapd_cli_attached = 1;
960 } else {
961 printf("Warning: Failed to attach to "
962 "hostapd.\n");
963 }
964 }
965 }
966 if (ctrl_conn)
967 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800968 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
969}
970
971
972static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
973{
974 eloop_terminate();
975}
976
977
978static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
979{
980 char *argv[max_args];
981 int argc;
982 argc = tokenize_cmd(cmd, argv);
983 if (argc)
984 wpa_request(ctrl_conn, argc, argv);
985}
986
987
988static void hostapd_cli_edit_eof_cb(void *ctx)
989{
990 eloop_terminate();
991}
992
993
994static void hostapd_cli_interactive(void)
995{
996 printf("\nInteractive mode\n\n");
997
998 eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
999 edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001000 NULL, NULL, NULL, NULL);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001001 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1002
1003 eloop_run();
1004
1005 edit_deinit(NULL, NULL);
1006 eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
1007}
1008
1009
1010static void hostapd_cli_cleanup(void)
1011{
1012 hostapd_cli_close_connection();
1013 if (pid_file)
1014 os_daemonize_terminate(pid_file);
1015
1016 os_program_deinit();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001017}
1018
1019
1020static void hostapd_cli_action(struct wpa_ctrl *ctrl)
1021{
1022 fd_set rfds;
1023 int fd, res;
1024 struct timeval tv;
1025 char buf[256];
1026 size_t len;
1027
1028 fd = wpa_ctrl_get_fd(ctrl);
1029
1030 while (!hostapd_cli_quit) {
1031 FD_ZERO(&rfds);
1032 FD_SET(fd, &rfds);
1033 tv.tv_sec = ping_interval;
1034 tv.tv_usec = 0;
1035 res = select(fd + 1, &rfds, NULL, NULL, &tv);
1036 if (res < 0 && errno != EINTR) {
1037 perror("select");
1038 break;
1039 }
1040
1041 if (FD_ISSET(fd, &rfds))
1042 hostapd_cli_recv_pending(ctrl, 0, 1);
1043 else {
1044 len = sizeof(buf) - 1;
1045 if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1046 hostapd_cli_action_process) < 0 ||
1047 len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1048 printf("hostapd did not reply to PING "
1049 "command - exiting\n");
1050 break;
1051 }
1052 }
1053 }
1054}
1055
1056
1057int main(int argc, char *argv[])
1058{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001059 int warning_displayed = 0;
1060 int c;
1061 int daemonize = 0;
1062
1063 if (os_program_init())
1064 return -1;
1065
1066 for (;;) {
1067 c = getopt(argc, argv, "a:BhG:i:p:v");
1068 if (c < 0)
1069 break;
1070 switch (c) {
1071 case 'a':
1072 action_file = optarg;
1073 break;
1074 case 'B':
1075 daemonize = 1;
1076 break;
1077 case 'G':
1078 ping_interval = atoi(optarg);
1079 break;
1080 case 'h':
1081 usage();
1082 return 0;
1083 case 'v':
1084 printf("%s\n", hostapd_cli_version);
1085 return 0;
1086 case 'i':
1087 os_free(ctrl_ifname);
1088 ctrl_ifname = os_strdup(optarg);
1089 break;
1090 case 'p':
1091 ctrl_iface_dir = optarg;
1092 break;
1093 default:
1094 usage();
1095 return -1;
1096 }
1097 }
1098
1099 interactive = (argc == optind) && (action_file == NULL);
1100
1101 if (interactive) {
1102 printf("%s\n\n%s\n\n", hostapd_cli_version,
1103 hostapd_cli_license);
1104 }
1105
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001106 if (eloop_init())
1107 return -1;
1108
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001109 for (;;) {
1110 if (ctrl_ifname == NULL) {
1111 struct dirent *dent;
1112 DIR *dir = opendir(ctrl_iface_dir);
1113 if (dir) {
1114 while ((dent = readdir(dir))) {
1115 if (os_strcmp(dent->d_name, ".") == 0
1116 ||
1117 os_strcmp(dent->d_name, "..") == 0)
1118 continue;
1119 printf("Selected interface '%s'\n",
1120 dent->d_name);
1121 ctrl_ifname = os_strdup(dent->d_name);
1122 break;
1123 }
1124 closedir(dir);
1125 }
1126 }
1127 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1128 if (ctrl_conn) {
1129 if (warning_displayed)
1130 printf("Connection established.\n");
1131 break;
1132 }
1133
1134 if (!interactive) {
1135 perror("Failed to connect to hostapd - "
1136 "wpa_ctrl_open");
1137 return -1;
1138 }
1139
1140 if (!warning_displayed) {
1141 printf("Could not connect to hostapd - re-trying\n");
1142 warning_displayed = 1;
1143 }
1144 os_sleep(1, 0);
1145 continue;
1146 }
1147
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001148 if (interactive || action_file) {
1149 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1150 hostapd_cli_attached = 1;
1151 } else {
1152 printf("Warning: Failed to attach to hostapd.\n");
1153 if (action_file)
1154 return -1;
1155 }
1156 }
1157
1158 if (daemonize && os_daemonize(pid_file))
1159 return -1;
1160
1161 if (interactive)
1162 hostapd_cli_interactive();
1163 else if (action_file)
1164 hostapd_cli_action(ctrl_conn);
1165 else
1166 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1167
1168 os_free(ctrl_ifname);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001169 eloop_destroy();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001170 hostapd_cli_cleanup();
1171 return 0;
1172}