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