blob: 46c2f37e4601747347c17c7345dc918aed3891f9 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * hostapd - command line interface for hostapd daemon
Dmitry Shmidt807291d2015-01-27 13:40:23 -08003 * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "includes.h"
10#include <dirent.h>
11
12#include "common/wpa_ctrl.h"
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -070013#include "common/ieee802_11_defs.h"
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -080014#include "utils/common.h"
15#include "utils/eloop.h"
16#include "utils/edit.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070017#include "common/version.h"
18
19
Dmitry Shmidt1d755d02015-04-28 10:34:29 -070020static const char *const hostapd_cli_version =
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070021"hostapd_cli v" VERSION_STR "\n"
Dmitry Shmidt807291d2015-01-27 13:40:23 -080022"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070023
24
Dmitry Shmidt1d755d02015-04-28 10:34:29 -070025static const char *const hostapd_cli_license =
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -080026"This software may be distributed under the terms of the BSD license.\n"
27"See README for more details.\n";
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070028
Dmitry Shmidt1d755d02015-04-28 10:34:29 -070029static const char *const hostapd_cli_full_license =
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -080030"This software may be distributed under the terms of the BSD license.\n"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070031"\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
Dmitry Shmidt1d755d02015-04-28 10:34:29 -070060static const char *const commands_help =
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070061"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"
66" deauthenticate <addr> deauthenticate a station\n"
67" disassociate <addr> disassociate a station\n"
68#ifdef CONFIG_IEEE80211W
69" sa_query <addr> send SA Query to a station\n"
70#endif /* CONFIG_IEEE80211W */
71#ifdef CONFIG_WPS
72" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n"
73" wps_check_pin <PIN> verify PIN checksum\n"
74" wps_pbc indicate button pushed to initiate PBC\n"
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070075" wps_cancel cancel the pending WPS operation\n"
Dmitry Shmidt04949592012-07-19 12:16:46 -070076#ifdef CONFIG_WPS_NFC
77" wps_nfc_tag_read <hexdump> report read NFC tag with WPS data\n"
78" wps_nfc_config_token <WPS/NDEF> build NFC configuration token\n"
79" wps_nfc_token <WPS/NDEF/enable/disable> manager NFC password token\n"
80#endif /* CONFIG_WPS_NFC */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070081" wps_ap_pin <cmd> [params..] enable/disable AP PIN\n"
82" wps_config <SSID> <auth> <encr> <key> configure AP\n"
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -070083" wps_get_status show current WPS status\n"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070084#endif /* CONFIG_WPS */
85" get_config show current configuration\n"
86" 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 Johnson205f2142012-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;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800100static const char *client_socket_dir = NULL;
Jeff Johnson205f2142012-09-03 22:12:17 -0700101
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700102static char *ctrl_ifname = NULL;
103static const char *pid_file = NULL;
104static const char *action_file = NULL;
105static int ping_interval = 5;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800106static int interactive = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700107
108
109static void usage(void)
110{
111 fprintf(stderr, "%s\n", hostapd_cli_version);
112 fprintf(stderr,
113 "\n"
114 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
115 "[-a<path>] \\\n"
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800116 " [-P<pid file>] [-G<ping interval>] [command..]\n"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700117 "\n"
118 "Options:\n"
119 " -h help (show this usage text)\n"
120 " -v shown version information\n"
121 " -p<path> path to find control sockets (default: "
122 "/var/run/hostapd)\n"
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800123 " -s<dir_path> dir path to open client sockets (default: "
124 CONFIG_CTRL_IFACE_DIR ")\n"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700125 " -a<file> run in daemon mode executing the action file "
126 "based on events\n"
127 " from hostapd\n"
128 " -B run a daemon in the background\n"
129 " -i<ifname> Interface to listen on (default: first "
130 "interface found in the\n"
131 " socket path)\n\n"
132 "%s",
133 commands_help);
134}
135
136
137static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
138{
139 char *cfile;
140 int flen;
141
142 if (ifname == NULL)
143 return NULL;
144
145 flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
146 cfile = malloc(flen);
147 if (cfile == NULL)
148 return NULL;
149 snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
150
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800151 if (client_socket_dir && client_socket_dir[0] &&
152 access(client_socket_dir, F_OK) < 0) {
153 perror(client_socket_dir);
154 free(cfile);
155 return NULL;
156 }
157
158 ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700159 free(cfile);
160 return ctrl_conn;
161}
162
163
164static void hostapd_cli_close_connection(void)
165{
166 if (ctrl_conn == NULL)
167 return;
168
169 if (hostapd_cli_attached) {
170 wpa_ctrl_detach(ctrl_conn);
171 hostapd_cli_attached = 0;
172 }
173 wpa_ctrl_close(ctrl_conn);
174 ctrl_conn = NULL;
175}
176
177
178static void hostapd_cli_msg_cb(char *msg, size_t len)
179{
180 printf("%s\n", msg);
181}
182
183
184static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
185{
186 char buf[4096];
187 size_t len;
188 int ret;
189
190 if (ctrl_conn == NULL) {
191 printf("Not connected to hostapd - command dropped.\n");
192 return -1;
193 }
194 len = sizeof(buf) - 1;
195 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
196 hostapd_cli_msg_cb);
197 if (ret == -2) {
198 printf("'%s' command timed out.\n", cmd);
199 return -2;
200 } else if (ret < 0) {
201 printf("'%s' command failed.\n", cmd);
202 return -1;
203 }
204 if (print) {
205 buf[len] = '\0';
206 printf("%s", buf);
207 }
208 return 0;
209}
210
211
212static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
213{
214 return _wpa_ctrl_command(ctrl, cmd, 1);
215}
216
217
218static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
219{
220 return wpa_ctrl_command(ctrl, "PING");
221}
222
223
224static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
225{
226 return wpa_ctrl_command(ctrl, "RELOG");
227}
228
229
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800230static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
231{
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800232 if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
233 return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800234 return wpa_ctrl_command(ctrl, "STATUS");
235}
236
237
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700238static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
239{
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800240 if (argc > 0) {
241 char buf[100];
242 os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]);
243 return wpa_ctrl_command(ctrl, buf);
244 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700245 return wpa_ctrl_command(ctrl, "MIB");
246}
247
248
249static int hostapd_cli_exec(const char *program, const char *arg1,
250 const char *arg2)
251{
Jouni Malinen772e12c2014-10-07 10:29:35 -0700252 char *arg;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700253 size_t len;
254 int res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700255
Jouni Malinen772e12c2014-10-07 10:29:35 -0700256 len = os_strlen(arg1) + os_strlen(arg2) + 2;
257 arg = os_malloc(len);
258 if (arg == NULL)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700259 return -1;
Jouni Malinen772e12c2014-10-07 10:29:35 -0700260 os_snprintf(arg, len, "%s %s", arg1, arg2);
261 res = os_exec(program, arg, 1);
262 os_free(arg);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700263
Jouni Malinen772e12c2014-10-07 10:29:35 -0700264 return res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700265}
266
267
268static void hostapd_cli_action_process(char *msg, size_t len)
269{
270 const char *pos;
271
272 pos = msg;
273 if (*pos == '<') {
274 pos = os_strchr(pos, '>');
275 if (pos)
276 pos++;
277 else
278 pos = msg;
279 }
280
281 hostapd_cli_exec(action_file, ctrl_ifname, pos);
282}
283
284
285static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
286{
287 char buf[64];
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800288 if (argc < 1) {
289 printf("Invalid 'sta' command - at least one argument, STA "
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700290 "address, is required.\n");
291 return -1;
292 }
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800293 if (argc > 1)
294 snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]);
295 else
296 snprintf(buf, sizeof(buf), "STA %s", argv[0]);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700297 return wpa_ctrl_command(ctrl, buf);
298}
299
300
301static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
302 char *argv[])
303{
304 char buf[64];
305 if (argc != 1) {
306 printf("Invalid 'new_sta' command - exactly one argument, STA "
307 "address, is required.\n");
308 return -1;
309 }
310 snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
311 return wpa_ctrl_command(ctrl, buf);
312}
313
314
315static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
316 char *argv[])
317{
318 char buf[64];
319 if (argc < 1) {
320 printf("Invalid 'deauthenticate' command - exactly one "
321 "argument, STA address, is required.\n");
322 return -1;
323 }
324 if (argc > 1)
325 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
326 argv[0], argv[1]);
327 else
328 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
329 return wpa_ctrl_command(ctrl, buf);
330}
331
332
333static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
334 char *argv[])
335{
336 char buf[64];
337 if (argc < 1) {
338 printf("Invalid 'disassociate' command - exactly one "
339 "argument, STA address, is required.\n");
340 return -1;
341 }
342 if (argc > 1)
343 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
344 argv[0], argv[1]);
345 else
346 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
347 return wpa_ctrl_command(ctrl, buf);
348}
349
350
351#ifdef CONFIG_IEEE80211W
352static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
353 char *argv[])
354{
355 char buf[64];
356 if (argc != 1) {
357 printf("Invalid 'sa_query' command - exactly one argument, "
358 "STA address, is required.\n");
359 return -1;
360 }
361 snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
362 return wpa_ctrl_command(ctrl, buf);
363}
364#endif /* CONFIG_IEEE80211W */
365
366
367#ifdef CONFIG_WPS
368static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
369 char *argv[])
370{
371 char buf[256];
372 if (argc < 2) {
373 printf("Invalid 'wps_pin' command - at least two arguments, "
374 "UUID and PIN, are required.\n");
375 return -1;
376 }
377 if (argc > 3)
378 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
379 argv[0], argv[1], argv[2], argv[3]);
380 else if (argc > 2)
381 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
382 argv[0], argv[1], argv[2]);
383 else
384 snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
385 return wpa_ctrl_command(ctrl, buf);
386}
387
388
389static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
390 char *argv[])
391{
392 char cmd[256];
393 int res;
394
395 if (argc != 1 && argc != 2) {
396 printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
397 "- PIN to be verified\n");
398 return -1;
399 }
400
401 if (argc == 2)
402 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
403 argv[0], argv[1]);
404 else
405 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
406 argv[0]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800407 if (os_snprintf_error(sizeof(cmd), res)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700408 printf("Too long WPS_CHECK_PIN command.\n");
409 return -1;
410 }
411 return wpa_ctrl_command(ctrl, cmd);
412}
413
414
415static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
416 char *argv[])
417{
418 return wpa_ctrl_command(ctrl, "WPS_PBC");
419}
420
421
Dmitry Shmidt04949592012-07-19 12:16:46 -0700422static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
423 char *argv[])
424{
425 return wpa_ctrl_command(ctrl, "WPS_CANCEL");
426}
427
428
Dmitry Shmidt04949592012-07-19 12:16:46 -0700429#ifdef CONFIG_WPS_NFC
430static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
431 char *argv[])
432{
433 int ret;
434 char *buf;
435 size_t buflen;
436
437 if (argc != 1) {
438 printf("Invalid 'wps_nfc_tag_read' command - one argument "
439 "is required.\n");
440 return -1;
441 }
442
443 buflen = 18 + os_strlen(argv[0]);
444 buf = os_malloc(buflen);
445 if (buf == NULL)
446 return -1;
447 os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
448
449 ret = wpa_ctrl_command(ctrl, buf);
450 os_free(buf);
451
452 return ret;
453}
454
455
456static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
457 int argc, char *argv[])
458{
459 char cmd[64];
460 int res;
461
462 if (argc != 1) {
463 printf("Invalid 'wps_nfc_config_token' command - one argument "
464 "is required.\n");
465 return -1;
466 }
467
468 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
469 argv[0]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800470 if (os_snprintf_error(sizeof(cmd), res)) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700471 printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
472 return -1;
473 }
474 return wpa_ctrl_command(ctrl, cmd);
475}
476
477
478static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
479 int argc, char *argv[])
480{
481 char cmd[64];
482 int res;
483
484 if (argc != 1) {
485 printf("Invalid 'wps_nfc_token' command - one argument is "
486 "required.\n");
487 return -1;
488 }
489
490 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800491 if (os_snprintf_error(sizeof(cmd), res)) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700492 printf("Too long WPS_NFC_TOKEN command.\n");
493 return -1;
494 }
495 return wpa_ctrl_command(ctrl, cmd);
496}
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800497
498
499static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
500 int argc, char *argv[])
501{
502 char cmd[64];
503 int res;
504
505 if (argc != 2) {
506 printf("Invalid 'nfc_get_handover_sel' command - two arguments "
507 "are required.\n");
508 return -1;
509 }
510
511 res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
512 argv[0], argv[1]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800513 if (os_snprintf_error(sizeof(cmd), res)) {
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800514 printf("Too long NFC_GET_HANDOVER_SEL command.\n");
515 return -1;
516 }
517 return wpa_ctrl_command(ctrl, cmd);
518}
519
Dmitry Shmidt04949592012-07-19 12:16:46 -0700520#endif /* CONFIG_WPS_NFC */
521
522
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700523static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
524 char *argv[])
525{
526 char buf[64];
527 if (argc < 1) {
528 printf("Invalid 'wps_ap_pin' command - at least one argument "
529 "is required.\n");
530 return -1;
531 }
532 if (argc > 2)
533 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
534 argv[0], argv[1], argv[2]);
535 else if (argc > 1)
536 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
537 argv[0], argv[1]);
538 else
539 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
540 return wpa_ctrl_command(ctrl, buf);
541}
542
543
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700544static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
545 char *argv[])
546{
547 return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
548}
549
550
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700551static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
552 char *argv[])
553{
554 char buf[256];
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700555 char ssid_hex[2 * SSID_MAX_LEN + 1];
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700556 char key_hex[2 * 64 + 1];
557 int i;
558
559 if (argc < 1) {
560 printf("Invalid 'wps_config' command - at least two arguments "
561 "are required.\n");
562 return -1;
563 }
564
565 ssid_hex[0] = '\0';
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700566 for (i = 0; i < SSID_MAX_LEN; i++) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700567 if (argv[0][i] == '\0')
568 break;
569 os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
570 }
571
572 key_hex[0] = '\0';
573 if (argc > 3) {
574 for (i = 0; i < 64; i++) {
575 if (argv[3][i] == '\0')
576 break;
577 os_snprintf(&key_hex[i * 2], 3, "%02x",
578 argv[3][i]);
579 }
580 }
581
582 if (argc > 3)
583 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
584 ssid_hex, argv[1], argv[2], key_hex);
585 else if (argc > 2)
586 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
587 ssid_hex, argv[1], argv[2]);
588 else
589 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
590 ssid_hex, argv[1]);
591 return wpa_ctrl_command(ctrl, buf);
592}
593#endif /* CONFIG_WPS */
594
595
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800596static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
597 char *argv[])
598{
599 char buf[300];
600 int res;
601
602 if (argc < 2) {
603 printf("Invalid 'disassoc_imminent' command - two arguments "
604 "(STA addr and Disassociation Timer) are needed\n");
605 return -1;
606 }
607
608 res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
609 argv[0], argv[1]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800610 if (os_snprintf_error(sizeof(buf), res))
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800611 return -1;
612 return wpa_ctrl_command(ctrl, buf);
613}
614
615
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800616static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
617 char *argv[])
618{
619 char buf[300];
620 int res;
621
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700622 if (argc < 3) {
623 printf("Invalid 'ess_disassoc' command - three arguments (STA "
624 "addr, disassoc timer, and URL) are needed\n");
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800625 return -1;
626 }
627
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700628 res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
629 argv[0], argv[1], argv[2]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800630 if (os_snprintf_error(sizeof(buf), res))
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800631 return -1;
632 return wpa_ctrl_command(ctrl, buf);
633}
634
635
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800636static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
637 char *argv[])
638{
639 char buf[2000], *tmp;
640 int res, i, total;
641
642 if (argc < 1) {
643 printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n");
644 return -1;
645 }
646
647 res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]);
648 if (os_snprintf_error(sizeof(buf), res))
649 return -1;
650
651 total = res;
652 for (i = 1; i < argc; i++) {
653 tmp = &buf[total];
654 res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]);
655 if (os_snprintf_error(sizeof(buf) - total, res))
656 return -1;
657 total += res;
658 }
659 return wpa_ctrl_command(ctrl, buf);
660}
661
662
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700663static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
664 char *argv[])
665{
666 return wpa_ctrl_command(ctrl, "GET_CONFIG");
667}
668
669
670static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
671 char *addr, size_t addr_len)
672{
673 char buf[4096], *pos;
674 size_t len;
675 int ret;
676
677 if (ctrl_conn == NULL) {
678 printf("Not connected to hostapd - command dropped.\n");
679 return -1;
680 }
681 len = sizeof(buf) - 1;
682 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
683 hostapd_cli_msg_cb);
684 if (ret == -2) {
685 printf("'%s' command timed out.\n", cmd);
686 return -2;
687 } else if (ret < 0) {
688 printf("'%s' command failed.\n", cmd);
689 return -1;
690 }
691
692 buf[len] = '\0';
693 if (memcmp(buf, "FAIL", 4) == 0)
694 return -1;
695 printf("%s", buf);
696
697 pos = buf;
698 while (*pos != '\0' && *pos != '\n')
699 pos++;
700 *pos = '\0';
701 os_strlcpy(addr, buf, addr_len);
702 return 0;
703}
704
705
706static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
707 char *argv[])
708{
709 char addr[32], cmd[64];
710
711 if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
712 return 0;
713 do {
714 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
715 } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
716
717 return -1;
718}
719
720
721static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
722{
723 printf("%s", commands_help);
724 return 0;
725}
726
727
728static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
729 char *argv[])
730{
731 printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
732 return 0;
733}
734
735
Dmitry Shmidt051af732013-10-22 13:52:46 -0700736static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
737 int argc, char *argv[])
738{
739 char buf[200];
740 int res;
741
742 if (argc != 1) {
743 printf("Invalid 'set_qos_map_set' command - "
744 "one argument (comma delimited QoS map set) "
745 "is needed\n");
746 return -1;
747 }
748
749 res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800750 if (os_snprintf_error(sizeof(buf), res))
Dmitry Shmidt051af732013-10-22 13:52:46 -0700751 return -1;
752 return wpa_ctrl_command(ctrl, buf);
753}
754
755
756static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
757 int argc, char *argv[])
758{
759 char buf[50];
760 int res;
761
762 if (argc != 1) {
763 printf("Invalid 'send_qos_map_conf' command - "
764 "one argument (STA addr) is needed\n");
765 return -1;
766 }
767
768 res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800769 if (os_snprintf_error(sizeof(buf), res))
Dmitry Shmidt051af732013-10-22 13:52:46 -0700770 return -1;
771 return wpa_ctrl_command(ctrl, buf);
772}
773
774
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800775static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
776 char *argv[])
777{
778 char buf[300];
779 int res;
780
781 if (argc < 2) {
782 printf("Invalid 'hs20_wnm_notif' command - two arguments (STA "
783 "addr and URL) are needed\n");
784 return -1;
785 }
786
787 res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
788 argv[0], argv[1]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800789 if (os_snprintf_error(sizeof(buf), res))
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800790 return -1;
791 return wpa_ctrl_command(ctrl, buf);
792}
793
794
795static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
796 char *argv[])
797{
798 char buf[300];
799 int res;
800
801 if (argc < 3) {
802 printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
803 return -1;
804 }
805
806 if (argc > 3)
807 res = os_snprintf(buf, sizeof(buf),
808 "HS20_DEAUTH_REQ %s %s %s %s",
809 argv[0], argv[1], argv[2], argv[3]);
810 else
811 res = os_snprintf(buf, sizeof(buf),
812 "HS20_DEAUTH_REQ %s %s %s",
813 argv[0], argv[1], argv[2]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800814 if (os_snprintf_error(sizeof(buf), res))
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800815 return -1;
816 return wpa_ctrl_command(ctrl, buf);
817}
818
819
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700820static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
821{
822 hostapd_cli_quit = 1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800823 if (interactive)
824 eloop_terminate();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700825 return 0;
826}
827
828
829static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
830{
831 char cmd[256];
832 if (argc != 1) {
833 printf("Invalid LEVEL command: needs one argument (debug "
834 "level)\n");
835 return 0;
836 }
837 snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
838 return wpa_ctrl_command(ctrl, cmd);
839}
840
841
842static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
843{
844 struct dirent *dent;
845 DIR *dir;
846
847 dir = opendir(ctrl_iface_dir);
848 if (dir == NULL) {
849 printf("Control interface directory '%s' could not be "
850 "openned.\n", ctrl_iface_dir);
851 return;
852 }
853
854 printf("Available interfaces:\n");
855 while ((dent = readdir(dir))) {
856 if (strcmp(dent->d_name, ".") == 0 ||
857 strcmp(dent->d_name, "..") == 0)
858 continue;
859 printf("%s\n", dent->d_name);
860 }
861 closedir(dir);
862}
863
864
865static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
866 char *argv[])
867{
868 if (argc < 1) {
869 hostapd_cli_list_interfaces(ctrl);
870 return 0;
871 }
872
873 hostapd_cli_close_connection();
Dmitry Shmidt71757432014-06-02 13:50:35 -0700874 os_free(ctrl_ifname);
875 ctrl_ifname = os_strdup(argv[0]);
Dmitry Shmidt661b4f72014-09-29 14:58:27 -0700876 if (ctrl_ifname == NULL)
877 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700878
879 if (hostapd_cli_open_connection(ctrl_ifname)) {
880 printf("Connected to interface '%s.\n", ctrl_ifname);
881 if (wpa_ctrl_attach(ctrl_conn) == 0) {
882 hostapd_cli_attached = 1;
883 } else {
884 printf("Warning: Failed to attach to "
885 "hostapd.\n");
886 }
887 } else {
888 printf("Could not connect to interface '%s' - re-trying\n",
889 ctrl_ifname);
890 }
891 return 0;
892}
893
894
895static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
896{
897 char cmd[256];
898 int res;
899
900 if (argc != 2) {
901 printf("Invalid SET command: needs two arguments (variable "
902 "name and value)\n");
903 return -1;
904 }
905
906 res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800907 if (os_snprintf_error(sizeof(cmd), res)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700908 printf("Too long SET command.\n");
909 return -1;
910 }
911 return wpa_ctrl_command(ctrl, cmd);
912}
913
914
915static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
916{
917 char cmd[256];
918 int res;
919
920 if (argc != 1) {
921 printf("Invalid GET command: needs one argument (variable "
922 "name)\n");
923 return -1;
924 }
925
926 res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800927 if (os_snprintf_error(sizeof(cmd), res)) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700928 printf("Too long GET command.\n");
929 return -1;
930 }
931 return wpa_ctrl_command(ctrl, cmd);
932}
933
934
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800935#ifdef CONFIG_FST
936static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
937{
938 char cmd[256];
939 int res;
940 int i;
941 int total;
942
943 if (argc <= 0) {
944 printf("FST command: parameters are required.\n");
945 return -1;
946 }
947
948 total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER");
949
950 for (i = 0; i < argc; i++) {
951 res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s",
952 argv[i]);
953 if (os_snprintf_error(sizeof(cmd) - total, res)) {
954 printf("Too long fst command.\n");
955 return -1;
956 }
957 total += res;
958 }
959 return wpa_ctrl_command(ctrl, cmd);
960}
961#endif /* CONFIG_FST */
962
963
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -0800964static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
965 int argc, char *argv[])
966{
967 char cmd[256];
968 int res;
969 int i;
970 char *tmp;
971 int total;
972
973 if (argc < 2) {
974 printf("Invalid chan_switch command: needs at least two "
975 "arguments (count and freq)\n"
976 "usage: <cs_count> <freq> [sec_channel_offset=] "
977 "[center_freq1=] [center_freq2=] [bandwidth=] "
978 "[blocktx] [ht|vht]\n");
979 return -1;
980 }
981
982 res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
983 argv[0], argv[1]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800984 if (os_snprintf_error(sizeof(cmd), res)) {
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -0800985 printf("Too long CHAN_SWITCH command.\n");
986 return -1;
987 }
988
989 total = res;
990 for (i = 2; i < argc; i++) {
991 tmp = cmd + total;
992 res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800993 if (os_snprintf_error(sizeof(cmd) - total, res)) {
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -0800994 printf("Too long CHAN_SWITCH command.\n");
995 return -1;
996 }
997 total += res;
998 }
999 return wpa_ctrl_command(ctrl, cmd);
1000}
1001
1002
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001003static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
1004 char *argv[])
1005{
1006 return wpa_ctrl_command(ctrl, "ENABLE");
1007}
1008
1009
1010static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
1011 char *argv[])
1012{
1013 return wpa_ctrl_command(ctrl, "RELOAD");
1014}
1015
1016
1017static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
1018 char *argv[])
1019{
1020 return wpa_ctrl_command(ctrl, "DISABLE");
1021}
1022
1023
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001024static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
1025{
1026 char cmd[256];
1027 int res;
1028
1029 if (argc < 2 || argc > 3) {
1030 printf("Invalid vendor command\n"
1031 "usage: <vendor id> <command id> [<hex formatted command argument>]\n");
1032 return -1;
1033 }
1034
1035 res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
1036 argc == 3 ? argv[2] : "");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001037 if (os_snprintf_error(sizeof(cmd), res)) {
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001038 printf("Too long VENDOR command.\n");
1039 return -1;
1040 }
1041 return wpa_ctrl_command(ctrl, cmd);
1042}
1043
1044
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001045static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
1046 char *argv[])
1047{
1048 return wpa_ctrl_command(ctrl, "ERP_FLUSH");
1049}
1050
1051
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001052static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc,
1053 char *argv[])
1054{
1055 char cmd[256];
1056 int res;
1057
1058 res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s",
1059 argc >= 1 ? " " : "",
1060 argc >= 1 ? argv[0] : "",
1061 argc == 2 ? " " : "",
1062 argc == 2 ? argv[1] : "");
1063 if (os_snprintf_error(sizeof(cmd), res)) {
1064 printf("Too long option\n");
1065 return -1;
1066 }
1067 return wpa_ctrl_command(ctrl, cmd);
1068}
1069
1070
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001071struct hostapd_cli_cmd {
1072 const char *cmd;
1073 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
1074};
1075
Dmitry Shmidt1d755d02015-04-28 10:34:29 -07001076static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001077 { "ping", hostapd_cli_cmd_ping },
1078 { "mib", hostapd_cli_cmd_mib },
1079 { "relog", hostapd_cli_cmd_relog },
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001080 { "status", hostapd_cli_cmd_status },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001081 { "sta", hostapd_cli_cmd_sta },
1082 { "all_sta", hostapd_cli_cmd_all_sta },
1083 { "new_sta", hostapd_cli_cmd_new_sta },
1084 { "deauthenticate", hostapd_cli_cmd_deauthenticate },
1085 { "disassociate", hostapd_cli_cmd_disassociate },
1086#ifdef CONFIG_IEEE80211W
1087 { "sa_query", hostapd_cli_cmd_sa_query },
1088#endif /* CONFIG_IEEE80211W */
1089#ifdef CONFIG_WPS
1090 { "wps_pin", hostapd_cli_cmd_wps_pin },
1091 { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
1092 { "wps_pbc", hostapd_cli_cmd_wps_pbc },
Dmitry Shmidt04949592012-07-19 12:16:46 -07001093 { "wps_cancel", hostapd_cli_cmd_wps_cancel },
Dmitry Shmidt04949592012-07-19 12:16:46 -07001094#ifdef CONFIG_WPS_NFC
1095 { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
1096 { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
1097 { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
Dmitry Shmidtf8623282013-02-20 14:34:59 -08001098 { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
Dmitry Shmidt04949592012-07-19 12:16:46 -07001099#endif /* CONFIG_WPS_NFC */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001100 { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
1101 { "wps_config", hostapd_cli_cmd_wps_config },
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -07001102 { "wps_get_status", hostapd_cli_cmd_wps_get_status },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001103#endif /* CONFIG_WPS */
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001104 { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001105 { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001106 { "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001107 { "get_config", hostapd_cli_cmd_get_config },
1108 { "help", hostapd_cli_cmd_help },
1109 { "interface", hostapd_cli_cmd_interface },
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001110#ifdef CONFIG_FST
1111 { "fst", hostapd_cli_cmd_fst },
1112#endif /* CONFIG_FST */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001113 { "level", hostapd_cli_cmd_level },
1114 { "license", hostapd_cli_cmd_license },
1115 { "quit", hostapd_cli_cmd_quit },
1116 { "set", hostapd_cli_cmd_set },
1117 { "get", hostapd_cli_cmd_get },
Dmitry Shmidt051af732013-10-22 13:52:46 -07001118 { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
1119 { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -08001120 { "chan_switch", hostapd_cli_cmd_chan_switch },
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001121 { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
1122 { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001123 { "vendor", hostapd_cli_cmd_vendor },
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001124 { "enable", hostapd_cli_cmd_enable },
1125 { "reload", hostapd_cli_cmd_reload },
1126 { "disable", hostapd_cli_cmd_disable },
1127 { "erp_flush", hostapd_cli_cmd_erp_flush },
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001128 { "log_level", hostapd_cli_cmd_log_level },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001129 { NULL, NULL }
1130};
1131
1132
1133static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
1134{
Dmitry Shmidt1d755d02015-04-28 10:34:29 -07001135 const struct hostapd_cli_cmd *cmd, *match = NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001136 int count;
1137
1138 count = 0;
1139 cmd = hostapd_cli_commands;
1140 while (cmd->cmd) {
1141 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
1142 match = cmd;
1143 if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
1144 /* we have an exact match */
1145 count = 1;
1146 break;
1147 }
1148 count++;
1149 }
1150 cmd++;
1151 }
1152
1153 if (count > 1) {
1154 printf("Ambiguous command '%s'; possible commands:", argv[0]);
1155 cmd = hostapd_cli_commands;
1156 while (cmd->cmd) {
1157 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
1158 0) {
1159 printf(" %s", cmd->cmd);
1160 }
1161 cmd++;
1162 }
1163 printf("\n");
1164 } else if (count == 0) {
1165 printf("Unknown command '%s'\n", argv[0]);
1166 } else {
1167 match->handler(ctrl, argc - 1, &argv[1]);
1168 }
1169}
1170
1171
1172static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
1173 int action_monitor)
1174{
1175 int first = 1;
1176 if (ctrl_conn == NULL)
1177 return;
1178 while (wpa_ctrl_pending(ctrl)) {
1179 char buf[256];
1180 size_t len = sizeof(buf) - 1;
1181 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
1182 buf[len] = '\0';
1183 if (action_monitor)
1184 hostapd_cli_action_process(buf, len);
1185 else {
1186 if (in_read && first)
1187 printf("\n");
1188 first = 0;
1189 printf("%s\n", buf);
1190 }
1191 } else {
1192 printf("Could not read pending message.\n");
1193 break;
1194 }
1195 }
1196}
1197
1198
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001199#define max_args 10
1200
1201static int tokenize_cmd(char *cmd, char *argv[])
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001202{
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001203 char *pos;
1204 int argc = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001205
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001206 pos = cmd;
1207 for (;;) {
1208 while (*pos == ' ')
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001209 pos++;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001210 if (*pos == '\0')
1211 break;
1212 argv[argc] = pos;
1213 argc++;
1214 if (argc == max_args)
1215 break;
1216 if (*pos == '"') {
1217 char *pos2 = os_strrchr(pos, '"');
1218 if (pos2)
1219 pos = pos2 + 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001220 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001221 while (*pos != '\0' && *pos != ' ')
1222 pos++;
1223 if (*pos == ' ')
1224 *pos++ = '\0';
1225 }
1226
1227 return argc;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001228}
1229
1230
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001231static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001232{
1233 if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
1234 printf("Connection to hostapd lost - trying to reconnect\n");
1235 hostapd_cli_close_connection();
1236 }
1237 if (!ctrl_conn) {
1238 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1239 if (ctrl_conn) {
1240 printf("Connection to hostapd re-established\n");
1241 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1242 hostapd_cli_attached = 1;
1243 } else {
1244 printf("Warning: Failed to attach to "
1245 "hostapd.\n");
1246 }
1247 }
1248 }
1249 if (ctrl_conn)
1250 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001251 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1252}
1253
1254
1255static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
1256{
1257 eloop_terminate();
1258}
1259
1260
1261static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
1262{
1263 char *argv[max_args];
1264 int argc;
1265 argc = tokenize_cmd(cmd, argv);
1266 if (argc)
1267 wpa_request(ctrl_conn, argc, argv);
1268}
1269
1270
1271static void hostapd_cli_edit_eof_cb(void *ctx)
1272{
1273 eloop_terminate();
1274}
1275
1276
1277static void hostapd_cli_interactive(void)
1278{
1279 printf("\nInteractive mode\n\n");
1280
1281 eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
1282 edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001283 NULL, NULL, NULL, NULL);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001284 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1285
1286 eloop_run();
1287
1288 edit_deinit(NULL, NULL);
1289 eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
1290}
1291
1292
1293static void hostapd_cli_cleanup(void)
1294{
1295 hostapd_cli_close_connection();
1296 if (pid_file)
1297 os_daemonize_terminate(pid_file);
1298
1299 os_program_deinit();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001300}
1301
1302
1303static void hostapd_cli_action(struct wpa_ctrl *ctrl)
1304{
1305 fd_set rfds;
1306 int fd, res;
1307 struct timeval tv;
1308 char buf[256];
1309 size_t len;
1310
1311 fd = wpa_ctrl_get_fd(ctrl);
1312
1313 while (!hostapd_cli_quit) {
1314 FD_ZERO(&rfds);
1315 FD_SET(fd, &rfds);
1316 tv.tv_sec = ping_interval;
1317 tv.tv_usec = 0;
1318 res = select(fd + 1, &rfds, NULL, NULL, &tv);
1319 if (res < 0 && errno != EINTR) {
1320 perror("select");
1321 break;
1322 }
1323
1324 if (FD_ISSET(fd, &rfds))
1325 hostapd_cli_recv_pending(ctrl, 0, 1);
1326 else {
1327 len = sizeof(buf) - 1;
1328 if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1329 hostapd_cli_action_process) < 0 ||
1330 len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1331 printf("hostapd did not reply to PING "
1332 "command - exiting\n");
1333 break;
1334 }
1335 }
1336 }
1337}
1338
1339
1340int main(int argc, char *argv[])
1341{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001342 int warning_displayed = 0;
1343 int c;
1344 int daemonize = 0;
1345
1346 if (os_program_init())
1347 return -1;
1348
1349 for (;;) {
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001350 c = getopt(argc, argv, "a:BhG:i:p:P:s:v");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001351 if (c < 0)
1352 break;
1353 switch (c) {
1354 case 'a':
1355 action_file = optarg;
1356 break;
1357 case 'B':
1358 daemonize = 1;
1359 break;
1360 case 'G':
1361 ping_interval = atoi(optarg);
1362 break;
1363 case 'h':
1364 usage();
1365 return 0;
1366 case 'v':
1367 printf("%s\n", hostapd_cli_version);
1368 return 0;
1369 case 'i':
1370 os_free(ctrl_ifname);
1371 ctrl_ifname = os_strdup(optarg);
1372 break;
1373 case 'p':
1374 ctrl_iface_dir = optarg;
1375 break;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001376 case 'P':
1377 pid_file = optarg;
1378 break;
1379 case 's':
1380 client_socket_dir = optarg;
1381 break;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001382 default:
1383 usage();
1384 return -1;
1385 }
1386 }
1387
1388 interactive = (argc == optind) && (action_file == NULL);
1389
1390 if (interactive) {
1391 printf("%s\n\n%s\n\n", hostapd_cli_version,
1392 hostapd_cli_license);
1393 }
1394
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001395 if (eloop_init())
1396 return -1;
1397
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001398 for (;;) {
1399 if (ctrl_ifname == NULL) {
1400 struct dirent *dent;
1401 DIR *dir = opendir(ctrl_iface_dir);
1402 if (dir) {
1403 while ((dent = readdir(dir))) {
1404 if (os_strcmp(dent->d_name, ".") == 0
1405 ||
1406 os_strcmp(dent->d_name, "..") == 0)
1407 continue;
1408 printf("Selected interface '%s'\n",
1409 dent->d_name);
1410 ctrl_ifname = os_strdup(dent->d_name);
1411 break;
1412 }
1413 closedir(dir);
1414 }
1415 }
1416 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1417 if (ctrl_conn) {
1418 if (warning_displayed)
1419 printf("Connection established.\n");
1420 break;
1421 }
1422
1423 if (!interactive) {
1424 perror("Failed to connect to hostapd - "
1425 "wpa_ctrl_open");
1426 return -1;
1427 }
1428
1429 if (!warning_displayed) {
1430 printf("Could not connect to hostapd - re-trying\n");
1431 warning_displayed = 1;
1432 }
1433 os_sleep(1, 0);
1434 continue;
1435 }
1436
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001437 if (interactive || action_file) {
1438 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1439 hostapd_cli_attached = 1;
1440 } else {
1441 printf("Warning: Failed to attach to hostapd.\n");
1442 if (action_file)
1443 return -1;
1444 }
1445 }
1446
1447 if (daemonize && os_daemonize(pid_file))
1448 return -1;
1449
1450 if (interactive)
1451 hostapd_cli_interactive();
1452 else if (action_file)
1453 hostapd_cli_action(ctrl_conn);
1454 else
1455 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1456
1457 os_free(ctrl_ifname);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001458 eloop_destroy();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001459 hostapd_cli_cleanup();
1460 return 0;
1461}