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