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 "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-2011, 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] [addr]  add WPS Enrollee PIN\n"
93"   wps_check_pin <PIN>  verify PIN checksum\n"
94"   wps_pbc              indicate button pushed to initiate PBC\n"
95#ifdef CONFIG_WPS_OOB
96"   wps_oob <type> <path> <method>  use WPS with out-of-band (UFD)\n"
97#endif /* CONFIG_WPS_OOB */
98"   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
99"   wps_config <SSID> <auth> <encr> <key>  configure AP\n"
100#endif /* CONFIG_WPS */
101"   get_config           show current configuration\n"
102"   help                 show this usage help\n"
103"   interface [ifname]   show interfaces/select interface\n"
104"   level <debug level>  change debug level\n"
105"   license              show full hostapd_cli license\n"
106"   quit                 exit hostapd_cli\n";
107
108static struct wpa_ctrl *ctrl_conn;
109static int hostapd_cli_quit = 0;
110static int hostapd_cli_attached = 0;
111static const char *ctrl_iface_dir = "/var/run/hostapd";
112static char *ctrl_ifname = NULL;
113static const char *pid_file = NULL;
114static const char *action_file = NULL;
115static int ping_interval = 5;
116
117
118static void usage(void)
119{
120	fprintf(stderr, "%s\n", hostapd_cli_version);
121	fprintf(stderr,
122		"\n"
123		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
124		"[-a<path>] \\\n"
125		"                   [-G<ping interval>] [command..]\n"
126		"\n"
127		"Options:\n"
128		"   -h           help (show this usage text)\n"
129		"   -v           shown version information\n"
130		"   -p<path>     path to find control sockets (default: "
131		"/var/run/hostapd)\n"
132		"   -a<file>     run in daemon mode executing the action file "
133		"based on events\n"
134		"                from hostapd\n"
135		"   -B           run a daemon in the background\n"
136		"   -i<ifname>   Interface to listen on (default: first "
137		"interface found in the\n"
138		"                socket path)\n\n"
139		"%s",
140		commands_help);
141}
142
143
144static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
145{
146	char *cfile;
147	int flen;
148
149	if (ifname == NULL)
150		return NULL;
151
152	flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
153	cfile = malloc(flen);
154	if (cfile == NULL)
155		return NULL;
156	snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
157
158	ctrl_conn = wpa_ctrl_open(cfile);
159	free(cfile);
160	return ctrl_conn;
161}
162
163
164static void hostapd_cli_close_connection(void)
165{
166	if (ctrl_conn == NULL)
167		return;
168
169	if (hostapd_cli_attached) {
170		wpa_ctrl_detach(ctrl_conn);
171		hostapd_cli_attached = 0;
172	}
173	wpa_ctrl_close(ctrl_conn);
174	ctrl_conn = NULL;
175}
176
177
178static void hostapd_cli_msg_cb(char *msg, size_t len)
179{
180	printf("%s\n", msg);
181}
182
183
184static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
185{
186	char buf[4096];
187	size_t len;
188	int ret;
189
190	if (ctrl_conn == NULL) {
191		printf("Not connected to hostapd - command dropped.\n");
192		return -1;
193	}
194	len = sizeof(buf) - 1;
195	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
196			       hostapd_cli_msg_cb);
197	if (ret == -2) {
198		printf("'%s' command timed out.\n", cmd);
199		return -2;
200	} else if (ret < 0) {
201		printf("'%s' command failed.\n", cmd);
202		return -1;
203	}
204	if (print) {
205		buf[len] = '\0';
206		printf("%s", buf);
207	}
208	return 0;
209}
210
211
212static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
213{
214	return _wpa_ctrl_command(ctrl, cmd, 1);
215}
216
217
218static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
219{
220	return wpa_ctrl_command(ctrl, "PING");
221}
222
223
224static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
225{
226	return wpa_ctrl_command(ctrl, "RELOG");
227}
228
229
230static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
231{
232	return wpa_ctrl_command(ctrl, "MIB");
233}
234
235
236static int hostapd_cli_exec(const char *program, const char *arg1,
237			    const char *arg2)
238{
239	char *cmd;
240	size_t len;
241	int res;
242	int ret = 0;
243
244	len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
245	cmd = os_malloc(len);
246	if (cmd == NULL)
247		return -1;
248	res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
249	if (res < 0 || (size_t) res >= len) {
250		os_free(cmd);
251		return -1;
252	}
253	cmd[len - 1] = '\0';
254#ifndef _WIN32_WCE
255	if (system(cmd) < 0)
256		ret = -1;
257#endif /* _WIN32_WCE */
258	os_free(cmd);
259
260	return ret;
261}
262
263
264static void hostapd_cli_action_process(char *msg, size_t len)
265{
266	const char *pos;
267
268	pos = msg;
269	if (*pos == '<') {
270		pos = os_strchr(pos, '>');
271		if (pos)
272			pos++;
273		else
274			pos = msg;
275	}
276
277	hostapd_cli_exec(action_file, ctrl_ifname, pos);
278}
279
280
281static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
282{
283	char buf[64];
284	if (argc != 1) {
285		printf("Invalid 'sta' command - exactly one argument, STA "
286		       "address, is required.\n");
287		return -1;
288	}
289	snprintf(buf, sizeof(buf), "STA %s", argv[0]);
290	return wpa_ctrl_command(ctrl, buf);
291}
292
293
294static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
295				   char *argv[])
296{
297	char buf[64];
298	if (argc != 1) {
299		printf("Invalid 'new_sta' command - exactly one argument, STA "
300		       "address, is required.\n");
301		return -1;
302	}
303	snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
304	return wpa_ctrl_command(ctrl, buf);
305}
306
307
308static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
309					  char *argv[])
310{
311	char buf[64];
312	if (argc < 1) {
313		printf("Invalid 'deauthenticate' command - exactly one "
314		       "argument, STA address, is required.\n");
315		return -1;
316	}
317	if (argc > 1)
318		os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
319			    argv[0], argv[1]);
320	else
321		os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
322	return wpa_ctrl_command(ctrl, buf);
323}
324
325
326static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
327					char *argv[])
328{
329	char buf[64];
330	if (argc < 1) {
331		printf("Invalid 'disassociate' command - exactly one "
332		       "argument, STA address, is required.\n");
333		return -1;
334	}
335	if (argc > 1)
336		os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
337			    argv[0], argv[1]);
338	else
339		os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
340	return wpa_ctrl_command(ctrl, buf);
341}
342
343
344#ifdef CONFIG_IEEE80211W
345static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
346				    char *argv[])
347{
348	char buf[64];
349	if (argc != 1) {
350		printf("Invalid 'sa_query' command - exactly one argument, "
351		       "STA address, is required.\n");
352		return -1;
353	}
354	snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
355	return wpa_ctrl_command(ctrl, buf);
356}
357#endif /* CONFIG_IEEE80211W */
358
359
360#ifdef CONFIG_WPS
361static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
362				   char *argv[])
363{
364	char buf[256];
365	if (argc < 2) {
366		printf("Invalid 'wps_pin' command - at least two arguments, "
367		       "UUID and PIN, are required.\n");
368		return -1;
369	}
370	if (argc > 3)
371		snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
372			 argv[0], argv[1], argv[2], argv[3]);
373	else if (argc > 2)
374		snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
375			 argv[0], argv[1], argv[2]);
376	else
377		snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
378	return wpa_ctrl_command(ctrl, buf);
379}
380
381
382static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
383					 char *argv[])
384{
385	char cmd[256];
386	int res;
387
388	if (argc != 1 && argc != 2) {
389		printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
390		       "- PIN to be verified\n");
391		return -1;
392	}
393
394	if (argc == 2)
395		res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
396				  argv[0], argv[1]);
397	else
398		res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
399				  argv[0]);
400	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
401		printf("Too long WPS_CHECK_PIN command.\n");
402		return -1;
403	}
404	return wpa_ctrl_command(ctrl, cmd);
405}
406
407
408static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
409				   char *argv[])
410{
411	return wpa_ctrl_command(ctrl, "WPS_PBC");
412}
413
414
415#ifdef CONFIG_WPS_OOB
416static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
417				   char *argv[])
418{
419	char cmd[256];
420	int res;
421
422	if (argc != 3 && argc != 4) {
423		printf("Invalid WPS_OOB command: need three or four "
424		       "arguments:\n"
425		       "- DEV_TYPE: use 'ufd' or 'nfc'\n"
426		       "- PATH: path of OOB device like '/mnt'\n"
427		       "- METHOD: OOB method 'pin-e' or 'pin-r', "
428		       "'cred'\n"
429		       "- DEV_NAME: (only for NFC) device name like "
430		       "'pn531'\n");
431		return -1;
432	}
433
434	if (argc == 3)
435		res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
436				  argv[0], argv[1], argv[2]);
437	else
438		res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
439				  argv[0], argv[1], argv[2], argv[3]);
440	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
441		printf("Too long WPS_OOB command.\n");
442		return -1;
443	}
444	return wpa_ctrl_command(ctrl, cmd);
445}
446#endif /* CONFIG_WPS_OOB */
447
448
449static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
450				      char *argv[])
451{
452	char buf[64];
453	if (argc < 1) {
454		printf("Invalid 'wps_ap_pin' command - at least one argument "
455		       "is required.\n");
456		return -1;
457	}
458	if (argc > 2)
459		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
460			 argv[0], argv[1], argv[2]);
461	else if (argc > 1)
462		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
463			 argv[0], argv[1]);
464	else
465		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
466	return wpa_ctrl_command(ctrl, buf);
467}
468
469
470static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
471				      char *argv[])
472{
473	char buf[256];
474	char ssid_hex[2 * 32 + 1];
475	char key_hex[2 * 64 + 1];
476	int i;
477
478	if (argc < 1) {
479		printf("Invalid 'wps_config' command - at least two arguments "
480		       "are required.\n");
481		return -1;
482	}
483
484	ssid_hex[0] = '\0';
485	for (i = 0; i < 32; i++) {
486		if (argv[0][i] == '\0')
487			break;
488		os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
489	}
490
491	key_hex[0] = '\0';
492	if (argc > 3) {
493		for (i = 0; i < 64; i++) {
494			if (argv[3][i] == '\0')
495				break;
496			os_snprintf(&key_hex[i * 2], 3, "%02x",
497				    argv[3][i]);
498		}
499	}
500
501	if (argc > 3)
502		snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
503			 ssid_hex, argv[1], argv[2], key_hex);
504	else if (argc > 2)
505		snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
506			 ssid_hex, argv[1], argv[2]);
507	else
508		snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
509			 ssid_hex, argv[1]);
510	return wpa_ctrl_command(ctrl, buf);
511}
512#endif /* CONFIG_WPS */
513
514
515static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
516				      char *argv[])
517{
518	return wpa_ctrl_command(ctrl, "GET_CONFIG");
519}
520
521
522static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
523				char *addr, size_t addr_len)
524{
525	char buf[4096], *pos;
526	size_t len;
527	int ret;
528
529	if (ctrl_conn == NULL) {
530		printf("Not connected to hostapd - command dropped.\n");
531		return -1;
532	}
533	len = sizeof(buf) - 1;
534	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
535			       hostapd_cli_msg_cb);
536	if (ret == -2) {
537		printf("'%s' command timed out.\n", cmd);
538		return -2;
539	} else if (ret < 0) {
540		printf("'%s' command failed.\n", cmd);
541		return -1;
542	}
543
544	buf[len] = '\0';
545	if (memcmp(buf, "FAIL", 4) == 0)
546		return -1;
547	printf("%s", buf);
548
549	pos = buf;
550	while (*pos != '\0' && *pos != '\n')
551		pos++;
552	*pos = '\0';
553	os_strlcpy(addr, buf, addr_len);
554	return 0;
555}
556
557
558static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
559				   char *argv[])
560{
561	char addr[32], cmd[64];
562
563	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
564		return 0;
565	do {
566		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
567	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
568
569	return -1;
570}
571
572
573static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
574{
575	printf("%s", commands_help);
576	return 0;
577}
578
579
580static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
581				   char *argv[])
582{
583	printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
584	return 0;
585}
586
587
588static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
589{
590	hostapd_cli_quit = 1;
591	return 0;
592}
593
594
595static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
596{
597	char cmd[256];
598	if (argc != 1) {
599		printf("Invalid LEVEL command: needs one argument (debug "
600		       "level)\n");
601		return 0;
602	}
603	snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
604	return wpa_ctrl_command(ctrl, cmd);
605}
606
607
608static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
609{
610	struct dirent *dent;
611	DIR *dir;
612
613	dir = opendir(ctrl_iface_dir);
614	if (dir == NULL) {
615		printf("Control interface directory '%s' could not be "
616		       "openned.\n", ctrl_iface_dir);
617		return;
618	}
619
620	printf("Available interfaces:\n");
621	while ((dent = readdir(dir))) {
622		if (strcmp(dent->d_name, ".") == 0 ||
623		    strcmp(dent->d_name, "..") == 0)
624			continue;
625		printf("%s\n", dent->d_name);
626	}
627	closedir(dir);
628}
629
630
631static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
632				     char *argv[])
633{
634	if (argc < 1) {
635		hostapd_cli_list_interfaces(ctrl);
636		return 0;
637	}
638
639	hostapd_cli_close_connection();
640	free(ctrl_ifname);
641	ctrl_ifname = strdup(argv[0]);
642
643	if (hostapd_cli_open_connection(ctrl_ifname)) {
644		printf("Connected to interface '%s.\n", ctrl_ifname);
645		if (wpa_ctrl_attach(ctrl_conn) == 0) {
646			hostapd_cli_attached = 1;
647		} else {
648			printf("Warning: Failed to attach to "
649			       "hostapd.\n");
650		}
651	} else {
652		printf("Could not connect to interface '%s' - re-trying\n",
653			ctrl_ifname);
654	}
655	return 0;
656}
657
658
659static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
660{
661	char cmd[256];
662	int res;
663
664	if (argc != 2) {
665		printf("Invalid SET command: needs two arguments (variable "
666		       "name and value)\n");
667		return -1;
668	}
669
670	res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
671	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
672		printf("Too long SET command.\n");
673		return -1;
674	}
675	return wpa_ctrl_command(ctrl, cmd);
676}
677
678
679static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
680{
681	char cmd[256];
682	int res;
683
684	if (argc != 1) {
685		printf("Invalid GET command: needs one argument (variable "
686		       "name)\n");
687		return -1;
688	}
689
690	res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
691	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
692		printf("Too long GET command.\n");
693		return -1;
694	}
695	return wpa_ctrl_command(ctrl, cmd);
696}
697
698
699struct hostapd_cli_cmd {
700	const char *cmd;
701	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
702};
703
704static struct hostapd_cli_cmd hostapd_cli_commands[] = {
705	{ "ping", hostapd_cli_cmd_ping },
706	{ "mib", hostapd_cli_cmd_mib },
707	{ "relog", hostapd_cli_cmd_relog },
708	{ "sta", hostapd_cli_cmd_sta },
709	{ "all_sta", hostapd_cli_cmd_all_sta },
710	{ "new_sta", hostapd_cli_cmd_new_sta },
711	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
712	{ "disassociate", hostapd_cli_cmd_disassociate },
713#ifdef CONFIG_IEEE80211W
714	{ "sa_query", hostapd_cli_cmd_sa_query },
715#endif /* CONFIG_IEEE80211W */
716#ifdef CONFIG_WPS
717	{ "wps_pin", hostapd_cli_cmd_wps_pin },
718	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
719	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
720#ifdef CONFIG_WPS_OOB
721	{ "wps_oob", hostapd_cli_cmd_wps_oob },
722#endif /* CONFIG_WPS_OOB */
723	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
724	{ "wps_config", hostapd_cli_cmd_wps_config },
725#endif /* CONFIG_WPS */
726	{ "get_config", hostapd_cli_cmd_get_config },
727	{ "help", hostapd_cli_cmd_help },
728	{ "interface", hostapd_cli_cmd_interface },
729	{ "level", hostapd_cli_cmd_level },
730	{ "license", hostapd_cli_cmd_license },
731	{ "quit", hostapd_cli_cmd_quit },
732	{ "set", hostapd_cli_cmd_set },
733	{ "get", hostapd_cli_cmd_get },
734	{ NULL, NULL }
735};
736
737
738static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
739{
740	struct hostapd_cli_cmd *cmd, *match = NULL;
741	int count;
742
743	count = 0;
744	cmd = hostapd_cli_commands;
745	while (cmd->cmd) {
746		if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
747			match = cmd;
748			if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
749				/* we have an exact match */
750				count = 1;
751				break;
752			}
753			count++;
754		}
755		cmd++;
756	}
757
758	if (count > 1) {
759		printf("Ambiguous command '%s'; possible commands:", argv[0]);
760		cmd = hostapd_cli_commands;
761		while (cmd->cmd) {
762			if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
763			    0) {
764				printf(" %s", cmd->cmd);
765			}
766			cmd++;
767		}
768		printf("\n");
769	} else if (count == 0) {
770		printf("Unknown command '%s'\n", argv[0]);
771	} else {
772		match->handler(ctrl, argc - 1, &argv[1]);
773	}
774}
775
776
777static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
778				     int action_monitor)
779{
780	int first = 1;
781	if (ctrl_conn == NULL)
782		return;
783	while (wpa_ctrl_pending(ctrl)) {
784		char buf[256];
785		size_t len = sizeof(buf) - 1;
786		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
787			buf[len] = '\0';
788			if (action_monitor)
789				hostapd_cli_action_process(buf, len);
790			else {
791				if (in_read && first)
792					printf("\n");
793				first = 0;
794				printf("%s\n", buf);
795			}
796		} else {
797			printf("Could not read pending message.\n");
798			break;
799		}
800	}
801}
802
803
804static void hostapd_cli_interactive(void)
805{
806	const int max_args = 10;
807	char cmd[256], *res, *argv[max_args], *pos;
808	int argc;
809
810	printf("\nInteractive mode\n\n");
811
812	do {
813		hostapd_cli_recv_pending(ctrl_conn, 0, 0);
814		printf("> ");
815		alarm(ping_interval);
816		res = fgets(cmd, sizeof(cmd), stdin);
817		alarm(0);
818		if (res == NULL)
819			break;
820		pos = cmd;
821		while (*pos != '\0') {
822			if (*pos == '\n') {
823				*pos = '\0';
824				break;
825			}
826			pos++;
827		}
828		argc = 0;
829		pos = cmd;
830		for (;;) {
831			while (*pos == ' ')
832				pos++;
833			if (*pos == '\0')
834				break;
835			argv[argc] = pos;
836			argc++;
837			if (argc == max_args)
838				break;
839			while (*pos != '\0' && *pos != ' ')
840				pos++;
841			if (*pos == ' ')
842				*pos++ = '\0';
843		}
844		if (argc)
845			wpa_request(ctrl_conn, argc, argv);
846	} while (!hostapd_cli_quit);
847}
848
849
850static void hostapd_cli_cleanup(void)
851{
852	hostapd_cli_close_connection();
853	if (pid_file)
854		os_daemonize_terminate(pid_file);
855
856	os_program_deinit();
857}
858
859
860static void hostapd_cli_terminate(int sig)
861{
862	hostapd_cli_cleanup();
863	exit(0);
864}
865
866
867static void hostapd_cli_alarm(int sig)
868{
869	if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
870		printf("Connection to hostapd lost - trying to reconnect\n");
871		hostapd_cli_close_connection();
872	}
873	if (!ctrl_conn) {
874		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
875		if (ctrl_conn) {
876			printf("Connection to hostapd re-established\n");
877			if (wpa_ctrl_attach(ctrl_conn) == 0) {
878				hostapd_cli_attached = 1;
879			} else {
880				printf("Warning: Failed to attach to "
881				       "hostapd.\n");
882			}
883		}
884	}
885	if (ctrl_conn)
886		hostapd_cli_recv_pending(ctrl_conn, 1, 0);
887	alarm(ping_interval);
888}
889
890
891static void hostapd_cli_action(struct wpa_ctrl *ctrl)
892{
893	fd_set rfds;
894	int fd, res;
895	struct timeval tv;
896	char buf[256];
897	size_t len;
898
899	fd = wpa_ctrl_get_fd(ctrl);
900
901	while (!hostapd_cli_quit) {
902		FD_ZERO(&rfds);
903		FD_SET(fd, &rfds);
904		tv.tv_sec = ping_interval;
905		tv.tv_usec = 0;
906		res = select(fd + 1, &rfds, NULL, NULL, &tv);
907		if (res < 0 && errno != EINTR) {
908			perror("select");
909			break;
910		}
911
912		if (FD_ISSET(fd, &rfds))
913			hostapd_cli_recv_pending(ctrl, 0, 1);
914		else {
915			len = sizeof(buf) - 1;
916			if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
917					     hostapd_cli_action_process) < 0 ||
918			    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
919				printf("hostapd did not reply to PING "
920				       "command - exiting\n");
921				break;
922			}
923		}
924	}
925}
926
927
928int main(int argc, char *argv[])
929{
930	int interactive;
931	int warning_displayed = 0;
932	int c;
933	int daemonize = 0;
934
935	if (os_program_init())
936		return -1;
937
938	for (;;) {
939		c = getopt(argc, argv, "a:BhG:i:p:v");
940		if (c < 0)
941			break;
942		switch (c) {
943		case 'a':
944			action_file = optarg;
945			break;
946		case 'B':
947			daemonize = 1;
948			break;
949		case 'G':
950			ping_interval = atoi(optarg);
951			break;
952		case 'h':
953			usage();
954			return 0;
955		case 'v':
956			printf("%s\n", hostapd_cli_version);
957			return 0;
958		case 'i':
959			os_free(ctrl_ifname);
960			ctrl_ifname = os_strdup(optarg);
961			break;
962		case 'p':
963			ctrl_iface_dir = optarg;
964			break;
965		default:
966			usage();
967			return -1;
968		}
969	}
970
971	interactive = (argc == optind) && (action_file == NULL);
972
973	if (interactive) {
974		printf("%s\n\n%s\n\n", hostapd_cli_version,
975		       hostapd_cli_license);
976	}
977
978	for (;;) {
979		if (ctrl_ifname == NULL) {
980			struct dirent *dent;
981			DIR *dir = opendir(ctrl_iface_dir);
982			if (dir) {
983				while ((dent = readdir(dir))) {
984					if (os_strcmp(dent->d_name, ".") == 0
985					    ||
986					    os_strcmp(dent->d_name, "..") == 0)
987						continue;
988					printf("Selected interface '%s'\n",
989					       dent->d_name);
990					ctrl_ifname = os_strdup(dent->d_name);
991					break;
992				}
993				closedir(dir);
994			}
995		}
996		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
997		if (ctrl_conn) {
998			if (warning_displayed)
999				printf("Connection established.\n");
1000			break;
1001		}
1002
1003		if (!interactive) {
1004			perror("Failed to connect to hostapd - "
1005			       "wpa_ctrl_open");
1006			return -1;
1007		}
1008
1009		if (!warning_displayed) {
1010			printf("Could not connect to hostapd - re-trying\n");
1011			warning_displayed = 1;
1012		}
1013		os_sleep(1, 0);
1014		continue;
1015	}
1016
1017	signal(SIGINT, hostapd_cli_terminate);
1018	signal(SIGTERM, hostapd_cli_terminate);
1019	signal(SIGALRM, hostapd_cli_alarm);
1020
1021	if (interactive || action_file) {
1022		if (wpa_ctrl_attach(ctrl_conn) == 0) {
1023			hostapd_cli_attached = 1;
1024		} else {
1025			printf("Warning: Failed to attach to hostapd.\n");
1026			if (action_file)
1027				return -1;
1028		}
1029	}
1030
1031	if (daemonize && os_daemonize(pid_file))
1032		return -1;
1033
1034	if (interactive)
1035		hostapd_cli_interactive();
1036	else if (action_file)
1037		hostapd_cli_action(ctrl_conn);
1038	else
1039		wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1040
1041	os_free(ctrl_ifname);
1042	hostapd_cli_cleanup();
1043	return 0;
1044}
1045