blob: 709b86c0c42fa9f1b8e57a2c0636505b8c4cebfa [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
Dmitry Shmidt051af732013-10-22 13:52:46 -0700691static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
692 int argc, char *argv[])
693{
694 char buf[200];
695 int res;
696
697 if (argc != 1) {
698 printf("Invalid 'set_qos_map_set' command - "
699 "one argument (comma delimited QoS map set) "
700 "is needed\n");
701 return -1;
702 }
703
704 res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
705 if (res < 0 || res >= (int) sizeof(buf))
706 return -1;
707 return wpa_ctrl_command(ctrl, buf);
708}
709
710
711static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
712 int argc, char *argv[])
713{
714 char buf[50];
715 int res;
716
717 if (argc != 1) {
718 printf("Invalid 'send_qos_map_conf' command - "
719 "one argument (STA addr) is needed\n");
720 return -1;
721 }
722
723 res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
724 if (res < 0 || res >= (int) sizeof(buf))
725 return -1;
726 return wpa_ctrl_command(ctrl, buf);
727}
728
729
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700730static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
731{
732 hostapd_cli_quit = 1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800733 if (interactive)
734 eloop_terminate();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700735 return 0;
736}
737
738
739static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
740{
741 char cmd[256];
742 if (argc != 1) {
743 printf("Invalid LEVEL command: needs one argument (debug "
744 "level)\n");
745 return 0;
746 }
747 snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
748 return wpa_ctrl_command(ctrl, cmd);
749}
750
751
752static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
753{
754 struct dirent *dent;
755 DIR *dir;
756
757 dir = opendir(ctrl_iface_dir);
758 if (dir == NULL) {
759 printf("Control interface directory '%s' could not be "
760 "openned.\n", ctrl_iface_dir);
761 return;
762 }
763
764 printf("Available interfaces:\n");
765 while ((dent = readdir(dir))) {
766 if (strcmp(dent->d_name, ".") == 0 ||
767 strcmp(dent->d_name, "..") == 0)
768 continue;
769 printf("%s\n", dent->d_name);
770 }
771 closedir(dir);
772}
773
774
775static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
776 char *argv[])
777{
778 if (argc < 1) {
779 hostapd_cli_list_interfaces(ctrl);
780 return 0;
781 }
782
783 hostapd_cli_close_connection();
784 free(ctrl_ifname);
785 ctrl_ifname = strdup(argv[0]);
786
787 if (hostapd_cli_open_connection(ctrl_ifname)) {
788 printf("Connected to interface '%s.\n", ctrl_ifname);
789 if (wpa_ctrl_attach(ctrl_conn) == 0) {
790 hostapd_cli_attached = 1;
791 } else {
792 printf("Warning: Failed to attach to "
793 "hostapd.\n");
794 }
795 } else {
796 printf("Could not connect to interface '%s' - re-trying\n",
797 ctrl_ifname);
798 }
799 return 0;
800}
801
802
803static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
804{
805 char cmd[256];
806 int res;
807
808 if (argc != 2) {
809 printf("Invalid SET command: needs two arguments (variable "
810 "name and value)\n");
811 return -1;
812 }
813
814 res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
815 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
816 printf("Too long SET command.\n");
817 return -1;
818 }
819 return wpa_ctrl_command(ctrl, cmd);
820}
821
822
823static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
824{
825 char cmd[256];
826 int res;
827
828 if (argc != 1) {
829 printf("Invalid GET command: needs one argument (variable "
830 "name)\n");
831 return -1;
832 }
833
834 res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
835 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
836 printf("Too long GET command.\n");
837 return -1;
838 }
839 return wpa_ctrl_command(ctrl, cmd);
840}
841
842
843struct hostapd_cli_cmd {
844 const char *cmd;
845 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
846};
847
848static struct hostapd_cli_cmd hostapd_cli_commands[] = {
849 { "ping", hostapd_cli_cmd_ping },
850 { "mib", hostapd_cli_cmd_mib },
851 { "relog", hostapd_cli_cmd_relog },
852 { "sta", hostapd_cli_cmd_sta },
853 { "all_sta", hostapd_cli_cmd_all_sta },
854 { "new_sta", hostapd_cli_cmd_new_sta },
855 { "deauthenticate", hostapd_cli_cmd_deauthenticate },
856 { "disassociate", hostapd_cli_cmd_disassociate },
857#ifdef CONFIG_IEEE80211W
858 { "sa_query", hostapd_cli_cmd_sa_query },
859#endif /* CONFIG_IEEE80211W */
860#ifdef CONFIG_WPS
861 { "wps_pin", hostapd_cli_cmd_wps_pin },
862 { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
863 { "wps_pbc", hostapd_cli_cmd_wps_pbc },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700864 { "wps_cancel", hostapd_cli_cmd_wps_cancel },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700865#ifdef CONFIG_WPS_NFC
866 { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
867 { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
868 { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800869 { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700870#endif /* CONFIG_WPS_NFC */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700871 { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
872 { "wps_config", hostapd_cli_cmd_wps_config },
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700873 { "wps_get_status", hostapd_cli_cmd_wps_get_status },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700874#endif /* CONFIG_WPS */
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800875 { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800876 { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700877 { "get_config", hostapd_cli_cmd_get_config },
878 { "help", hostapd_cli_cmd_help },
879 { "interface", hostapd_cli_cmd_interface },
880 { "level", hostapd_cli_cmd_level },
881 { "license", hostapd_cli_cmd_license },
882 { "quit", hostapd_cli_cmd_quit },
883 { "set", hostapd_cli_cmd_set },
884 { "get", hostapd_cli_cmd_get },
Dmitry Shmidt051af732013-10-22 13:52:46 -0700885 { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
886 { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700887 { NULL, NULL }
888};
889
890
891static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
892{
893 struct hostapd_cli_cmd *cmd, *match = NULL;
894 int count;
895
896 count = 0;
897 cmd = hostapd_cli_commands;
898 while (cmd->cmd) {
899 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
900 match = cmd;
901 if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
902 /* we have an exact match */
903 count = 1;
904 break;
905 }
906 count++;
907 }
908 cmd++;
909 }
910
911 if (count > 1) {
912 printf("Ambiguous command '%s'; possible commands:", argv[0]);
913 cmd = hostapd_cli_commands;
914 while (cmd->cmd) {
915 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
916 0) {
917 printf(" %s", cmd->cmd);
918 }
919 cmd++;
920 }
921 printf("\n");
922 } else if (count == 0) {
923 printf("Unknown command '%s'\n", argv[0]);
924 } else {
925 match->handler(ctrl, argc - 1, &argv[1]);
926 }
927}
928
929
930static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
931 int action_monitor)
932{
933 int first = 1;
934 if (ctrl_conn == NULL)
935 return;
936 while (wpa_ctrl_pending(ctrl)) {
937 char buf[256];
938 size_t len = sizeof(buf) - 1;
939 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
940 buf[len] = '\0';
941 if (action_monitor)
942 hostapd_cli_action_process(buf, len);
943 else {
944 if (in_read && first)
945 printf("\n");
946 first = 0;
947 printf("%s\n", buf);
948 }
949 } else {
950 printf("Could not read pending message.\n");
951 break;
952 }
953 }
954}
955
956
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800957#define max_args 10
958
959static int tokenize_cmd(char *cmd, char *argv[])
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700960{
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800961 char *pos;
962 int argc = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700963
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800964 pos = cmd;
965 for (;;) {
966 while (*pos == ' ')
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700967 pos++;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800968 if (*pos == '\0')
969 break;
970 argv[argc] = pos;
971 argc++;
972 if (argc == max_args)
973 break;
974 if (*pos == '"') {
975 char *pos2 = os_strrchr(pos, '"');
976 if (pos2)
977 pos = pos2 + 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700978 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800979 while (*pos != '\0' && *pos != ' ')
980 pos++;
981 if (*pos == ' ')
982 *pos++ = '\0';
983 }
984
985 return argc;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700986}
987
988
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800989static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700990{
991 if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
992 printf("Connection to hostapd lost - trying to reconnect\n");
993 hostapd_cli_close_connection();
994 }
995 if (!ctrl_conn) {
996 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
997 if (ctrl_conn) {
998 printf("Connection to hostapd re-established\n");
999 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1000 hostapd_cli_attached = 1;
1001 } else {
1002 printf("Warning: Failed to attach to "
1003 "hostapd.\n");
1004 }
1005 }
1006 }
1007 if (ctrl_conn)
1008 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001009 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1010}
1011
1012
1013static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
1014{
1015 eloop_terminate();
1016}
1017
1018
1019static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
1020{
1021 char *argv[max_args];
1022 int argc;
1023 argc = tokenize_cmd(cmd, argv);
1024 if (argc)
1025 wpa_request(ctrl_conn, argc, argv);
1026}
1027
1028
1029static void hostapd_cli_edit_eof_cb(void *ctx)
1030{
1031 eloop_terminate();
1032}
1033
1034
1035static void hostapd_cli_interactive(void)
1036{
1037 printf("\nInteractive mode\n\n");
1038
1039 eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
1040 edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001041 NULL, NULL, NULL, NULL);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001042 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1043
1044 eloop_run();
1045
1046 edit_deinit(NULL, NULL);
1047 eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
1048}
1049
1050
1051static void hostapd_cli_cleanup(void)
1052{
1053 hostapd_cli_close_connection();
1054 if (pid_file)
1055 os_daemonize_terminate(pid_file);
1056
1057 os_program_deinit();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001058}
1059
1060
1061static void hostapd_cli_action(struct wpa_ctrl *ctrl)
1062{
1063 fd_set rfds;
1064 int fd, res;
1065 struct timeval tv;
1066 char buf[256];
1067 size_t len;
1068
1069 fd = wpa_ctrl_get_fd(ctrl);
1070
1071 while (!hostapd_cli_quit) {
1072 FD_ZERO(&rfds);
1073 FD_SET(fd, &rfds);
1074 tv.tv_sec = ping_interval;
1075 tv.tv_usec = 0;
1076 res = select(fd + 1, &rfds, NULL, NULL, &tv);
1077 if (res < 0 && errno != EINTR) {
1078 perror("select");
1079 break;
1080 }
1081
1082 if (FD_ISSET(fd, &rfds))
1083 hostapd_cli_recv_pending(ctrl, 0, 1);
1084 else {
1085 len = sizeof(buf) - 1;
1086 if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1087 hostapd_cli_action_process) < 0 ||
1088 len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1089 printf("hostapd did not reply to PING "
1090 "command - exiting\n");
1091 break;
1092 }
1093 }
1094 }
1095}
1096
1097
1098int main(int argc, char *argv[])
1099{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001100 int warning_displayed = 0;
1101 int c;
1102 int daemonize = 0;
1103
1104 if (os_program_init())
1105 return -1;
1106
1107 for (;;) {
1108 c = getopt(argc, argv, "a:BhG:i:p:v");
1109 if (c < 0)
1110 break;
1111 switch (c) {
1112 case 'a':
1113 action_file = optarg;
1114 break;
1115 case 'B':
1116 daemonize = 1;
1117 break;
1118 case 'G':
1119 ping_interval = atoi(optarg);
1120 break;
1121 case 'h':
1122 usage();
1123 return 0;
1124 case 'v':
1125 printf("%s\n", hostapd_cli_version);
1126 return 0;
1127 case 'i':
1128 os_free(ctrl_ifname);
1129 ctrl_ifname = os_strdup(optarg);
1130 break;
1131 case 'p':
1132 ctrl_iface_dir = optarg;
1133 break;
1134 default:
1135 usage();
1136 return -1;
1137 }
1138 }
1139
1140 interactive = (argc == optind) && (action_file == NULL);
1141
1142 if (interactive) {
1143 printf("%s\n\n%s\n\n", hostapd_cli_version,
1144 hostapd_cli_license);
1145 }
1146
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001147 if (eloop_init())
1148 return -1;
1149
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001150 for (;;) {
1151 if (ctrl_ifname == NULL) {
1152 struct dirent *dent;
1153 DIR *dir = opendir(ctrl_iface_dir);
1154 if (dir) {
1155 while ((dent = readdir(dir))) {
1156 if (os_strcmp(dent->d_name, ".") == 0
1157 ||
1158 os_strcmp(dent->d_name, "..") == 0)
1159 continue;
1160 printf("Selected interface '%s'\n",
1161 dent->d_name);
1162 ctrl_ifname = os_strdup(dent->d_name);
1163 break;
1164 }
1165 closedir(dir);
1166 }
1167 }
1168 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1169 if (ctrl_conn) {
1170 if (warning_displayed)
1171 printf("Connection established.\n");
1172 break;
1173 }
1174
1175 if (!interactive) {
1176 perror("Failed to connect to hostapd - "
1177 "wpa_ctrl_open");
1178 return -1;
1179 }
1180
1181 if (!warning_displayed) {
1182 printf("Could not connect to hostapd - re-trying\n");
1183 warning_displayed = 1;
1184 }
1185 os_sleep(1, 0);
1186 continue;
1187 }
1188
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001189 if (interactive || action_file) {
1190 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1191 hostapd_cli_attached = 1;
1192 } else {
1193 printf("Warning: Failed to attach to hostapd.\n");
1194 if (action_file)
1195 return -1;
1196 }
1197 }
1198
1199 if (daemonize && os_daemonize(pid_file))
1200 return -1;
1201
1202 if (interactive)
1203 hostapd_cli_interactive();
1204 else if (action_file)
1205 hostapd_cli_action(ctrl_conn);
1206 else
1207 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1208
1209 os_free(ctrl_ifname);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001210 eloop_destroy();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001211 hostapd_cli_cleanup();
1212 return 0;
1213}