hostapd_cli.c revision 214734
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
23static 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
28static 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
35static 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
80static 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
105static struct wpa_ctrl *ctrl_conn;
106static int hostapd_cli_quit = 0;
107static int hostapd_cli_attached = 0;
108static const char *ctrl_iface_dir = "/var/run/hostapd";
109static char *ctrl_ifname = NULL;
110static const char *pid_file = NULL;
111static const char *action_file = NULL;
112static int ping_interval = 5;
113
114
115static 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
141static 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
161static 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
175static void hostapd_cli_msg_cb(char *msg, size_t len)
176{
177	printf("%s\n", msg);
178}
179
180
181static 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
209static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
210{
211	return _wpa_ctrl_command(ctrl, cmd, 1);
212}
213
214
215static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
216{
217	return wpa_ctrl_command(ctrl, "PING");
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[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
370static 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
378static 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
411static 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
433static 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
469static 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
484static 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
491static 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
499static 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
506static 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
519static 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
542static 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
570struct hostapd_cli_cmd {
571	const char *cmd;
572	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
573};
574
575static 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
603static 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
637static 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
664static 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
710static 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
720static void hostapd_cli_terminate(int sig)
721{
722	hostapd_cli_cleanup();
723	exit(0);
724}
725
726
727static 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
751static 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
788int 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