xref: /freebsd/contrib/wpa/hostapd/hostapd_cli.c (revision 2e1417489338b971e5fd599ff48b5f65df9e8d3b)
1 /*
2  * hostapd - command line interface for hostapd daemon
3  * Copyright (c) 2004-2010, 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 "common.h"
20 #include "common/version.h"
21 
22 
23 static const char *hostapd_cli_version =
24 "hostapd_cli v" VERSION_STR "\n"
25 "Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
26 
27 
28 static const char *hostapd_cli_license =
29 "This program is free software. You can distribute it and/or modify it\n"
30 "under the terms of the GNU General Public License version 2.\n"
31 "\n"
32 "Alternatively, this software may be distributed under the terms of the\n"
33 "BSD license. See README and COPYING for more details.\n";
34 
35 static const char *hostapd_cli_full_license =
36 "This program is free software; you can redistribute it and/or modify\n"
37 "it under the terms of the GNU General Public License version 2 as\n"
38 "published by the Free Software Foundation.\n"
39 "\n"
40 "This program is distributed in the hope that it will be useful,\n"
41 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
42 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
43 "GNU General Public License for more details.\n"
44 "\n"
45 "You should have received a copy of the GNU General Public License\n"
46 "along with this program; if not, write to the Free Software\n"
47 "Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n"
48 "\n"
49 "Alternatively, this software may be distributed under the terms of the\n"
50 "BSD license.\n"
51 "\n"
52 "Redistribution and use in source and binary forms, with or without\n"
53 "modification, are permitted provided that the following conditions are\n"
54 "met:\n"
55 "\n"
56 "1. Redistributions of source code must retain the above copyright\n"
57 "   notice, this list of conditions and the following disclaimer.\n"
58 "\n"
59 "2. Redistributions in binary form must reproduce the above copyright\n"
60 "   notice, this list of conditions and the following disclaimer in the\n"
61 "   documentation and/or other materials provided with the distribution.\n"
62 "\n"
63 "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
64 "   names of its contributors may be used to endorse or promote products\n"
65 "   derived from this software without specific prior written permission.\n"
66 "\n"
67 "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
68 "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
69 "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
70 "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
71 "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
72 "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
73 "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
74 "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
75 "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
76 "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
77 "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
78 "\n";
79 
80 static const char *commands_help =
81 "Commands:\n"
82 "   mib                  get MIB variables (dot1x, dot11, radius)\n"
83 "   sta <addr>           get MIB variables for one station\n"
84 "   all_sta              get MIB variables for all stations\n"
85 "   new_sta <addr>       add a new station\n"
86 "   deauthenticate <addr>  deauthenticate a station\n"
87 "   disassociate <addr>  disassociate a station\n"
88 #ifdef CONFIG_IEEE80211W
89 "   sa_query <addr>      send SA Query to a station\n"
90 #endif /* CONFIG_IEEE80211W */
91 #ifdef CONFIG_WPS
92 "   wps_pin <uuid> <pin> [timeout]  add WPS Enrollee PIN (Device Password)\n"
93 "   wps_pbc              indicate button pushed to initiate PBC\n"
94 #ifdef CONFIG_WPS_OOB
95 "   wps_oob <type> <path> <method>  use WPS with out-of-band (UFD)\n"
96 #endif /* CONFIG_WPS_OOB */
97 "   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
98 #endif /* CONFIG_WPS */
99 "   help                 show this usage help\n"
100 "   interface [ifname]   show interfaces/select interface\n"
101 "   level <debug level>  change debug level\n"
102 "   license              show full hostapd_cli license\n"
103 "   quit                 exit hostapd_cli\n";
104 
105 static struct wpa_ctrl *ctrl_conn;
106 static int hostapd_cli_quit = 0;
107 static int hostapd_cli_attached = 0;
108 static const char *ctrl_iface_dir = "/var/run/hostapd";
109 static char *ctrl_ifname = NULL;
110 static const char *pid_file = NULL;
111 static const char *action_file = NULL;
112 static int ping_interval = 5;
113 
114 
115 static void usage(void)
116 {
117 	fprintf(stderr, "%s\n", hostapd_cli_version);
118 	fprintf(stderr,
119 		"\n"
120 		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
121 		"[-a<path>] \\\n"
122 		"                   [-G<ping interval>] [command..]\n"
123 		"\n"
124 		"Options:\n"
125 		"   -h           help (show this usage text)\n"
126 		"   -v           shown version information\n"
127 		"   -p<path>     path to find control sockets (default: "
128 		"/var/run/hostapd)\n"
129 		"   -a<file>     run in daemon mode executing the action file "
130 		"based on events\n"
131 		"                from hostapd\n"
132 		"   -B           run a daemon in the background\n"
133 		"   -i<ifname>   Interface to listen on (default: first "
134 		"interface found in the\n"
135 		"                socket path)\n\n"
136 		"%s",
137 		commands_help);
138 }
139 
140 
141 static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
142 {
143 	char *cfile;
144 	int flen;
145 
146 	if (ifname == NULL)
147 		return NULL;
148 
149 	flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
150 	cfile = malloc(flen);
151 	if (cfile == NULL)
152 		return NULL;
153 	snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
154 
155 	ctrl_conn = wpa_ctrl_open(cfile);
156 	free(cfile);
157 	return ctrl_conn;
158 }
159 
160 
161 static void hostapd_cli_close_connection(void)
162 {
163 	if (ctrl_conn == NULL)
164 		return;
165 
166 	if (hostapd_cli_attached) {
167 		wpa_ctrl_detach(ctrl_conn);
168 		hostapd_cli_attached = 0;
169 	}
170 	wpa_ctrl_close(ctrl_conn);
171 	ctrl_conn = NULL;
172 }
173 
174 
175 static void hostapd_cli_msg_cb(char *msg, size_t len)
176 {
177 	printf("%s\n", msg);
178 }
179 
180 
181 static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
182 {
183 	char buf[4096];
184 	size_t len;
185 	int ret;
186 
187 	if (ctrl_conn == NULL) {
188 		printf("Not connected to hostapd - command dropped.\n");
189 		return -1;
190 	}
191 	len = sizeof(buf) - 1;
192 	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
193 			       hostapd_cli_msg_cb);
194 	if (ret == -2) {
195 		printf("'%s' command timed out.\n", cmd);
196 		return -2;
197 	} else if (ret < 0) {
198 		printf("'%s' command failed.\n", cmd);
199 		return -1;
200 	}
201 	if (print) {
202 		buf[len] = '\0';
203 		printf("%s", buf);
204 	}
205 	return 0;
206 }
207 
208 
209 static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
210 {
211 	return _wpa_ctrl_command(ctrl, cmd, 1);
212 }
213 
214 
215 static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
216 {
217 	return wpa_ctrl_command(ctrl, "PING");
218 }
219 
220 
221 static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
222 {
223 	return wpa_ctrl_command(ctrl, "MIB");
224 }
225 
226 
227 static 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 
255 static 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 
272 static 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 
285 static 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 
299 static 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 
317 static 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
336 static 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
352 static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
353 				   char *argv[])
354 {
355 	char buf[64];
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 > 2)
362 		snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
363 			 argv[0], argv[1], argv[2]);
364 	else
365 		snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
366 	return wpa_ctrl_command(ctrl, buf);
367 }
368 
369 
370 static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
371 				   char *argv[])
372 {
373 	return wpa_ctrl_command(ctrl, "WPS_PBC");
374 }
375 
376 
377 #ifdef CONFIG_WPS_OOB
378 static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
379 				   char *argv[])
380 {
381 	char cmd[256];
382 	int res;
383 
384 	if (argc != 3 && argc != 4) {
385 		printf("Invalid WPS_OOB command: need three or four "
386 		       "arguments:\n"
387 		       "- DEV_TYPE: use 'ufd' or 'nfc'\n"
388 		       "- PATH: path of OOB device like '/mnt'\n"
389 		       "- METHOD: OOB method 'pin-e' or 'pin-r', "
390 		       "'cred'\n"
391 		       "- DEV_NAME: (only for NFC) device name like "
392 		       "'pn531'\n");
393 		return -1;
394 	}
395 
396 	if (argc == 3)
397 		res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
398 				  argv[0], argv[1], argv[2]);
399 	else
400 		res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
401 				  argv[0], argv[1], argv[2], argv[3]);
402 	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
403 		printf("Too long WPS_OOB command.\n");
404 		return -1;
405 	}
406 	return wpa_ctrl_command(ctrl, cmd);
407 }
408 #endif /* CONFIG_WPS_OOB */
409 
410 
411 static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
412 				      char *argv[])
413 {
414 	char buf[64];
415 	if (argc < 1) {
416 		printf("Invalid 'wps_ap_pin' command - at least one argument "
417 		       "is required.\n");
418 		return -1;
419 	}
420 	if (argc > 2)
421 		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
422 			 argv[0], argv[1], argv[2]);
423 	else if (argc > 1)
424 		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
425 			 argv[0], argv[1]);
426 	else
427 		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
428 	return wpa_ctrl_command(ctrl, buf);
429 }
430 #endif /* CONFIG_WPS */
431 
432 
433 static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
434 				char *addr, size_t addr_len)
435 {
436 	char buf[4096], *pos;
437 	size_t len;
438 	int ret;
439 
440 	if (ctrl_conn == NULL) {
441 		printf("Not connected to hostapd - command dropped.\n");
442 		return -1;
443 	}
444 	len = sizeof(buf) - 1;
445 	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
446 			       hostapd_cli_msg_cb);
447 	if (ret == -2) {
448 		printf("'%s' command timed out.\n", cmd);
449 		return -2;
450 	} else if (ret < 0) {
451 		printf("'%s' command failed.\n", cmd);
452 		return -1;
453 	}
454 
455 	buf[len] = '\0';
456 	if (memcmp(buf, "FAIL", 4) == 0)
457 		return -1;
458 	printf("%s", buf);
459 
460 	pos = buf;
461 	while (*pos != '\0' && *pos != '\n')
462 		pos++;
463 	*pos = '\0';
464 	os_strlcpy(addr, buf, addr_len);
465 	return 0;
466 }
467 
468 
469 static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
470 				   char *argv[])
471 {
472 	char addr[32], cmd[64];
473 
474 	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
475 		return 0;
476 	do {
477 		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
478 	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
479 
480 	return -1;
481 }
482 
483 
484 static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
485 {
486 	printf("%s", commands_help);
487 	return 0;
488 }
489 
490 
491 static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
492 				   char *argv[])
493 {
494 	printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
495 	return 0;
496 }
497 
498 
499 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
500 {
501 	hostapd_cli_quit = 1;
502 	return 0;
503 }
504 
505 
506 static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
507 {
508 	char cmd[256];
509 	if (argc != 1) {
510 		printf("Invalid LEVEL command: needs one argument (debug "
511 		       "level)\n");
512 		return 0;
513 	}
514 	snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
515 	return wpa_ctrl_command(ctrl, cmd);
516 }
517 
518 
519 static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
520 {
521 	struct dirent *dent;
522 	DIR *dir;
523 
524 	dir = opendir(ctrl_iface_dir);
525 	if (dir == NULL) {
526 		printf("Control interface directory '%s' could not be "
527 		       "openned.\n", ctrl_iface_dir);
528 		return;
529 	}
530 
531 	printf("Available interfaces:\n");
532 	while ((dent = readdir(dir))) {
533 		if (strcmp(dent->d_name, ".") == 0 ||
534 		    strcmp(dent->d_name, "..") == 0)
535 			continue;
536 		printf("%s\n", dent->d_name);
537 	}
538 	closedir(dir);
539 }
540 
541 
542 static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
543 				     char *argv[])
544 {
545 	if (argc < 1) {
546 		hostapd_cli_list_interfaces(ctrl);
547 		return 0;
548 	}
549 
550 	hostapd_cli_close_connection();
551 	free(ctrl_ifname);
552 	ctrl_ifname = strdup(argv[0]);
553 
554 	if (hostapd_cli_open_connection(ctrl_ifname)) {
555 		printf("Connected to interface '%s.\n", ctrl_ifname);
556 		if (wpa_ctrl_attach(ctrl_conn) == 0) {
557 			hostapd_cli_attached = 1;
558 		} else {
559 			printf("Warning: Failed to attach to "
560 			       "hostapd.\n");
561 		}
562 	} else {
563 		printf("Could not connect to interface '%s' - re-trying\n",
564 			ctrl_ifname);
565 	}
566 	return 0;
567 }
568 
569 
570 struct hostapd_cli_cmd {
571 	const char *cmd;
572 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
573 };
574 
575 static struct hostapd_cli_cmd hostapd_cli_commands[] = {
576 	{ "ping", hostapd_cli_cmd_ping },
577 	{ "mib", hostapd_cli_cmd_mib },
578 	{ "sta", hostapd_cli_cmd_sta },
579 	{ "all_sta", hostapd_cli_cmd_all_sta },
580 	{ "new_sta", hostapd_cli_cmd_new_sta },
581 	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
582 	{ "disassociate", hostapd_cli_cmd_disassociate },
583 #ifdef CONFIG_IEEE80211W
584 	{ "sa_query", hostapd_cli_cmd_sa_query },
585 #endif /* CONFIG_IEEE80211W */
586 #ifdef CONFIG_WPS
587 	{ "wps_pin", hostapd_cli_cmd_wps_pin },
588 	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
589 #ifdef CONFIG_WPS_OOB
590 	{ "wps_oob", hostapd_cli_cmd_wps_oob },
591 #endif /* CONFIG_WPS_OOB */
592 	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
593 #endif /* CONFIG_WPS */
594 	{ "help", hostapd_cli_cmd_help },
595 	{ "interface", hostapd_cli_cmd_interface },
596 	{ "level", hostapd_cli_cmd_level },
597 	{ "license", hostapd_cli_cmd_license },
598 	{ "quit", hostapd_cli_cmd_quit },
599 	{ NULL, NULL }
600 };
601 
602 
603 static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
604 {
605 	struct hostapd_cli_cmd *cmd, *match = NULL;
606 	int count;
607 
608 	count = 0;
609 	cmd = hostapd_cli_commands;
610 	while (cmd->cmd) {
611 		if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
612 			match = cmd;
613 			count++;
614 		}
615 		cmd++;
616 	}
617 
618 	if (count > 1) {
619 		printf("Ambiguous command '%s'; possible commands:", argv[0]);
620 		cmd = hostapd_cli_commands;
621 		while (cmd->cmd) {
622 			if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
623 			    0) {
624 				printf(" %s", cmd->cmd);
625 			}
626 			cmd++;
627 		}
628 		printf("\n");
629 	} else if (count == 0) {
630 		printf("Unknown command '%s'\n", argv[0]);
631 	} else {
632 		match->handler(ctrl, argc - 1, &argv[1]);
633 	}
634 }
635 
636 
637 static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
638 				     int action_monitor)
639 {
640 	int first = 1;
641 	if (ctrl_conn == NULL)
642 		return;
643 	while (wpa_ctrl_pending(ctrl)) {
644 		char buf[256];
645 		size_t len = sizeof(buf) - 1;
646 		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
647 			buf[len] = '\0';
648 			if (action_monitor)
649 				hostapd_cli_action_process(buf, len);
650 			else {
651 				if (in_read && first)
652 					printf("\n");
653 				first = 0;
654 				printf("%s\n", buf);
655 			}
656 		} else {
657 			printf("Could not read pending message.\n");
658 			break;
659 		}
660 	}
661 }
662 
663 
664 static void hostapd_cli_interactive(void)
665 {
666 	const int max_args = 10;
667 	char cmd[256], *res, *argv[max_args], *pos;
668 	int argc;
669 
670 	printf("\nInteractive mode\n\n");
671 
672 	do {
673 		hostapd_cli_recv_pending(ctrl_conn, 0, 0);
674 		printf("> ");
675 		alarm(ping_interval);
676 		res = fgets(cmd, sizeof(cmd), stdin);
677 		alarm(0);
678 		if (res == NULL)
679 			break;
680 		pos = cmd;
681 		while (*pos != '\0') {
682 			if (*pos == '\n') {
683 				*pos = '\0';
684 				break;
685 			}
686 			pos++;
687 		}
688 		argc = 0;
689 		pos = cmd;
690 		for (;;) {
691 			while (*pos == ' ')
692 				pos++;
693 			if (*pos == '\0')
694 				break;
695 			argv[argc] = pos;
696 			argc++;
697 			if (argc == max_args)
698 				break;
699 			while (*pos != '\0' && *pos != ' ')
700 				pos++;
701 			if (*pos == ' ')
702 				*pos++ = '\0';
703 		}
704 		if (argc)
705 			wpa_request(ctrl_conn, argc, argv);
706 	} while (!hostapd_cli_quit);
707 }
708 
709 
710 static void hostapd_cli_cleanup(void)
711 {
712 	hostapd_cli_close_connection();
713 	if (pid_file)
714 		os_daemonize_terminate(pid_file);
715 
716 	os_program_deinit();
717 }
718 
719 
720 static void hostapd_cli_terminate(int sig)
721 {
722 	hostapd_cli_cleanup();
723 	exit(0);
724 }
725 
726 
727 static void hostapd_cli_alarm(int sig)
728 {
729 	if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
730 		printf("Connection to hostapd lost - trying to reconnect\n");
731 		hostapd_cli_close_connection();
732 	}
733 	if (!ctrl_conn) {
734 		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
735 		if (ctrl_conn) {
736 			printf("Connection to hostapd re-established\n");
737 			if (wpa_ctrl_attach(ctrl_conn) == 0) {
738 				hostapd_cli_attached = 1;
739 			} else {
740 				printf("Warning: Failed to attach to "
741 				       "hostapd.\n");
742 			}
743 		}
744 	}
745 	if (ctrl_conn)
746 		hostapd_cli_recv_pending(ctrl_conn, 1, 0);
747 	alarm(ping_interval);
748 }
749 
750 
751 static void hostapd_cli_action(struct wpa_ctrl *ctrl)
752 {
753 	fd_set rfds;
754 	int fd, res;
755 	struct timeval tv;
756 	char buf[256];
757 	size_t len;
758 
759 	fd = wpa_ctrl_get_fd(ctrl);
760 
761 	while (!hostapd_cli_quit) {
762 		FD_ZERO(&rfds);
763 		FD_SET(fd, &rfds);
764 		tv.tv_sec = ping_interval;
765 		tv.tv_usec = 0;
766 		res = select(fd + 1, &rfds, NULL, NULL, &tv);
767 		if (res < 0 && errno != EINTR) {
768 			perror("select");
769 			break;
770 		}
771 
772 		if (FD_ISSET(fd, &rfds))
773 			hostapd_cli_recv_pending(ctrl, 0, 1);
774 		else {
775 			len = sizeof(buf) - 1;
776 			if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
777 					     hostapd_cli_action_process) < 0 ||
778 			    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
779 				printf("hostapd did not reply to PING "
780 				       "command - exiting\n");
781 				break;
782 			}
783 		}
784 	}
785 }
786 
787 
788 int main(int argc, char *argv[])
789 {
790 	int interactive;
791 	int warning_displayed = 0;
792 	int c;
793 	int daemonize = 0;
794 
795 	if (os_program_init())
796 		return -1;
797 
798 	for (;;) {
799 		c = getopt(argc, argv, "a:BhG:i:p:v");
800 		if (c < 0)
801 			break;
802 		switch (c) {
803 		case 'a':
804 			action_file = optarg;
805 			break;
806 		case 'B':
807 			daemonize = 1;
808 			break;
809 		case 'G':
810 			ping_interval = atoi(optarg);
811 			break;
812 		case 'h':
813 			usage();
814 			return 0;
815 		case 'v':
816 			printf("%s\n", hostapd_cli_version);
817 			return 0;
818 		case 'i':
819 			os_free(ctrl_ifname);
820 			ctrl_ifname = os_strdup(optarg);
821 			break;
822 		case 'p':
823 			ctrl_iface_dir = optarg;
824 			break;
825 		default:
826 			usage();
827 			return -1;
828 		}
829 	}
830 
831 	interactive = (argc == optind) && (action_file == NULL);
832 
833 	if (interactive) {
834 		printf("%s\n\n%s\n\n", hostapd_cli_version,
835 		       hostapd_cli_license);
836 	}
837 
838 	for (;;) {
839 		if (ctrl_ifname == NULL) {
840 			struct dirent *dent;
841 			DIR *dir = opendir(ctrl_iface_dir);
842 			if (dir) {
843 				while ((dent = readdir(dir))) {
844 					if (os_strcmp(dent->d_name, ".") == 0
845 					    ||
846 					    os_strcmp(dent->d_name, "..") == 0)
847 						continue;
848 					printf("Selected interface '%s'\n",
849 					       dent->d_name);
850 					ctrl_ifname = os_strdup(dent->d_name);
851 					break;
852 				}
853 				closedir(dir);
854 			}
855 		}
856 		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
857 		if (ctrl_conn) {
858 			if (warning_displayed)
859 				printf("Connection established.\n");
860 			break;
861 		}
862 
863 		if (!interactive) {
864 			perror("Failed to connect to hostapd - "
865 			       "wpa_ctrl_open");
866 			return -1;
867 		}
868 
869 		if (!warning_displayed) {
870 			printf("Could not connect to hostapd - re-trying\n");
871 			warning_displayed = 1;
872 		}
873 		os_sleep(1, 0);
874 		continue;
875 	}
876 
877 	signal(SIGINT, hostapd_cli_terminate);
878 	signal(SIGTERM, hostapd_cli_terminate);
879 	signal(SIGALRM, hostapd_cli_alarm);
880 
881 	if (interactive || action_file) {
882 		if (wpa_ctrl_attach(ctrl_conn) == 0) {
883 			hostapd_cli_attached = 1;
884 		} else {
885 			printf("Warning: Failed to attach to hostapd.\n");
886 			if (action_file)
887 				return -1;
888 		}
889 	}
890 
891 	if (daemonize && os_daemonize(pid_file))
892 		return -1;
893 
894 	if (interactive)
895 		hostapd_cli_interactive();
896 	else if (action_file)
897 		hostapd_cli_action(ctrl_conn);
898 	else
899 		wpa_request(ctrl_conn, argc - optind, &argv[optind]);
900 
901 	os_free(ctrl_ifname);
902 	hostapd_cli_cleanup();
903 	return 0;
904 }
905