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