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