1/*
2 * hostapd - command line interface for hostapd daemon
3 * Copyright (c) 2004-2014, 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-2014, 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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
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 (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
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 (res < 0 || res >= (int) sizeof(buf))
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 (res < 0 || res >= (int) sizeof(buf))
620		return -1;
621	return wpa_ctrl_command(ctrl, buf);
622}
623
624
625static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
626				      char *argv[])
627{
628	return wpa_ctrl_command(ctrl, "GET_CONFIG");
629}
630
631
632static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
633				char *addr, size_t addr_len)
634{
635	char buf[4096], *pos;
636	size_t len;
637	int ret;
638
639	if (ctrl_conn == NULL) {
640		printf("Not connected to hostapd - command dropped.\n");
641		return -1;
642	}
643	len = sizeof(buf) - 1;
644	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
645			       hostapd_cli_msg_cb);
646	if (ret == -2) {
647		printf("'%s' command timed out.\n", cmd);
648		return -2;
649	} else if (ret < 0) {
650		printf("'%s' command failed.\n", cmd);
651		return -1;
652	}
653
654	buf[len] = '\0';
655	if (memcmp(buf, "FAIL", 4) == 0)
656		return -1;
657	printf("%s", buf);
658
659	pos = buf;
660	while (*pos != '\0' && *pos != '\n')
661		pos++;
662	*pos = '\0';
663	os_strlcpy(addr, buf, addr_len);
664	return 0;
665}
666
667
668static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
669				   char *argv[])
670{
671	char addr[32], cmd[64];
672
673	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
674		return 0;
675	do {
676		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
677	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
678
679	return -1;
680}
681
682
683static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
684{
685	printf("%s", commands_help);
686	return 0;
687}
688
689
690static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
691				   char *argv[])
692{
693	printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
694	return 0;
695}
696
697
698static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
699					   int argc, char *argv[])
700{
701	char buf[200];
702	int res;
703
704	if (argc != 1) {
705		printf("Invalid 'set_qos_map_set' command - "
706		       "one argument (comma delimited QoS map set) "
707		       "is needed\n");
708		return -1;
709	}
710
711	res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
712	if (res < 0 || res >= (int) sizeof(buf))
713		return -1;
714	return wpa_ctrl_command(ctrl, buf);
715}
716
717
718static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
719					     int argc, char *argv[])
720{
721	char buf[50];
722	int res;
723
724	if (argc != 1) {
725		printf("Invalid 'send_qos_map_conf' command - "
726		       "one argument (STA addr) is needed\n");
727		return -1;
728	}
729
730	res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
731	if (res < 0 || res >= (int) sizeof(buf))
732		return -1;
733	return wpa_ctrl_command(ctrl, buf);
734}
735
736
737static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
738					  char *argv[])
739{
740	char buf[300];
741	int res;
742
743	if (argc < 2) {
744		printf("Invalid 'hs20_wnm_notif' command - two arguments (STA "
745		       "addr and URL) are needed\n");
746		return -1;
747	}
748
749	res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
750			  argv[0], argv[1]);
751	if (res < 0 || res >= (int) sizeof(buf))
752		return -1;
753	return wpa_ctrl_command(ctrl, buf);
754}
755
756
757static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
758					   char *argv[])
759{
760	char buf[300];
761	int res;
762
763	if (argc < 3) {
764		printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
765		return -1;
766	}
767
768	if (argc > 3)
769		res = os_snprintf(buf, sizeof(buf),
770				  "HS20_DEAUTH_REQ %s %s %s %s",
771				  argv[0], argv[1], argv[2], argv[3]);
772	else
773		res = os_snprintf(buf, sizeof(buf),
774				  "HS20_DEAUTH_REQ %s %s %s",
775				  argv[0], argv[1], argv[2]);
776	if (res < 0 || res >= (int) sizeof(buf))
777		return -1;
778	return wpa_ctrl_command(ctrl, buf);
779}
780
781
782static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
783{
784	hostapd_cli_quit = 1;
785	if (interactive)
786		eloop_terminate();
787	return 0;
788}
789
790
791static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
792{
793	char cmd[256];
794	if (argc != 1) {
795		printf("Invalid LEVEL command: needs one argument (debug "
796		       "level)\n");
797		return 0;
798	}
799	snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
800	return wpa_ctrl_command(ctrl, cmd);
801}
802
803
804static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
805{
806	struct dirent *dent;
807	DIR *dir;
808
809	dir = opendir(ctrl_iface_dir);
810	if (dir == NULL) {
811		printf("Control interface directory '%s' could not be "
812		       "openned.\n", ctrl_iface_dir);
813		return;
814	}
815
816	printf("Available interfaces:\n");
817	while ((dent = readdir(dir))) {
818		if (strcmp(dent->d_name, ".") == 0 ||
819		    strcmp(dent->d_name, "..") == 0)
820			continue;
821		printf("%s\n", dent->d_name);
822	}
823	closedir(dir);
824}
825
826
827static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
828				     char *argv[])
829{
830	if (argc < 1) {
831		hostapd_cli_list_interfaces(ctrl);
832		return 0;
833	}
834
835	hostapd_cli_close_connection();
836	os_free(ctrl_ifname);
837	ctrl_ifname = os_strdup(argv[0]);
838	if (ctrl_ifname == NULL)
839		return -1;
840
841	if (hostapd_cli_open_connection(ctrl_ifname)) {
842		printf("Connected to interface '%s.\n", ctrl_ifname);
843		if (wpa_ctrl_attach(ctrl_conn) == 0) {
844			hostapd_cli_attached = 1;
845		} else {
846			printf("Warning: Failed to attach to "
847			       "hostapd.\n");
848		}
849	} else {
850		printf("Could not connect to interface '%s' - re-trying\n",
851			ctrl_ifname);
852	}
853	return 0;
854}
855
856
857static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
858{
859	char cmd[256];
860	int res;
861
862	if (argc != 2) {
863		printf("Invalid SET command: needs two arguments (variable "
864		       "name and value)\n");
865		return -1;
866	}
867
868	res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
869	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
870		printf("Too long SET command.\n");
871		return -1;
872	}
873	return wpa_ctrl_command(ctrl, cmd);
874}
875
876
877static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
878{
879	char cmd[256];
880	int res;
881
882	if (argc != 1) {
883		printf("Invalid GET command: needs one argument (variable "
884		       "name)\n");
885		return -1;
886	}
887
888	res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
889	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
890		printf("Too long GET command.\n");
891		return -1;
892	}
893	return wpa_ctrl_command(ctrl, cmd);
894}
895
896
897static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
898				       int argc, char *argv[])
899{
900	char cmd[256];
901	int res;
902	int i;
903	char *tmp;
904	int total;
905
906	if (argc < 2) {
907		printf("Invalid chan_switch command: needs at least two "
908		       "arguments (count and freq)\n"
909		       "usage: <cs_count> <freq> [sec_channel_offset=] "
910		       "[center_freq1=] [center_freq2=] [bandwidth=] "
911		       "[blocktx] [ht|vht]\n");
912		return -1;
913	}
914
915	res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
916			  argv[0], argv[1]);
917	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
918		printf("Too long CHAN_SWITCH command.\n");
919		return -1;
920	}
921
922	total = res;
923	for (i = 2; i < argc; i++) {
924		tmp = cmd + total;
925		res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
926		if (res < 0 || (size_t) res >= sizeof(cmd) - total - 1) {
927			printf("Too long CHAN_SWITCH command.\n");
928			return -1;
929		}
930		total += res;
931	}
932	return wpa_ctrl_command(ctrl, cmd);
933}
934
935
936static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
937{
938	char cmd[256];
939	int res;
940
941	if (argc < 2 || argc > 3) {
942		printf("Invalid vendor command\n"
943		       "usage: <vendor id> <command id> [<hex formatted command argument>]\n");
944		return -1;
945	}
946
947	res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
948			  argc == 3 ? argv[2] : "");
949	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
950		printf("Too long VENDOR command.\n");
951		return -1;
952	}
953	return wpa_ctrl_command(ctrl, cmd);
954}
955
956
957struct hostapd_cli_cmd {
958	const char *cmd;
959	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
960};
961
962static struct hostapd_cli_cmd hostapd_cli_commands[] = {
963	{ "ping", hostapd_cli_cmd_ping },
964	{ "mib", hostapd_cli_cmd_mib },
965	{ "relog", hostapd_cli_cmd_relog },
966	{ "status", hostapd_cli_cmd_status },
967	{ "sta", hostapd_cli_cmd_sta },
968	{ "all_sta", hostapd_cli_cmd_all_sta },
969	{ "new_sta", hostapd_cli_cmd_new_sta },
970	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
971	{ "disassociate", hostapd_cli_cmd_disassociate },
972#ifdef CONFIG_IEEE80211W
973	{ "sa_query", hostapd_cli_cmd_sa_query },
974#endif /* CONFIG_IEEE80211W */
975#ifdef CONFIG_WPS
976	{ "wps_pin", hostapd_cli_cmd_wps_pin },
977	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
978	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
979	{ "wps_cancel", hostapd_cli_cmd_wps_cancel },
980#ifdef CONFIG_WPS_NFC
981	{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
982	{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
983	{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
984	{ "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
985#endif /* CONFIG_WPS_NFC */
986	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
987	{ "wps_config", hostapd_cli_cmd_wps_config },
988	{ "wps_get_status", hostapd_cli_cmd_wps_get_status },
989#endif /* CONFIG_WPS */
990	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
991	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
992	{ "get_config", hostapd_cli_cmd_get_config },
993	{ "help", hostapd_cli_cmd_help },
994	{ "interface", hostapd_cli_cmd_interface },
995	{ "level", hostapd_cli_cmd_level },
996	{ "license", hostapd_cli_cmd_license },
997	{ "quit", hostapd_cli_cmd_quit },
998	{ "set", hostapd_cli_cmd_set },
999	{ "get", hostapd_cli_cmd_get },
1000	{ "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
1001	{ "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
1002	{ "chan_switch", hostapd_cli_cmd_chan_switch },
1003	{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
1004	{ "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
1005	{ "vendor", hostapd_cli_cmd_vendor },
1006	{ NULL, NULL }
1007};
1008
1009
1010static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
1011{
1012	struct hostapd_cli_cmd *cmd, *match = NULL;
1013	int count;
1014
1015	count = 0;
1016	cmd = hostapd_cli_commands;
1017	while (cmd->cmd) {
1018		if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
1019			match = cmd;
1020			if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
1021				/* we have an exact match */
1022				count = 1;
1023				break;
1024			}
1025			count++;
1026		}
1027		cmd++;
1028	}
1029
1030	if (count > 1) {
1031		printf("Ambiguous command '%s'; possible commands:", argv[0]);
1032		cmd = hostapd_cli_commands;
1033		while (cmd->cmd) {
1034			if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
1035			    0) {
1036				printf(" %s", cmd->cmd);
1037			}
1038			cmd++;
1039		}
1040		printf("\n");
1041	} else if (count == 0) {
1042		printf("Unknown command '%s'\n", argv[0]);
1043	} else {
1044		match->handler(ctrl, argc - 1, &argv[1]);
1045	}
1046}
1047
1048
1049static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
1050				     int action_monitor)
1051{
1052	int first = 1;
1053	if (ctrl_conn == NULL)
1054		return;
1055	while (wpa_ctrl_pending(ctrl)) {
1056		char buf[256];
1057		size_t len = sizeof(buf) - 1;
1058		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
1059			buf[len] = '\0';
1060			if (action_monitor)
1061				hostapd_cli_action_process(buf, len);
1062			else {
1063				if (in_read && first)
1064					printf("\n");
1065				first = 0;
1066				printf("%s\n", buf);
1067			}
1068		} else {
1069			printf("Could not read pending message.\n");
1070			break;
1071		}
1072	}
1073}
1074
1075
1076#define max_args 10
1077
1078static int tokenize_cmd(char *cmd, char *argv[])
1079{
1080	char *pos;
1081	int argc = 0;
1082
1083	pos = cmd;
1084	for (;;) {
1085		while (*pos == ' ')
1086			pos++;
1087		if (*pos == '\0')
1088			break;
1089		argv[argc] = pos;
1090		argc++;
1091		if (argc == max_args)
1092			break;
1093		if (*pos == '"') {
1094			char *pos2 = os_strrchr(pos, '"');
1095			if (pos2)
1096				pos = pos2 + 1;
1097		}
1098		while (*pos != '\0' && *pos != ' ')
1099			pos++;
1100		if (*pos == ' ')
1101			*pos++ = '\0';
1102	}
1103
1104	return argc;
1105}
1106
1107
1108static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
1109{
1110	if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
1111		printf("Connection to hostapd lost - trying to reconnect\n");
1112		hostapd_cli_close_connection();
1113	}
1114	if (!ctrl_conn) {
1115		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1116		if (ctrl_conn) {
1117			printf("Connection to hostapd re-established\n");
1118			if (wpa_ctrl_attach(ctrl_conn) == 0) {
1119				hostapd_cli_attached = 1;
1120			} else {
1121				printf("Warning: Failed to attach to "
1122				       "hostapd.\n");
1123			}
1124		}
1125	}
1126	if (ctrl_conn)
1127		hostapd_cli_recv_pending(ctrl_conn, 1, 0);
1128	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1129}
1130
1131
1132static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
1133{
1134	eloop_terminate();
1135}
1136
1137
1138static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
1139{
1140	char *argv[max_args];
1141	int argc;
1142	argc = tokenize_cmd(cmd, argv);
1143	if (argc)
1144		wpa_request(ctrl_conn, argc, argv);
1145}
1146
1147
1148static void hostapd_cli_edit_eof_cb(void *ctx)
1149{
1150	eloop_terminate();
1151}
1152
1153
1154static void hostapd_cli_interactive(void)
1155{
1156	printf("\nInteractive mode\n\n");
1157
1158	eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
1159	edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
1160		  NULL, NULL, NULL, NULL);
1161	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1162
1163	eloop_run();
1164
1165	edit_deinit(NULL, NULL);
1166	eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
1167}
1168
1169
1170static void hostapd_cli_cleanup(void)
1171{
1172	hostapd_cli_close_connection();
1173	if (pid_file)
1174		os_daemonize_terminate(pid_file);
1175
1176	os_program_deinit();
1177}
1178
1179
1180static void hostapd_cli_action(struct wpa_ctrl *ctrl)
1181{
1182	fd_set rfds;
1183	int fd, res;
1184	struct timeval tv;
1185	char buf[256];
1186	size_t len;
1187
1188	fd = wpa_ctrl_get_fd(ctrl);
1189
1190	while (!hostapd_cli_quit) {
1191		FD_ZERO(&rfds);
1192		FD_SET(fd, &rfds);
1193		tv.tv_sec = ping_interval;
1194		tv.tv_usec = 0;
1195		res = select(fd + 1, &rfds, NULL, NULL, &tv);
1196		if (res < 0 && errno != EINTR) {
1197			perror("select");
1198			break;
1199		}
1200
1201		if (FD_ISSET(fd, &rfds))
1202			hostapd_cli_recv_pending(ctrl, 0, 1);
1203		else {
1204			len = sizeof(buf) - 1;
1205			if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1206					     hostapd_cli_action_process) < 0 ||
1207			    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1208				printf("hostapd did not reply to PING "
1209				       "command - exiting\n");
1210				break;
1211			}
1212		}
1213	}
1214}
1215
1216
1217int main(int argc, char *argv[])
1218{
1219	int warning_displayed = 0;
1220	int c;
1221	int daemonize = 0;
1222
1223	if (os_program_init())
1224		return -1;
1225
1226	for (;;) {
1227		c = getopt(argc, argv, "a:BhG:i:p:v");
1228		if (c < 0)
1229			break;
1230		switch (c) {
1231		case 'a':
1232			action_file = optarg;
1233			break;
1234		case 'B':
1235			daemonize = 1;
1236			break;
1237		case 'G':
1238			ping_interval = atoi(optarg);
1239			break;
1240		case 'h':
1241			usage();
1242			return 0;
1243		case 'v':
1244			printf("%s\n", hostapd_cli_version);
1245			return 0;
1246		case 'i':
1247			os_free(ctrl_ifname);
1248			ctrl_ifname = os_strdup(optarg);
1249			break;
1250		case 'p':
1251			ctrl_iface_dir = optarg;
1252			break;
1253		default:
1254			usage();
1255			return -1;
1256		}
1257	}
1258
1259	interactive = (argc == optind) && (action_file == NULL);
1260
1261	if (interactive) {
1262		printf("%s\n\n%s\n\n", hostapd_cli_version,
1263		       hostapd_cli_license);
1264	}
1265
1266	if (eloop_init())
1267		return -1;
1268
1269	for (;;) {
1270		if (ctrl_ifname == NULL) {
1271			struct dirent *dent;
1272			DIR *dir = opendir(ctrl_iface_dir);
1273			if (dir) {
1274				while ((dent = readdir(dir))) {
1275					if (os_strcmp(dent->d_name, ".") == 0
1276					    ||
1277					    os_strcmp(dent->d_name, "..") == 0)
1278						continue;
1279					printf("Selected interface '%s'\n",
1280					       dent->d_name);
1281					ctrl_ifname = os_strdup(dent->d_name);
1282					break;
1283				}
1284				closedir(dir);
1285			}
1286		}
1287		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1288		if (ctrl_conn) {
1289			if (warning_displayed)
1290				printf("Connection established.\n");
1291			break;
1292		}
1293
1294		if (!interactive) {
1295			perror("Failed to connect to hostapd - "
1296			       "wpa_ctrl_open");
1297			return -1;
1298		}
1299
1300		if (!warning_displayed) {
1301			printf("Could not connect to hostapd - re-trying\n");
1302			warning_displayed = 1;
1303		}
1304		os_sleep(1, 0);
1305		continue;
1306	}
1307
1308	if (interactive || action_file) {
1309		if (wpa_ctrl_attach(ctrl_conn) == 0) {
1310			hostapd_cli_attached = 1;
1311		} else {
1312			printf("Warning: Failed to attach to hostapd.\n");
1313			if (action_file)
1314				return -1;
1315		}
1316	}
1317
1318	if (daemonize && os_daemonize(pid_file))
1319		return -1;
1320
1321	if (interactive)
1322		hostapd_cli_interactive();
1323	else if (action_file)
1324		hostapd_cli_action(ctrl_conn);
1325	else
1326		wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1327
1328	os_free(ctrl_ifname);
1329	eloop_destroy();
1330	hostapd_cli_cleanup();
1331	return 0;
1332}
1333