wpa_cli.c revision 189251
1189251Ssam/*
2189251Ssam * WPA Supplicant - command line interface for wpa_supplicant daemon
3189251Ssam * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
4189251Ssam *
5189251Ssam * This program is free software; you can redistribute it and/or modify
6189251Ssam * it under the terms of the GNU General Public License version 2 as
7189251Ssam * published by the Free Software Foundation.
8189251Ssam *
9189251Ssam * Alternatively, this software may be distributed under the terms of BSD
10189251Ssam * license.
11189251Ssam *
12189251Ssam * See README and COPYING for more details.
13189251Ssam */
14189251Ssam
15189251Ssam#include "includes.h"
16189251Ssam
17189251Ssam#ifdef CONFIG_CTRL_IFACE
18189251Ssam
19189251Ssam#ifdef CONFIG_CTRL_IFACE_UNIX
20189251Ssam#include <dirent.h>
21189251Ssam#endif /* CONFIG_CTRL_IFACE_UNIX */
22189251Ssam#ifdef CONFIG_READLINE
23189251Ssam#include <readline/readline.h>
24189251Ssam#include <readline/history.h>
25189251Ssam#endif /* CONFIG_READLINE */
26189251Ssam
27189251Ssam#include "wpa_ctrl.h"
28189251Ssam#include "common.h"
29189251Ssam#include "version.h"
30189251Ssam
31189251Ssam
32189251Ssamstatic const char *wpa_cli_version =
33189251Ssam"wpa_cli v" VERSION_STR "\n"
34189251Ssam"Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> and contributors";
35189251Ssam
36189251Ssam
37189251Ssamstatic const char *wpa_cli_license =
38189251Ssam"This program is free software. You can distribute it and/or modify it\n"
39189251Ssam"under the terms of the GNU General Public License version 2.\n"
40189251Ssam"\n"
41189251Ssam"Alternatively, this software may be distributed under the terms of the\n"
42189251Ssam"BSD license. See README and COPYING for more details.\n";
43189251Ssam
44189251Ssamstatic const char *wpa_cli_full_license =
45189251Ssam"This program is free software; you can redistribute it and/or modify\n"
46189251Ssam"it under the terms of the GNU General Public License version 2 as\n"
47189251Ssam"published by the Free Software Foundation.\n"
48189251Ssam"\n"
49189251Ssam"This program is distributed in the hope that it will be useful,\n"
50189251Ssam"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
51189251Ssam"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
52189251Ssam"GNU General Public License for more details.\n"
53189251Ssam"\n"
54189251Ssam"You should have received a copy of the GNU General Public License\n"
55189251Ssam"along with this program; if not, write to the Free Software\n"
56189251Ssam"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n"
57189251Ssam"\n"
58189251Ssam"Alternatively, this software may be distributed under the terms of the\n"
59189251Ssam"BSD license.\n"
60189251Ssam"\n"
61189251Ssam"Redistribution and use in source and binary forms, with or without\n"
62189251Ssam"modification, are permitted provided that the following conditions are\n"
63189251Ssam"met:\n"
64189251Ssam"\n"
65189251Ssam"1. Redistributions of source code must retain the above copyright\n"
66189251Ssam"   notice, this list of conditions and the following disclaimer.\n"
67189251Ssam"\n"
68189251Ssam"2. Redistributions in binary form must reproduce the above copyright\n"
69189251Ssam"   notice, this list of conditions and the following disclaimer in the\n"
70189251Ssam"   documentation and/or other materials provided with the distribution.\n"
71189251Ssam"\n"
72189251Ssam"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
73189251Ssam"   names of its contributors may be used to endorse or promote products\n"
74189251Ssam"   derived from this software without specific prior written permission.\n"
75189251Ssam"\n"
76189251Ssam"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
77189251Ssam"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
78189251Ssam"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
79189251Ssam"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
80189251Ssam"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
81189251Ssam"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
82189251Ssam"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
83189251Ssam"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
84189251Ssam"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
85189251Ssam"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
86189251Ssam"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
87189251Ssam"\n";
88189251Ssam
89189251Ssamstatic struct wpa_ctrl *ctrl_conn;
90189251Ssamstatic int wpa_cli_quit = 0;
91189251Ssamstatic int wpa_cli_attached = 0;
92189251Ssamstatic int wpa_cli_connected = 0;
93189251Ssamstatic int wpa_cli_last_id = 0;
94189251Ssamstatic const char *ctrl_iface_dir = "/var/run/wpa_supplicant";
95189251Ssamstatic char *ctrl_ifname = NULL;
96189251Ssamstatic const char *pid_file = NULL;
97189251Ssamstatic const char *action_file = NULL;
98189251Ssamstatic int ping_interval = 5;
99189251Ssam
100189251Ssam
101189251Ssamstatic void print_help();
102189251Ssam
103189251Ssam
104189251Ssamstatic void usage(void)
105189251Ssam{
106189251Ssam	printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] "
107189251Ssam	       "[-a<action file>] \\\n"
108189251Ssam	       "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>]  "
109189251Ssam	       "[command..]\n"
110189251Ssam	       "  -h = help (show this usage text)\n"
111189251Ssam	       "  -v = shown version information\n"
112189251Ssam	       "  -a = run in daemon mode executing the action file based on "
113189251Ssam	       "events from\n"
114189251Ssam	       "       wpa_supplicant\n"
115189251Ssam	       "  -B = run a daemon in the background\n"
116189251Ssam	       "  default path: /var/run/wpa_supplicant\n"
117189251Ssam	       "  default interface: first interface found in socket path\n");
118189251Ssam	print_help();
119189251Ssam}
120189251Ssam
121189251Ssam
122189251Ssamstatic struct wpa_ctrl * wpa_cli_open_connection(const char *ifname)
123189251Ssam{
124189251Ssam#if defined(CONFIG_CTRL_IFACE_UDP) || defined(CONFIG_CTRL_IFACE_NAMED_PIPE)
125189251Ssam	ctrl_conn = wpa_ctrl_open(ifname);
126189251Ssam	return ctrl_conn;
127189251Ssam#else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
128189251Ssam	char *cfile;
129189251Ssam	int flen, res;
130189251Ssam
131189251Ssam	if (ifname == NULL)
132189251Ssam		return NULL;
133189251Ssam
134189251Ssam	flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
135189251Ssam	cfile = os_malloc(flen);
136189251Ssam	if (cfile == NULL)
137189251Ssam		return NULL;
138189251Ssam	res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
139189251Ssam	if (res < 0 || res >= flen) {
140189251Ssam		os_free(cfile);
141189251Ssam		return NULL;
142189251Ssam	}
143189251Ssam
144189251Ssam	ctrl_conn = wpa_ctrl_open(cfile);
145189251Ssam	os_free(cfile);
146189251Ssam	return ctrl_conn;
147189251Ssam#endif /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
148189251Ssam}
149189251Ssam
150189251Ssam
151189251Ssamstatic void wpa_cli_close_connection(void)
152189251Ssam{
153189251Ssam	if (ctrl_conn == NULL)
154189251Ssam		return;
155189251Ssam
156189251Ssam	if (wpa_cli_attached) {
157189251Ssam		wpa_ctrl_detach(ctrl_conn);
158189251Ssam		wpa_cli_attached = 0;
159189251Ssam	}
160189251Ssam	wpa_ctrl_close(ctrl_conn);
161189251Ssam	ctrl_conn = NULL;
162189251Ssam}
163189251Ssam
164189251Ssam
165189251Ssamstatic void wpa_cli_msg_cb(char *msg, size_t len)
166189251Ssam{
167189251Ssam	printf("%s\n", msg);
168189251Ssam}
169189251Ssam
170189251Ssam
171189251Ssamstatic int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
172189251Ssam{
173189251Ssam	char buf[2048];
174189251Ssam	size_t len;
175189251Ssam	int ret;
176189251Ssam
177189251Ssam	if (ctrl_conn == NULL) {
178189251Ssam		printf("Not connected to wpa_supplicant - command dropped.\n");
179189251Ssam		return -1;
180189251Ssam	}
181189251Ssam	len = sizeof(buf) - 1;
182189251Ssam	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
183189251Ssam			       wpa_cli_msg_cb);
184189251Ssam	if (ret == -2) {
185189251Ssam		printf("'%s' command timed out.\n", cmd);
186189251Ssam		return -2;
187189251Ssam	} else if (ret < 0) {
188189251Ssam		printf("'%s' command failed.\n", cmd);
189189251Ssam		return -1;
190189251Ssam	}
191189251Ssam	if (print) {
192189251Ssam		buf[len] = '\0';
193189251Ssam		printf("%s", buf);
194189251Ssam	}
195189251Ssam	return 0;
196189251Ssam}
197189251Ssam
198189251Ssam
199189251Ssamstatic int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
200189251Ssam{
201189251Ssam	return _wpa_ctrl_command(ctrl, cmd, 1);
202189251Ssam}
203189251Ssam
204189251Ssam
205189251Ssamstatic int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
206189251Ssam{
207189251Ssam	int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0;
208189251Ssam	return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS");
209189251Ssam}
210189251Ssam
211189251Ssam
212189251Ssamstatic int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
213189251Ssam{
214189251Ssam	return wpa_ctrl_command(ctrl, "PING");
215189251Ssam}
216189251Ssam
217189251Ssam
218189251Ssamstatic int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
219189251Ssam{
220189251Ssam	return wpa_ctrl_command(ctrl, "MIB");
221189251Ssam}
222189251Ssam
223189251Ssam
224189251Ssamstatic int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
225189251Ssam{
226189251Ssam	return wpa_ctrl_command(ctrl, "PMKSA");
227189251Ssam}
228189251Ssam
229189251Ssam
230189251Ssamstatic int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
231189251Ssam{
232189251Ssam	print_help();
233189251Ssam	return 0;
234189251Ssam}
235189251Ssam
236189251Ssam
237189251Ssamstatic int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
238189251Ssam{
239189251Ssam	printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license);
240189251Ssam	return 0;
241189251Ssam}
242189251Ssam
243189251Ssam
244189251Ssamstatic int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
245189251Ssam{
246189251Ssam	wpa_cli_quit = 1;
247189251Ssam	return 0;
248189251Ssam}
249189251Ssam
250189251Ssam
251189251Ssamstatic void wpa_cli_show_variables(void)
252189251Ssam{
253189251Ssam	printf("set variables:\n"
254189251Ssam	       "  EAPOL::heldPeriod (EAPOL state machine held period, "
255189251Ssam	       "in seconds)\n"
256189251Ssam	       "  EAPOL::authPeriod (EAPOL state machine authentication "
257189251Ssam	       "period, in seconds)\n"
258189251Ssam	       "  EAPOL::startPeriod (EAPOL state machine start period, in "
259189251Ssam	       "seconds)\n"
260189251Ssam	       "  EAPOL::maxStart (EAPOL state machine maximum start "
261189251Ssam	       "attempts)\n");
262189251Ssam	printf("  dot11RSNAConfigPMKLifetime (WPA/WPA2 PMK lifetime in "
263189251Ssam	       "seconds)\n"
264189251Ssam	       "  dot11RSNAConfigPMKReauthThreshold (WPA/WPA2 reauthentication"
265189251Ssam	       " threshold\n\tpercentage)\n"
266189251Ssam	       "  dot11RSNAConfigSATimeout (WPA/WPA2 timeout for completing "
267189251Ssam	       "security\n\tassociation in seconds)\n");
268189251Ssam}
269189251Ssam
270189251Ssam
271189251Ssamstatic int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
272189251Ssam{
273189251Ssam	char cmd[256];
274189251Ssam	int res;
275189251Ssam
276189251Ssam	if (argc == 0) {
277189251Ssam		wpa_cli_show_variables();
278189251Ssam		return 0;
279189251Ssam	}
280189251Ssam
281189251Ssam	if (argc != 2) {
282189251Ssam		printf("Invalid SET command: needs two arguments (variable "
283189251Ssam		       "name and value)\n");
284189251Ssam		return -1;
285189251Ssam	}
286189251Ssam
287189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
288189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
289189251Ssam		printf("Too long SET command.\n");
290189251Ssam		return -1;
291189251Ssam	}
292189251Ssam	return wpa_ctrl_command(ctrl, cmd);
293189251Ssam}
294189251Ssam
295189251Ssam
296189251Ssamstatic int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
297189251Ssam{
298189251Ssam	return wpa_ctrl_command(ctrl, "LOGOFF");
299189251Ssam}
300189251Ssam
301189251Ssam
302189251Ssamstatic int wpa_cli_cmd_logon(struct wpa_ctrl *ctrl, int argc, char *argv[])
303189251Ssam{
304189251Ssam	return wpa_ctrl_command(ctrl, "LOGON");
305189251Ssam}
306189251Ssam
307189251Ssam
308189251Ssamstatic int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc,
309189251Ssam				   char *argv[])
310189251Ssam{
311189251Ssam	return wpa_ctrl_command(ctrl, "REASSOCIATE");
312189251Ssam}
313189251Ssam
314189251Ssam
315189251Ssamstatic int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc,
316189251Ssam				       char *argv[])
317189251Ssam{
318189251Ssam	char cmd[256];
319189251Ssam	int res;
320189251Ssam
321189251Ssam	if (argc != 1) {
322189251Ssam		printf("Invalid PREAUTH command: needs one argument "
323189251Ssam		       "(BSSID)\n");
324189251Ssam		return -1;
325189251Ssam	}
326189251Ssam
327189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]);
328189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
329189251Ssam		printf("Too long PREAUTH command.\n");
330189251Ssam		return -1;
331189251Ssam	}
332189251Ssam	return wpa_ctrl_command(ctrl, cmd);
333189251Ssam}
334189251Ssam
335189251Ssam
336189251Ssamstatic int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
337189251Ssam{
338189251Ssam	char cmd[256];
339189251Ssam	int res;
340189251Ssam
341189251Ssam	if (argc != 1) {
342189251Ssam		printf("Invalid AP_SCAN command: needs one argument (ap_scan "
343189251Ssam		       "value)\n");
344189251Ssam		return -1;
345189251Ssam	}
346189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "AP_SCAN %s", argv[0]);
347189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
348189251Ssam		printf("Too long AP_SCAN command.\n");
349189251Ssam		return -1;
350189251Ssam	}
351189251Ssam	return wpa_ctrl_command(ctrl, cmd);
352189251Ssam}
353189251Ssam
354189251Ssam
355189251Ssamstatic int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
356189251Ssam				char *argv[])
357189251Ssam{
358189251Ssam	char cmd[256];
359189251Ssam	int res;
360189251Ssam
361189251Ssam	if (argc != 1) {
362189251Ssam		printf("Invalid STKSTART command: needs one argument "
363189251Ssam		       "(Peer STA MAC address)\n");
364189251Ssam		return -1;
365189251Ssam	}
366189251Ssam
367189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "STKSTART %s", argv[0]);
368189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
369189251Ssam		printf("Too long STKSTART command.\n");
370189251Ssam		return -1;
371189251Ssam	}
372189251Ssam	return wpa_ctrl_command(ctrl, cmd);
373189251Ssam}
374189251Ssam
375189251Ssam
376189251Ssamstatic int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
377189251Ssam{
378189251Ssam	char cmd[256];
379189251Ssam	int res;
380189251Ssam
381189251Ssam	if (argc != 1) {
382189251Ssam		printf("Invalid FT_DS command: needs one argument "
383189251Ssam		       "(Target AP MAC address)\n");
384189251Ssam		return -1;
385189251Ssam	}
386189251Ssam
387189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "FT_DS %s", argv[0]);
388189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
389189251Ssam		printf("Too long FT_DS command.\n");
390189251Ssam		return -1;
391189251Ssam	}
392189251Ssam	return wpa_ctrl_command(ctrl, cmd);
393189251Ssam}
394189251Ssam
395189251Ssam
396189251Ssamstatic int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[])
397189251Ssam{
398189251Ssam	char cmd[256];
399189251Ssam	int res;
400189251Ssam
401189251Ssam	if (argc == 0) {
402189251Ssam		/* Any BSSID */
403189251Ssam		return wpa_ctrl_command(ctrl, "WPS_PBC");
404189251Ssam	}
405189251Ssam
406189251Ssam	/* Specific BSSID */
407189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]);
408189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
409189251Ssam		printf("Too long WPS_PBC command.\n");
410189251Ssam		return -1;
411189251Ssam	}
412189251Ssam	return wpa_ctrl_command(ctrl, cmd);
413189251Ssam}
414189251Ssam
415189251Ssam
416189251Ssamstatic int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
417189251Ssam{
418189251Ssam	char cmd[256];
419189251Ssam	int res;
420189251Ssam
421189251Ssam	if (argc == 0) {
422189251Ssam		printf("Invalid WPS_PIN command: need one or two arguments:\n"
423189251Ssam		       "- BSSID: use 'any' to select any\n"
424189251Ssam		       "- PIN: optional, used only with devices that have no "
425189251Ssam		       "display\n");
426189251Ssam		return -1;
427189251Ssam	}
428189251Ssam
429189251Ssam	if (argc == 1) {
430189251Ssam		/* Use dynamically generated PIN (returned as reply) */
431189251Ssam		res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]);
432189251Ssam		if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
433189251Ssam			printf("Too long WPS_PIN command.\n");
434189251Ssam			return -1;
435189251Ssam		}
436189251Ssam		return wpa_ctrl_command(ctrl, cmd);
437189251Ssam	}
438189251Ssam
439189251Ssam	/* Use hardcoded PIN from a label */
440189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]);
441189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
442189251Ssam		printf("Too long WPS_PIN command.\n");
443189251Ssam		return -1;
444189251Ssam	}
445189251Ssam	return wpa_ctrl_command(ctrl, cmd);
446189251Ssam}
447189251Ssam
448189251Ssam
449189251Ssamstatic int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
450189251Ssam{
451189251Ssam	char cmd[256];
452189251Ssam	int res;
453189251Ssam
454189251Ssam	if (argc != 2) {
455189251Ssam		printf("Invalid WPS_REG command: need two arguments:\n"
456189251Ssam		       "- BSSID: use 'any' to select any\n"
457189251Ssam		       "- AP PIN\n");
458189251Ssam		return -1;
459189251Ssam	}
460189251Ssam
461189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", argv[0], argv[1]);
462189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
463189251Ssam		printf("Too long WPS_REG command.\n");
464189251Ssam		return -1;
465189251Ssam	}
466189251Ssam	return wpa_ctrl_command(ctrl, cmd);
467189251Ssam}
468189251Ssam
469189251Ssam
470189251Ssamstatic int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
471189251Ssam{
472189251Ssam	char cmd[256];
473189251Ssam	int res;
474189251Ssam
475189251Ssam	if (argc != 1) {
476189251Ssam		printf("Invalid LEVEL command: needs one argument (debug "
477189251Ssam		       "level)\n");
478189251Ssam		return -1;
479189251Ssam	}
480189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
481189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
482189251Ssam		printf("Too long LEVEL command.\n");
483189251Ssam		return -1;
484189251Ssam	}
485189251Ssam	return wpa_ctrl_command(ctrl, cmd);
486189251Ssam}
487189251Ssam
488189251Ssam
489189251Ssamstatic int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[])
490189251Ssam{
491189251Ssam	char cmd[256], *pos, *end;
492189251Ssam	int i, ret;
493189251Ssam
494189251Ssam	if (argc < 2) {
495189251Ssam		printf("Invalid IDENTITY command: needs two arguments "
496189251Ssam		       "(network id and identity)\n");
497189251Ssam		return -1;
498189251Ssam	}
499189251Ssam
500189251Ssam	end = cmd + sizeof(cmd);
501189251Ssam	pos = cmd;
502189251Ssam	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s",
503189251Ssam			  argv[0], argv[1]);
504189251Ssam	if (ret < 0 || ret >= end - pos) {
505189251Ssam		printf("Too long IDENTITY command.\n");
506189251Ssam		return -1;
507189251Ssam	}
508189251Ssam	pos += ret;
509189251Ssam	for (i = 2; i < argc; i++) {
510189251Ssam		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
511189251Ssam		if (ret < 0 || ret >= end - pos) {
512189251Ssam			printf("Too long IDENTITY command.\n");
513189251Ssam			return -1;
514189251Ssam		}
515189251Ssam		pos += ret;
516189251Ssam	}
517189251Ssam
518189251Ssam	return wpa_ctrl_command(ctrl, cmd);
519189251Ssam}
520189251Ssam
521189251Ssam
522189251Ssamstatic int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[])
523189251Ssam{
524189251Ssam	char cmd[256], *pos, *end;
525189251Ssam	int i, ret;
526189251Ssam
527189251Ssam	if (argc < 2) {
528189251Ssam		printf("Invalid PASSWORD command: needs two arguments "
529189251Ssam		       "(network id and password)\n");
530189251Ssam		return -1;
531189251Ssam	}
532189251Ssam
533189251Ssam	end = cmd + sizeof(cmd);
534189251Ssam	pos = cmd;
535189251Ssam	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s",
536189251Ssam			  argv[0], argv[1]);
537189251Ssam	if (ret < 0 || ret >= end - pos) {
538189251Ssam		printf("Too long PASSWORD command.\n");
539189251Ssam		return -1;
540189251Ssam	}
541189251Ssam	pos += ret;
542189251Ssam	for (i = 2; i < argc; i++) {
543189251Ssam		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
544189251Ssam		if (ret < 0 || ret >= end - pos) {
545189251Ssam			printf("Too long PASSWORD command.\n");
546189251Ssam			return -1;
547189251Ssam		}
548189251Ssam		pos += ret;
549189251Ssam	}
550189251Ssam
551189251Ssam	return wpa_ctrl_command(ctrl, cmd);
552189251Ssam}
553189251Ssam
554189251Ssam
555189251Ssamstatic int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc,
556189251Ssam				    char *argv[])
557189251Ssam{
558189251Ssam	char cmd[256], *pos, *end;
559189251Ssam	int i, ret;
560189251Ssam
561189251Ssam	if (argc < 2) {
562189251Ssam		printf("Invalid NEW_PASSWORD command: needs two arguments "
563189251Ssam		       "(network id and password)\n");
564189251Ssam		return -1;
565189251Ssam	}
566189251Ssam
567189251Ssam	end = cmd + sizeof(cmd);
568189251Ssam	pos = cmd;
569189251Ssam	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s",
570189251Ssam			  argv[0], argv[1]);
571189251Ssam	if (ret < 0 || ret >= end - pos) {
572189251Ssam		printf("Too long NEW_PASSWORD command.\n");
573189251Ssam		return -1;
574189251Ssam	}
575189251Ssam	pos += ret;
576189251Ssam	for (i = 2; i < argc; i++) {
577189251Ssam		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
578189251Ssam		if (ret < 0 || ret >= end - pos) {
579189251Ssam			printf("Too long NEW_PASSWORD command.\n");
580189251Ssam			return -1;
581189251Ssam		}
582189251Ssam		pos += ret;
583189251Ssam	}
584189251Ssam
585189251Ssam	return wpa_ctrl_command(ctrl, cmd);
586189251Ssam}
587189251Ssam
588189251Ssam
589189251Ssamstatic int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
590189251Ssam{
591189251Ssam	char cmd[256], *pos, *end;
592189251Ssam	int i, ret;
593189251Ssam
594189251Ssam	if (argc < 2) {
595189251Ssam		printf("Invalid PIN command: needs two arguments "
596189251Ssam		       "(network id and pin)\n");
597189251Ssam		return -1;
598189251Ssam	}
599189251Ssam
600189251Ssam	end = cmd + sizeof(cmd);
601189251Ssam	pos = cmd;
602189251Ssam	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s",
603189251Ssam			  argv[0], argv[1]);
604189251Ssam	if (ret < 0 || ret >= end - pos) {
605189251Ssam		printf("Too long PIN command.\n");
606189251Ssam		return -1;
607189251Ssam	}
608189251Ssam	pos += ret;
609189251Ssam	for (i = 2; i < argc; i++) {
610189251Ssam		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
611189251Ssam		if (ret < 0 || ret >= end - pos) {
612189251Ssam			printf("Too long PIN command.\n");
613189251Ssam			return -1;
614189251Ssam		}
615189251Ssam		pos += ret;
616189251Ssam	}
617189251Ssam	return wpa_ctrl_command(ctrl, cmd);
618189251Ssam}
619189251Ssam
620189251Ssam
621189251Ssamstatic int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[])
622189251Ssam{
623189251Ssam	char cmd[256], *pos, *end;
624189251Ssam	int i, ret;
625189251Ssam
626189251Ssam	if (argc < 2) {
627189251Ssam		printf("Invalid OTP command: needs two arguments (network "
628189251Ssam		       "id and password)\n");
629189251Ssam		return -1;
630189251Ssam	}
631189251Ssam
632189251Ssam	end = cmd + sizeof(cmd);
633189251Ssam	pos = cmd;
634189251Ssam	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s",
635189251Ssam			  argv[0], argv[1]);
636189251Ssam	if (ret < 0 || ret >= end - pos) {
637189251Ssam		printf("Too long OTP command.\n");
638189251Ssam		return -1;
639189251Ssam	}
640189251Ssam	pos += ret;
641189251Ssam	for (i = 2; i < argc; i++) {
642189251Ssam		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
643189251Ssam		if (ret < 0 || ret >= end - pos) {
644189251Ssam			printf("Too long OTP command.\n");
645189251Ssam			return -1;
646189251Ssam		}
647189251Ssam		pos += ret;
648189251Ssam	}
649189251Ssam
650189251Ssam	return wpa_ctrl_command(ctrl, cmd);
651189251Ssam}
652189251Ssam
653189251Ssam
654189251Ssamstatic int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc,
655189251Ssam				  char *argv[])
656189251Ssam{
657189251Ssam	char cmd[256], *pos, *end;
658189251Ssam	int i, ret;
659189251Ssam
660189251Ssam	if (argc < 2) {
661189251Ssam		printf("Invalid PASSPHRASE command: needs two arguments "
662189251Ssam		       "(network id and passphrase)\n");
663189251Ssam		return -1;
664189251Ssam	}
665189251Ssam
666189251Ssam	end = cmd + sizeof(cmd);
667189251Ssam	pos = cmd;
668189251Ssam	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s",
669189251Ssam			  argv[0], argv[1]);
670189251Ssam	if (ret < 0 || ret >= end - pos) {
671189251Ssam		printf("Too long PASSPHRASE command.\n");
672189251Ssam		return -1;
673189251Ssam	}
674189251Ssam	pos += ret;
675189251Ssam	for (i = 2; i < argc; i++) {
676189251Ssam		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
677189251Ssam		if (ret < 0 || ret >= end - pos) {
678189251Ssam			printf("Too long PASSPHRASE command.\n");
679189251Ssam			return -1;
680189251Ssam		}
681189251Ssam		pos += ret;
682189251Ssam	}
683189251Ssam
684189251Ssam	return wpa_ctrl_command(ctrl, cmd);
685189251Ssam}
686189251Ssam
687189251Ssam
688189251Ssamstatic int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[])
689189251Ssam{
690189251Ssam	char cmd[256], *pos, *end;
691189251Ssam	int i, ret;
692189251Ssam
693189251Ssam	if (argc < 2) {
694189251Ssam		printf("Invalid BSSID command: needs two arguments (network "
695189251Ssam		       "id and BSSID)\n");
696189251Ssam		return -1;
697189251Ssam	}
698189251Ssam
699189251Ssam	end = cmd + sizeof(cmd);
700189251Ssam	pos = cmd;
701189251Ssam	ret = os_snprintf(pos, end - pos, "BSSID");
702189251Ssam	if (ret < 0 || ret >= end - pos) {
703189251Ssam		printf("Too long BSSID command.\n");
704189251Ssam		return -1;
705189251Ssam	}
706189251Ssam	pos += ret;
707189251Ssam	for (i = 0; i < argc; i++) {
708189251Ssam		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
709189251Ssam		if (ret < 0 || ret >= end - pos) {
710189251Ssam			printf("Too long BSSID command.\n");
711189251Ssam			return -1;
712189251Ssam		}
713189251Ssam		pos += ret;
714189251Ssam	}
715189251Ssam
716189251Ssam	return wpa_ctrl_command(ctrl, cmd);
717189251Ssam}
718189251Ssam
719189251Ssam
720189251Ssamstatic int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc,
721189251Ssam				     char *argv[])
722189251Ssam{
723189251Ssam	return wpa_ctrl_command(ctrl, "LIST_NETWORKS");
724189251Ssam}
725189251Ssam
726189251Ssam
727189251Ssamstatic int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc,
728189251Ssam				      char *argv[])
729189251Ssam{
730189251Ssam	char cmd[32];
731189251Ssam	int res;
732189251Ssam
733189251Ssam	if (argc < 1) {
734189251Ssam		printf("Invalid SELECT_NETWORK command: needs one argument "
735189251Ssam		       "(network id)\n");
736189251Ssam		return -1;
737189251Ssam	}
738189251Ssam
739189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[0]);
740189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd))
741189251Ssam		return -1;
742189251Ssam	cmd[sizeof(cmd) - 1] = '\0';
743189251Ssam
744189251Ssam	return wpa_ctrl_command(ctrl, cmd);
745189251Ssam}
746189251Ssam
747189251Ssam
748189251Ssamstatic int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc,
749189251Ssam				      char *argv[])
750189251Ssam{
751189251Ssam	char cmd[32];
752189251Ssam	int res;
753189251Ssam
754189251Ssam	if (argc < 1) {
755189251Ssam		printf("Invalid ENABLE_NETWORK command: needs one argument "
756189251Ssam		       "(network id)\n");
757189251Ssam		return -1;
758189251Ssam	}
759189251Ssam
760189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", argv[0]);
761189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd))
762189251Ssam		return -1;
763189251Ssam	cmd[sizeof(cmd) - 1] = '\0';
764189251Ssam
765189251Ssam	return wpa_ctrl_command(ctrl, cmd);
766189251Ssam}
767189251Ssam
768189251Ssam
769189251Ssamstatic int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc,
770189251Ssam				       char *argv[])
771189251Ssam{
772189251Ssam	char cmd[32];
773189251Ssam	int res;
774189251Ssam
775189251Ssam	if (argc < 1) {
776189251Ssam		printf("Invalid DISABLE_NETWORK command: needs one argument "
777189251Ssam		       "(network id)\n");
778189251Ssam		return -1;
779189251Ssam	}
780189251Ssam
781189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[0]);
782189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd))
783189251Ssam		return -1;
784189251Ssam	cmd[sizeof(cmd) - 1] = '\0';
785189251Ssam
786189251Ssam	return wpa_ctrl_command(ctrl, cmd);
787189251Ssam}
788189251Ssam
789189251Ssam
790189251Ssamstatic int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc,
791189251Ssam				   char *argv[])
792189251Ssam{
793189251Ssam	return wpa_ctrl_command(ctrl, "ADD_NETWORK");
794189251Ssam}
795189251Ssam
796189251Ssam
797189251Ssamstatic int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc,
798189251Ssam				      char *argv[])
799189251Ssam{
800189251Ssam	char cmd[32];
801189251Ssam	int res;
802189251Ssam
803189251Ssam	if (argc < 1) {
804189251Ssam		printf("Invalid REMOVE_NETWORK command: needs one argument "
805189251Ssam		       "(network id)\n");
806189251Ssam		return -1;
807189251Ssam	}
808189251Ssam
809189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[0]);
810189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd))
811189251Ssam		return -1;
812189251Ssam	cmd[sizeof(cmd) - 1] = '\0';
813189251Ssam
814189251Ssam	return wpa_ctrl_command(ctrl, cmd);
815189251Ssam}
816189251Ssam
817189251Ssam
818189251Ssamstatic void wpa_cli_show_network_variables(void)
819189251Ssam{
820189251Ssam	printf("set_network variables:\n"
821189251Ssam	       "  ssid (network name, SSID)\n"
822189251Ssam	       "  psk (WPA passphrase or pre-shared key)\n"
823189251Ssam	       "  key_mgmt (key management protocol)\n"
824189251Ssam	       "  identity (EAP identity)\n"
825189251Ssam	       "  password (EAP password)\n"
826189251Ssam	       "  ...\n"
827189251Ssam	       "\n"
828189251Ssam	       "Note: Values are entered in the same format as the "
829189251Ssam	       "configuration file is using,\n"
830189251Ssam	       "i.e., strings values need to be inside double quotation "
831189251Ssam	       "marks.\n"
832189251Ssam	       "For example: set_network 1 ssid \"network name\"\n"
833189251Ssam	       "\n"
834189251Ssam	       "Please see wpa_supplicant.conf documentation for full list "
835189251Ssam	       "of\navailable variables.\n");
836189251Ssam}
837189251Ssam
838189251Ssam
839189251Ssamstatic int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc,
840189251Ssam				   char *argv[])
841189251Ssam{
842189251Ssam	char cmd[256];
843189251Ssam	int res;
844189251Ssam
845189251Ssam	if (argc == 0) {
846189251Ssam		wpa_cli_show_network_variables();
847189251Ssam		return 0;
848189251Ssam	}
849189251Ssam
850189251Ssam	if (argc != 3) {
851189251Ssam		printf("Invalid SET_NETWORK command: needs three arguments\n"
852189251Ssam		       "(network id, variable name, and value)\n");
853189251Ssam		return -1;
854189251Ssam	}
855189251Ssam
856189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s",
857189251Ssam			  argv[0], argv[1], argv[2]);
858189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
859189251Ssam		printf("Too long SET_NETWORK command.\n");
860189251Ssam		return -1;
861189251Ssam	}
862189251Ssam	return wpa_ctrl_command(ctrl, cmd);
863189251Ssam}
864189251Ssam
865189251Ssam
866189251Ssamstatic int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc,
867189251Ssam				   char *argv[])
868189251Ssam{
869189251Ssam	char cmd[256];
870189251Ssam	int res;
871189251Ssam
872189251Ssam	if (argc == 0) {
873189251Ssam		wpa_cli_show_network_variables();
874189251Ssam		return 0;
875189251Ssam	}
876189251Ssam
877189251Ssam	if (argc != 2) {
878189251Ssam		printf("Invalid GET_NETWORK command: needs two arguments\n"
879189251Ssam		       "(network id and variable name)\n");
880189251Ssam		return -1;
881189251Ssam	}
882189251Ssam
883189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s",
884189251Ssam			  argv[0], argv[1]);
885189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
886189251Ssam		printf("Too long GET_NETWORK command.\n");
887189251Ssam		return -1;
888189251Ssam	}
889189251Ssam	return wpa_ctrl_command(ctrl, cmd);
890189251Ssam}
891189251Ssam
892189251Ssam
893189251Ssamstatic int wpa_cli_cmd_disconnect(struct wpa_ctrl *ctrl, int argc,
894189251Ssam				  char *argv[])
895189251Ssam{
896189251Ssam	return wpa_ctrl_command(ctrl, "DISCONNECT");
897189251Ssam}
898189251Ssam
899189251Ssam
900189251Ssamstatic int wpa_cli_cmd_reconnect(struct wpa_ctrl *ctrl, int argc,
901189251Ssam				  char *argv[])
902189251Ssam{
903189251Ssam	return wpa_ctrl_command(ctrl, "RECONNECT");
904189251Ssam}
905189251Ssam
906189251Ssam
907189251Ssamstatic int wpa_cli_cmd_save_config(struct wpa_ctrl *ctrl, int argc,
908189251Ssam				   char *argv[])
909189251Ssam{
910189251Ssam	return wpa_ctrl_command(ctrl, "SAVE_CONFIG");
911189251Ssam}
912189251Ssam
913189251Ssam
914189251Ssamstatic int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
915189251Ssam{
916189251Ssam	return wpa_ctrl_command(ctrl, "SCAN");
917189251Ssam}
918189251Ssam
919189251Ssam
920189251Ssamstatic int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc,
921189251Ssam				    char *argv[])
922189251Ssam{
923189251Ssam	return wpa_ctrl_command(ctrl, "SCAN_RESULTS");
924189251Ssam}
925189251Ssam
926189251Ssam
927189251Ssamstatic int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
928189251Ssam{
929189251Ssam	char cmd[64];
930189251Ssam	int res;
931189251Ssam
932189251Ssam	if (argc != 1) {
933189251Ssam		printf("Invalid BSS command: need one argument (index or "
934189251Ssam		       "BSSID)\n");
935189251Ssam		return -1;
936189251Ssam	}
937189251Ssam
938189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "BSS %s", argv[0]);
939189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd))
940189251Ssam		return -1;
941189251Ssam	cmd[sizeof(cmd) - 1] = '\0';
942189251Ssam
943189251Ssam	return wpa_ctrl_command(ctrl, cmd);
944189251Ssam}
945189251Ssam
946189251Ssam
947189251Ssamstatic int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
948189251Ssam				      char *argv[])
949189251Ssam{
950189251Ssam	char cmd[64];
951189251Ssam	int res;
952189251Ssam
953189251Ssam	if (argc < 1 || argc > 2) {
954189251Ssam		printf("Invalid GET_CAPABILITY command: need either one or "
955189251Ssam		       "two arguments\n");
956189251Ssam		return -1;
957189251Ssam	}
958189251Ssam
959189251Ssam	if ((argc == 2) && os_strcmp(argv[1], "strict") != 0) {
960189251Ssam		printf("Invalid GET_CAPABILITY command: second argument, "
961189251Ssam		       "if any, must be 'strict'\n");
962189251Ssam		return -1;
963189251Ssam	}
964189251Ssam
965189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s%s", argv[0],
966189251Ssam			  (argc == 2) ? " strict" : "");
967189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd))
968189251Ssam		return -1;
969189251Ssam	cmd[sizeof(cmd) - 1] = '\0';
970189251Ssam
971189251Ssam	return wpa_ctrl_command(ctrl, cmd);
972189251Ssam}
973189251Ssam
974189251Ssam
975189251Ssamstatic int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl)
976189251Ssam{
977189251Ssam	printf("Available interfaces:\n");
978189251Ssam	return wpa_ctrl_command(ctrl, "INTERFACES");
979189251Ssam}
980189251Ssam
981189251Ssam
982189251Ssamstatic int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[])
983189251Ssam{
984189251Ssam	if (argc < 1) {
985189251Ssam		wpa_cli_list_interfaces(ctrl);
986189251Ssam		return 0;
987189251Ssam	}
988189251Ssam
989189251Ssam	wpa_cli_close_connection();
990189251Ssam	os_free(ctrl_ifname);
991189251Ssam	ctrl_ifname = os_strdup(argv[0]);
992189251Ssam
993189251Ssam	if (wpa_cli_open_connection(ctrl_ifname)) {
994189251Ssam		printf("Connected to interface '%s.\n", ctrl_ifname);
995189251Ssam		if (wpa_ctrl_attach(ctrl_conn) == 0) {
996189251Ssam			wpa_cli_attached = 1;
997189251Ssam		} else {
998189251Ssam			printf("Warning: Failed to attach to "
999189251Ssam			       "wpa_supplicant.\n");
1000189251Ssam		}
1001189251Ssam	} else {
1002189251Ssam		printf("Could not connect to interface '%s' - re-trying\n",
1003189251Ssam		       ctrl_ifname);
1004189251Ssam	}
1005189251Ssam	return 0;
1006189251Ssam}
1007189251Ssam
1008189251Ssam
1009189251Ssamstatic int wpa_cli_cmd_reconfigure(struct wpa_ctrl *ctrl, int argc,
1010189251Ssam				   char *argv[])
1011189251Ssam{
1012189251Ssam	return wpa_ctrl_command(ctrl, "RECONFIGURE");
1013189251Ssam}
1014189251Ssam
1015189251Ssam
1016189251Ssamstatic int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc,
1017189251Ssam				 char *argv[])
1018189251Ssam{
1019189251Ssam	return wpa_ctrl_command(ctrl, "TERMINATE");
1020189251Ssam}
1021189251Ssam
1022189251Ssam
1023189251Ssamstatic int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc,
1024189251Ssam				     char *argv[])
1025189251Ssam{
1026189251Ssam	char cmd[256];
1027189251Ssam	int res;
1028189251Ssam
1029189251Ssam	if (argc < 1) {
1030189251Ssam		printf("Invalid INTERFACE_ADD command: needs at least one "
1031189251Ssam		       "argument (interface name)\n"
1032189251Ssam		       "All arguments: ifname confname driver ctrl_interface "
1033189251Ssam		       "driver_param bridge_name\n");
1034189251Ssam		return -1;
1035189251Ssam	}
1036189251Ssam
1037189251Ssam	/*
1038189251Ssam	 * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
1039189251Ssam	 * <driver_param>TAB<bridge_name>
1040189251Ssam	 */
1041189251Ssam	res = os_snprintf(cmd, sizeof(cmd),
1042189251Ssam			  "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s",
1043189251Ssam			  argv[0],
1044189251Ssam			  argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "",
1045189251Ssam			  argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "",
1046189251Ssam			  argc > 5 ? argv[5] : "");
1047189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd))
1048189251Ssam		return -1;
1049189251Ssam	cmd[sizeof(cmd) - 1] = '\0';
1050189251Ssam	return wpa_ctrl_command(ctrl, cmd);
1051189251Ssam}
1052189251Ssam
1053189251Ssam
1054189251Ssamstatic int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc,
1055189251Ssam					char *argv[])
1056189251Ssam{
1057189251Ssam	char cmd[128];
1058189251Ssam	int res;
1059189251Ssam
1060189251Ssam	if (argc != 1) {
1061189251Ssam		printf("Invalid INTERFACE_REMOVE command: needs one argument "
1062189251Ssam		       "(interface name)\n");
1063189251Ssam		return -1;
1064189251Ssam	}
1065189251Ssam
1066189251Ssam	res = os_snprintf(cmd, sizeof(cmd), "INTERFACE_REMOVE %s", argv[0]);
1067189251Ssam	if (res < 0 || (size_t) res >= sizeof(cmd))
1068189251Ssam		return -1;
1069189251Ssam	cmd[sizeof(cmd) - 1] = '\0';
1070189251Ssam	return wpa_ctrl_command(ctrl, cmd);
1071189251Ssam}
1072189251Ssam
1073189251Ssam
1074189251Ssamstatic int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc,
1075189251Ssam				      char *argv[])
1076189251Ssam{
1077189251Ssam	return wpa_ctrl_command(ctrl, "INTERFACE_LIST");
1078189251Ssam}
1079189251Ssam
1080189251Ssam
1081189251Ssamenum wpa_cli_cmd_flags {
1082189251Ssam	cli_cmd_flag_none		= 0x00,
1083189251Ssam	cli_cmd_flag_sensitive		= 0x01
1084189251Ssam};
1085189251Ssam
1086189251Ssamstruct wpa_cli_cmd {
1087189251Ssam	const char *cmd;
1088189251Ssam	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
1089189251Ssam	enum wpa_cli_cmd_flags flags;
1090189251Ssam	const char *usage;
1091189251Ssam};
1092189251Ssam
1093189251Ssamstatic struct wpa_cli_cmd wpa_cli_commands[] = {
1094189251Ssam	{ "status", wpa_cli_cmd_status,
1095189251Ssam	  cli_cmd_flag_none,
1096189251Ssam	  "[verbose] = get current WPA/EAPOL/EAP status" },
1097189251Ssam	{ "ping", wpa_cli_cmd_ping,
1098189251Ssam	  cli_cmd_flag_none,
1099189251Ssam	  "= pings wpa_supplicant" },
1100189251Ssam	{ "mib", wpa_cli_cmd_mib,
1101189251Ssam	  cli_cmd_flag_none,
1102189251Ssam	  "= get MIB variables (dot1x, dot11)" },
1103189251Ssam	{ "help", wpa_cli_cmd_help,
1104189251Ssam	  cli_cmd_flag_none,
1105189251Ssam	  "= show this usage help" },
1106189251Ssam	{ "interface", wpa_cli_cmd_interface,
1107189251Ssam	  cli_cmd_flag_none,
1108189251Ssam	  "[ifname] = show interfaces/select interface" },
1109189251Ssam	{ "level", wpa_cli_cmd_level,
1110189251Ssam	  cli_cmd_flag_none,
1111189251Ssam	  "<debug level> = change debug level" },
1112189251Ssam	{ "license", wpa_cli_cmd_license,
1113189251Ssam	  cli_cmd_flag_none,
1114189251Ssam	  "= show full wpa_cli license" },
1115189251Ssam	{ "quit", wpa_cli_cmd_quit,
1116189251Ssam	  cli_cmd_flag_none,
1117189251Ssam	  "= exit wpa_cli" },
1118189251Ssam	{ "set", wpa_cli_cmd_set,
1119189251Ssam	  cli_cmd_flag_none,
1120189251Ssam	  "= set variables (shows list of variables when run without "
1121189251Ssam	  "arguments)" },
1122189251Ssam	{ "logon", wpa_cli_cmd_logon,
1123189251Ssam	  cli_cmd_flag_none,
1124189251Ssam	  "= IEEE 802.1X EAPOL state machine logon" },
1125189251Ssam	{ "logoff", wpa_cli_cmd_logoff,
1126189251Ssam	  cli_cmd_flag_none,
1127189251Ssam	  "= IEEE 802.1X EAPOL state machine logoff" },
1128189251Ssam	{ "pmksa", wpa_cli_cmd_pmksa,
1129189251Ssam	  cli_cmd_flag_none,
1130189251Ssam	  "= show PMKSA cache" },
1131189251Ssam	{ "reassociate", wpa_cli_cmd_reassociate,
1132189251Ssam	  cli_cmd_flag_none,
1133189251Ssam	  "= force reassociation" },
1134189251Ssam	{ "preauthenticate", wpa_cli_cmd_preauthenticate,
1135189251Ssam	  cli_cmd_flag_none,
1136189251Ssam	  "<BSSID> = force preauthentication" },
1137189251Ssam	{ "identity", wpa_cli_cmd_identity,
1138189251Ssam	  cli_cmd_flag_none,
1139189251Ssam	  "<network id> <identity> = configure identity for an SSID" },
1140189251Ssam	{ "password", wpa_cli_cmd_password,
1141189251Ssam	  cli_cmd_flag_sensitive,
1142189251Ssam	  "<network id> <password> = configure password for an SSID" },
1143189251Ssam	{ "new_password", wpa_cli_cmd_new_password,
1144189251Ssam	  cli_cmd_flag_sensitive,
1145189251Ssam	  "<network id> <password> = change password for an SSID" },
1146189251Ssam	{ "pin", wpa_cli_cmd_pin,
1147189251Ssam	  cli_cmd_flag_sensitive,
1148189251Ssam	  "<network id> <pin> = configure pin for an SSID" },
1149189251Ssam	{ "otp", wpa_cli_cmd_otp,
1150189251Ssam	  cli_cmd_flag_sensitive,
1151189251Ssam	  "<network id> <password> = configure one-time-password for an SSID"
1152189251Ssam	},
1153189251Ssam	{ "passphrase", wpa_cli_cmd_passphrase,
1154189251Ssam	  cli_cmd_flag_sensitive,
1155189251Ssam	  "<network id> <passphrase> = configure private key passphrase\n"
1156189251Ssam	  "  for an SSID" },
1157189251Ssam	{ "bssid", wpa_cli_cmd_bssid,
1158189251Ssam	  cli_cmd_flag_none,
1159189251Ssam	  "<network id> <BSSID> = set preferred BSSID for an SSID" },
1160189251Ssam	{ "list_networks", wpa_cli_cmd_list_networks,
1161189251Ssam	  cli_cmd_flag_none,
1162189251Ssam	  "= list configured networks" },
1163189251Ssam	{ "select_network", wpa_cli_cmd_select_network,
1164189251Ssam	  cli_cmd_flag_none,
1165189251Ssam	  "<network id> = select a network (disable others)" },
1166189251Ssam	{ "enable_network", wpa_cli_cmd_enable_network,
1167189251Ssam	  cli_cmd_flag_none,
1168189251Ssam	  "<network id> = enable a network" },
1169189251Ssam	{ "disable_network", wpa_cli_cmd_disable_network,
1170189251Ssam	  cli_cmd_flag_none,
1171189251Ssam	  "<network id> = disable a network" },
1172189251Ssam	{ "add_network", wpa_cli_cmd_add_network,
1173189251Ssam	  cli_cmd_flag_none,
1174189251Ssam	  "= add a network" },
1175189251Ssam	{ "remove_network", wpa_cli_cmd_remove_network,
1176189251Ssam	  cli_cmd_flag_none,
1177189251Ssam	  "<network id> = remove a network" },
1178189251Ssam	{ "set_network", wpa_cli_cmd_set_network,
1179189251Ssam	  cli_cmd_flag_sensitive,
1180189251Ssam	  "<network id> <variable> <value> = set network variables (shows\n"
1181189251Ssam	  "  list of variables when run without arguments)" },
1182189251Ssam	{ "get_network", wpa_cli_cmd_get_network,
1183189251Ssam	  cli_cmd_flag_none,
1184189251Ssam	  "<network id> <variable> = get network variables" },
1185189251Ssam	{ "save_config", wpa_cli_cmd_save_config,
1186189251Ssam	  cli_cmd_flag_none,
1187189251Ssam	  "= save the current configuration" },
1188189251Ssam	{ "disconnect", wpa_cli_cmd_disconnect,
1189189251Ssam	  cli_cmd_flag_none,
1190189251Ssam	  "= disconnect and wait for reassociate/reconnect command before\n"
1191189251Ssam	  "  connecting" },
1192189251Ssam	{ "reconnect", wpa_cli_cmd_reconnect,
1193189251Ssam	  cli_cmd_flag_none,
1194189251Ssam	  "= like reassociate, but only takes effect if already disconnected"
1195189251Ssam	},
1196189251Ssam	{ "scan", wpa_cli_cmd_scan,
1197189251Ssam	  cli_cmd_flag_none,
1198189251Ssam	  "= request new BSS scan" },
1199189251Ssam	{ "scan_results", wpa_cli_cmd_scan_results,
1200189251Ssam	  cli_cmd_flag_none,
1201189251Ssam	  "= get latest scan results" },
1202189251Ssam	{ "bss", wpa_cli_cmd_bss,
1203189251Ssam	  cli_cmd_flag_none,
1204189251Ssam	  "<<idx> | <bssid>> = get detailed scan result info" },
1205189251Ssam	{ "get_capability", wpa_cli_cmd_get_capability,
1206189251Ssam	  cli_cmd_flag_none,
1207189251Ssam	  "<eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies" },
1208189251Ssam	{ "reconfigure", wpa_cli_cmd_reconfigure,
1209189251Ssam	  cli_cmd_flag_none,
1210189251Ssam	  "= force wpa_supplicant to re-read its configuration file" },
1211189251Ssam	{ "terminate", wpa_cli_cmd_terminate,
1212189251Ssam	  cli_cmd_flag_none,
1213189251Ssam	  "= terminate wpa_supplicant" },
1214189251Ssam	{ "interface_add", wpa_cli_cmd_interface_add,
1215189251Ssam	  cli_cmd_flag_none,
1216189251Ssam	  "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n"
1217189251Ssam	  "  <bridge_name> = adds new interface, all parameters but <ifname>\n"
1218189251Ssam	  "  are optional" },
1219189251Ssam	{ "interface_remove", wpa_cli_cmd_interface_remove,
1220189251Ssam	  cli_cmd_flag_none,
1221189251Ssam	  "<ifname> = removes the interface" },
1222189251Ssam	{ "interface_list", wpa_cli_cmd_interface_list,
1223189251Ssam	  cli_cmd_flag_none,
1224189251Ssam	  "= list available interfaces" },
1225189251Ssam	{ "ap_scan", wpa_cli_cmd_ap_scan,
1226189251Ssam	  cli_cmd_flag_none,
1227189251Ssam	  "<value> = set ap_scan parameter" },
1228189251Ssam	{ "stkstart", wpa_cli_cmd_stkstart,
1229189251Ssam	  cli_cmd_flag_none,
1230189251Ssam	  "<addr> = request STK negotiation with <addr>" },
1231189251Ssam	{ "ft_ds", wpa_cli_cmd_ft_ds,
1232189251Ssam	  cli_cmd_flag_none,
1233189251Ssam	  "<addr> = request over-the-DS FT with <addr>" },
1234189251Ssam	{ "wps_pbc", wpa_cli_cmd_wps_pbc,
1235189251Ssam	  cli_cmd_flag_none,
1236189251Ssam	  "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" },
1237189251Ssam	{ "wps_pin", wpa_cli_cmd_wps_pin,
1238189251Ssam	  cli_cmd_flag_sensitive,
1239189251Ssam	  "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
1240189251Ssam	  "hardcoded)" },
1241189251Ssam	{ "wps_reg", wpa_cli_cmd_wps_reg,
1242189251Ssam	  cli_cmd_flag_sensitive,
1243189251Ssam	  "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
1244189251Ssam	{ NULL, NULL, cli_cmd_flag_none, NULL }
1245189251Ssam};
1246189251Ssam
1247189251Ssam
1248189251Ssam/*
1249189251Ssam * Prints command usage, lines are padded with the specified string.
1250189251Ssam */
1251189251Ssamstatic void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad)
1252189251Ssam{
1253189251Ssam	char c;
1254189251Ssam	size_t n;
1255189251Ssam
1256189251Ssam	printf("%s%s ", pad, cmd->cmd);
1257189251Ssam	for (n = 0; (c = cmd->usage[n]); n++) {
1258189251Ssam		printf("%c", c);
1259189251Ssam		if (c == '\n')
1260189251Ssam			printf("%s", pad);
1261189251Ssam	}
1262189251Ssam	printf("\n");
1263189251Ssam}
1264189251Ssam
1265189251Ssam
1266189251Ssamstatic void print_help(void)
1267189251Ssam{
1268189251Ssam	int n;
1269189251Ssam	printf("commands:\n");
1270189251Ssam	for (n = 0; wpa_cli_commands[n].cmd; n++)
1271189251Ssam		print_cmd_help(&wpa_cli_commands[n], "  ");
1272189251Ssam}
1273189251Ssam
1274189251Ssam
1275189251Ssam#ifdef CONFIG_READLINE
1276189251Ssamstatic int cmd_has_sensitive_data(const char *cmd)
1277189251Ssam{
1278189251Ssam	const char *c, *delim;
1279189251Ssam	int n;
1280189251Ssam	size_t len;
1281189251Ssam
1282189251Ssam	delim = os_strchr(cmd, ' ');
1283189251Ssam	if (delim)
1284189251Ssam		len = delim - cmd;
1285189251Ssam	else
1286189251Ssam		len = os_strlen(cmd);
1287189251Ssam
1288189251Ssam	for (n = 0; (c = wpa_cli_commands[n].cmd); n++) {
1289189251Ssam		if (os_strncasecmp(cmd, c, len) == 0 && len == os_strlen(c))
1290189251Ssam			return (wpa_cli_commands[n].flags &
1291189251Ssam				cli_cmd_flag_sensitive);
1292189251Ssam	}
1293189251Ssam	return 0;
1294189251Ssam}
1295189251Ssam#endif /* CONFIG_READLINE */
1296189251Ssam
1297189251Ssam
1298189251Ssamstatic int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
1299189251Ssam{
1300189251Ssam	struct wpa_cli_cmd *cmd, *match = NULL;
1301189251Ssam	int count;
1302189251Ssam	int ret = 0;
1303189251Ssam
1304189251Ssam	count = 0;
1305189251Ssam	cmd = wpa_cli_commands;
1306189251Ssam	while (cmd->cmd) {
1307189251Ssam		if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
1308189251Ssam		{
1309189251Ssam			match = cmd;
1310189251Ssam			if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
1311189251Ssam				/* we have an exact match */
1312189251Ssam				count = 1;
1313189251Ssam				break;
1314189251Ssam			}
1315189251Ssam			count++;
1316189251Ssam		}
1317189251Ssam		cmd++;
1318189251Ssam	}
1319189251Ssam
1320189251Ssam	if (count > 1) {
1321189251Ssam		printf("Ambiguous command '%s'; possible commands:", argv[0]);
1322189251Ssam		cmd = wpa_cli_commands;
1323189251Ssam		while (cmd->cmd) {
1324189251Ssam			if (os_strncasecmp(cmd->cmd, argv[0],
1325189251Ssam					   os_strlen(argv[0])) == 0) {
1326189251Ssam				printf(" %s", cmd->cmd);
1327189251Ssam			}
1328189251Ssam			cmd++;
1329189251Ssam		}
1330189251Ssam		printf("\n");
1331189251Ssam		ret = 1;
1332189251Ssam	} else if (count == 0) {
1333189251Ssam		printf("Unknown command '%s'\n", argv[0]);
1334189251Ssam		ret = 1;
1335189251Ssam	} else {
1336189251Ssam		ret = match->handler(ctrl, argc - 1, &argv[1]);
1337189251Ssam	}
1338189251Ssam
1339189251Ssam	return ret;
1340189251Ssam}
1341189251Ssam
1342189251Ssam
1343189251Ssamstatic int str_match(const char *a, const char *b)
1344189251Ssam{
1345189251Ssam	return os_strncmp(a, b, os_strlen(b)) == 0;
1346189251Ssam}
1347189251Ssam
1348189251Ssam
1349189251Ssamstatic int wpa_cli_exec(const char *program, const char *arg1,
1350189251Ssam			const char *arg2)
1351189251Ssam{
1352189251Ssam	char *cmd;
1353189251Ssam	size_t len;
1354189251Ssam	int res;
1355189251Ssam	int ret = 0;
1356189251Ssam
1357189251Ssam	len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
1358189251Ssam	cmd = os_malloc(len);
1359189251Ssam	if (cmd == NULL)
1360189251Ssam		return -1;
1361189251Ssam	res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
1362189251Ssam	if (res < 0 || (size_t) res >= len) {
1363189251Ssam		os_free(cmd);
1364189251Ssam		return -1;
1365189251Ssam	}
1366189251Ssam	cmd[len - 1] = '\0';
1367189251Ssam#ifndef _WIN32_WCE
1368189251Ssam	if (system(cmd) < 0)
1369189251Ssam		ret = -1;
1370189251Ssam#endif /* _WIN32_WCE */
1371189251Ssam	os_free(cmd);
1372189251Ssam
1373189251Ssam	return ret;
1374189251Ssam}
1375189251Ssam
1376189251Ssam
1377189251Ssamstatic void wpa_cli_action_process(const char *msg)
1378189251Ssam{
1379189251Ssam	const char *pos;
1380189251Ssam	char *copy = NULL, *id, *pos2;
1381189251Ssam
1382189251Ssam	pos = msg;
1383189251Ssam	if (*pos == '<') {
1384189251Ssam		/* skip priority */
1385189251Ssam		pos = os_strchr(pos, '>');
1386189251Ssam		if (pos)
1387189251Ssam			pos++;
1388189251Ssam		else
1389189251Ssam			pos = msg;
1390189251Ssam	}
1391189251Ssam
1392189251Ssam	if (str_match(pos, WPA_EVENT_CONNECTED)) {
1393189251Ssam		int new_id = -1;
1394189251Ssam		os_unsetenv("WPA_ID");
1395189251Ssam		os_unsetenv("WPA_ID_STR");
1396189251Ssam		os_unsetenv("WPA_CTRL_DIR");
1397189251Ssam
1398189251Ssam		pos = os_strstr(pos, "[id=");
1399189251Ssam		if (pos)
1400189251Ssam			copy = os_strdup(pos + 4);
1401189251Ssam
1402189251Ssam		if (copy) {
1403189251Ssam			pos2 = id = copy;
1404189251Ssam			while (*pos2 && *pos2 != ' ')
1405189251Ssam				pos2++;
1406189251Ssam			*pos2++ = '\0';
1407189251Ssam			new_id = atoi(id);
1408189251Ssam			os_setenv("WPA_ID", id, 1);
1409189251Ssam			while (*pos2 && *pos2 != '=')
1410189251Ssam				pos2++;
1411189251Ssam			if (*pos2 == '=')
1412189251Ssam				pos2++;
1413189251Ssam			id = pos2;
1414189251Ssam			while (*pos2 && *pos2 != ']')
1415189251Ssam				pos2++;
1416189251Ssam			*pos2 = '\0';
1417189251Ssam			os_setenv("WPA_ID_STR", id, 1);
1418189251Ssam			os_free(copy);
1419189251Ssam		}
1420189251Ssam
1421189251Ssam		os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1);
1422189251Ssam
1423189251Ssam		if (!wpa_cli_connected || new_id != wpa_cli_last_id) {
1424189251Ssam			wpa_cli_connected = 1;
1425189251Ssam			wpa_cli_last_id = new_id;
1426189251Ssam			wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED");
1427189251Ssam		}
1428189251Ssam	} else if (str_match(pos, WPA_EVENT_DISCONNECTED)) {
1429189251Ssam		if (wpa_cli_connected) {
1430189251Ssam			wpa_cli_connected = 0;
1431189251Ssam			wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED");
1432189251Ssam		}
1433189251Ssam	} else if (str_match(pos, WPA_EVENT_TERMINATING)) {
1434189251Ssam		printf("wpa_supplicant is terminating - stop monitoring\n");
1435189251Ssam		wpa_cli_quit = 1;
1436189251Ssam	}
1437189251Ssam}
1438189251Ssam
1439189251Ssam
1440189251Ssam#ifndef CONFIG_ANSI_C_EXTRA
1441189251Ssamstatic void wpa_cli_action_cb(char *msg, size_t len)
1442189251Ssam{
1443189251Ssam	wpa_cli_action_process(msg);
1444189251Ssam}
1445189251Ssam#endif /* CONFIG_ANSI_C_EXTRA */
1446189251Ssam
1447189251Ssam
1448189251Ssamstatic void wpa_cli_reconnect(void)
1449189251Ssam{
1450189251Ssam	wpa_cli_close_connection();
1451189251Ssam	ctrl_conn = wpa_cli_open_connection(ctrl_ifname);
1452189251Ssam	if (ctrl_conn) {
1453189251Ssam		printf("Connection to wpa_supplicant re-established\n");
1454189251Ssam		if (wpa_ctrl_attach(ctrl_conn) == 0) {
1455189251Ssam			wpa_cli_attached = 1;
1456189251Ssam		} else {
1457189251Ssam			printf("Warning: Failed to attach to "
1458189251Ssam			       "wpa_supplicant.\n");
1459189251Ssam		}
1460189251Ssam	}
1461189251Ssam}
1462189251Ssam
1463189251Ssam
1464189251Ssamstatic void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
1465189251Ssam				 int action_monitor)
1466189251Ssam{
1467189251Ssam	int first = 1;
1468189251Ssam	if (ctrl_conn == NULL) {
1469189251Ssam		wpa_cli_reconnect();
1470189251Ssam		return;
1471189251Ssam	}
1472189251Ssam	while (wpa_ctrl_pending(ctrl) > 0) {
1473189251Ssam		char buf[256];
1474189251Ssam		size_t len = sizeof(buf) - 1;
1475189251Ssam		if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
1476189251Ssam			buf[len] = '\0';
1477189251Ssam			if (action_monitor)
1478189251Ssam				wpa_cli_action_process(buf);
1479189251Ssam			else {
1480189251Ssam				if (in_read && first)
1481189251Ssam					printf("\n");
1482189251Ssam				first = 0;
1483189251Ssam				printf("%s\n", buf);
1484189251Ssam			}
1485189251Ssam		} else {
1486189251Ssam			printf("Could not read pending message.\n");
1487189251Ssam			break;
1488189251Ssam		}
1489189251Ssam	}
1490189251Ssam
1491189251Ssam	if (wpa_ctrl_pending(ctrl) < 0) {
1492189251Ssam		printf("Connection to wpa_supplicant lost - trying to "
1493189251Ssam		       "reconnect\n");
1494189251Ssam		wpa_cli_reconnect();
1495189251Ssam	}
1496189251Ssam}
1497189251Ssam
1498189251Ssam
1499189251Ssam#ifdef CONFIG_READLINE
1500189251Ssamstatic char * wpa_cli_cmd_gen(const char *text, int state)
1501189251Ssam{
1502189251Ssam	static int i, len;
1503189251Ssam	const char *cmd;
1504189251Ssam
1505189251Ssam	if (state == 0) {
1506189251Ssam		i = 0;
1507189251Ssam		len = os_strlen(text);
1508189251Ssam	}
1509189251Ssam
1510189251Ssam	while ((cmd = wpa_cli_commands[i].cmd)) {
1511189251Ssam		i++;
1512189251Ssam		if (os_strncasecmp(cmd, text, len) == 0)
1513189251Ssam			return os_strdup(cmd);
1514189251Ssam	}
1515189251Ssam
1516189251Ssam	return NULL;
1517189251Ssam}
1518189251Ssam
1519189251Ssam
1520189251Ssamstatic char * wpa_cli_dummy_gen(const char *text, int state)
1521189251Ssam{
1522189251Ssam	return NULL;
1523189251Ssam}
1524189251Ssam
1525189251Ssam
1526189251Ssamstatic char ** wpa_cli_completion(const char *text, int start, int end)
1527189251Ssam{
1528189251Ssam	return rl_completion_matches(text, start == 0 ?
1529189251Ssam				     wpa_cli_cmd_gen : wpa_cli_dummy_gen);
1530189251Ssam}
1531189251Ssam#endif /* CONFIG_READLINE */
1532189251Ssam
1533189251Ssam
1534189251Ssamstatic void wpa_cli_interactive(void)
1535189251Ssam{
1536189251Ssam#define max_args 10
1537189251Ssam	char cmdbuf[256], *cmd, *argv[max_args], *pos;
1538189251Ssam	int argc;
1539189251Ssam#ifdef CONFIG_READLINE
1540189251Ssam	char *home, *hfile = NULL;
1541189251Ssam#endif /* CONFIG_READLINE */
1542189251Ssam
1543189251Ssam	printf("\nInteractive mode\n\n");
1544189251Ssam
1545189251Ssam#ifdef CONFIG_READLINE
1546189251Ssam	rl_attempted_completion_function = wpa_cli_completion;
1547189251Ssam	home = getenv("HOME");
1548189251Ssam	if (home) {
1549189251Ssam		const char *fname = ".wpa_cli_history";
1550189251Ssam		int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
1551189251Ssam		hfile = os_malloc(hfile_len);
1552189251Ssam		if (hfile) {
1553189251Ssam			int res;
1554189251Ssam			res = os_snprintf(hfile, hfile_len, "%s/%s", home,
1555189251Ssam					  fname);
1556189251Ssam			if (res >= 0 && res < hfile_len) {
1557189251Ssam				hfile[hfile_len - 1] = '\0';
1558189251Ssam				read_history(hfile);
1559189251Ssam				stifle_history(100);
1560189251Ssam			}
1561189251Ssam		}
1562189251Ssam	}
1563189251Ssam#endif /* CONFIG_READLINE */
1564189251Ssam
1565189251Ssam	do {
1566189251Ssam		wpa_cli_recv_pending(ctrl_conn, 0, 0);
1567189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
1568189251Ssam		alarm(ping_interval);
1569189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
1570189251Ssam#ifdef CONFIG_READLINE
1571189251Ssam		cmd = readline("> ");
1572189251Ssam		if (cmd && *cmd) {
1573189251Ssam			HIST_ENTRY *h;
1574189251Ssam			while (next_history())
1575189251Ssam				;
1576189251Ssam			h = previous_history();
1577189251Ssam			if (h == NULL || os_strcmp(cmd, h->line) != 0)
1578189251Ssam				add_history(cmd);
1579189251Ssam			next_history();
1580189251Ssam		}
1581189251Ssam#else /* CONFIG_READLINE */
1582189251Ssam		printf("> ");
1583189251Ssam		cmd = fgets(cmdbuf, sizeof(cmdbuf), stdin);
1584189251Ssam#endif /* CONFIG_READLINE */
1585189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
1586189251Ssam		alarm(0);
1587189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
1588189251Ssam		if (cmd == NULL)
1589189251Ssam			break;
1590189251Ssam		wpa_cli_recv_pending(ctrl_conn, 0, 0);
1591189251Ssam		pos = cmd;
1592189251Ssam		while (*pos != '\0') {
1593189251Ssam			if (*pos == '\n') {
1594189251Ssam				*pos = '\0';
1595189251Ssam				break;
1596189251Ssam			}
1597189251Ssam			pos++;
1598189251Ssam		}
1599189251Ssam		argc = 0;
1600189251Ssam		pos = cmd;
1601189251Ssam		for (;;) {
1602189251Ssam			while (*pos == ' ')
1603189251Ssam				pos++;
1604189251Ssam			if (*pos == '\0')
1605189251Ssam				break;
1606189251Ssam			argv[argc] = pos;
1607189251Ssam			argc++;
1608189251Ssam			if (argc == max_args)
1609189251Ssam				break;
1610189251Ssam			if (*pos == '"') {
1611189251Ssam				char *pos2 = os_strrchr(pos, '"');
1612189251Ssam				if (pos2)
1613189251Ssam					pos = pos2 + 1;
1614189251Ssam			}
1615189251Ssam			while (*pos != '\0' && *pos != ' ')
1616189251Ssam				pos++;
1617189251Ssam			if (*pos == ' ')
1618189251Ssam				*pos++ = '\0';
1619189251Ssam		}
1620189251Ssam		if (argc)
1621189251Ssam			wpa_request(ctrl_conn, argc, argv);
1622189251Ssam
1623189251Ssam		if (cmd != cmdbuf)
1624189251Ssam			os_free(cmd);
1625189251Ssam	} while (!wpa_cli_quit);
1626189251Ssam
1627189251Ssam#ifdef CONFIG_READLINE
1628189251Ssam	if (hfile) {
1629189251Ssam		/* Save command history, excluding lines that may contain
1630189251Ssam		 * passwords. */
1631189251Ssam		HIST_ENTRY *h;
1632189251Ssam		history_set_pos(0);
1633189251Ssam		while ((h = current_history())) {
1634189251Ssam			char *p = h->line;
1635189251Ssam			while (*p == ' ' || *p == '\t')
1636189251Ssam				p++;
1637189251Ssam			if (cmd_has_sensitive_data(p)) {
1638189251Ssam				h = remove_history(where_history());
1639189251Ssam				if (h) {
1640189251Ssam					os_free(h->line);
1641189251Ssam					os_free(h->data);
1642189251Ssam					os_free(h);
1643189251Ssam				} else
1644189251Ssam					next_history();
1645189251Ssam			} else
1646189251Ssam				next_history();
1647189251Ssam		}
1648189251Ssam		write_history(hfile);
1649189251Ssam		os_free(hfile);
1650189251Ssam	}
1651189251Ssam#endif /* CONFIG_READLINE */
1652189251Ssam}
1653189251Ssam
1654189251Ssam
1655189251Ssamstatic void wpa_cli_action(struct wpa_ctrl *ctrl)
1656189251Ssam{
1657189251Ssam#ifdef CONFIG_ANSI_C_EXTRA
1658189251Ssam	/* TODO: ANSI C version(?) */
1659189251Ssam	printf("Action processing not supported in ANSI C build.\n");
1660189251Ssam#else /* CONFIG_ANSI_C_EXTRA */
1661189251Ssam	fd_set rfds;
1662189251Ssam	int fd, res;
1663189251Ssam	struct timeval tv;
1664189251Ssam	char buf[256]; /* note: large enough to fit in unsolicited messages */
1665189251Ssam	size_t len;
1666189251Ssam
1667189251Ssam	fd = wpa_ctrl_get_fd(ctrl);
1668189251Ssam
1669189251Ssam	while (!wpa_cli_quit) {
1670189251Ssam		FD_ZERO(&rfds);
1671189251Ssam		FD_SET(fd, &rfds);
1672189251Ssam		tv.tv_sec = ping_interval;
1673189251Ssam		tv.tv_usec = 0;
1674189251Ssam		res = select(fd + 1, &rfds, NULL, NULL, &tv);
1675189251Ssam		if (res < 0 && errno != EINTR) {
1676189251Ssam			perror("select");
1677189251Ssam			break;
1678189251Ssam		}
1679189251Ssam
1680189251Ssam		if (FD_ISSET(fd, &rfds))
1681189251Ssam			wpa_cli_recv_pending(ctrl, 0, 1);
1682189251Ssam		else {
1683189251Ssam			/* verify that connection is still working */
1684189251Ssam			len = sizeof(buf) - 1;
1685189251Ssam			if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1686189251Ssam					     wpa_cli_action_cb) < 0 ||
1687189251Ssam			    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1688189251Ssam				printf("wpa_supplicant did not reply to PING "
1689189251Ssam				       "command - exiting\n");
1690189251Ssam				break;
1691189251Ssam			}
1692189251Ssam		}
1693189251Ssam	}
1694189251Ssam#endif /* CONFIG_ANSI_C_EXTRA */
1695189251Ssam}
1696189251Ssam
1697189251Ssam
1698189251Ssamstatic void wpa_cli_cleanup(void)
1699189251Ssam{
1700189251Ssam	wpa_cli_close_connection();
1701189251Ssam	if (pid_file)
1702189251Ssam		os_daemonize_terminate(pid_file);
1703189251Ssam
1704189251Ssam	os_program_deinit();
1705189251Ssam}
1706189251Ssam
1707189251Ssamstatic void wpa_cli_terminate(int sig)
1708189251Ssam{
1709189251Ssam	wpa_cli_cleanup();
1710189251Ssam	exit(0);
1711189251Ssam}
1712189251Ssam
1713189251Ssam
1714189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
1715189251Ssamstatic void wpa_cli_alarm(int sig)
1716189251Ssam{
1717189251Ssam	if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
1718189251Ssam		printf("Connection to wpa_supplicant lost - trying to "
1719189251Ssam		       "reconnect\n");
1720189251Ssam		wpa_cli_close_connection();
1721189251Ssam	}
1722189251Ssam	if (!ctrl_conn)
1723189251Ssam		wpa_cli_reconnect();
1724189251Ssam	if (ctrl_conn)
1725189251Ssam		wpa_cli_recv_pending(ctrl_conn, 1, 0);
1726189251Ssam	alarm(ping_interval);
1727189251Ssam}
1728189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
1729189251Ssam
1730189251Ssam
1731189251Ssamstatic char * wpa_cli_get_default_ifname(void)
1732189251Ssam{
1733189251Ssam	char *ifname = NULL;
1734189251Ssam
1735189251Ssam#ifdef CONFIG_CTRL_IFACE_UNIX
1736189251Ssam	struct dirent *dent;
1737189251Ssam	DIR *dir = opendir(ctrl_iface_dir);
1738189251Ssam	if (!dir)
1739189251Ssam		return NULL;
1740189251Ssam	while ((dent = readdir(dir))) {
1741189251Ssam#ifdef _DIRENT_HAVE_D_TYPE
1742189251Ssam		/*
1743189251Ssam		 * Skip the file if it is not a socket. Also accept
1744189251Ssam		 * DT_UNKNOWN (0) in case the C library or underlying
1745189251Ssam		 * file system does not support d_type.
1746189251Ssam		 */
1747189251Ssam		if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
1748189251Ssam			continue;
1749189251Ssam#endif /* _DIRENT_HAVE_D_TYPE */
1750189251Ssam		if (os_strcmp(dent->d_name, ".") == 0 ||
1751189251Ssam		    os_strcmp(dent->d_name, "..") == 0)
1752189251Ssam			continue;
1753189251Ssam		printf("Selected interface '%s'\n", dent->d_name);
1754189251Ssam		ifname = os_strdup(dent->d_name);
1755189251Ssam		break;
1756189251Ssam	}
1757189251Ssam	closedir(dir);
1758189251Ssam#endif /* CONFIG_CTRL_IFACE_UNIX */
1759189251Ssam
1760189251Ssam#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
1761189251Ssam	char buf[2048], *pos;
1762189251Ssam	size_t len;
1763189251Ssam	struct wpa_ctrl *ctrl;
1764189251Ssam	int ret;
1765189251Ssam
1766189251Ssam	ctrl = wpa_ctrl_open(NULL);
1767189251Ssam	if (ctrl == NULL)
1768189251Ssam		return NULL;
1769189251Ssam
1770189251Ssam	len = sizeof(buf) - 1;
1771189251Ssam	ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL);
1772189251Ssam	if (ret >= 0) {
1773189251Ssam		buf[len] = '\0';
1774189251Ssam		pos = os_strchr(buf, '\n');
1775189251Ssam		if (pos)
1776189251Ssam			*pos = '\0';
1777189251Ssam		ifname = os_strdup(buf);
1778189251Ssam	}
1779189251Ssam	wpa_ctrl_close(ctrl);
1780189251Ssam#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
1781189251Ssam
1782189251Ssam	return ifname;
1783189251Ssam}
1784189251Ssam
1785189251Ssam
1786189251Ssamint main(int argc, char *argv[])
1787189251Ssam{
1788189251Ssam	int interactive;
1789189251Ssam	int warning_displayed = 0;
1790189251Ssam	int c;
1791189251Ssam	int daemonize = 0;
1792189251Ssam	int ret = 0;
1793189251Ssam	const char *global = NULL;
1794189251Ssam
1795189251Ssam	if (os_program_init())
1796189251Ssam		return -1;
1797189251Ssam
1798189251Ssam	for (;;) {
1799189251Ssam		c = getopt(argc, argv, "a:Bg:G:hi:p:P:v");
1800189251Ssam		if (c < 0)
1801189251Ssam			break;
1802189251Ssam		switch (c) {
1803189251Ssam		case 'a':
1804189251Ssam			action_file = optarg;
1805189251Ssam			break;
1806189251Ssam		case 'B':
1807189251Ssam			daemonize = 1;
1808189251Ssam			break;
1809189251Ssam		case 'g':
1810189251Ssam			global = optarg;
1811189251Ssam			break;
1812189251Ssam		case 'G':
1813189251Ssam			ping_interval = atoi(optarg);
1814189251Ssam			break;
1815189251Ssam		case 'h':
1816189251Ssam			usage();
1817189251Ssam			return 0;
1818189251Ssam		case 'v':
1819189251Ssam			printf("%s\n", wpa_cli_version);
1820189251Ssam			return 0;
1821189251Ssam		case 'i':
1822189251Ssam			os_free(ctrl_ifname);
1823189251Ssam			ctrl_ifname = os_strdup(optarg);
1824189251Ssam			break;
1825189251Ssam		case 'p':
1826189251Ssam			ctrl_iface_dir = optarg;
1827189251Ssam			break;
1828189251Ssam		case 'P':
1829189251Ssam			pid_file = optarg;
1830189251Ssam			break;
1831189251Ssam		default:
1832189251Ssam			usage();
1833189251Ssam			return -1;
1834189251Ssam		}
1835189251Ssam	}
1836189251Ssam
1837189251Ssam	interactive = (argc == optind) && (action_file == NULL);
1838189251Ssam
1839189251Ssam	if (interactive)
1840189251Ssam		printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license);
1841189251Ssam
1842189251Ssam	if (global) {
1843189251Ssam#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
1844189251Ssam		ctrl_conn = wpa_ctrl_open(NULL);
1845189251Ssam#else /* CONFIG_CTRL_IFACE_NAMED_PIPE */
1846189251Ssam		ctrl_conn = wpa_ctrl_open(global);
1847189251Ssam#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
1848189251Ssam		if (ctrl_conn == NULL) {
1849189251Ssam			perror("Failed to connect to wpa_supplicant - "
1850189251Ssam			       "wpa_ctrl_open");
1851189251Ssam			return -1;
1852189251Ssam		}
1853189251Ssam	}
1854189251Ssam
1855189251Ssam	for (; !global;) {
1856189251Ssam		if (ctrl_ifname == NULL)
1857189251Ssam			ctrl_ifname = wpa_cli_get_default_ifname();
1858189251Ssam		ctrl_conn = wpa_cli_open_connection(ctrl_ifname);
1859189251Ssam		if (ctrl_conn) {
1860189251Ssam			if (warning_displayed)
1861189251Ssam				printf("Connection established.\n");
1862189251Ssam			break;
1863189251Ssam		}
1864189251Ssam
1865189251Ssam		if (!interactive) {
1866189251Ssam			perror("Failed to connect to wpa_supplicant - "
1867189251Ssam			       "wpa_ctrl_open");
1868189251Ssam			return -1;
1869189251Ssam		}
1870189251Ssam
1871189251Ssam		if (!warning_displayed) {
1872189251Ssam			printf("Could not connect to wpa_supplicant - "
1873189251Ssam			       "re-trying\n");
1874189251Ssam			warning_displayed = 1;
1875189251Ssam		}
1876189251Ssam		os_sleep(1, 0);
1877189251Ssam		continue;
1878189251Ssam	}
1879189251Ssam
1880189251Ssam#ifndef _WIN32_WCE
1881189251Ssam	signal(SIGINT, wpa_cli_terminate);
1882189251Ssam	signal(SIGTERM, wpa_cli_terminate);
1883189251Ssam#endif /* _WIN32_WCE */
1884189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
1885189251Ssam	signal(SIGALRM, wpa_cli_alarm);
1886189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
1887189251Ssam
1888189251Ssam	if (interactive || action_file) {
1889189251Ssam		if (wpa_ctrl_attach(ctrl_conn) == 0) {
1890189251Ssam			wpa_cli_attached = 1;
1891189251Ssam		} else {
1892189251Ssam			printf("Warning: Failed to attach to "
1893189251Ssam			       "wpa_supplicant.\n");
1894189251Ssam			if (!interactive)
1895189251Ssam				return -1;
1896189251Ssam		}
1897189251Ssam	}
1898189251Ssam
1899189251Ssam	if (daemonize && os_daemonize(pid_file))
1900189251Ssam		return -1;
1901189251Ssam
1902189251Ssam	if (interactive)
1903189251Ssam		wpa_cli_interactive();
1904189251Ssam	else if (action_file)
1905189251Ssam		wpa_cli_action(ctrl_conn);
1906189251Ssam	else
1907189251Ssam		ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1908189251Ssam
1909189251Ssam	os_free(ctrl_ifname);
1910189251Ssam	wpa_cli_cleanup();
1911189251Ssam
1912189251Ssam	return ret;
1913189251Ssam}
1914189251Ssam
1915189251Ssam#else /* CONFIG_CTRL_IFACE */
1916189251Ssamint main(int argc, char *argv[])
1917189251Ssam{
1918189251Ssam	printf("CONFIG_CTRL_IFACE not defined - wpa_cli disabled\n");
1919189251Ssam	return -1;
1920189251Ssam}
1921189251Ssam#endif /* CONFIG_CTRL_IFACE */
1922