xref: /netbsd-src/external/bsd/wpa/dist/hostapd/hostapd_cli.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*
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"
19 #include "utils/common.h"
20 #include "utils/eloop.h"
21 #include "utils/edit.h"
22 #include "common/version.h"
23 
24 
25 static const char *hostapd_cli_version =
26 "hostapd_cli v" VERSION_STR "\n"
27 "Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors";
28 
29 
30 static 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 
37 static 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 
82 static 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 
110 static struct wpa_ctrl *ctrl_conn;
111 static int hostapd_cli_quit = 0;
112 static int hostapd_cli_attached = 0;
113 static const char *ctrl_iface_dir = "/var/run/hostapd";
114 static char *ctrl_ifname = NULL;
115 static const char *pid_file = NULL;
116 static const char *action_file = NULL;
117 static int ping_interval = 5;
118 static int interactive = 0;
119 
120 
121 static 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 
147 static 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 
167 static 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 
181 static void hostapd_cli_msg_cb(char *msg, size_t len)
182 {
183 	printf("%s\n", msg);
184 }
185 
186 
187 static 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 
215 static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
216 {
217 	return _wpa_ctrl_command(ctrl, cmd, 1);
218 }
219 
220 
221 static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
222 {
223 	return wpa_ctrl_command(ctrl, "PING");
224 }
225 
226 
227 static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
228 {
229 	return wpa_ctrl_command(ctrl, "RELOG");
230 }
231 
232 
233 static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
234 {
235 	return wpa_ctrl_command(ctrl, "MIB");
236 }
237 
238 
239 static 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 
267 static 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 
284 static 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 
297 static 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 
311 static 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 
329 static 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
348 static 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
364 static 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 
385 static 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 
411 static 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
419 static 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 
452 static 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 
473 static 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 
518 static 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 
538 static 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 
545 static 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 
581 static 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 
596 static 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 
603 static 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 
611 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
612 {
613 	hostapd_cli_quit = 1;
614 	if (interactive)
615 		eloop_terminate();
616 	return 0;
617 }
618 
619 
620 static 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 
633 static 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 
656 static 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 
684 static 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 
704 static 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 
724 struct hostapd_cli_cmd {
725 	const char *cmd;
726 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
727 };
728 
729 static 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 */
751 	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
752 	{ "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 
764 static 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 
803 static 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 
830 #define max_args 10
831 
832 static int tokenize_cmd(char *cmd, char *argv[])
833 {
834 	char *pos;
835 	int argc = 0;
836 
837 	pos = cmd;
838 	for (;;) {
839 		while (*pos == ' ')
840 			pos++;
841 		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;
851 		}
852 		while (*pos != '\0' && *pos != ' ')
853 			pos++;
854 		if (*pos == ' ')
855 			*pos++ = '\0';
856 	}
857 
858 	return argc;
859 }
860 
861 
862 static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
863 {
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);
882 	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
883 }
884 
885 
886 static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
887 {
888 	eloop_terminate();
889 }
890 
891 
892 static 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 
902 static void hostapd_cli_edit_eof_cb(void *ctx)
903 {
904 	eloop_terminate();
905 }
906 
907 
908 static 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 
924 static 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();
931 }
932 
933 
934 static 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 
971 int main(int argc, char *argv[])
972 {
973 	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 
1020 	if (eloop_init())
1021 		return -1;
1022 
1023 	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 
1062 	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);
1083 	eloop_destroy();
1084 	hostapd_cli_cleanup();
1085 	return 0;
1086 }
1087