1214503Srpaulo/*
2214503Srpaulo * hostapd - command line interface for hostapd daemon
3214503Srpaulo * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
4214503Srpaulo *
5214503Srpaulo * This program is free software; you can redistribute it and/or modify
6214503Srpaulo * it under the terms of the GNU General Public License version 2 as
7214503Srpaulo * published by the Free Software Foundation.
8214503Srpaulo *
9214503Srpaulo * Alternatively, this software may be distributed under the terms of BSD
10214503Srpaulo * license.
11214503Srpaulo *
12214503Srpaulo * See README and COPYING for more details.
13214503Srpaulo */
14214503Srpaulo
15214503Srpaulo#include "includes.h"
16214503Srpaulo#include <dirent.h>
17214503Srpaulo
18214503Srpaulo#include "common/wpa_ctrl.h"
19214503Srpaulo#include "common.h"
20214503Srpaulo#include "common/version.h"
21214503Srpaulo
22214503Srpaulo
23214503Srpaulostatic const char *hostapd_cli_version =
24214503Srpaulo"hostapd_cli v" VERSION_STR "\n"
25214503Srpaulo"Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
26214503Srpaulo
27214503Srpaulo
28214503Srpaulostatic const char *hostapd_cli_license =
29214503Srpaulo"This program is free software. You can distribute it and/or modify it\n"
30214503Srpaulo"under the terms of the GNU General Public License version 2.\n"
31214503Srpaulo"\n"
32214503Srpaulo"Alternatively, this software may be distributed under the terms of the\n"
33214503Srpaulo"BSD license. See README and COPYING for more details.\n";
34214503Srpaulo
35214503Srpaulostatic const char *hostapd_cli_full_license =
36214503Srpaulo"This program is free software; you can redistribute it and/or modify\n"
37214503Srpaulo"it under the terms of the GNU General Public License version 2 as\n"
38214503Srpaulo"published by the Free Software Foundation.\n"
39214503Srpaulo"\n"
40214503Srpaulo"This program is distributed in the hope that it will be useful,\n"
41214503Srpaulo"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
42214503Srpaulo"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
43214503Srpaulo"GNU General Public License for more details.\n"
44214503Srpaulo"\n"
45214503Srpaulo"You should have received a copy of the GNU General Public License\n"
46214503Srpaulo"along with this program; if not, write to the Free Software\n"
47214503Srpaulo"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n"
48214503Srpaulo"\n"
49214503Srpaulo"Alternatively, this software may be distributed under the terms of the\n"
50214503Srpaulo"BSD license.\n"
51214503Srpaulo"\n"
52214503Srpaulo"Redistribution and use in source and binary forms, with or without\n"
53214503Srpaulo"modification, are permitted provided that the following conditions are\n"
54214503Srpaulo"met:\n"
55214503Srpaulo"\n"
56214503Srpaulo"1. Redistributions of source code must retain the above copyright\n"
57214503Srpaulo"   notice, this list of conditions and the following disclaimer.\n"
58214503Srpaulo"\n"
59214503Srpaulo"2. Redistributions in binary form must reproduce the above copyright\n"
60214503Srpaulo"   notice, this list of conditions and the following disclaimer in the\n"
61214503Srpaulo"   documentation and/or other materials provided with the distribution.\n"
62214503Srpaulo"\n"
63214503Srpaulo"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
64214503Srpaulo"   names of its contributors may be used to endorse or promote products\n"
65214503Srpaulo"   derived from this software without specific prior written permission.\n"
66214503Srpaulo"\n"
67214503Srpaulo"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
68214503Srpaulo"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
69214503Srpaulo"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
70214503Srpaulo"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
71214503Srpaulo"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
72214503Srpaulo"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
73214503Srpaulo"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
74214503Srpaulo"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
75214503Srpaulo"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
76214503Srpaulo"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
77214503Srpaulo"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
78214503Srpaulo"\n";
79214503Srpaulo
80214503Srpaulostatic const char *commands_help =
81214503Srpaulo"Commands:\n"
82214503Srpaulo"   mib                  get MIB variables (dot1x, dot11, radius)\n"
83214503Srpaulo"   sta <addr>           get MIB variables for one station\n"
84214503Srpaulo"   all_sta              get MIB variables for all stations\n"
85214503Srpaulo"   new_sta <addr>       add a new station\n"
86214503Srpaulo"   deauthenticate <addr>  deauthenticate a station\n"
87214503Srpaulo"   disassociate <addr>  disassociate a station\n"
88214503Srpaulo#ifdef CONFIG_IEEE80211W
89214503Srpaulo"   sa_query <addr>      send SA Query to a station\n"
90214503Srpaulo#endif /* CONFIG_IEEE80211W */
91214503Srpaulo#ifdef CONFIG_WPS
92214503Srpaulo"   wps_pin <uuid> <pin> [timeout]  add WPS Enrollee PIN (Device Password)\n"
93214503Srpaulo"   wps_pbc              indicate button pushed to initiate PBC\n"
94214503Srpaulo#ifdef CONFIG_WPS_OOB
95214503Srpaulo"   wps_oob <type> <path> <method>  use WPS with out-of-band (UFD)\n"
96214503Srpaulo#endif /* CONFIG_WPS_OOB */
97214503Srpaulo"   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
98214503Srpaulo#endif /* CONFIG_WPS */
99214503Srpaulo"   help                 show this usage help\n"
100214503Srpaulo"   interface [ifname]   show interfaces/select interface\n"
101214503Srpaulo"   level <debug level>  change debug level\n"
102214503Srpaulo"   license              show full hostapd_cli license\n"
103214503Srpaulo"   quit                 exit hostapd_cli\n";
104214503Srpaulo
105214503Srpaulostatic struct wpa_ctrl *ctrl_conn;
106214503Srpaulostatic int hostapd_cli_quit = 0;
107214503Srpaulostatic int hostapd_cli_attached = 0;
108214503Srpaulostatic const char *ctrl_iface_dir = "/var/run/hostapd";
109214503Srpaulostatic char *ctrl_ifname = NULL;
110214503Srpaulostatic const char *pid_file = NULL;
111214503Srpaulostatic const char *action_file = NULL;
112214503Srpaulostatic int ping_interval = 5;
113214503Srpaulo
114214503Srpaulo
115214503Srpaulostatic void usage(void)
116214503Srpaulo{
117214503Srpaulo	fprintf(stderr, "%s\n", hostapd_cli_version);
118214503Srpaulo	fprintf(stderr,
119214503Srpaulo		"\n"
120214503Srpaulo		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
121214503Srpaulo		"[-a<path>] \\\n"
122214503Srpaulo		"                   [-G<ping interval>] [command..]\n"
123214503Srpaulo		"\n"
124214503Srpaulo		"Options:\n"
125214503Srpaulo		"   -h           help (show this usage text)\n"
126214503Srpaulo		"   -v           shown version information\n"
127214503Srpaulo		"   -p<path>     path to find control sockets (default: "
128214503Srpaulo		"/var/run/hostapd)\n"
129214503Srpaulo		"   -a<file>     run in daemon mode executing the action file "
130214503Srpaulo		"based on events\n"
131214503Srpaulo		"                from hostapd\n"
132214503Srpaulo		"   -B           run a daemon in the background\n"
133214503Srpaulo		"   -i<ifname>   Interface to listen on (default: first "
134214503Srpaulo		"interface found in the\n"
135214503Srpaulo		"                socket path)\n\n"
136214503Srpaulo		"%s",
137214503Srpaulo		commands_help);
138214503Srpaulo}
139214503Srpaulo
140214503Srpaulo
141214503Srpaulostatic struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
142214503Srpaulo{
143214503Srpaulo	char *cfile;
144214503Srpaulo	int flen;
145214503Srpaulo
146214503Srpaulo	if (ifname == NULL)
147214503Srpaulo		return NULL;
148214503Srpaulo
149214503Srpaulo	flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
150214503Srpaulo	cfile = malloc(flen);
151214503Srpaulo	if (cfile == NULL)
152214503Srpaulo		return NULL;
153214503Srpaulo	snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
154214503Srpaulo
155214503Srpaulo	ctrl_conn = wpa_ctrl_open(cfile);
156214503Srpaulo	free(cfile);
157214503Srpaulo	return ctrl_conn;
158214503Srpaulo}
159214503Srpaulo
160214503Srpaulo
161214503Srpaulostatic void hostapd_cli_close_connection(void)
162214503Srpaulo{
163214503Srpaulo	if (ctrl_conn == NULL)
164214503Srpaulo		return;
165214503Srpaulo
166214503Srpaulo	if (hostapd_cli_attached) {
167214503Srpaulo		wpa_ctrl_detach(ctrl_conn);
168214503Srpaulo		hostapd_cli_attached = 0;
169214503Srpaulo	}
170214503Srpaulo	wpa_ctrl_close(ctrl_conn);
171214503Srpaulo	ctrl_conn = NULL;
172214503Srpaulo}
173214503Srpaulo
174214503Srpaulo
175214503Srpaulostatic void hostapd_cli_msg_cb(char *msg, size_t len)
176214503Srpaulo{
177214503Srpaulo	printf("%s\n", msg);
178214503Srpaulo}
179214503Srpaulo
180214503Srpaulo
181214503Srpaulostatic int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
182214503Srpaulo{
183214503Srpaulo	char buf[4096];
184214503Srpaulo	size_t len;
185214503Srpaulo	int ret;
186214503Srpaulo
187214503Srpaulo	if (ctrl_conn == NULL) {
188214503Srpaulo		printf("Not connected to hostapd - command dropped.\n");
189214503Srpaulo		return -1;
190214503Srpaulo	}
191214503Srpaulo	len = sizeof(buf) - 1;
192214503Srpaulo	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
193214503Srpaulo			       hostapd_cli_msg_cb);
194214503Srpaulo	if (ret == -2) {
195214503Srpaulo		printf("'%s' command timed out.\n", cmd);
196214503Srpaulo		return -2;
197214503Srpaulo	} else if (ret < 0) {
198214503Srpaulo		printf("'%s' command failed.\n", cmd);
199214503Srpaulo		return -1;
200214503Srpaulo	}
201214503Srpaulo	if (print) {
202214503Srpaulo		buf[len] = '\0';
203214503Srpaulo		printf("%s", buf);
204214503Srpaulo	}
205214503Srpaulo	return 0;
206214503Srpaulo}
207214503Srpaulo
208214503Srpaulo
209214503Srpaulostatic inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
210214503Srpaulo{
211214503Srpaulo	return _wpa_ctrl_command(ctrl, cmd, 1);
212214503Srpaulo}
213214503Srpaulo
214214503Srpaulo
215214503Srpaulostatic int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
216214503Srpaulo{
217214503Srpaulo	return wpa_ctrl_command(ctrl, "PING");
218214503Srpaulo}
219214503Srpaulo
220214503Srpaulo
221214503Srpaulostatic int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
222214503Srpaulo{
223214503Srpaulo	return wpa_ctrl_command(ctrl, "MIB");
224214503Srpaulo}
225214503Srpaulo
226214503Srpaulo
227214503Srpaulostatic int hostapd_cli_exec(const char *program, const char *arg1,
228214503Srpaulo			    const char *arg2)
229214503Srpaulo{
230214503Srpaulo	char *cmd;
231214503Srpaulo	size_t len;
232214503Srpaulo	int res;
233214503Srpaulo	int ret = 0;
234214503Srpaulo
235214503Srpaulo	len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
236214503Srpaulo	cmd = os_malloc(len);
237214503Srpaulo	if (cmd == NULL)
238214503Srpaulo		return -1;
239214503Srpaulo	res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
240214503Srpaulo	if (res < 0 || (size_t) res >= len) {
241214503Srpaulo		os_free(cmd);
242214503Srpaulo		return -1;
243214503Srpaulo	}
244214503Srpaulo	cmd[len - 1] = '\0';
245214503Srpaulo#ifndef _WIN32_WCE
246214503Srpaulo	if (system(cmd) < 0)
247214503Srpaulo		ret = -1;
248214503Srpaulo#endif /* _WIN32_WCE */
249214503Srpaulo	os_free(cmd);
250214503Srpaulo
251214503Srpaulo	return ret;
252214503Srpaulo}
253214503Srpaulo
254214503Srpaulo
255214503Srpaulostatic void hostapd_cli_action_process(char *msg, size_t len)
256214503Srpaulo{
257214503Srpaulo	const char *pos;
258214503Srpaulo
259214503Srpaulo	pos = msg;
260214503Srpaulo	if (*pos == '<') {
261214503Srpaulo		pos = os_strchr(pos, '>');
262214503Srpaulo		if (pos)
263214503Srpaulo			pos++;
264214503Srpaulo		else
265214503Srpaulo			pos = msg;
266214503Srpaulo	}
267214503Srpaulo
268214503Srpaulo	hostapd_cli_exec(action_file, ctrl_ifname, pos);
269214503Srpaulo}
270214503Srpaulo
271214503Srpaulo
272214503Srpaulostatic int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
273214503Srpaulo{
274214503Srpaulo	char buf[64];
275214503Srpaulo	if (argc != 1) {
276214503Srpaulo		printf("Invalid 'sta' command - exactly one argument, STA "
277214503Srpaulo		       "address, is required.\n");
278214503Srpaulo		return -1;
279214503Srpaulo	}
280214503Srpaulo	snprintf(buf, sizeof(buf), "STA %s", argv[0]);
281214503Srpaulo	return wpa_ctrl_command(ctrl, buf);
282214503Srpaulo}
283214503Srpaulo
284214503Srpaulo
285214503Srpaulostatic int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
286214503Srpaulo				   char *argv[])
287214503Srpaulo{
288214503Srpaulo	char buf[64];
289214503Srpaulo	if (argc != 1) {
290214503Srpaulo		printf("Invalid 'new_sta' command - exactly one argument, STA "
291214503Srpaulo		       "address, is required.\n");
292214503Srpaulo		return -1;
293214503Srpaulo	}
294214503Srpaulo	snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
295214503Srpaulo	return wpa_ctrl_command(ctrl, buf);
296214503Srpaulo}
297214503Srpaulo
298214503Srpaulo
299214503Srpaulostatic int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
300214503Srpaulo					  char *argv[])
301214503Srpaulo{
302214503Srpaulo	char buf[64];
303214503Srpaulo	if (argc < 1) {
304214503Srpaulo		printf("Invalid 'deauthenticate' command - exactly one "
305214503Srpaulo		       "argument, STA address, is required.\n");
306214503Srpaulo		return -1;
307214503Srpaulo	}
308214503Srpaulo	if (argc > 1)
309214503Srpaulo		os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
310214503Srpaulo			    argv[0], argv[1]);
311214503Srpaulo	else
312214503Srpaulo		os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
313214503Srpaulo	return wpa_ctrl_command(ctrl, buf);
314214503Srpaulo}
315214503Srpaulo
316214503Srpaulo
317214503Srpaulostatic int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
318214503Srpaulo					char *argv[])
319214503Srpaulo{
320214503Srpaulo	char buf[64];
321214503Srpaulo	if (argc < 1) {
322214503Srpaulo		printf("Invalid 'disassociate' command - exactly one "
323214503Srpaulo		       "argument, STA address, is required.\n");
324214503Srpaulo		return -1;
325214503Srpaulo	}
326214503Srpaulo	if (argc > 1)
327214503Srpaulo		os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
328214503Srpaulo			    argv[0], argv[1]);
329214503Srpaulo	else
330214503Srpaulo		os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
331214503Srpaulo	return wpa_ctrl_command(ctrl, buf);
332214503Srpaulo}
333214503Srpaulo
334214503Srpaulo
335214503Srpaulo#ifdef CONFIG_IEEE80211W
336214503Srpaulostatic int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
337214503Srpaulo				    char *argv[])
338214503Srpaulo{
339214503Srpaulo	char buf[64];
340214503Srpaulo	if (argc != 1) {
341214503Srpaulo		printf("Invalid 'sa_query' command - exactly one argument, "
342214503Srpaulo		       "STA address, is required.\n");
343214503Srpaulo		return -1;
344214503Srpaulo	}
345214503Srpaulo	snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
346214503Srpaulo	return wpa_ctrl_command(ctrl, buf);
347214503Srpaulo}
348214503Srpaulo#endif /* CONFIG_IEEE80211W */
349214503Srpaulo
350214503Srpaulo
351214503Srpaulo#ifdef CONFIG_WPS
352214503Srpaulostatic int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
353214503Srpaulo				   char *argv[])
354214503Srpaulo{
355214503Srpaulo	char buf[64];
356214503Srpaulo	if (argc < 2) {
357214503Srpaulo		printf("Invalid 'wps_pin' command - at least two arguments, "
358214503Srpaulo		       "UUID and PIN, are required.\n");
359214503Srpaulo		return -1;
360214503Srpaulo	}
361214503Srpaulo	if (argc > 2)
362214503Srpaulo		snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
363214503Srpaulo			 argv[0], argv[1], argv[2]);
364214503Srpaulo	else
365214503Srpaulo		snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
366214503Srpaulo	return wpa_ctrl_command(ctrl, buf);
367214503Srpaulo}
368214503Srpaulo
369214503Srpaulo
370214503Srpaulostatic int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
371214503Srpaulo				   char *argv[])
372214503Srpaulo{
373214503Srpaulo	return wpa_ctrl_command(ctrl, "WPS_PBC");
374214503Srpaulo}
375214503Srpaulo
376214503Srpaulo
377214503Srpaulo#ifdef CONFIG_WPS_OOB
378214503Srpaulostatic int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
379214503Srpaulo				   char *argv[])
380214503Srpaulo{
381214503Srpaulo	char cmd[256];
382214503Srpaulo	int res;
383214503Srpaulo
384214503Srpaulo	if (argc != 3 && argc != 4) {
385214503Srpaulo		printf("Invalid WPS_OOB command: need three or four "
386214503Srpaulo		       "arguments:\n"
387214503Srpaulo		       "- DEV_TYPE: use 'ufd' or 'nfc'\n"
388214503Srpaulo		       "- PATH: path of OOB device like '/mnt'\n"
389214503Srpaulo		       "- METHOD: OOB method 'pin-e' or 'pin-r', "
390214503Srpaulo		       "'cred'\n"
391214503Srpaulo		       "- DEV_NAME: (only for NFC) device name like "
392214503Srpaulo		       "'pn531'\n");
393214503Srpaulo		return -1;
394214503Srpaulo	}
395214503Srpaulo
396214503Srpaulo	if (argc == 3)
397214503Srpaulo		res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
398214503Srpaulo				  argv[0], argv[1], argv[2]);
399214503Srpaulo	else
400214503Srpaulo		res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
401214503Srpaulo				  argv[0], argv[1], argv[2], argv[3]);
402214503Srpaulo	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
403214503Srpaulo		printf("Too long WPS_OOB command.\n");
404214503Srpaulo		return -1;
405214503Srpaulo	}
406214503Srpaulo	return wpa_ctrl_command(ctrl, cmd);
407214503Srpaulo}
408214503Srpaulo#endif /* CONFIG_WPS_OOB */
409214503Srpaulo
410214503Srpaulo
411214503Srpaulostatic int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
412214503Srpaulo				      char *argv[])
413214503Srpaulo{
414214503Srpaulo	char buf[64];
415214503Srpaulo	if (argc < 1) {
416214503Srpaulo		printf("Invalid 'wps_ap_pin' command - at least one argument "
417214503Srpaulo		       "is required.\n");
418214503Srpaulo		return -1;
419214503Srpaulo	}
420214503Srpaulo	if (argc > 2)
421214503Srpaulo		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
422214503Srpaulo			 argv[0], argv[1], argv[2]);
423214503Srpaulo	else if (argc > 1)
424214503Srpaulo		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
425214503Srpaulo			 argv[0], argv[1]);
426214503Srpaulo	else
427214503Srpaulo		snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
428214503Srpaulo	return wpa_ctrl_command(ctrl, buf);
429214503Srpaulo}
430214503Srpaulo#endif /* CONFIG_WPS */
431214503Srpaulo
432214503Srpaulo
433214503Srpaulostatic int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
434214503Srpaulo				char *addr, size_t addr_len)
435214503Srpaulo{
436214503Srpaulo	char buf[4096], *pos;
437214503Srpaulo	size_t len;
438214503Srpaulo	int ret;
439214503Srpaulo
440214503Srpaulo	if (ctrl_conn == NULL) {
441214503Srpaulo		printf("Not connected to hostapd - command dropped.\n");
442214503Srpaulo		return -1;
443214503Srpaulo	}
444214503Srpaulo	len = sizeof(buf) - 1;
445214503Srpaulo	ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
446214503Srpaulo			       hostapd_cli_msg_cb);
447214503Srpaulo	if (ret == -2) {
448214503Srpaulo		printf("'%s' command timed out.\n", cmd);
449214503Srpaulo		return -2;
450214503Srpaulo	} else if (ret < 0) {
451214503Srpaulo		printf("'%s' command failed.\n", cmd);
452214503Srpaulo		return -1;
453214503Srpaulo	}
454214503Srpaulo
455214503Srpaulo	buf[len] = '\0';
456214503Srpaulo	if (memcmp(buf, "FAIL", 4) == 0)
457214503Srpaulo		return -1;
458214503Srpaulo	printf("%s", buf);
459214503Srpaulo
460214503Srpaulo	pos = buf;
461214503Srpaulo	while (*pos != '\0' && *pos != '\n')
462214503Srpaulo		pos++;
463214503Srpaulo	*pos = '\0';
464214503Srpaulo	os_strlcpy(addr, buf, addr_len);
465214503Srpaulo	return 0;
466214503Srpaulo}
467214503Srpaulo
468214503Srpaulo
469214503Srpaulostatic int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
470214503Srpaulo				   char *argv[])
471214503Srpaulo{
472214503Srpaulo	char addr[32], cmd[64];
473214503Srpaulo
474214503Srpaulo	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
475214503Srpaulo		return 0;
476214503Srpaulo	do {
477214503Srpaulo		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
478214503Srpaulo	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
479214503Srpaulo
480214503Srpaulo	return -1;
481214503Srpaulo}
482214503Srpaulo
483214503Srpaulo
484214503Srpaulostatic int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
485214503Srpaulo{
486214503Srpaulo	printf("%s", commands_help);
487214503Srpaulo	return 0;
488214503Srpaulo}
489214503Srpaulo
490214503Srpaulo
491214503Srpaulostatic int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
492214503Srpaulo				   char *argv[])
493214503Srpaulo{
494214503Srpaulo	printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
495214503Srpaulo	return 0;
496214503Srpaulo}
497214503Srpaulo
498214503Srpaulo
499214503Srpaulostatic int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
500214503Srpaulo{
501214503Srpaulo	hostapd_cli_quit = 1;
502214503Srpaulo	return 0;
503214503Srpaulo}
504214503Srpaulo
505214503Srpaulo
506214503Srpaulostatic int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
507214503Srpaulo{
508214503Srpaulo	char cmd[256];
509214503Srpaulo	if (argc != 1) {
510214503Srpaulo		printf("Invalid LEVEL command: needs one argument (debug "
511214503Srpaulo		       "level)\n");
512214503Srpaulo		return 0;
513214503Srpaulo	}
514214503Srpaulo	snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
515214503Srpaulo	return wpa_ctrl_command(ctrl, cmd);
516214503Srpaulo}
517214503Srpaulo
518214503Srpaulo
519214503Srpaulostatic void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
520214503Srpaulo{
521214503Srpaulo	struct dirent *dent;
522214503Srpaulo	DIR *dir;
523214503Srpaulo
524214503Srpaulo	dir = opendir(ctrl_iface_dir);
525214503Srpaulo	if (dir == NULL) {
526214503Srpaulo		printf("Control interface directory '%s' could not be "
527214503Srpaulo		       "openned.\n", ctrl_iface_dir);
528214503Srpaulo		return;
529214503Srpaulo	}
530214503Srpaulo
531214503Srpaulo	printf("Available interfaces:\n");
532214503Srpaulo	while ((dent = readdir(dir))) {
533214503Srpaulo		if (strcmp(dent->d_name, ".") == 0 ||
534214503Srpaulo		    strcmp(dent->d_name, "..") == 0)
535214503Srpaulo			continue;
536214503Srpaulo		printf("%s\n", dent->d_name);
537214503Srpaulo	}
538214503Srpaulo	closedir(dir);
539214503Srpaulo}
540214503Srpaulo
541214503Srpaulo
542214503Srpaulostatic int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
543214503Srpaulo				     char *argv[])
544214503Srpaulo{
545214503Srpaulo	if (argc < 1) {
546214503Srpaulo		hostapd_cli_list_interfaces(ctrl);
547214503Srpaulo		return 0;
548214503Srpaulo	}
549214503Srpaulo
550214503Srpaulo	hostapd_cli_close_connection();
551214503Srpaulo	free(ctrl_ifname);
552214503Srpaulo	ctrl_ifname = strdup(argv[0]);
553214503Srpaulo
554214503Srpaulo	if (hostapd_cli_open_connection(ctrl_ifname)) {
555214503Srpaulo		printf("Connected to interface '%s.\n", ctrl_ifname);
556214503Srpaulo		if (wpa_ctrl_attach(ctrl_conn) == 0) {
557214503Srpaulo			hostapd_cli_attached = 1;
558214503Srpaulo		} else {
559214503Srpaulo			printf("Warning: Failed to attach to "
560214503Srpaulo			       "hostapd.\n");
561214503Srpaulo		}
562214503Srpaulo	} else {
563214503Srpaulo		printf("Could not connect to interface '%s' - re-trying\n",
564214503Srpaulo			ctrl_ifname);
565214503Srpaulo	}
566214503Srpaulo	return 0;
567214503Srpaulo}
568214503Srpaulo
569214503Srpaulo
570214503Srpaulostruct hostapd_cli_cmd {
571214503Srpaulo	const char *cmd;
572214503Srpaulo	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
573214503Srpaulo};
574214503Srpaulo
575214503Srpaulostatic struct hostapd_cli_cmd hostapd_cli_commands[] = {
576214503Srpaulo	{ "ping", hostapd_cli_cmd_ping },
577214503Srpaulo	{ "mib", hostapd_cli_cmd_mib },
578214503Srpaulo	{ "sta", hostapd_cli_cmd_sta },
579214503Srpaulo	{ "all_sta", hostapd_cli_cmd_all_sta },
580214503Srpaulo	{ "new_sta", hostapd_cli_cmd_new_sta },
581214503Srpaulo	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
582214503Srpaulo	{ "disassociate", hostapd_cli_cmd_disassociate },
583214503Srpaulo#ifdef CONFIG_IEEE80211W
584214503Srpaulo	{ "sa_query", hostapd_cli_cmd_sa_query },
585214503Srpaulo#endif /* CONFIG_IEEE80211W */
586214503Srpaulo#ifdef CONFIG_WPS
587214503Srpaulo	{ "wps_pin", hostapd_cli_cmd_wps_pin },
588214503Srpaulo	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
589214503Srpaulo#ifdef CONFIG_WPS_OOB
590214503Srpaulo	{ "wps_oob", hostapd_cli_cmd_wps_oob },
591214503Srpaulo#endif /* CONFIG_WPS_OOB */
592214503Srpaulo	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
593214503Srpaulo#endif /* CONFIG_WPS */
594214503Srpaulo	{ "help", hostapd_cli_cmd_help },
595214503Srpaulo	{ "interface", hostapd_cli_cmd_interface },
596214503Srpaulo	{ "level", hostapd_cli_cmd_level },
597214503Srpaulo	{ "license", hostapd_cli_cmd_license },
598214503Srpaulo	{ "quit", hostapd_cli_cmd_quit },
599214503Srpaulo	{ NULL, NULL }
600214503Srpaulo};
601214503Srpaulo
602214503Srpaulo
603214503Srpaulostatic void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
604214503Srpaulo{
605214503Srpaulo	struct hostapd_cli_cmd *cmd, *match = NULL;
606214503Srpaulo	int count;
607214503Srpaulo
608214503Srpaulo	count = 0;
609214503Srpaulo	cmd = hostapd_cli_commands;
610214503Srpaulo	while (cmd->cmd) {
611214503Srpaulo		if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
612214503Srpaulo			match = cmd;
613214503Srpaulo			count++;
614214503Srpaulo		}
615214503Srpaulo		cmd++;
616214503Srpaulo	}
617214503Srpaulo
618214503Srpaulo	if (count > 1) {
619214503Srpaulo		printf("Ambiguous command '%s'; possible commands:", argv[0]);
620214503Srpaulo		cmd = hostapd_cli_commands;
621214503Srpaulo		while (cmd->cmd) {
622214503Srpaulo			if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
623214503Srpaulo			    0) {
624214503Srpaulo				printf(" %s", cmd->cmd);
625214503Srpaulo			}
626214503Srpaulo			cmd++;
627214503Srpaulo		}
628214503Srpaulo		printf("\n");
629214503Srpaulo	} else if (count == 0) {
630214503Srpaulo		printf("Unknown command '%s'\n", argv[0]);
631214503Srpaulo	} else {
632214503Srpaulo		match->handler(ctrl, argc - 1, &argv[1]);
633214503Srpaulo	}
634214503Srpaulo}
635214503Srpaulo
636214503Srpaulo
637214503Srpaulostatic void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
638214503Srpaulo				     int action_monitor)
639214503Srpaulo{
640214503Srpaulo	int first = 1;
641214503Srpaulo	if (ctrl_conn == NULL)
642214503Srpaulo		return;
643214503Srpaulo	while (wpa_ctrl_pending(ctrl)) {
644214503Srpaulo		char buf[256];
645214503Srpaulo		size_t len = sizeof(buf) - 1;
646214503Srpaulo		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
647214503Srpaulo			buf[len] = '\0';
648214503Srpaulo			if (action_monitor)
649214503Srpaulo				hostapd_cli_action_process(buf, len);
650214503Srpaulo			else {
651214503Srpaulo				if (in_read && first)
652214503Srpaulo					printf("\n");
653214503Srpaulo				first = 0;
654214503Srpaulo				printf("%s\n", buf);
655214503Srpaulo			}
656214503Srpaulo		} else {
657214503Srpaulo			printf("Could not read pending message.\n");
658214503Srpaulo			break;
659214503Srpaulo		}
660214503Srpaulo	}
661214503Srpaulo}
662214503Srpaulo
663214503Srpaulo
664214503Srpaulostatic void hostapd_cli_interactive(void)
665214503Srpaulo{
666214503Srpaulo	const int max_args = 10;
667214503Srpaulo	char cmd[256], *res, *argv[max_args], *pos;
668214503Srpaulo	int argc;
669214503Srpaulo
670214503Srpaulo	printf("\nInteractive mode\n\n");
671214503Srpaulo
672214503Srpaulo	do {
673214503Srpaulo		hostapd_cli_recv_pending(ctrl_conn, 0, 0);
674214503Srpaulo		printf("> ");
675214503Srpaulo		alarm(ping_interval);
676214503Srpaulo		res = fgets(cmd, sizeof(cmd), stdin);
677214503Srpaulo		alarm(0);
678214503Srpaulo		if (res == NULL)
679214503Srpaulo			break;
680214503Srpaulo		pos = cmd;
681214503Srpaulo		while (*pos != '\0') {
682214503Srpaulo			if (*pos == '\n') {
683214503Srpaulo				*pos = '\0';
684214503Srpaulo				break;
685214503Srpaulo			}
686214503Srpaulo			pos++;
687214503Srpaulo		}
688214503Srpaulo		argc = 0;
689214503Srpaulo		pos = cmd;
690214503Srpaulo		for (;;) {
691214503Srpaulo			while (*pos == ' ')
692214503Srpaulo				pos++;
693214503Srpaulo			if (*pos == '\0')
694214503Srpaulo				break;
695214503Srpaulo			argv[argc] = pos;
696214503Srpaulo			argc++;
697214503Srpaulo			if (argc == max_args)
698214503Srpaulo				break;
699214503Srpaulo			while (*pos != '\0' && *pos != ' ')
700214503Srpaulo				pos++;
701214503Srpaulo			if (*pos == ' ')
702214503Srpaulo				*pos++ = '\0';
703214503Srpaulo		}
704214503Srpaulo		if (argc)
705214503Srpaulo			wpa_request(ctrl_conn, argc, argv);
706214503Srpaulo	} while (!hostapd_cli_quit);
707214503Srpaulo}
708214503Srpaulo
709214503Srpaulo
710214503Srpaulostatic void hostapd_cli_cleanup(void)
711214503Srpaulo{
712214503Srpaulo	hostapd_cli_close_connection();
713214503Srpaulo	if (pid_file)
714214503Srpaulo		os_daemonize_terminate(pid_file);
715214503Srpaulo
716214503Srpaulo	os_program_deinit();
717214503Srpaulo}
718214503Srpaulo
719214503Srpaulo
720214503Srpaulostatic void hostapd_cli_terminate(int sig)
721214503Srpaulo{
722214503Srpaulo	hostapd_cli_cleanup();
723214503Srpaulo	exit(0);
724214503Srpaulo}
725214503Srpaulo
726214503Srpaulo
727214503Srpaulostatic void hostapd_cli_alarm(int sig)
728214503Srpaulo{
729214503Srpaulo	if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
730214503Srpaulo		printf("Connection to hostapd lost - trying to reconnect\n");
731214503Srpaulo		hostapd_cli_close_connection();
732214503Srpaulo	}
733214503Srpaulo	if (!ctrl_conn) {
734214503Srpaulo		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
735214503Srpaulo		if (ctrl_conn) {
736214503Srpaulo			printf("Connection to hostapd re-established\n");
737214503Srpaulo			if (wpa_ctrl_attach(ctrl_conn) == 0) {
738214503Srpaulo				hostapd_cli_attached = 1;
739214503Srpaulo			} else {
740214503Srpaulo				printf("Warning: Failed to attach to "
741214503Srpaulo				       "hostapd.\n");
742214503Srpaulo			}
743214503Srpaulo		}
744214503Srpaulo	}
745214503Srpaulo	if (ctrl_conn)
746214503Srpaulo		hostapd_cli_recv_pending(ctrl_conn, 1, 0);
747214503Srpaulo	alarm(ping_interval);
748214503Srpaulo}
749214503Srpaulo
750214503Srpaulo
751214503Srpaulostatic void hostapd_cli_action(struct wpa_ctrl *ctrl)
752214503Srpaulo{
753214503Srpaulo	fd_set rfds;
754214503Srpaulo	int fd, res;
755214503Srpaulo	struct timeval tv;
756214503Srpaulo	char buf[256];
757214503Srpaulo	size_t len;
758214503Srpaulo
759214503Srpaulo	fd = wpa_ctrl_get_fd(ctrl);
760214503Srpaulo
761214503Srpaulo	while (!hostapd_cli_quit) {
762214503Srpaulo		FD_ZERO(&rfds);
763214503Srpaulo		FD_SET(fd, &rfds);
764214503Srpaulo		tv.tv_sec = ping_interval;
765214503Srpaulo		tv.tv_usec = 0;
766214503Srpaulo		res = select(fd + 1, &rfds, NULL, NULL, &tv);
767214503Srpaulo		if (res < 0 && errno != EINTR) {
768214503Srpaulo			perror("select");
769214503Srpaulo			break;
770214503Srpaulo		}
771214503Srpaulo
772214503Srpaulo		if (FD_ISSET(fd, &rfds))
773214503Srpaulo			hostapd_cli_recv_pending(ctrl, 0, 1);
774214503Srpaulo		else {
775214503Srpaulo			len = sizeof(buf) - 1;
776214503Srpaulo			if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
777214503Srpaulo					     hostapd_cli_action_process) < 0 ||
778214503Srpaulo			    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
779214503Srpaulo				printf("hostapd did not reply to PING "
780214503Srpaulo				       "command - exiting\n");
781214503Srpaulo				break;
782214503Srpaulo			}
783214503Srpaulo		}
784214503Srpaulo	}
785214503Srpaulo}
786214503Srpaulo
787214503Srpaulo
788214503Srpauloint main(int argc, char *argv[])
789214503Srpaulo{
790214503Srpaulo	int interactive;
791214503Srpaulo	int warning_displayed = 0;
792214503Srpaulo	int c;
793214503Srpaulo	int daemonize = 0;
794214503Srpaulo
795214503Srpaulo	if (os_program_init())
796214503Srpaulo		return -1;
797214503Srpaulo
798214503Srpaulo	for (;;) {
799214503Srpaulo		c = getopt(argc, argv, "a:BhG:i:p:v");
800214503Srpaulo		if (c < 0)
801214503Srpaulo			break;
802214503Srpaulo		switch (c) {
803214503Srpaulo		case 'a':
804214503Srpaulo			action_file = optarg;
805214503Srpaulo			break;
806214503Srpaulo		case 'B':
807214503Srpaulo			daemonize = 1;
808214503Srpaulo			break;
809214503Srpaulo		case 'G':
810214503Srpaulo			ping_interval = atoi(optarg);
811214503Srpaulo			break;
812214503Srpaulo		case 'h':
813214503Srpaulo			usage();
814214503Srpaulo			return 0;
815214503Srpaulo		case 'v':
816214503Srpaulo			printf("%s\n", hostapd_cli_version);
817214503Srpaulo			return 0;
818214503Srpaulo		case 'i':
819214503Srpaulo			os_free(ctrl_ifname);
820214503Srpaulo			ctrl_ifname = os_strdup(optarg);
821214503Srpaulo			break;
822214503Srpaulo		case 'p':
823214503Srpaulo			ctrl_iface_dir = optarg;
824214503Srpaulo			break;
825214503Srpaulo		default:
826214503Srpaulo			usage();
827214503Srpaulo			return -1;
828214503Srpaulo		}
829214503Srpaulo	}
830214503Srpaulo
831214503Srpaulo	interactive = (argc == optind) && (action_file == NULL);
832214503Srpaulo
833214503Srpaulo	if (interactive) {
834214503Srpaulo		printf("%s\n\n%s\n\n", hostapd_cli_version,
835214503Srpaulo		       hostapd_cli_license);
836214503Srpaulo	}
837214503Srpaulo
838214503Srpaulo	for (;;) {
839214503Srpaulo		if (ctrl_ifname == NULL) {
840214503Srpaulo			struct dirent *dent;
841214503Srpaulo			DIR *dir = opendir(ctrl_iface_dir);
842214503Srpaulo			if (dir) {
843214503Srpaulo				while ((dent = readdir(dir))) {
844214503Srpaulo					if (os_strcmp(dent->d_name, ".") == 0
845214503Srpaulo					    ||
846214503Srpaulo					    os_strcmp(dent->d_name, "..") == 0)
847214503Srpaulo						continue;
848214503Srpaulo					printf("Selected interface '%s'\n",
849214503Srpaulo					       dent->d_name);
850214503Srpaulo					ctrl_ifname = os_strdup(dent->d_name);
851214503Srpaulo					break;
852214503Srpaulo				}
853214503Srpaulo				closedir(dir);
854214503Srpaulo			}
855214503Srpaulo		}
856214503Srpaulo		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
857214503Srpaulo		if (ctrl_conn) {
858214503Srpaulo			if (warning_displayed)
859214503Srpaulo				printf("Connection established.\n");
860214503Srpaulo			break;
861214503Srpaulo		}
862214503Srpaulo
863214503Srpaulo		if (!interactive) {
864214503Srpaulo			perror("Failed to connect to hostapd - "
865214503Srpaulo			       "wpa_ctrl_open");
866214503Srpaulo			return -1;
867214503Srpaulo		}
868214503Srpaulo
869214503Srpaulo		if (!warning_displayed) {
870214503Srpaulo			printf("Could not connect to hostapd - re-trying\n");
871214503Srpaulo			warning_displayed = 1;
872214503Srpaulo		}
873214503Srpaulo		os_sleep(1, 0);
874214503Srpaulo		continue;
875214503Srpaulo	}
876214503Srpaulo
877214503Srpaulo	signal(SIGINT, hostapd_cli_terminate);
878214503Srpaulo	signal(SIGTERM, hostapd_cli_terminate);
879214503Srpaulo	signal(SIGALRM, hostapd_cli_alarm);
880214503Srpaulo
881214503Srpaulo	if (interactive || action_file) {
882214503Srpaulo		if (wpa_ctrl_attach(ctrl_conn) == 0) {
883214503Srpaulo			hostapd_cli_attached = 1;
884214503Srpaulo		} else {
885214503Srpaulo			printf("Warning: Failed to attach to hostapd.\n");
886214503Srpaulo			if (action_file)
887214503Srpaulo				return -1;
888214503Srpaulo		}
889214503Srpaulo	}
890214503Srpaulo
891214503Srpaulo	if (daemonize && os_daemonize(pid_file))
892214503Srpaulo		return -1;
893214503Srpaulo
894214503Srpaulo	if (interactive)
895214503Srpaulo		hostapd_cli_interactive();
896214503Srpaulo	else if (action_file)
897214503Srpaulo		hostapd_cli_action(ctrl_conn);
898214503Srpaulo	else
899214503Srpaulo		wpa_request(ctrl_conn, argc - optind, &argv[optind]);
900214503Srpaulo
901214503Srpaulo	os_free(ctrl_ifname);
902214503Srpaulo	hostapd_cli_cleanup();
903214503Srpaulo	return 0;
904214503Srpaulo}
905