hostapd_cli.c revision 281806
1/*
2 * hostapd - command line interface for hostapd daemon
3 * Copyright (c) 2004-2015, 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-2015, 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"   wps_cancel           cancel the pending WPS operation\n"
75#ifdef CONFIG_WPS_NFC
76"   wps_nfc_tag_read <hexdump>  report read NFC tag with WPS data\n"
77"   wps_nfc_config_token <WPS/NDEF>  build NFC configuration token\n"
78"   wps_nfc_token <WPS/NDEF/enable/disable>  manager NFC password token\n"
79#endif /* CONFIG_WPS_NFC */
80"   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
81"   wps_config <SSID> <auth> <encr> <key>  configure AP\n"
82"   wps_get_status       show current WPS status\n"
83#endif /* CONFIG_WPS */
84"   get_config           show current configuration\n"
85"   help                 show this usage help\n"
86"   interface [ifname]   show interfaces/select interface\n"
87"   level <debug level>  change debug level\n"
88"   license              show full hostapd_cli license\n"
89"   quit                 exit hostapd_cli\n";
90
91static struct wpa_ctrl *ctrl_conn;
92static int hostapd_cli_quit = 0;
93static int hostapd_cli_attached = 0;
94
95#ifndef CONFIG_CTRL_IFACE_DIR
96#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
97#endif /* CONFIG_CTRL_IFACE_DIR */
98static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
99
100static char *ctrl_ifname = NULL;
101static const char *pid_file = NULL;
102static const char *action_file = NULL;
103static int ping_interval = 5;
104static int interactive = 0;
105
106
107static void usage(void)
108{
109	fprintf(stderr, "%s\n", hostapd_cli_version);
110	fprintf(stderr,
111		"\n"
112		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
113		"[-a<path>] \\\n"
114		"                   [-G<ping interval>] [command..]\n"
115		"\n"
116		"Options:\n"
117		"   -h           help (show this usage text)\n"
118		"   -v           shown version information\n"
119		"   -p<path>     path to find control sockets (default: "
120		"/var/run/hostapd)\n"
121		"   -a<file>     run in daemon mode executing the action file "
122		"based on events\n"
123		"                from hostapd\n"
124		"   -B           run a daemon in the background\n"
125		"   -i<ifname>   Interface to listen on (default: first "
126		"interface found in the\n"
127		"                socket path)\n\n"
128		"%s",
129		commands_help);
130}
131
132
133static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
134{
135	char *cfile;
136	int flen;
137
138	if (ifname == NULL)
139		return NULL;
140
141	flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
142	cfile = malloc(flen);
143	if (cfile == NULL)
144		return NULL;
145	snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
146
147	ctrl_conn = wpa_ctrl_open(cfile);
148	free(cfile);
149	return ctrl_conn;
150}
151
152
153static void hostapd_cli_close_connection(void)
154{
155	if (ctrl_conn == NULL)
156		return;
157
158	if (hostapd_cli_attached) {
159		wpa_ctrl_detach(ctrl_conn);
160		hostapd_cli_attached = 0;
161	}
162	wpa_ctrl_close(ctrl_conn);
163	ctrl_conn = NULL;
164}
165
166
167static void hostapd_cli_msg_cb(char *msg, size_t len)
168{
169	printf("%s\n", msg);
170}
171
172
173static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
174{
175	char buf[4096];
176	size_t len;
177	int ret;
178
179	if (ctrl_conn == NULL) {
180		printf("Not connected to hostapd - command dropped.\n");
181		return -1;
182	}
183	len = sizeof(buf) - 1;
184	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
185			       hostapd_cli_msg_cb);
186	if (ret == -2) {
187		printf("'%s' command timed out.\n", cmd);
188		return -2;
189	} else if (ret < 0) {
190		printf("'%s' command failed.\n", cmd);
191		return -1;
192	}
193	if (print) {
194		buf[len] = '\0';
195		printf("%s", buf);
196	}
197	return 0;
198}
199
200
201static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
202{
203	return _wpa_ctrl_command(ctrl, cmd, 1);
204}
205
206
207static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
208{
209	return wpa_ctrl_command(ctrl, "PING");
210}
211
212
213static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
214{
215	return wpa_ctrl_command(ctrl, "RELOG");
216}
217
218
219static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
220{
221	if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
222		return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
223	return wpa_ctrl_command(ctrl, "STATUS");
224}
225
226
227static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
228{
229	if (argc > 0) {
230		char buf[100];
231		os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]);
232		return wpa_ctrl_command(ctrl, buf);
233	}
234	return wpa_ctrl_command(ctrl, "MIB");
235}
236
237
238static int hostapd_cli_exec(const char *program, const char *arg1,
239			    const char *arg2)
240{
241	char *arg;
242	size_t len;
243	int res;
244
245	len = os_strlen(arg1) + os_strlen(arg2) + 2;
246	arg = os_malloc(len);
247	if (arg == NULL)
248		return -1;
249	os_snprintf(arg, len, "%s %s", arg1, arg2);
250	res = os_exec(program, arg, 1);
251	os_free(arg);
252
253	return res;
254}
255
256
257static void hostapd_cli_action_process(char *msg, size_t len)
258{
259	const char *pos;
260
261	pos = msg;
262	if (*pos == '<') {
263		pos = os_strchr(pos, '>');
264		if (pos)
265			pos++;
266		else
267			pos = msg;
268	}
269
270	hostapd_cli_exec(action_file, ctrl_ifname, pos);
271}
272
273
274static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
275{
276	char buf[64];
277	if (argc < 1) {
278		printf("Invalid 'sta' command - at least one argument, STA "
279		       "address, is required.\n");
280		return -1;
281	}
282	if (argc > 1)
283		snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]);
284	else
285		snprintf(buf, sizeof(buf), "STA %s", argv[0]);
286	return wpa_ctrl_command(ctrl, buf);
287}
288
289
290static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
291				   char *argv[])
292{
293	char buf[64];
294	if (argc != 1) {
295		printf("Invalid 'new_sta' command - exactly one argument, STA "
296		       "address, is required.\n");
297		return -1;
298	}
299	snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
300	return wpa_ctrl_command(ctrl, buf);
301}
302
303
304static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
305					  char *argv[])
306{
307	char buf[64];
308	if (argc < 1) {
309		printf("Invalid 'deauthenticate' command - exactly one "
310		       "argument, STA address, is required.\n");
311		return -1;
312	}
313	if (argc > 1)
314		os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
315			    argv[0], argv[1]);
316	else
317		os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
318	return wpa_ctrl_command(ctrl, buf);
319}
320
321
322static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
323					char *argv[])
324{
325	char buf[64];
326	if (argc < 1) {
327		printf("Invalid 'disassociate' command - exactly one "
328		       "argument, STA address, is required.\n");
329		return -1;
330	}
331	if (argc > 1)
332		os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
333			    argv[0], argv[1]);
334	else
335		os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
336	return wpa_ctrl_command(ctrl, buf);
337}
338
339
340#ifdef CONFIG_IEEE80211W
341static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
342				    char *argv[])
343{
344	char buf[64];
345	if (argc != 1) {
346		printf("Invalid 'sa_query' command - exactly one argument, "
347		       "STA address, is required.\n");
348		return -1;
349	}
350	snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
351	return wpa_ctrl_command(ctrl, buf);
352}
353#endif /* CONFIG_IEEE80211W */
354
355
356#ifdef CONFIG_WPS
357static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
358				   char *argv[])
359{
360	char buf[256];
361	if (argc < 2) {
362		printf("Invalid 'wps_pin' command - at least two arguments, "
363		       "UUID and PIN, are required.\n");
364		return -1;
365	}
366	if (argc > 3)
367		snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
368			 argv[0], argv[1], argv[2], argv[3]);
369	else if (argc > 2)
370		snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
371			 argv[0], argv[1], argv[2]);
372	else
373		snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
374	return wpa_ctrl_command(ctrl, buf);
375}
376
377
378static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
379					 char *argv[])
380{
381	char cmd[256];
382	int res;
383
384	if (argc != 1 && argc != 2) {
385		printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
386		       "- PIN to be verified\n");
387		return -1;
388	}
389
390	if (argc == 2)
391		res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
392				  argv[0], argv[1]);
393	else
394		res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
395				  argv[0]);
396	if (os_snprintf_error(sizeof(cmd), res)) {
397		printf("Too long WPS_CHECK_PIN command.\n");
398		return -1;
399	}
400	return wpa_ctrl_command(ctrl, cmd);
401}
402
403
404static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
405				   char *argv[])
406{
407	return wpa_ctrl_command(ctrl, "WPS_PBC");
408}
409
410
411static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
412				      char *argv[])
413{
414	return wpa_ctrl_command(ctrl, "WPS_CANCEL");
415}
416
417
418#ifdef CONFIG_WPS_NFC
419static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
420					    char *argv[])
421{
422	int ret;
423	char *buf;
424	size_t buflen;
425
426	if (argc != 1) {
427		printf("Invalid 'wps_nfc_tag_read' command - one argument "
428		       "is required.\n");
429		return -1;
430	}
431
432	buflen = 18 + os_strlen(argv[0]);
433	buf = os_malloc(buflen);
434	if (buf == NULL)
435		return -1;
436	os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
437
438	ret = wpa_ctrl_command(ctrl, buf);
439	os_free(buf);
440
441	return ret;
442}
443
444
445static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
446						int argc, char *argv[])
447{
448	char cmd[64];
449	int res;
450
451	if (argc != 1) {
452		printf("Invalid 'wps_nfc_config_token' command - one argument "
453		       "is required.\n");
454		return -1;
455	}
456
457	res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
458			  argv[0]);
459	if (os_snprintf_error(sizeof(cmd), res)) {
460		printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
461		return -1;
462	}
463	return wpa_ctrl_command(ctrl, cmd);
464}
465
466
467static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
468					 int argc, char *argv[])
469{
470	char cmd[64];
471	int res;
472
473	if (argc != 1) {
474		printf("Invalid 'wps_nfc_token' command - one argument is "
475		       "required.\n");
476		return -1;
477	}
478
479	res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
480	if (os_snprintf_error(sizeof(cmd), res)) {
481		printf("Too long WPS_NFC_TOKEN command.\n");
482		return -1;
483	}
484	return wpa_ctrl_command(ctrl, cmd);
485}
486
487
488static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
489						int argc, char *argv[])
490{
491	char cmd[64];
492	int res;
493
494	if (argc != 2) {
495		printf("Invalid 'nfc_get_handover_sel' command - two arguments "
496		       "are required.\n");
497		return -1;
498	}
499
500	res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
501			  argv[0], argv[1]);
502	if (os_snprintf_error(sizeof(cmd), res)) {
503		printf("Too long NFC_GET_HANDOVER_SEL command.\n");
504		return -1;
505	}
506	return wpa_ctrl_command(ctrl, cmd);
507}
508
509#endif /* CONFIG_WPS_NFC */
510
511
512static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
513				      char *argv[])
514{
515	char buf[64];
516	if (argc < 1) {
517		printf("Invalid 'wps_ap_pin' command - at least one argument "
518		       "is required.\n");
519		return -1;
520	}
521	if (argc > 2)
522		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
523			 argv[0], argv[1], argv[2]);
524	else if (argc > 1)
525		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
526			 argv[0], argv[1]);
527	else
528		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
529	return wpa_ctrl_command(ctrl, buf);
530}
531
532
533static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
534					  char *argv[])
535{
536	return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
537}
538
539
540static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
541				      char *argv[])
542{
543	char buf[256];
544	char ssid_hex[2 * 32 + 1];
545	char key_hex[2 * 64 + 1];
546	int i;
547
548	if (argc < 1) {
549		printf("Invalid 'wps_config' command - at least two arguments "
550		       "are required.\n");
551		return -1;
552	}
553
554	ssid_hex[0] = '\0';
555	for (i = 0; i < 32; i++) {
556		if (argv[0][i] == '\0')
557			break;
558		os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
559	}
560
561	key_hex[0] = '\0';
562	if (argc > 3) {
563		for (i = 0; i < 64; i++) {
564			if (argv[3][i] == '\0')
565				break;
566			os_snprintf(&key_hex[i * 2], 3, "%02x",
567				    argv[3][i]);
568		}
569	}
570
571	if (argc > 3)
572		snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
573			 ssid_hex, argv[1], argv[2], key_hex);
574	else if (argc > 2)
575		snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
576			 ssid_hex, argv[1], argv[2]);
577	else
578		snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
579			 ssid_hex, argv[1]);
580	return wpa_ctrl_command(ctrl, buf);
581}
582#endif /* CONFIG_WPS */
583
584
585static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
586					     char *argv[])
587{
588	char buf[300];
589	int res;
590
591	if (argc < 2) {
592		printf("Invalid 'disassoc_imminent' command - two arguments "
593		       "(STA addr and Disassociation Timer) are needed\n");
594		return -1;
595	}
596
597	res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
598			  argv[0], argv[1]);
599	if (os_snprintf_error(sizeof(buf), res))
600		return -1;
601	return wpa_ctrl_command(ctrl, buf);
602}
603
604
605static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
606					char *argv[])
607{
608	char buf[300];
609	int res;
610
611	if (argc < 3) {
612		printf("Invalid 'ess_disassoc' command - three arguments (STA "
613		       "addr, disassoc timer, and URL) are needed\n");
614		return -1;
615	}
616
617	res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
618			  argv[0], argv[1], argv[2]);
619	if (os_snprintf_error(sizeof(buf), res))
620		return -1;
621	return wpa_ctrl_command(ctrl, buf);
622}
623
624
625static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
626				      char *argv[])
627{
628	char buf[2000], *tmp;
629	int res, i, total;
630
631	if (argc < 1) {
632		printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n");
633		return -1;
634	}
635
636	res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]);
637	if (os_snprintf_error(sizeof(buf), res))
638		return -1;
639
640	total = res;
641	for (i = 1; i < argc; i++) {
642		tmp = &buf[total];
643		res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]);
644		if (os_snprintf_error(sizeof(buf) - total, res))
645			return -1;
646		total += res;
647	}
648	return wpa_ctrl_command(ctrl, buf);
649}
650
651
652static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
653				      char *argv[])
654{
655	return wpa_ctrl_command(ctrl, "GET_CONFIG");
656}
657
658
659static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
660				char *addr, size_t addr_len)
661{
662	char buf[4096], *pos;
663	size_t len;
664	int ret;
665
666	if (ctrl_conn == NULL) {
667		printf("Not connected to hostapd - command dropped.\n");
668		return -1;
669	}
670	len = sizeof(buf) - 1;
671	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
672			       hostapd_cli_msg_cb);
673	if (ret == -2) {
674		printf("'%s' command timed out.\n", cmd);
675		return -2;
676	} else if (ret < 0) {
677		printf("'%s' command failed.\n", cmd);
678		return -1;
679	}
680
681	buf[len] = '\0';
682	if (memcmp(buf, "FAIL", 4) == 0)
683		return -1;
684	printf("%s", buf);
685
686	pos = buf;
687	while (*pos != '\0' && *pos != '\n')
688		pos++;
689	*pos = '\0';
690	os_strlcpy(addr, buf, addr_len);
691	return 0;
692}
693
694
695static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
696				   char *argv[])
697{
698	char addr[32], cmd[64];
699
700	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
701		return 0;
702	do {
703		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
704	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
705
706	return -1;
707}
708
709
710static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
711{
712	printf("%s", commands_help);
713	return 0;
714}
715
716
717static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
718				   char *argv[])
719{
720	printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
721	return 0;
722}
723
724
725static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
726					   int argc, char *argv[])
727{
728	char buf[200];
729	int res;
730
731	if (argc != 1) {
732		printf("Invalid 'set_qos_map_set' command - "
733		       "one argument (comma delimited QoS map set) "
734		       "is needed\n");
735		return -1;
736	}
737
738	res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
739	if (os_snprintf_error(sizeof(buf), res))
740		return -1;
741	return wpa_ctrl_command(ctrl, buf);
742}
743
744
745static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
746					     int argc, char *argv[])
747{
748	char buf[50];
749	int res;
750
751	if (argc != 1) {
752		printf("Invalid 'send_qos_map_conf' command - "
753		       "one argument (STA addr) is needed\n");
754		return -1;
755	}
756
757	res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
758	if (os_snprintf_error(sizeof(buf), res))
759		return -1;
760	return wpa_ctrl_command(ctrl, buf);
761}
762
763
764static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
765					  char *argv[])
766{
767	char buf[300];
768	int res;
769
770	if (argc < 2) {
771		printf("Invalid 'hs20_wnm_notif' command - two arguments (STA "
772		       "addr and URL) are needed\n");
773		return -1;
774	}
775
776	res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
777			  argv[0], argv[1]);
778	if (os_snprintf_error(sizeof(buf), res))
779		return -1;
780	return wpa_ctrl_command(ctrl, buf);
781}
782
783
784static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
785					   char *argv[])
786{
787	char buf[300];
788	int res;
789
790	if (argc < 3) {
791		printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
792		return -1;
793	}
794
795	if (argc > 3)
796		res = os_snprintf(buf, sizeof(buf),
797				  "HS20_DEAUTH_REQ %s %s %s %s",
798				  argv[0], argv[1], argv[2], argv[3]);
799	else
800		res = os_snprintf(buf, sizeof(buf),
801				  "HS20_DEAUTH_REQ %s %s %s",
802				  argv[0], argv[1], argv[2]);
803	if (os_snprintf_error(sizeof(buf), res))
804		return -1;
805	return wpa_ctrl_command(ctrl, buf);
806}
807
808
809static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
810{
811	hostapd_cli_quit = 1;
812	if (interactive)
813		eloop_terminate();
814	return 0;
815}
816
817
818static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
819{
820	char cmd[256];
821	if (argc != 1) {
822		printf("Invalid LEVEL command: needs one argument (debug "
823		       "level)\n");
824		return 0;
825	}
826	snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
827	return wpa_ctrl_command(ctrl, cmd);
828}
829
830
831static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
832{
833	struct dirent *dent;
834	DIR *dir;
835
836	dir = opendir(ctrl_iface_dir);
837	if (dir == NULL) {
838		printf("Control interface directory '%s' could not be "
839		       "openned.\n", ctrl_iface_dir);
840		return;
841	}
842
843	printf("Available interfaces:\n");
844	while ((dent = readdir(dir))) {
845		if (strcmp(dent->d_name, ".") == 0 ||
846		    strcmp(dent->d_name, "..") == 0)
847			continue;
848		printf("%s\n", dent->d_name);
849	}
850	closedir(dir);
851}
852
853
854static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
855				     char *argv[])
856{
857	if (argc < 1) {
858		hostapd_cli_list_interfaces(ctrl);
859		return 0;
860	}
861
862	hostapd_cli_close_connection();
863	os_free(ctrl_ifname);
864	ctrl_ifname = os_strdup(argv[0]);
865	if (ctrl_ifname == NULL)
866		return -1;
867
868	if (hostapd_cli_open_connection(ctrl_ifname)) {
869		printf("Connected to interface '%s.\n", ctrl_ifname);
870		if (wpa_ctrl_attach(ctrl_conn) == 0) {
871			hostapd_cli_attached = 1;
872		} else {
873			printf("Warning: Failed to attach to "
874			       "hostapd.\n");
875		}
876	} else {
877		printf("Could not connect to interface '%s' - re-trying\n",
878			ctrl_ifname);
879	}
880	return 0;
881}
882
883
884static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
885{
886	char cmd[256];
887	int res;
888
889	if (argc != 2) {
890		printf("Invalid SET command: needs two arguments (variable "
891		       "name and value)\n");
892		return -1;
893	}
894
895	res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
896	if (os_snprintf_error(sizeof(cmd), res)) {
897		printf("Too long SET command.\n");
898		return -1;
899	}
900	return wpa_ctrl_command(ctrl, cmd);
901}
902
903
904static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
905{
906	char cmd[256];
907	int res;
908
909	if (argc != 1) {
910		printf("Invalid GET command: needs one argument (variable "
911		       "name)\n");
912		return -1;
913	}
914
915	res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
916	if (os_snprintf_error(sizeof(cmd), res)) {
917		printf("Too long GET command.\n");
918		return -1;
919	}
920	return wpa_ctrl_command(ctrl, cmd);
921}
922
923
924static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
925				       int argc, char *argv[])
926{
927	char cmd[256];
928	int res;
929	int i;
930	char *tmp;
931	int total;
932
933	if (argc < 2) {
934		printf("Invalid chan_switch command: needs at least two "
935		       "arguments (count and freq)\n"
936		       "usage: <cs_count> <freq> [sec_channel_offset=] "
937		       "[center_freq1=] [center_freq2=] [bandwidth=] "
938		       "[blocktx] [ht|vht]\n");
939		return -1;
940	}
941
942	res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
943			  argv[0], argv[1]);
944	if (os_snprintf_error(sizeof(cmd), res)) {
945		printf("Too long CHAN_SWITCH command.\n");
946		return -1;
947	}
948
949	total = res;
950	for (i = 2; i < argc; i++) {
951		tmp = cmd + total;
952		res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
953		if (os_snprintf_error(sizeof(cmd) - total, res)) {
954			printf("Too long CHAN_SWITCH command.\n");
955			return -1;
956		}
957		total += res;
958	}
959	return wpa_ctrl_command(ctrl, cmd);
960}
961
962
963static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
964				      char *argv[])
965{
966	return wpa_ctrl_command(ctrl, "ENABLE");
967}
968
969
970static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
971				      char *argv[])
972{
973	return wpa_ctrl_command(ctrl, "RELOAD");
974}
975
976
977static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
978				      char *argv[])
979{
980	return wpa_ctrl_command(ctrl, "DISABLE");
981}
982
983
984static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
985{
986	char cmd[256];
987	int res;
988
989	if (argc < 2 || argc > 3) {
990		printf("Invalid vendor command\n"
991		       "usage: <vendor id> <command id> [<hex formatted command argument>]\n");
992		return -1;
993	}
994
995	res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
996			  argc == 3 ? argv[2] : "");
997	if (os_snprintf_error(sizeof(cmd), res)) {
998		printf("Too long VENDOR command.\n");
999		return -1;
1000	}
1001	return wpa_ctrl_command(ctrl, cmd);
1002}
1003
1004
1005static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
1006				     char *argv[])
1007{
1008	return wpa_ctrl_command(ctrl, "ERP_FLUSH");
1009}
1010
1011
1012struct hostapd_cli_cmd {
1013	const char *cmd;
1014	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
1015};
1016
1017static struct hostapd_cli_cmd hostapd_cli_commands[] = {
1018	{ "ping", hostapd_cli_cmd_ping },
1019	{ "mib", hostapd_cli_cmd_mib },
1020	{ "relog", hostapd_cli_cmd_relog },
1021	{ "status", hostapd_cli_cmd_status },
1022	{ "sta", hostapd_cli_cmd_sta },
1023	{ "all_sta", hostapd_cli_cmd_all_sta },
1024	{ "new_sta", hostapd_cli_cmd_new_sta },
1025	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
1026	{ "disassociate", hostapd_cli_cmd_disassociate },
1027#ifdef CONFIG_IEEE80211W
1028	{ "sa_query", hostapd_cli_cmd_sa_query },
1029#endif /* CONFIG_IEEE80211W */
1030#ifdef CONFIG_WPS
1031	{ "wps_pin", hostapd_cli_cmd_wps_pin },
1032	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
1033	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
1034	{ "wps_cancel", hostapd_cli_cmd_wps_cancel },
1035#ifdef CONFIG_WPS_NFC
1036	{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
1037	{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
1038	{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
1039	{ "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
1040#endif /* CONFIG_WPS_NFC */
1041	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
1042	{ "wps_config", hostapd_cli_cmd_wps_config },
1043	{ "wps_get_status", hostapd_cli_cmd_wps_get_status },
1044#endif /* CONFIG_WPS */
1045	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
1046	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
1047	{ "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
1048	{ "get_config", hostapd_cli_cmd_get_config },
1049	{ "help", hostapd_cli_cmd_help },
1050	{ "interface", hostapd_cli_cmd_interface },
1051	{ "level", hostapd_cli_cmd_level },
1052	{ "license", hostapd_cli_cmd_license },
1053	{ "quit", hostapd_cli_cmd_quit },
1054	{ "set", hostapd_cli_cmd_set },
1055	{ "get", hostapd_cli_cmd_get },
1056	{ "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
1057	{ "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
1058	{ "chan_switch", hostapd_cli_cmd_chan_switch },
1059	{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
1060	{ "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
1061	{ "vendor", hostapd_cli_cmd_vendor },
1062	{ "enable", hostapd_cli_cmd_enable },
1063	{ "reload", hostapd_cli_cmd_reload },
1064	{ "disable", hostapd_cli_cmd_disable },
1065	{ "erp_flush", hostapd_cli_cmd_erp_flush },
1066	{ NULL, NULL }
1067};
1068
1069
1070static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
1071{
1072	struct hostapd_cli_cmd *cmd, *match = NULL;
1073	int count;
1074
1075	count = 0;
1076	cmd = hostapd_cli_commands;
1077	while (cmd->cmd) {
1078		if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
1079			match = cmd;
1080			if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
1081				/* we have an exact match */
1082				count = 1;
1083				break;
1084			}
1085			count++;
1086		}
1087		cmd++;
1088	}
1089
1090	if (count > 1) {
1091		printf("Ambiguous command '%s'; possible commands:", argv[0]);
1092		cmd = hostapd_cli_commands;
1093		while (cmd->cmd) {
1094			if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
1095			    0) {
1096				printf(" %s", cmd->cmd);
1097			}
1098			cmd++;
1099		}
1100		printf("\n");
1101	} else if (count == 0) {
1102		printf("Unknown command '%s'\n", argv[0]);
1103	} else {
1104		match->handler(ctrl, argc - 1, &argv[1]);
1105	}
1106}
1107
1108
1109static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
1110				     int action_monitor)
1111{
1112	int first = 1;
1113	if (ctrl_conn == NULL)
1114		return;
1115	while (wpa_ctrl_pending(ctrl)) {
1116		char buf[256];
1117		size_t len = sizeof(buf) - 1;
1118		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
1119			buf[len] = '\0';
1120			if (action_monitor)
1121				hostapd_cli_action_process(buf, len);
1122			else {
1123				if (in_read && first)
1124					printf("\n");
1125				first = 0;
1126				printf("%s\n", buf);
1127			}
1128		} else {
1129			printf("Could not read pending message.\n");
1130			break;
1131		}
1132	}
1133}
1134
1135
1136#define max_args 10
1137
1138static int tokenize_cmd(char *cmd, char *argv[])
1139{
1140	char *pos;
1141	int argc = 0;
1142
1143	pos = cmd;
1144	for (;;) {
1145		while (*pos == ' ')
1146			pos++;
1147		if (*pos == '\0')
1148			break;
1149		argv[argc] = pos;
1150		argc++;
1151		if (argc == max_args)
1152			break;
1153		if (*pos == '"') {
1154			char *pos2 = os_strrchr(pos, '"');
1155			if (pos2)
1156				pos = pos2 + 1;
1157		}
1158		while (*pos != '\0' && *pos != ' ')
1159			pos++;
1160		if (*pos == ' ')
1161			*pos++ = '\0';
1162	}
1163
1164	return argc;
1165}
1166
1167
1168static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
1169{
1170	if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
1171		printf("Connection to hostapd lost - trying to reconnect\n");
1172		hostapd_cli_close_connection();
1173	}
1174	if (!ctrl_conn) {
1175		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1176		if (ctrl_conn) {
1177			printf("Connection to hostapd re-established\n");
1178			if (wpa_ctrl_attach(ctrl_conn) == 0) {
1179				hostapd_cli_attached = 1;
1180			} else {
1181				printf("Warning: Failed to attach to "
1182				       "hostapd.\n");
1183			}
1184		}
1185	}
1186	if (ctrl_conn)
1187		hostapd_cli_recv_pending(ctrl_conn, 1, 0);
1188	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1189}
1190
1191
1192static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
1193{
1194	eloop_terminate();
1195}
1196
1197
1198static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
1199{
1200	char *argv[max_args];
1201	int argc;
1202	argc = tokenize_cmd(cmd, argv);
1203	if (argc)
1204		wpa_request(ctrl_conn, argc, argv);
1205}
1206
1207
1208static void hostapd_cli_edit_eof_cb(void *ctx)
1209{
1210	eloop_terminate();
1211}
1212
1213
1214static void hostapd_cli_interactive(void)
1215{
1216	printf("\nInteractive mode\n\n");
1217
1218	eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
1219	edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
1220		  NULL, NULL, NULL, NULL);
1221	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1222
1223	eloop_run();
1224
1225	edit_deinit(NULL, NULL);
1226	eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
1227}
1228
1229
1230static void hostapd_cli_cleanup(void)
1231{
1232	hostapd_cli_close_connection();
1233	if (pid_file)
1234		os_daemonize_terminate(pid_file);
1235
1236	os_program_deinit();
1237}
1238
1239
1240static void hostapd_cli_action(struct wpa_ctrl *ctrl)
1241{
1242	fd_set rfds;
1243	int fd, res;
1244	struct timeval tv;
1245	char buf[256];
1246	size_t len;
1247
1248	fd = wpa_ctrl_get_fd(ctrl);
1249
1250	while (!hostapd_cli_quit) {
1251		FD_ZERO(&rfds);
1252		FD_SET(fd, &rfds);
1253		tv.tv_sec = ping_interval;
1254		tv.tv_usec = 0;
1255		res = select(fd + 1, &rfds, NULL, NULL, &tv);
1256		if (res < 0 && errno != EINTR) {
1257			perror("select");
1258			break;
1259		}
1260
1261		if (FD_ISSET(fd, &rfds))
1262			hostapd_cli_recv_pending(ctrl, 0, 1);
1263		else {
1264			len = sizeof(buf) - 1;
1265			if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1266					     hostapd_cli_action_process) < 0 ||
1267			    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1268				printf("hostapd did not reply to PING "
1269				       "command - exiting\n");
1270				break;
1271			}
1272		}
1273	}
1274}
1275
1276
1277int main(int argc, char *argv[])
1278{
1279	int warning_displayed = 0;
1280	int c;
1281	int daemonize = 0;
1282
1283	if (os_program_init())
1284		return -1;
1285
1286	for (;;) {
1287		c = getopt(argc, argv, "a:BhG:i:p:v");
1288		if (c < 0)
1289			break;
1290		switch (c) {
1291		case 'a':
1292			action_file = optarg;
1293			break;
1294		case 'B':
1295			daemonize = 1;
1296			break;
1297		case 'G':
1298			ping_interval = atoi(optarg);
1299			break;
1300		case 'h':
1301			usage();
1302			return 0;
1303		case 'v':
1304			printf("%s\n", hostapd_cli_version);
1305			return 0;
1306		case 'i':
1307			os_free(ctrl_ifname);
1308			ctrl_ifname = os_strdup(optarg);
1309			break;
1310		case 'p':
1311			ctrl_iface_dir = optarg;
1312			break;
1313		default:
1314			usage();
1315			return -1;
1316		}
1317	}
1318
1319	interactive = (argc == optind) && (action_file == NULL);
1320
1321	if (interactive) {
1322		printf("%s\n\n%s\n\n", hostapd_cli_version,
1323		       hostapd_cli_license);
1324	}
1325
1326	if (eloop_init())
1327		return -1;
1328
1329	for (;;) {
1330		if (ctrl_ifname == NULL) {
1331			struct dirent *dent;
1332			DIR *dir = opendir(ctrl_iface_dir);
1333			if (dir) {
1334				while ((dent = readdir(dir))) {
1335					if (os_strcmp(dent->d_name, ".") == 0
1336					    ||
1337					    os_strcmp(dent->d_name, "..") == 0)
1338						continue;
1339					printf("Selected interface '%s'\n",
1340					       dent->d_name);
1341					ctrl_ifname = os_strdup(dent->d_name);
1342					break;
1343				}
1344				closedir(dir);
1345			}
1346		}
1347		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1348		if (ctrl_conn) {
1349			if (warning_displayed)
1350				printf("Connection established.\n");
1351			break;
1352		}
1353
1354		if (!interactive) {
1355			perror("Failed to connect to hostapd - "
1356			       "wpa_ctrl_open");
1357			return -1;
1358		}
1359
1360		if (!warning_displayed) {
1361			printf("Could not connect to hostapd - re-trying\n");
1362			warning_displayed = 1;
1363		}
1364		os_sleep(1, 0);
1365		continue;
1366	}
1367
1368	if (interactive || action_file) {
1369		if (wpa_ctrl_attach(ctrl_conn) == 0) {
1370			hostapd_cli_attached = 1;
1371		} else {
1372			printf("Warning: Failed to attach to hostapd.\n");
1373			if (action_file)
1374				return -1;
1375		}
1376	}
1377
1378	if (daemonize && os_daemonize(pid_file))
1379		return -1;
1380
1381	if (interactive)
1382		hostapd_cli_interactive();
1383	else if (action_file)
1384		hostapd_cli_action(ctrl_conn);
1385	else
1386		wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1387
1388	os_free(ctrl_ifname);
1389	eloop_destroy();
1390	hostapd_cli_cleanup();
1391	return 0;
1392}
1393