blob: 719d021cca807cf5e3ffda8002bd02f021bf588b [file] [log] [blame]
Jouni Malinen6fc68792008-02-27 17:34:43 -08001/*
2 * hostapd - command line interface for hostapd daemon
Jouni Malinen937403b2015-01-26 02:25:22 +02003 * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
Jouni Malinen6fc68792008-02-27 17:34:43 -08004 *
Jouni Malinen0f3d5782012-02-11 16:46:35 +02005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Jouni Malinen6fc68792008-02-27 17:34:43 -08007 */
8
9#include "includes.h"
10#include <dirent.h>
11
Jouni Malinen90973fb2009-11-29 17:51:55 +020012#include "common/wpa_ctrl.h"
Jouni Malinend9d1b952015-04-07 12:00:13 +030013#include "common/ieee802_11_defs.h"
Jouni Malinen42838052011-07-16 17:37:18 +030014#include "utils/common.h"
15#include "utils/eloop.h"
16#include "utils/edit.h"
Jouni Malinen90973fb2009-11-29 17:51:55 +020017#include "common/version.h"
Jouni Malinen6fc68792008-02-27 17:34:43 -080018
19
20static const char *hostapd_cli_version =
21"hostapd_cli v" VERSION_STR "\n"
Jouni Malinen937403b2015-01-26 02:25:22 +020022"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
Jouni Malinen6fc68792008-02-27 17:34:43 -080023
24
25static const char *hostapd_cli_license =
Jouni Malinen331f89f2012-02-11 14:04:12 +020026"This software may be distributed under the terms of the BSD license.\n"
27"See README for more details.\n";
Jouni Malinen6fc68792008-02-27 17:34:43 -080028
29static const char *hostapd_cli_full_license =
Jouni Malinen331f89f2012-02-11 14:04:12 +020030"This software may be distributed under the terms of the BSD license.\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -080031"\n"
32"Redistribution and use in source and binary forms, with or without\n"
33"modification, are permitted provided that the following conditions are\n"
34"met:\n"
35"\n"
36"1. Redistributions of source code must retain the above copyright\n"
37" notice, this list of conditions and the following disclaimer.\n"
38"\n"
39"2. Redistributions in binary form must reproduce the above copyright\n"
40" notice, this list of conditions and the following disclaimer in the\n"
41" documentation and/or other materials provided with the distribution.\n"
42"\n"
43"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
44" names of its contributors may be used to endorse or promote products\n"
45" derived from this software without specific prior written permission.\n"
46"\n"
47"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
48"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
49"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
50"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
51"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
52"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
53"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
54"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
55"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
56"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
57"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
58"\n";
59
60static const char *commands_help =
61"Commands:\n"
62" mib get MIB variables (dot1x, dot11, radius)\n"
63" sta <addr> get MIB variables for one station\n"
64" all_sta get MIB variables for all stations\n"
65" new_sta <addr> add a new station\n"
Jouni Malinen488d0932010-04-11 21:00:16 +030066" deauthenticate <addr> deauthenticate a station\n"
67" disassociate <addr> disassociate a station\n"
Jouni Malinen88b4b422008-12-30 18:04:29 +020068#ifdef CONFIG_IEEE80211W
69" sa_query <addr> send SA Query to a station\n"
70#endif /* CONFIG_IEEE80211W */
Jouni Malinenad08c362008-11-23 19:34:26 +020071#ifdef CONFIG_WPS
Jouni Malinen31fcea92009-12-12 16:40:10 +020072" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n"
Jouni Malinen3981cb32010-09-23 10:30:52 -070073" wps_check_pin <PIN> verify PIN checksum\n"
Jouni Malinenad08c362008-11-23 19:34:26 +020074" wps_pbc indicate button pushed to initiate PBC\n"
Anirban Sirkhell1c79cdf2012-08-24 19:02:56 +030075" wps_cancel cancel the pending WPS operation\n"
Jouni Malinenbb45b6d2012-06-27 20:54:56 +030076#ifdef CONFIG_WPS_NFC
77" wps_nfc_tag_read <hexdump> report read NFC tag with WPS data\n"
Jouni Malinen3cf7a592012-06-28 13:25:48 +030078" wps_nfc_config_token <WPS/NDEF> build NFC configuration token\n"
Jouni Malinenffdaa052012-06-28 19:43:29 +030079" wps_nfc_token <WPS/NDEF/enable/disable> manager NFC password token\n"
Jouni Malinenbb45b6d2012-06-27 20:54:56 +030080#endif /* CONFIG_WPS_NFC */
Jouni Malinen5a1cc302010-08-24 16:35:37 +030081" wps_ap_pin <cmd> [params..] enable/disable AP PIN\n"
Jouni Malinen450eddc2010-10-21 16:49:41 +030082" wps_config <SSID> <auth> <encr> <key> configure AP\n"
Jeffin Mammen3351a382013-08-23 16:03:15 +030083" wps_get_status show current WPS status\n"
Jouni Malinenad08c362008-11-23 19:34:26 +020084#endif /* CONFIG_WPS */
Jouni Malinen403b96f2010-09-23 12:02:28 -070085" get_config show current configuration\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -080086" help show this usage help\n"
87" interface [ifname] show interfaces/select interface\n"
88" level <debug level> change debug level\n"
89" license show full hostapd_cli license\n"
90" quit exit hostapd_cli\n";
91
92static struct wpa_ctrl *ctrl_conn;
93static int hostapd_cli_quit = 0;
94static int hostapd_cli_attached = 0;
Jeff Johnson2d39a4d2012-09-03 22:12:17 -070095
96#ifndef CONFIG_CTRL_IFACE_DIR
97#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
98#endif /* CONFIG_CTRL_IFACE_DIR */
99static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
100
Jouni Malinen6fc68792008-02-27 17:34:43 -0800101static char *ctrl_ifname = NULL;
Gregory Detalbae92172010-04-07 11:14:54 +0300102static const char *pid_file = NULL;
103static const char *action_file = NULL;
Jouni Malinen1cc84c12009-01-20 21:12:00 +0200104static int ping_interval = 5;
Jouni Malinen42838052011-07-16 17:37:18 +0300105static int interactive = 0;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800106
107
108static void usage(void)
109{
110 fprintf(stderr, "%s\n", hostapd_cli_version);
Gregory Detalbae92172010-04-07 11:14:54 +0300111 fprintf(stderr,
112 "\n"
113 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
114 "[-a<path>] \\\n"
115 " [-G<ping interval>] [command..]\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -0800116 "\n"
117 "Options:\n"
118 " -h help (show this usage text)\n"
119 " -v shown version information\n"
120 " -p<path> path to find control sockets (default: "
121 "/var/run/hostapd)\n"
Gregory Detalbae92172010-04-07 11:14:54 +0300122 " -a<file> run in daemon mode executing the action file "
123 "based on events\n"
124 " from hostapd\n"
125 " -B run a daemon in the background\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -0800126 " -i<ifname> Interface to listen on (default: first "
127 "interface found in the\n"
128 " socket path)\n\n"
129 "%s",
130 commands_help);
131}
132
133
134static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
135{
136 char *cfile;
137 int flen;
138
139 if (ifname == NULL)
140 return NULL;
141
142 flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
143 cfile = malloc(flen);
144 if (cfile == NULL)
145 return NULL;
146 snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
147
148 ctrl_conn = wpa_ctrl_open(cfile);
149 free(cfile);
150 return ctrl_conn;
151}
152
153
154static void hostapd_cli_close_connection(void)
155{
156 if (ctrl_conn == NULL)
157 return;
158
159 if (hostapd_cli_attached) {
160 wpa_ctrl_detach(ctrl_conn);
161 hostapd_cli_attached = 0;
162 }
163 wpa_ctrl_close(ctrl_conn);
164 ctrl_conn = NULL;
165}
166
167
168static void hostapd_cli_msg_cb(char *msg, size_t len)
169{
170 printf("%s\n", msg);
171}
172
173
174static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
175{
176 char buf[4096];
177 size_t len;
178 int ret;
179
180 if (ctrl_conn == NULL) {
181 printf("Not connected to hostapd - command dropped.\n");
182 return -1;
183 }
184 len = sizeof(buf) - 1;
185 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
186 hostapd_cli_msg_cb);
187 if (ret == -2) {
188 printf("'%s' command timed out.\n", cmd);
189 return -2;
190 } else if (ret < 0) {
191 printf("'%s' command failed.\n", cmd);
192 return -1;
193 }
194 if (print) {
195 buf[len] = '\0';
196 printf("%s", buf);
197 }
198 return 0;
199}
200
201
202static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
203{
204 return _wpa_ctrl_command(ctrl, cmd, 1);
205}
206
207
208static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
209{
210 return wpa_ctrl_command(ctrl, "PING");
211}
212
213
Ben Greearb41a47c2011-02-06 20:24:16 +0200214static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
215{
216 return wpa_ctrl_command(ctrl, "RELOG");
217}
218
219
Jouni Malinen5ae64492013-11-03 18:13:14 +0200220static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
221{
Jouni Malinenf0cbb982014-01-05 21:50:34 +0200222 if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
223 return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
Jouni Malinen5ae64492013-11-03 18:13:14 +0200224 return wpa_ctrl_command(ctrl, "STATUS");
225}
226
227
Jouni Malinen6fc68792008-02-27 17:34:43 -0800228static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
229{
Jouni Malinen4c03a2b2014-01-02 17:19:04 +0200230 if (argc > 0) {
231 char buf[100];
232 os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]);
233 return wpa_ctrl_command(ctrl, buf);
234 }
Jouni Malinen6fc68792008-02-27 17:34:43 -0800235 return wpa_ctrl_command(ctrl, "MIB");
236}
237
238
Gregory Detalbae92172010-04-07 11:14:54 +0300239static int hostapd_cli_exec(const char *program, const char *arg1,
240 const char *arg2)
241{
Jouni Malinen5d4fa2a2014-10-06 18:49:01 +0300242 char *arg;
Gregory Detalbae92172010-04-07 11:14:54 +0300243 size_t len;
244 int res;
Gregory Detalbae92172010-04-07 11:14:54 +0300245
Jouni Malinen5d4fa2a2014-10-06 18:49:01 +0300246 len = os_strlen(arg1) + os_strlen(arg2) + 2;
247 arg = os_malloc(len);
248 if (arg == NULL)
Gregory Detalbae92172010-04-07 11:14:54 +0300249 return -1;
Jouni Malinen5d4fa2a2014-10-06 18:49:01 +0300250 os_snprintf(arg, len, "%s %s", arg1, arg2);
251 res = os_exec(program, arg, 1);
252 os_free(arg);
Gregory Detalbae92172010-04-07 11:14:54 +0300253
Jouni Malinen5d4fa2a2014-10-06 18:49:01 +0300254 return res;
Gregory Detalbae92172010-04-07 11:14:54 +0300255}
256
257
258static void hostapd_cli_action_process(char *msg, size_t len)
259{
260 const char *pos;
261
262 pos = msg;
263 if (*pos == '<') {
264 pos = os_strchr(pos, '>');
265 if (pos)
266 pos++;
267 else
268 pos = msg;
269 }
270
271 hostapd_cli_exec(action_file, ctrl_ifname, pos);
272}
273
274
Jouni Malinen6fc68792008-02-27 17:34:43 -0800275static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
276{
277 char buf[64];
Jouni Malinenea23df62014-01-02 17:49:48 +0200278 if (argc < 1) {
279 printf("Invalid 'sta' command - at least one argument, STA "
Jouni Malinen6fc68792008-02-27 17:34:43 -0800280 "address, is required.\n");
281 return -1;
282 }
Jouni Malinenea23df62014-01-02 17:49:48 +0200283 if (argc > 1)
284 snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]);
285 else
286 snprintf(buf, sizeof(buf), "STA %s", argv[0]);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800287 return wpa_ctrl_command(ctrl, buf);
288}
289
290
291static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
292 char *argv[])
293{
294 char buf[64];
295 if (argc != 1) {
296 printf("Invalid 'new_sta' command - exactly one argument, STA "
297 "address, is required.\n");
298 return -1;
299 }
300 snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
301 return wpa_ctrl_command(ctrl, buf);
302}
303
304
Jouni Malinen90a32062010-03-29 11:14:57 -0700305static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
306 char *argv[])
307{
308 char buf[64];
Jouni Malinenb91ab762010-03-29 12:01:40 -0700309 if (argc < 1) {
Jouni Malinen90a32062010-03-29 11:14:57 -0700310 printf("Invalid 'deauthenticate' command - exactly one "
311 "argument, STA address, is required.\n");
312 return -1;
313 }
Jouni Malinenb91ab762010-03-29 12:01:40 -0700314 if (argc > 1)
315 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
316 argv[0], argv[1]);
317 else
318 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
Jouni Malinen90a32062010-03-29 11:14:57 -0700319 return wpa_ctrl_command(ctrl, buf);
320}
321
322
323static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
324 char *argv[])
325{
326 char buf[64];
Jouni Malinenb91ab762010-03-29 12:01:40 -0700327 if (argc < 1) {
Jouni Malinen90a32062010-03-29 11:14:57 -0700328 printf("Invalid 'disassociate' command - exactly one "
329 "argument, STA address, is required.\n");
330 return -1;
331 }
Jouni Malinenb91ab762010-03-29 12:01:40 -0700332 if (argc > 1)
333 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
334 argv[0], argv[1]);
335 else
336 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
Jouni Malinen90a32062010-03-29 11:14:57 -0700337 return wpa_ctrl_command(ctrl, buf);
338}
339
340
Jouni Malinen88b4b422008-12-30 18:04:29 +0200341#ifdef CONFIG_IEEE80211W
342static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
343 char *argv[])
344{
345 char buf[64];
346 if (argc != 1) {
347 printf("Invalid 'sa_query' command - exactly one argument, "
348 "STA address, is required.\n");
349 return -1;
350 }
351 snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
352 return wpa_ctrl_command(ctrl, buf);
353}
354#endif /* CONFIG_IEEE80211W */
355
356
Jouni Malinenad08c362008-11-23 19:34:26 +0200357#ifdef CONFIG_WPS
358static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
359 char *argv[])
360{
Jouni Malinen31fcea92009-12-12 16:40:10 +0200361 char buf[256];
Jouni Malinen077a7812009-05-26 17:44:44 +0300362 if (argc < 2) {
363 printf("Invalid 'wps_pin' command - at least two arguments, "
Jouni Malinenad08c362008-11-23 19:34:26 +0200364 "UUID and PIN, are required.\n");
365 return -1;
366 }
Jouni Malinen31fcea92009-12-12 16:40:10 +0200367 if (argc > 3)
368 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
369 argv[0], argv[1], argv[2], argv[3]);
370 else if (argc > 2)
Jouni Malinen077a7812009-05-26 17:44:44 +0300371 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
372 argv[0], argv[1], argv[2]);
373 else
374 snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
Jouni Malinenad08c362008-11-23 19:34:26 +0200375 return wpa_ctrl_command(ctrl, buf);
376}
377
378
Jouni Malinen3981cb32010-09-23 10:30:52 -0700379static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
380 char *argv[])
381{
382 char cmd[256];
383 int res;
384
385 if (argc != 1 && argc != 2) {
386 printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
387 "- PIN to be verified\n");
388 return -1;
389 }
390
391 if (argc == 2)
392 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
393 argv[0], argv[1]);
394 else
395 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
396 argv[0]);
Jouni Malineneeab4f22014-12-08 11:22:52 +0200397 if (os_snprintf_error(sizeof(cmd), res)) {
Jouni Malinen3981cb32010-09-23 10:30:52 -0700398 printf("Too long WPS_CHECK_PIN command.\n");
399 return -1;
400 }
401 return wpa_ctrl_command(ctrl, cmd);
402}
403
404
Jouni Malinenad08c362008-11-23 19:34:26 +0200405static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
406 char *argv[])
407{
408 return wpa_ctrl_command(ctrl, "WPS_PBC");
409}
Masashi Honma46bdb832009-02-26 21:57:38 +0200410
411
Anirban Sirkhell4c374cd2012-03-18 20:23:31 -1000412static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
413 char *argv[])
414{
415 return wpa_ctrl_command(ctrl, "WPS_CANCEL");
416}
417
418
Jouni Malinenbb45b6d2012-06-27 20:54:56 +0300419#ifdef CONFIG_WPS_NFC
420static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
421 char *argv[])
422{
423 int ret;
424 char *buf;
425 size_t buflen;
426
427 if (argc != 1) {
428 printf("Invalid 'wps_nfc_tag_read' command - one argument "
429 "is required.\n");
430 return -1;
431 }
432
433 buflen = 18 + os_strlen(argv[0]);
434 buf = os_malloc(buflen);
435 if (buf == NULL)
436 return -1;
437 os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
438
439 ret = wpa_ctrl_command(ctrl, buf);
440 os_free(buf);
441
442 return ret;
443}
Jouni Malinen3cf7a592012-06-28 13:25:48 +0300444
445
446static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
447 int argc, char *argv[])
448{
449 char cmd[64];
450 int res;
451
452 if (argc != 1) {
453 printf("Invalid 'wps_nfc_config_token' command - one argument "
454 "is required.\n");
455 return -1;
456 }
457
458 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
459 argv[0]);
Jouni Malineneeab4f22014-12-08 11:22:52 +0200460 if (os_snprintf_error(sizeof(cmd), res)) {
Jouni Malinen3cf7a592012-06-28 13:25:48 +0300461 printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
462 return -1;
463 }
464 return wpa_ctrl_command(ctrl, cmd);
465}
Jouni Malinenffdaa052012-06-28 19:43:29 +0300466
467
468static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
469 int argc, char *argv[])
470{
471 char cmd[64];
472 int res;
473
474 if (argc != 1) {
475 printf("Invalid 'wps_nfc_token' command - one argument is "
476 "required.\n");
477 return -1;
478 }
479
480 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
Jouni Malineneeab4f22014-12-08 11:22:52 +0200481 if (os_snprintf_error(sizeof(cmd), res)) {
Jouni Malinenffdaa052012-06-28 19:43:29 +0300482 printf("Too long WPS_NFC_TOKEN command.\n");
483 return -1;
484 }
485 return wpa_ctrl_command(ctrl, cmd);
486}
Jouni Malinen6772a902013-02-10 17:12:55 +0200487
488
489static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
490 int argc, char *argv[])
491{
492 char cmd[64];
493 int res;
494
495 if (argc != 2) {
496 printf("Invalid 'nfc_get_handover_sel' command - two arguments "
497 "are required.\n");
498 return -1;
499 }
500
501 res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
502 argv[0], argv[1]);
Jouni Malineneeab4f22014-12-08 11:22:52 +0200503 if (os_snprintf_error(sizeof(cmd), res)) {
Jouni Malinen6772a902013-02-10 17:12:55 +0200504 printf("Too long NFC_GET_HANDOVER_SEL command.\n");
505 return -1;
506 }
507 return wpa_ctrl_command(ctrl, cmd);
508}
509
Jouni Malinenbb45b6d2012-06-27 20:54:56 +0300510#endif /* CONFIG_WPS_NFC */
511
512
Jouni Malinen5a1cc302010-08-24 16:35:37 +0300513static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
514 char *argv[])
515{
516 char buf[64];
517 if (argc < 1) {
518 printf("Invalid 'wps_ap_pin' command - at least one argument "
519 "is required.\n");
520 return -1;
521 }
522 if (argc > 2)
523 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
524 argv[0], argv[1], argv[2]);
525 else if (argc > 1)
526 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
527 argv[0], argv[1]);
528 else
529 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
530 return wpa_ctrl_command(ctrl, buf);
531}
Jouni Malinen450eddc2010-10-21 16:49:41 +0300532
533
Jeffin Mammen3351a382013-08-23 16:03:15 +0300534static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
535 char *argv[])
536{
537 return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
538}
539
540
Jouni Malinen450eddc2010-10-21 16:49:41 +0300541static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
542 char *argv[])
543{
544 char buf[256];
Jouni Malinend9d1b952015-04-07 12:00:13 +0300545 char ssid_hex[2 * SSID_MAX_LEN + 1];
Jouni Malinen450eddc2010-10-21 16:49:41 +0300546 char key_hex[2 * 64 + 1];
547 int i;
548
549 if (argc < 1) {
550 printf("Invalid 'wps_config' command - at least two arguments "
551 "are required.\n");
552 return -1;
553 }
554
555 ssid_hex[0] = '\0';
Jouni Malinend9d1b952015-04-07 12:00:13 +0300556 for (i = 0; i < SSID_MAX_LEN; i++) {
Jouni Malinen450eddc2010-10-21 16:49:41 +0300557 if (argv[0][i] == '\0')
558 break;
559 os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
560 }
561
562 key_hex[0] = '\0';
563 if (argc > 3) {
564 for (i = 0; i < 64; i++) {
565 if (argv[3][i] == '\0')
566 break;
567 os_snprintf(&key_hex[i * 2], 3, "%02x",
568 argv[3][i]);
569 }
570 }
571
572 if (argc > 3)
573 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
574 ssid_hex, argv[1], argv[2], key_hex);
575 else if (argc > 2)
576 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
577 ssid_hex, argv[1], argv[2]);
578 else
579 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
580 ssid_hex, argv[1]);
581 return wpa_ctrl_command(ctrl, buf);
582}
Jouni Malinenad08c362008-11-23 19:34:26 +0200583#endif /* CONFIG_WPS */
584
585
Jouni Malinen2049a872012-12-22 20:27:30 +0200586static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
587 char *argv[])
588{
589 char buf[300];
590 int res;
591
592 if (argc < 2) {
593 printf("Invalid 'disassoc_imminent' command - two arguments "
594 "(STA addr and Disassociation Timer) are needed\n");
595 return -1;
596 }
597
598 res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
599 argv[0], argv[1]);
Jouni Malinend85e1fc2014-12-08 11:15:51 +0200600 if (os_snprintf_error(sizeof(buf), res))
Jouni Malinen2049a872012-12-22 20:27:30 +0200601 return -1;
602 return wpa_ctrl_command(ctrl, buf);
603}
604
605
Jouni Malinen71269b32011-10-07 13:33:04 +0300606static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
607 char *argv[])
608{
609 char buf[300];
610 int res;
611
Kyeyoon Parkd5b559b2013-04-05 18:41:26 +0300612 if (argc < 3) {
613 printf("Invalid 'ess_disassoc' command - three arguments (STA "
614 "addr, disassoc timer, and URL) are needed\n");
Jouni Malinen71269b32011-10-07 13:33:04 +0300615 return -1;
616 }
617
Kyeyoon Parkd5b559b2013-04-05 18:41:26 +0300618 res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
619 argv[0], argv[1], argv[2]);
Jouni Malinend85e1fc2014-12-08 11:15:51 +0200620 if (os_snprintf_error(sizeof(buf), res))
Jouni Malinen71269b32011-10-07 13:33:04 +0300621 return -1;
622 return wpa_ctrl_command(ctrl, buf);
623}
624
625
Jouni Malinena30dff02014-11-22 11:31:03 +0200626static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
627 char *argv[])
628{
629 char buf[2000], *tmp;
630 int res, i, total;
631
632 if (argc < 1) {
633 printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n");
634 return -1;
635 }
636
637 res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]);
Jouni Malinend85e1fc2014-12-08 11:15:51 +0200638 if (os_snprintf_error(sizeof(buf), res))
Jouni Malinena30dff02014-11-22 11:31:03 +0200639 return -1;
640
641 total = res;
642 for (i = 1; i < argc; i++) {
643 tmp = &buf[total];
644 res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]);
Jouni Malineneeab4f22014-12-08 11:22:52 +0200645 if (os_snprintf_error(sizeof(buf) - total, res))
Jouni Malinena30dff02014-11-22 11:31:03 +0200646 return -1;
647 total += res;
648 }
649 return wpa_ctrl_command(ctrl, buf);
650}
651
652
Jouni Malinen403b96f2010-09-23 12:02:28 -0700653static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
654 char *argv[])
655{
656 return wpa_ctrl_command(ctrl, "GET_CONFIG");
657}
658
659
Jouni Malinen6fc68792008-02-27 17:34:43 -0800660static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
661 char *addr, size_t addr_len)
662{
663 char buf[4096], *pos;
664 size_t len;
665 int ret;
666
667 if (ctrl_conn == NULL) {
668 printf("Not connected to hostapd - command dropped.\n");
669 return -1;
670 }
671 len = sizeof(buf) - 1;
672 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
673 hostapd_cli_msg_cb);
674 if (ret == -2) {
675 printf("'%s' command timed out.\n", cmd);
676 return -2;
677 } else if (ret < 0) {
678 printf("'%s' command failed.\n", cmd);
679 return -1;
680 }
681
682 buf[len] = '\0';
683 if (memcmp(buf, "FAIL", 4) == 0)
684 return -1;
685 printf("%s", buf);
686
687 pos = buf;
688 while (*pos != '\0' && *pos != '\n')
689 pos++;
690 *pos = '\0';
691 os_strlcpy(addr, buf, addr_len);
692 return 0;
693}
694
695
696static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
697 char *argv[])
698{
699 char addr[32], cmd[64];
700
701 if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
702 return 0;
703 do {
704 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
705 } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
706
707 return -1;
708}
709
710
711static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
712{
713 printf("%s", commands_help);
714 return 0;
715}
716
717
718static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
719 char *argv[])
720{
721 printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
722 return 0;
723}
724
725
Kyeyoon Parkc5517002013-07-24 12:28:20 +0300726static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
727 int argc, char *argv[])
728{
729 char buf[200];
730 int res;
731
732 if (argc != 1) {
733 printf("Invalid 'set_qos_map_set' command - "
734 "one argument (comma delimited QoS map set) "
735 "is needed\n");
736 return -1;
737 }
738
739 res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
Jouni Malinend85e1fc2014-12-08 11:15:51 +0200740 if (os_snprintf_error(sizeof(buf), res))
Kyeyoon Parkc5517002013-07-24 12:28:20 +0300741 return -1;
742 return wpa_ctrl_command(ctrl, buf);
743}
744
745
746static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
747 int argc, char *argv[])
748{
749 char buf[50];
750 int res;
751
752 if (argc != 1) {
753 printf("Invalid 'send_qos_map_conf' command - "
754 "one argument (STA addr) is needed\n");
755 return -1;
756 }
757
758 res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
Jouni Malinend85e1fc2014-12-08 11:15:51 +0200759 if (os_snprintf_error(sizeof(buf), res))
Kyeyoon Parkc5517002013-07-24 12:28:20 +0300760 return -1;
761 return wpa_ctrl_command(ctrl, buf);
762}
763
764
Jouni Malinen3fb17a92013-03-17 16:03:42 +0200765static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
766 char *argv[])
767{
768 char buf[300];
769 int res;
770
771 if (argc < 2) {
772 printf("Invalid 'hs20_wnm_notif' command - two arguments (STA "
773 "addr and URL) are needed\n");
774 return -1;
775 }
776
777 res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
778 argv[0], argv[1]);
Jouni Malinend85e1fc2014-12-08 11:15:51 +0200779 if (os_snprintf_error(sizeof(buf), res))
Jouni Malinen3fb17a92013-03-17 16:03:42 +0200780 return -1;
781 return wpa_ctrl_command(ctrl, buf);
782}
783
784
Jouni Malinen8e1146d2013-07-26 22:13:58 +0300785static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
786 char *argv[])
787{
788 char buf[300];
789 int res;
790
791 if (argc < 3) {
792 printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
793 return -1;
794 }
795
796 if (argc > 3)
797 res = os_snprintf(buf, sizeof(buf),
798 "HS20_DEAUTH_REQ %s %s %s %s",
799 argv[0], argv[1], argv[2], argv[3]);
800 else
801 res = os_snprintf(buf, sizeof(buf),
802 "HS20_DEAUTH_REQ %s %s %s",
803 argv[0], argv[1], argv[2]);
Jouni Malinend85e1fc2014-12-08 11:15:51 +0200804 if (os_snprintf_error(sizeof(buf), res))
Jouni Malinen8e1146d2013-07-26 22:13:58 +0300805 return -1;
806 return wpa_ctrl_command(ctrl, buf);
807}
808
809
Jouni Malinen6fc68792008-02-27 17:34:43 -0800810static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
811{
812 hostapd_cli_quit = 1;
Jouni Malinen42838052011-07-16 17:37:18 +0300813 if (interactive)
814 eloop_terminate();
Jouni Malinen6fc68792008-02-27 17:34:43 -0800815 return 0;
816}
817
818
819static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
820{
821 char cmd[256];
822 if (argc != 1) {
823 printf("Invalid LEVEL command: needs one argument (debug "
824 "level)\n");
825 return 0;
826 }
827 snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
828 return wpa_ctrl_command(ctrl, cmd);
829}
830
831
832static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
833{
834 struct dirent *dent;
835 DIR *dir;
836
837 dir = opendir(ctrl_iface_dir);
838 if (dir == NULL) {
839 printf("Control interface directory '%s' could not be "
840 "openned.\n", ctrl_iface_dir);
841 return;
842 }
843
844 printf("Available interfaces:\n");
845 while ((dent = readdir(dir))) {
846 if (strcmp(dent->d_name, ".") == 0 ||
847 strcmp(dent->d_name, "..") == 0)
848 continue;
849 printf("%s\n", dent->d_name);
850 }
851 closedir(dir);
852}
853
854
855static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
856 char *argv[])
857{
858 if (argc < 1) {
859 hostapd_cli_list_interfaces(ctrl);
860 return 0;
861 }
862
863 hostapd_cli_close_connection();
Eduardo Abinadercc0b7cb2014-05-29 17:42:07 -0400864 os_free(ctrl_ifname);
865 ctrl_ifname = os_strdup(argv[0]);
Srinivas Girigowdadb9418b2014-08-28 18:25:32 +0300866 if (ctrl_ifname == NULL)
867 return -1;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800868
869 if (hostapd_cli_open_connection(ctrl_ifname)) {
870 printf("Connected to interface '%s.\n", ctrl_ifname);
871 if (wpa_ctrl_attach(ctrl_conn) == 0) {
872 hostapd_cli_attached = 1;
873 } else {
874 printf("Warning: Failed to attach to "
875 "hostapd.\n");
876 }
877 } else {
878 printf("Could not connect to interface '%s' - re-trying\n",
879 ctrl_ifname);
880 }
881 return 0;
882}
883
884
Jouni Malinenb4e34f22010-09-21 19:51:23 -0700885static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
886{
887 char cmd[256];
888 int res;
889
890 if (argc != 2) {
891 printf("Invalid SET command: needs two arguments (variable "
892 "name and value)\n");
893 return -1;
894 }
895
896 res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
Jouni Malineneeab4f22014-12-08 11:22:52 +0200897 if (os_snprintf_error(sizeof(cmd), res)) {
Jouni Malinenb4e34f22010-09-21 19:51:23 -0700898 printf("Too long SET command.\n");
899 return -1;
900 }
901 return wpa_ctrl_command(ctrl, cmd);
902}
903
904
Jouni Malinenacec8d32010-10-31 17:07:31 +0200905static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
906{
907 char cmd[256];
908 int res;
909
910 if (argc != 1) {
911 printf("Invalid GET command: needs one argument (variable "
912 "name)\n");
913 return -1;
914 }
915
916 res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
Jouni Malineneeab4f22014-12-08 11:22:52 +0200917 if (os_snprintf_error(sizeof(cmd), res)) {
Jouni Malinenacec8d32010-10-31 17:07:31 +0200918 printf("Too long GET command.\n");
919 return -1;
920 }
921 return wpa_ctrl_command(ctrl, cmd);
922}
923
924
Andrei Otcheretianski334bf362013-11-14 12:28:32 +0200925static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
926 int argc, char *argv[])
927{
928 char cmd[256];
929 int res;
930 int i;
931 char *tmp;
932 int total;
933
934 if (argc < 2) {
935 printf("Invalid chan_switch command: needs at least two "
936 "arguments (count and freq)\n"
937 "usage: <cs_count> <freq> [sec_channel_offset=] "
938 "[center_freq1=] [center_freq2=] [bandwidth=] "
939 "[blocktx] [ht|vht]\n");
940 return -1;
941 }
942
943 res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
944 argv[0], argv[1]);
Jouni Malineneeab4f22014-12-08 11:22:52 +0200945 if (os_snprintf_error(sizeof(cmd), res)) {
Andrei Otcheretianski334bf362013-11-14 12:28:32 +0200946 printf("Too long CHAN_SWITCH command.\n");
947 return -1;
948 }
949
950 total = res;
951 for (i = 2; i < argc; i++) {
952 tmp = cmd + total;
953 res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
Jouni Malineneeab4f22014-12-08 11:22:52 +0200954 if (os_snprintf_error(sizeof(cmd) - total, res)) {
Andrei Otcheretianski334bf362013-11-14 12:28:32 +0200955 printf("Too long CHAN_SWITCH command.\n");
956 return -1;
957 }
958 total += res;
959 }
960 return wpa_ctrl_command(ctrl, cmd);
961}
962
963
Cedric Baudeleta6938b72014-04-29 15:29:14 +0200964static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
965 char *argv[])
966{
967 return wpa_ctrl_command(ctrl, "ENABLE");
968}
969
970
971static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
972 char *argv[])
973{
974 return wpa_ctrl_command(ctrl, "RELOAD");
975}
976
977
978static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
979 char *argv[])
980{
981 return wpa_ctrl_command(ctrl, "DISABLE");
982}
983
984
Avraham Stern3ae8b7b2014-03-27 08:58:31 +0200985static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
986{
987 char cmd[256];
988 int res;
989
990 if (argc < 2 || argc > 3) {
991 printf("Invalid vendor command\n"
992 "usage: <vendor id> <command id> [<hex formatted command argument>]\n");
993 return -1;
994 }
995
996 res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
997 argc == 3 ? argv[2] : "");
Jouni Malineneeab4f22014-12-08 11:22:52 +0200998 if (os_snprintf_error(sizeof(cmd), res)) {
Avraham Stern3ae8b7b2014-03-27 08:58:31 +0200999 printf("Too long VENDOR command.\n");
1000 return -1;
1001 }
1002 return wpa_ctrl_command(ctrl, cmd);
1003}
1004
1005
Jouni Malinen2c6411e2014-12-14 13:31:12 +02001006static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
1007 char *argv[])
1008{
1009 return wpa_ctrl_command(ctrl, "ERP_FLUSH");
1010}
1011
1012
Jouni Malinen6fc68792008-02-27 17:34:43 -08001013struct hostapd_cli_cmd {
1014 const char *cmd;
1015 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
1016};
1017
1018static struct hostapd_cli_cmd hostapd_cli_commands[] = {
1019 { "ping", hostapd_cli_cmd_ping },
1020 { "mib", hostapd_cli_cmd_mib },
Ben Greearb41a47c2011-02-06 20:24:16 +02001021 { "relog", hostapd_cli_cmd_relog },
Jouni Malinen5ae64492013-11-03 18:13:14 +02001022 { "status", hostapd_cli_cmd_status },
Jouni Malinen6fc68792008-02-27 17:34:43 -08001023 { "sta", hostapd_cli_cmd_sta },
1024 { "all_sta", hostapd_cli_cmd_all_sta },
1025 { "new_sta", hostapd_cli_cmd_new_sta },
Jouni Malinen90a32062010-03-29 11:14:57 -07001026 { "deauthenticate", hostapd_cli_cmd_deauthenticate },
1027 { "disassociate", hostapd_cli_cmd_disassociate },
Jouni Malinen88b4b422008-12-30 18:04:29 +02001028#ifdef CONFIG_IEEE80211W
1029 { "sa_query", hostapd_cli_cmd_sa_query },
1030#endif /* CONFIG_IEEE80211W */
Jouni Malinenad08c362008-11-23 19:34:26 +02001031#ifdef CONFIG_WPS
1032 { "wps_pin", hostapd_cli_cmd_wps_pin },
Jouni Malinen3981cb32010-09-23 10:30:52 -07001033 { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
Jouni Malinenad08c362008-11-23 19:34:26 +02001034 { "wps_pbc", hostapd_cli_cmd_wps_pbc },
Anirban Sirkhell4c374cd2012-03-18 20:23:31 -10001035 { "wps_cancel", hostapd_cli_cmd_wps_cancel },
Jouni Malinenbb45b6d2012-06-27 20:54:56 +03001036#ifdef CONFIG_WPS_NFC
1037 { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
Jouni Malinen3cf7a592012-06-28 13:25:48 +03001038 { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
Jouni Malinenffdaa052012-06-28 19:43:29 +03001039 { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
Jouni Malinen6772a902013-02-10 17:12:55 +02001040 { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
Jouni Malinenbb45b6d2012-06-27 20:54:56 +03001041#endif /* CONFIG_WPS_NFC */
Jouni Malinen5a1cc302010-08-24 16:35:37 +03001042 { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
Jouni Malinen450eddc2010-10-21 16:49:41 +03001043 { "wps_config", hostapd_cli_cmd_wps_config },
Jeffin Mammen3351a382013-08-23 16:03:15 +03001044 { "wps_get_status", hostapd_cli_cmd_wps_get_status },
Jouni Malinenad08c362008-11-23 19:34:26 +02001045#endif /* CONFIG_WPS */
Jouni Malinen2049a872012-12-22 20:27:30 +02001046 { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
Jouni Malinen71269b32011-10-07 13:33:04 +03001047 { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
Jouni Malinena30dff02014-11-22 11:31:03 +02001048 { "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
Jouni Malinen403b96f2010-09-23 12:02:28 -07001049 { "get_config", hostapd_cli_cmd_get_config },
Jouni Malinen6fc68792008-02-27 17:34:43 -08001050 { "help", hostapd_cli_cmd_help },
1051 { "interface", hostapd_cli_cmd_interface },
1052 { "level", hostapd_cli_cmd_level },
1053 { "license", hostapd_cli_cmd_license },
1054 { "quit", hostapd_cli_cmd_quit },
Jouni Malinenb4e34f22010-09-21 19:51:23 -07001055 { "set", hostapd_cli_cmd_set },
Jouni Malinenacec8d32010-10-31 17:07:31 +02001056 { "get", hostapd_cli_cmd_get },
Kyeyoon Parkc5517002013-07-24 12:28:20 +03001057 { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
1058 { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
Andrei Otcheretianski334bf362013-11-14 12:28:32 +02001059 { "chan_switch", hostapd_cli_cmd_chan_switch },
Jouni Malinen3fb17a92013-03-17 16:03:42 +02001060 { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
Jouni Malinen8e1146d2013-07-26 22:13:58 +03001061 { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
Avraham Stern3ae8b7b2014-03-27 08:58:31 +02001062 { "vendor", hostapd_cli_cmd_vendor },
Cedric Baudeleta6938b72014-04-29 15:29:14 +02001063 { "enable", hostapd_cli_cmd_enable },
1064 { "reload", hostapd_cli_cmd_reload },
1065 { "disable", hostapd_cli_cmd_disable },
Jouni Malinen2c6411e2014-12-14 13:31:12 +02001066 { "erp_flush", hostapd_cli_cmd_erp_flush },
Jouni Malinen6fc68792008-02-27 17:34:43 -08001067 { NULL, NULL }
1068};
1069
1070
1071static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
1072{
1073 struct hostapd_cli_cmd *cmd, *match = NULL;
1074 int count;
1075
1076 count = 0;
1077 cmd = hostapd_cli_commands;
1078 while (cmd->cmd) {
1079 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
1080 match = cmd;
Jouni Malinenacec8d32010-10-31 17:07:31 +02001081 if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
1082 /* we have an exact match */
1083 count = 1;
1084 break;
1085 }
Jouni Malinen6fc68792008-02-27 17:34:43 -08001086 count++;
1087 }
1088 cmd++;
1089 }
1090
1091 if (count > 1) {
1092 printf("Ambiguous command '%s'; possible commands:", argv[0]);
1093 cmd = hostapd_cli_commands;
1094 while (cmd->cmd) {
1095 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
1096 0) {
1097 printf(" %s", cmd->cmd);
1098 }
1099 cmd++;
1100 }
1101 printf("\n");
1102 } else if (count == 0) {
1103 printf("Unknown command '%s'\n", argv[0]);
1104 } else {
1105 match->handler(ctrl, argc - 1, &argv[1]);
1106 }
1107}
1108
1109
Gregory Detalbae92172010-04-07 11:14:54 +03001110static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
1111 int action_monitor)
Jouni Malinen6fc68792008-02-27 17:34:43 -08001112{
1113 int first = 1;
1114 if (ctrl_conn == NULL)
1115 return;
1116 while (wpa_ctrl_pending(ctrl)) {
1117 char buf[256];
1118 size_t len = sizeof(buf) - 1;
1119 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
1120 buf[len] = '\0';
Gregory Detalbae92172010-04-07 11:14:54 +03001121 if (action_monitor)
1122 hostapd_cli_action_process(buf, len);
1123 else {
1124 if (in_read && first)
1125 printf("\n");
1126 first = 0;
1127 printf("%s\n", buf);
1128 }
Jouni Malinen6fc68792008-02-27 17:34:43 -08001129 } else {
1130 printf("Could not read pending message.\n");
1131 break;
1132 }
1133 }
1134}
1135
1136
Jouni Malinen42838052011-07-16 17:37:18 +03001137#define max_args 10
1138
1139static int tokenize_cmd(char *cmd, char *argv[])
Jouni Malinen6fc68792008-02-27 17:34:43 -08001140{
Jouni Malinen42838052011-07-16 17:37:18 +03001141 char *pos;
1142 int argc = 0;
Jouni Malinen6fc68792008-02-27 17:34:43 -08001143
Jouni Malinen42838052011-07-16 17:37:18 +03001144 pos = cmd;
1145 for (;;) {
1146 while (*pos == ' ')
Jouni Malinen6fc68792008-02-27 17:34:43 -08001147 pos++;
Jouni Malinen42838052011-07-16 17:37:18 +03001148 if (*pos == '\0')
1149 break;
1150 argv[argc] = pos;
1151 argc++;
1152 if (argc == max_args)
1153 break;
1154 if (*pos == '"') {
1155 char *pos2 = os_strrchr(pos, '"');
1156 if (pos2)
1157 pos = pos2 + 1;
Jouni Malinen6fc68792008-02-27 17:34:43 -08001158 }
Jouni Malinen42838052011-07-16 17:37:18 +03001159 while (*pos != '\0' && *pos != ' ')
1160 pos++;
1161 if (*pos == ' ')
1162 *pos++ = '\0';
1163 }
1164
1165 return argc;
Jouni Malinen6fc68792008-02-27 17:34:43 -08001166}
1167
1168
Jouni Malinen42838052011-07-16 17:37:18 +03001169static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
Jouni Malinen6fc68792008-02-27 17:34:43 -08001170{
1171 if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
1172 printf("Connection to hostapd lost - trying to reconnect\n");
1173 hostapd_cli_close_connection();
1174 }
1175 if (!ctrl_conn) {
1176 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1177 if (ctrl_conn) {
1178 printf("Connection to hostapd re-established\n");
1179 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1180 hostapd_cli_attached = 1;
1181 } else {
1182 printf("Warning: Failed to attach to "
1183 "hostapd.\n");
1184 }
1185 }
1186 }
1187 if (ctrl_conn)
Gregory Detalbae92172010-04-07 11:14:54 +03001188 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
Jouni Malinen42838052011-07-16 17:37:18 +03001189 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1190}
1191
1192
1193static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
1194{
1195 eloop_terminate();
1196}
1197
1198
1199static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
1200{
1201 char *argv[max_args];
1202 int argc;
1203 argc = tokenize_cmd(cmd, argv);
1204 if (argc)
1205 wpa_request(ctrl_conn, argc, argv);
1206}
1207
1208
1209static void hostapd_cli_edit_eof_cb(void *ctx)
1210{
1211 eloop_terminate();
1212}
1213
1214
1215static void hostapd_cli_interactive(void)
1216{
1217 printf("\nInteractive mode\n\n");
1218
1219 eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
1220 edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
Janusz Dziedzic2d2398a2012-08-05 20:46:34 +03001221 NULL, NULL, NULL, NULL);
Jouni Malinen42838052011-07-16 17:37:18 +03001222 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1223
1224 eloop_run();
1225
1226 edit_deinit(NULL, NULL);
1227 eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
1228}
1229
1230
1231static void hostapd_cli_cleanup(void)
1232{
1233 hostapd_cli_close_connection();
1234 if (pid_file)
1235 os_daemonize_terminate(pid_file);
1236
1237 os_program_deinit();
Jouni Malinen6fc68792008-02-27 17:34:43 -08001238}
1239
1240
Gregory Detalbae92172010-04-07 11:14:54 +03001241static void hostapd_cli_action(struct wpa_ctrl *ctrl)
1242{
1243 fd_set rfds;
1244 int fd, res;
1245 struct timeval tv;
1246 char buf[256];
1247 size_t len;
1248
1249 fd = wpa_ctrl_get_fd(ctrl);
1250
1251 while (!hostapd_cli_quit) {
1252 FD_ZERO(&rfds);
1253 FD_SET(fd, &rfds);
1254 tv.tv_sec = ping_interval;
1255 tv.tv_usec = 0;
1256 res = select(fd + 1, &rfds, NULL, NULL, &tv);
1257 if (res < 0 && errno != EINTR) {
1258 perror("select");
1259 break;
1260 }
1261
1262 if (FD_ISSET(fd, &rfds))
1263 hostapd_cli_recv_pending(ctrl, 0, 1);
1264 else {
1265 len = sizeof(buf) - 1;
1266 if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1267 hostapd_cli_action_process) < 0 ||
1268 len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1269 printf("hostapd did not reply to PING "
1270 "command - exiting\n");
1271 break;
1272 }
1273 }
1274 }
1275}
1276
1277
Jouni Malinen6fc68792008-02-27 17:34:43 -08001278int main(int argc, char *argv[])
1279{
Jouni Malinen6fc68792008-02-27 17:34:43 -08001280 int warning_displayed = 0;
1281 int c;
Gregory Detalbae92172010-04-07 11:14:54 +03001282 int daemonize = 0;
Jouni Malinen6fc68792008-02-27 17:34:43 -08001283
Jouni Malinen3433ed82009-12-19 22:26:36 +02001284 if (os_program_init())
1285 return -1;
1286
Jouni Malinen6fc68792008-02-27 17:34:43 -08001287 for (;;) {
Gregory Detalbae92172010-04-07 11:14:54 +03001288 c = getopt(argc, argv, "a:BhG:i:p:v");
Jouni Malinen6fc68792008-02-27 17:34:43 -08001289 if (c < 0)
1290 break;
1291 switch (c) {
Gregory Detalbae92172010-04-07 11:14:54 +03001292 case 'a':
1293 action_file = optarg;
1294 break;
1295 case 'B':
1296 daemonize = 1;
1297 break;
Jouni Malinen1cc84c12009-01-20 21:12:00 +02001298 case 'G':
1299 ping_interval = atoi(optarg);
1300 break;
Jouni Malinen6fc68792008-02-27 17:34:43 -08001301 case 'h':
1302 usage();
1303 return 0;
1304 case 'v':
1305 printf("%s\n", hostapd_cli_version);
1306 return 0;
1307 case 'i':
Jouni Malinenb242d392010-04-07 11:40:34 +03001308 os_free(ctrl_ifname);
1309 ctrl_ifname = os_strdup(optarg);
Jouni Malinen6fc68792008-02-27 17:34:43 -08001310 break;
1311 case 'p':
1312 ctrl_iface_dir = optarg;
1313 break;
1314 default:
1315 usage();
1316 return -1;
1317 }
1318 }
1319
Gregory Detalbae92172010-04-07 11:14:54 +03001320 interactive = (argc == optind) && (action_file == NULL);
Jouni Malinen6fc68792008-02-27 17:34:43 -08001321
1322 if (interactive) {
1323 printf("%s\n\n%s\n\n", hostapd_cli_version,
1324 hostapd_cli_license);
1325 }
1326
Jouni Malinen42838052011-07-16 17:37:18 +03001327 if (eloop_init())
1328 return -1;
1329
Jouni Malinen6fc68792008-02-27 17:34:43 -08001330 for (;;) {
1331 if (ctrl_ifname == NULL) {
1332 struct dirent *dent;
1333 DIR *dir = opendir(ctrl_iface_dir);
1334 if (dir) {
1335 while ((dent = readdir(dir))) {
Jouni Malinenb242d392010-04-07 11:40:34 +03001336 if (os_strcmp(dent->d_name, ".") == 0
1337 ||
1338 os_strcmp(dent->d_name, "..") == 0)
Jouni Malinen6fc68792008-02-27 17:34:43 -08001339 continue;
1340 printf("Selected interface '%s'\n",
1341 dent->d_name);
Jouni Malinenb242d392010-04-07 11:40:34 +03001342 ctrl_ifname = os_strdup(dent->d_name);
Jouni Malinen6fc68792008-02-27 17:34:43 -08001343 break;
1344 }
1345 closedir(dir);
1346 }
1347 }
1348 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1349 if (ctrl_conn) {
1350 if (warning_displayed)
1351 printf("Connection established.\n");
1352 break;
1353 }
1354
1355 if (!interactive) {
1356 perror("Failed to connect to hostapd - "
1357 "wpa_ctrl_open");
1358 return -1;
1359 }
1360
1361 if (!warning_displayed) {
1362 printf("Could not connect to hostapd - re-trying\n");
1363 warning_displayed = 1;
1364 }
Jouni Malinenb242d392010-04-07 11:40:34 +03001365 os_sleep(1, 0);
Jouni Malinen6fc68792008-02-27 17:34:43 -08001366 continue;
1367 }
1368
Gregory Detalbae92172010-04-07 11:14:54 +03001369 if (interactive || action_file) {
Jouni Malinen6fc68792008-02-27 17:34:43 -08001370 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1371 hostapd_cli_attached = 1;
1372 } else {
1373 printf("Warning: Failed to attach to hostapd.\n");
Gregory Detalbae92172010-04-07 11:14:54 +03001374 if (action_file)
1375 return -1;
Jouni Malinen6fc68792008-02-27 17:34:43 -08001376 }
Gregory Detalbae92172010-04-07 11:14:54 +03001377 }
1378
1379 if (daemonize && os_daemonize(pid_file))
1380 return -1;
1381
1382 if (interactive)
Jouni Malinen6fc68792008-02-27 17:34:43 -08001383 hostapd_cli_interactive();
Gregory Detalbae92172010-04-07 11:14:54 +03001384 else if (action_file)
1385 hostapd_cli_action(ctrl_conn);
1386 else
Jouni Malinen6fc68792008-02-27 17:34:43 -08001387 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1388
Gregory Detalbae92172010-04-07 11:14:54 +03001389 os_free(ctrl_ifname);
Jouni Malinen42838052011-07-16 17:37:18 +03001390 eloop_destroy();
Gregory Detalbae92172010-04-07 11:14:54 +03001391 hostapd_cli_cleanup();
Jouni Malinen6fc68792008-02-27 17:34:43 -08001392 return 0;
1393}