blob: a1fdf6ed352778f59fa36ab6273564ad97df7ef3 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * hostapd - command line interface for hostapd daemon
Dmitry Shmidtcce06662013-11-04 18:44:24 -08003 * Copyright (c) 2004-2013, 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 Shmidt1f69aa52012-01-24 16:10:04 -080013#include "utils/common.h"
14#include "utils/eloop.h"
15#include "utils/edit.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070016#include "common/version.h"
17
18
19static const char *hostapd_cli_version =
20"hostapd_cli v" VERSION_STR "\n"
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080021"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070022
23
24static const char *hostapd_cli_license =
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -080025"This software may be distributed under the terms of the BSD license.\n"
26"See README for more details.\n";
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070027
28static const char *hostapd_cli_full_license =
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -080029"This software may be distributed under the terms of the BSD license.\n"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070030"\n"
31"Redistribution and use in source and binary forms, with or without\n"
32"modification, are permitted provided that the following conditions are\n"
33"met:\n"
34"\n"
35"1. Redistributions of source code must retain the above copyright\n"
36" notice, this list of conditions and the following disclaimer.\n"
37"\n"
38"2. Redistributions in binary form must reproduce the above copyright\n"
39" notice, this list of conditions and the following disclaimer in the\n"
40" documentation and/or other materials provided with the distribution.\n"
41"\n"
42"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
43" names of its contributors may be used to endorse or promote products\n"
44" derived from this software without specific prior written permission.\n"
45"\n"
46"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
47"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
48"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
49"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
50"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
51"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
52"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
53"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
54"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
55"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
56"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
57"\n";
58
59static const char *commands_help =
60"Commands:\n"
61" mib get MIB variables (dot1x, dot11, radius)\n"
62" sta <addr> get MIB variables for one station\n"
63" all_sta get MIB variables for all stations\n"
64" new_sta <addr> add a new station\n"
65" deauthenticate <addr> deauthenticate a station\n"
66" disassociate <addr> disassociate a station\n"
67#ifdef CONFIG_IEEE80211W
68" sa_query <addr> send SA Query to a station\n"
69#endif /* CONFIG_IEEE80211W */
70#ifdef CONFIG_WPS
71" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n"
72" wps_check_pin <PIN> verify PIN checksum\n"
73" wps_pbc indicate button pushed to initiate PBC\n"
Dmitry Shmidt61d9df32012-08-29 16:22:06 -070074" wps_cancel cancel the pending WPS operation\n"
Dmitry Shmidt04949592012-07-19 12:16:46 -070075#ifdef CONFIG_WPS_NFC
76" wps_nfc_tag_read <hexdump> report read NFC tag with WPS data\n"
77" wps_nfc_config_token <WPS/NDEF> build NFC configuration token\n"
78" wps_nfc_token <WPS/NDEF/enable/disable> manager NFC password token\n"
79#endif /* CONFIG_WPS_NFC */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070080" wps_ap_pin <cmd> [params..] enable/disable AP PIN\n"
81" wps_config <SSID> <auth> <encr> <key> configure AP\n"
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -070082" wps_get_status show current WPS status\n"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070083#endif /* CONFIG_WPS */
84" get_config show current configuration\n"
85" help show this usage help\n"
86" interface [ifname] show interfaces/select interface\n"
87" level <debug level> change debug level\n"
88" license show full hostapd_cli license\n"
89" quit exit hostapd_cli\n";
90
91static struct wpa_ctrl *ctrl_conn;
92static int hostapd_cli_quit = 0;
93static int hostapd_cli_attached = 0;
Jeff Johnson205f2142012-09-03 22:12:17 -070094
95#ifndef CONFIG_CTRL_IFACE_DIR
96#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
97#endif /* CONFIG_CTRL_IFACE_DIR */
98static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
99
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700100static char *ctrl_ifname = NULL;
101static const char *pid_file = NULL;
102static const char *action_file = NULL;
103static int ping_interval = 5;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800104static int interactive = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700105
106
107static void usage(void)
108{
109 fprintf(stderr, "%s\n", hostapd_cli_version);
110 fprintf(stderr,
111 "\n"
112 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
113 "[-a<path>] \\\n"
114 " [-G<ping interval>] [command..]\n"
115 "\n"
116 "Options:\n"
117 " -h help (show this usage text)\n"
118 " -v shown version information\n"
119 " -p<path> path to find control sockets (default: "
120 "/var/run/hostapd)\n"
121 " -a<file> run in daemon mode executing the action file "
122 "based on events\n"
123 " from hostapd\n"
124 " -B run a daemon in the background\n"
125 " -i<ifname> Interface to listen on (default: first "
126 "interface found in the\n"
127 " socket path)\n\n"
128 "%s",
129 commands_help);
130}
131
132
133static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
134{
135 char *cfile;
136 int flen;
137
138 if (ifname == NULL)
139 return NULL;
140
141 flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
142 cfile = malloc(flen);
143 if (cfile == NULL)
144 return NULL;
145 snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
146
147 ctrl_conn = wpa_ctrl_open(cfile);
148 free(cfile);
149 return ctrl_conn;
150}
151
152
153static void hostapd_cli_close_connection(void)
154{
155 if (ctrl_conn == NULL)
156 return;
157
158 if (hostapd_cli_attached) {
159 wpa_ctrl_detach(ctrl_conn);
160 hostapd_cli_attached = 0;
161 }
162 wpa_ctrl_close(ctrl_conn);
163 ctrl_conn = NULL;
164}
165
166
167static void hostapd_cli_msg_cb(char *msg, size_t len)
168{
169 printf("%s\n", msg);
170}
171
172
173static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
174{
175 char buf[4096];
176 size_t len;
177 int ret;
178
179 if (ctrl_conn == NULL) {
180 printf("Not connected to hostapd - command dropped.\n");
181 return -1;
182 }
183 len = sizeof(buf) - 1;
184 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
185 hostapd_cli_msg_cb);
186 if (ret == -2) {
187 printf("'%s' command timed out.\n", cmd);
188 return -2;
189 } else if (ret < 0) {
190 printf("'%s' command failed.\n", cmd);
191 return -1;
192 }
193 if (print) {
194 buf[len] = '\0';
195 printf("%s", buf);
196 }
197 return 0;
198}
199
200
201static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
202{
203 return _wpa_ctrl_command(ctrl, cmd, 1);
204}
205
206
207static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
208{
209 return wpa_ctrl_command(ctrl, "PING");
210}
211
212
213static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
214{
215 return wpa_ctrl_command(ctrl, "RELOG");
216}
217
218
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800219static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
220{
221 return wpa_ctrl_command(ctrl, "STATUS");
222}
223
224
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700225static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
226{
227 return wpa_ctrl_command(ctrl, "MIB");
228}
229
230
231static int hostapd_cli_exec(const char *program, const char *arg1,
232 const char *arg2)
233{
234 char *cmd;
235 size_t len;
236 int res;
237 int ret = 0;
238
239 len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
240 cmd = os_malloc(len);
241 if (cmd == NULL)
242 return -1;
243 res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
244 if (res < 0 || (size_t) res >= len) {
245 os_free(cmd);
246 return -1;
247 }
248 cmd[len - 1] = '\0';
249#ifndef _WIN32_WCE
250 if (system(cmd) < 0)
251 ret = -1;
252#endif /* _WIN32_WCE */
253 os_free(cmd);
254
255 return ret;
256}
257
258
259static void hostapd_cli_action_process(char *msg, size_t len)
260{
261 const char *pos;
262
263 pos = msg;
264 if (*pos == '<') {
265 pos = os_strchr(pos, '>');
266 if (pos)
267 pos++;
268 else
269 pos = msg;
270 }
271
272 hostapd_cli_exec(action_file, ctrl_ifname, pos);
273}
274
275
276static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
277{
278 char buf[64];
279 if (argc != 1) {
280 printf("Invalid 'sta' command - exactly one argument, STA "
281 "address, is required.\n");
282 return -1;
283 }
284 snprintf(buf, sizeof(buf), "STA %s", argv[0]);
285 return wpa_ctrl_command(ctrl, buf);
286}
287
288
289static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
290 char *argv[])
291{
292 char buf[64];
293 if (argc != 1) {
294 printf("Invalid 'new_sta' command - exactly one argument, STA "
295 "address, is required.\n");
296 return -1;
297 }
298 snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
299 return wpa_ctrl_command(ctrl, buf);
300}
301
302
303static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
304 char *argv[])
305{
306 char buf[64];
307 if (argc < 1) {
308 printf("Invalid 'deauthenticate' command - exactly one "
309 "argument, STA address, is required.\n");
310 return -1;
311 }
312 if (argc > 1)
313 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
314 argv[0], argv[1]);
315 else
316 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
317 return wpa_ctrl_command(ctrl, buf);
318}
319
320
321static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
322 char *argv[])
323{
324 char buf[64];
325 if (argc < 1) {
326 printf("Invalid 'disassociate' command - exactly one "
327 "argument, STA address, is required.\n");
328 return -1;
329 }
330 if (argc > 1)
331 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
332 argv[0], argv[1]);
333 else
334 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
335 return wpa_ctrl_command(ctrl, buf);
336}
337
338
339#ifdef CONFIG_IEEE80211W
340static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
341 char *argv[])
342{
343 char buf[64];
344 if (argc != 1) {
345 printf("Invalid 'sa_query' command - exactly one argument, "
346 "STA address, is required.\n");
347 return -1;
348 }
349 snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
350 return wpa_ctrl_command(ctrl, buf);
351}
352#endif /* CONFIG_IEEE80211W */
353
354
355#ifdef CONFIG_WPS
356static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
357 char *argv[])
358{
359 char buf[256];
360 if (argc < 2) {
361 printf("Invalid 'wps_pin' command - at least two arguments, "
362 "UUID and PIN, are required.\n");
363 return -1;
364 }
365 if (argc > 3)
366 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
367 argv[0], argv[1], argv[2], argv[3]);
368 else if (argc > 2)
369 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
370 argv[0], argv[1], argv[2]);
371 else
372 snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
373 return wpa_ctrl_command(ctrl, buf);
374}
375
376
377static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
378 char *argv[])
379{
380 char cmd[256];
381 int res;
382
383 if (argc != 1 && argc != 2) {
384 printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
385 "- PIN to be verified\n");
386 return -1;
387 }
388
389 if (argc == 2)
390 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
391 argv[0], argv[1]);
392 else
393 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
394 argv[0]);
395 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
396 printf("Too long WPS_CHECK_PIN command.\n");
397 return -1;
398 }
399 return wpa_ctrl_command(ctrl, cmd);
400}
401
402
403static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
404 char *argv[])
405{
406 return wpa_ctrl_command(ctrl, "WPS_PBC");
407}
408
409
Dmitry Shmidt04949592012-07-19 12:16:46 -0700410static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
411 char *argv[])
412{
413 return wpa_ctrl_command(ctrl, "WPS_CANCEL");
414}
415
416
Dmitry Shmidt04949592012-07-19 12:16:46 -0700417#ifdef CONFIG_WPS_NFC
418static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
419 char *argv[])
420{
421 int ret;
422 char *buf;
423 size_t buflen;
424
425 if (argc != 1) {
426 printf("Invalid 'wps_nfc_tag_read' command - one argument "
427 "is required.\n");
428 return -1;
429 }
430
431 buflen = 18 + os_strlen(argv[0]);
432 buf = os_malloc(buflen);
433 if (buf == NULL)
434 return -1;
435 os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
436
437 ret = wpa_ctrl_command(ctrl, buf);
438 os_free(buf);
439
440 return ret;
441}
442
443
444static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
445 int argc, char *argv[])
446{
447 char cmd[64];
448 int res;
449
450 if (argc != 1) {
451 printf("Invalid 'wps_nfc_config_token' command - one argument "
452 "is required.\n");
453 return -1;
454 }
455
456 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
457 argv[0]);
458 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
459 printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
460 return -1;
461 }
462 return wpa_ctrl_command(ctrl, cmd);
463}
464
465
466static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
467 int argc, char *argv[])
468{
469 char cmd[64];
470 int res;
471
472 if (argc != 1) {
473 printf("Invalid 'wps_nfc_token' command - one argument is "
474 "required.\n");
475 return -1;
476 }
477
478 res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
479 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
480 printf("Too long WPS_NFC_TOKEN command.\n");
481 return -1;
482 }
483 return wpa_ctrl_command(ctrl, cmd);
484}
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800485
486
487static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
488 int argc, char *argv[])
489{
490 char cmd[64];
491 int res;
492
493 if (argc != 2) {
494 printf("Invalid 'nfc_get_handover_sel' command - two arguments "
495 "are required.\n");
496 return -1;
497 }
498
499 res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
500 argv[0], argv[1]);
501 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
502 printf("Too long NFC_GET_HANDOVER_SEL command.\n");
503 return -1;
504 }
505 return wpa_ctrl_command(ctrl, cmd);
506}
507
Dmitry Shmidt04949592012-07-19 12:16:46 -0700508#endif /* CONFIG_WPS_NFC */
509
510
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700511static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
512 char *argv[])
513{
514 char buf[64];
515 if (argc < 1) {
516 printf("Invalid 'wps_ap_pin' command - at least one argument "
517 "is required.\n");
518 return -1;
519 }
520 if (argc > 2)
521 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
522 argv[0], argv[1], argv[2]);
523 else if (argc > 1)
524 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
525 argv[0], argv[1]);
526 else
527 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
528 return wpa_ctrl_command(ctrl, buf);
529}
530
531
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700532static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
533 char *argv[])
534{
535 return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
536}
537
538
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700539static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
540 char *argv[])
541{
542 char buf[256];
543 char ssid_hex[2 * 32 + 1];
544 char key_hex[2 * 64 + 1];
545 int i;
546
547 if (argc < 1) {
548 printf("Invalid 'wps_config' command - at least two arguments "
549 "are required.\n");
550 return -1;
551 }
552
553 ssid_hex[0] = '\0';
554 for (i = 0; i < 32; i++) {
555 if (argv[0][i] == '\0')
556 break;
557 os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
558 }
559
560 key_hex[0] = '\0';
561 if (argc > 3) {
562 for (i = 0; i < 64; i++) {
563 if (argv[3][i] == '\0')
564 break;
565 os_snprintf(&key_hex[i * 2], 3, "%02x",
566 argv[3][i]);
567 }
568 }
569
570 if (argc > 3)
571 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
572 ssid_hex, argv[1], argv[2], key_hex);
573 else if (argc > 2)
574 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
575 ssid_hex, argv[1], argv[2]);
576 else
577 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
578 ssid_hex, argv[1]);
579 return wpa_ctrl_command(ctrl, buf);
580}
581#endif /* CONFIG_WPS */
582
583
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800584static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
585 char *argv[])
586{
587 char buf[300];
588 int res;
589
590 if (argc < 2) {
591 printf("Invalid 'disassoc_imminent' command - two arguments "
592 "(STA addr and Disassociation Timer) are needed\n");
593 return -1;
594 }
595
596 res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
597 argv[0], argv[1]);
598 if (res < 0 || res >= (int) sizeof(buf))
599 return -1;
600 return wpa_ctrl_command(ctrl, buf);
601}
602
603
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800604static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
605 char *argv[])
606{
607 char buf[300];
608 int res;
609
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700610 if (argc < 3) {
611 printf("Invalid 'ess_disassoc' command - three arguments (STA "
612 "addr, disassoc timer, and URL) are needed\n");
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800613 return -1;
614 }
615
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700616 res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
617 argv[0], argv[1], argv[2]);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800618 if (res < 0 || res >= (int) sizeof(buf))
619 return -1;
620 return wpa_ctrl_command(ctrl, buf);
621}
622
623
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700624static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
625 char *argv[])
626{
627 return wpa_ctrl_command(ctrl, "GET_CONFIG");
628}
629
630
631static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
632 char *addr, size_t addr_len)
633{
634 char buf[4096], *pos;
635 size_t len;
636 int ret;
637
638 if (ctrl_conn == NULL) {
639 printf("Not connected to hostapd - command dropped.\n");
640 return -1;
641 }
642 len = sizeof(buf) - 1;
643 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
644 hostapd_cli_msg_cb);
645 if (ret == -2) {
646 printf("'%s' command timed out.\n", cmd);
647 return -2;
648 } else if (ret < 0) {
649 printf("'%s' command failed.\n", cmd);
650 return -1;
651 }
652
653 buf[len] = '\0';
654 if (memcmp(buf, "FAIL", 4) == 0)
655 return -1;
656 printf("%s", buf);
657
658 pos = buf;
659 while (*pos != '\0' && *pos != '\n')
660 pos++;
661 *pos = '\0';
662 os_strlcpy(addr, buf, addr_len);
663 return 0;
664}
665
666
667static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
668 char *argv[])
669{
670 char addr[32], cmd[64];
671
672 if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
673 return 0;
674 do {
675 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
676 } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
677
678 return -1;
679}
680
681
682static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
683{
684 printf("%s", commands_help);
685 return 0;
686}
687
688
689static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
690 char *argv[])
691{
692 printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
693 return 0;
694}
695
696
Dmitry Shmidt051af732013-10-22 13:52:46 -0700697static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
698 int argc, char *argv[])
699{
700 char buf[200];
701 int res;
702
703 if (argc != 1) {
704 printf("Invalid 'set_qos_map_set' command - "
705 "one argument (comma delimited QoS map set) "
706 "is needed\n");
707 return -1;
708 }
709
710 res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
711 if (res < 0 || res >= (int) sizeof(buf))
712 return -1;
713 return wpa_ctrl_command(ctrl, buf);
714}
715
716
717static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
718 int argc, char *argv[])
719{
720 char buf[50];
721 int res;
722
723 if (argc != 1) {
724 printf("Invalid 'send_qos_map_conf' command - "
725 "one argument (STA addr) is needed\n");
726 return -1;
727 }
728
729 res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
730 if (res < 0 || res >= (int) sizeof(buf))
731 return -1;
732 return wpa_ctrl_command(ctrl, buf);
733}
734
735
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700736static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
737{
738 hostapd_cli_quit = 1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800739 if (interactive)
740 eloop_terminate();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700741 return 0;
742}
743
744
745static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
746{
747 char cmd[256];
748 if (argc != 1) {
749 printf("Invalid LEVEL command: needs one argument (debug "
750 "level)\n");
751 return 0;
752 }
753 snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
754 return wpa_ctrl_command(ctrl, cmd);
755}
756
757
758static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
759{
760 struct dirent *dent;
761 DIR *dir;
762
763 dir = opendir(ctrl_iface_dir);
764 if (dir == NULL) {
765 printf("Control interface directory '%s' could not be "
766 "openned.\n", ctrl_iface_dir);
767 return;
768 }
769
770 printf("Available interfaces:\n");
771 while ((dent = readdir(dir))) {
772 if (strcmp(dent->d_name, ".") == 0 ||
773 strcmp(dent->d_name, "..") == 0)
774 continue;
775 printf("%s\n", dent->d_name);
776 }
777 closedir(dir);
778}
779
780
781static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
782 char *argv[])
783{
784 if (argc < 1) {
785 hostapd_cli_list_interfaces(ctrl);
786 return 0;
787 }
788
789 hostapd_cli_close_connection();
790 free(ctrl_ifname);
791 ctrl_ifname = strdup(argv[0]);
792
793 if (hostapd_cli_open_connection(ctrl_ifname)) {
794 printf("Connected to interface '%s.\n", ctrl_ifname);
795 if (wpa_ctrl_attach(ctrl_conn) == 0) {
796 hostapd_cli_attached = 1;
797 } else {
798 printf("Warning: Failed to attach to "
799 "hostapd.\n");
800 }
801 } else {
802 printf("Could not connect to interface '%s' - re-trying\n",
803 ctrl_ifname);
804 }
805 return 0;
806}
807
808
809static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
810{
811 char cmd[256];
812 int res;
813
814 if (argc != 2) {
815 printf("Invalid SET command: needs two arguments (variable "
816 "name and value)\n");
817 return -1;
818 }
819
820 res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
821 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
822 printf("Too long SET command.\n");
823 return -1;
824 }
825 return wpa_ctrl_command(ctrl, cmd);
826}
827
828
829static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
830{
831 char cmd[256];
832 int res;
833
834 if (argc != 1) {
835 printf("Invalid GET command: needs one argument (variable "
836 "name)\n");
837 return -1;
838 }
839
840 res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
841 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
842 printf("Too long GET command.\n");
843 return -1;
844 }
845 return wpa_ctrl_command(ctrl, cmd);
846}
847
848
849struct hostapd_cli_cmd {
850 const char *cmd;
851 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
852};
853
854static struct hostapd_cli_cmd hostapd_cli_commands[] = {
855 { "ping", hostapd_cli_cmd_ping },
856 { "mib", hostapd_cli_cmd_mib },
857 { "relog", hostapd_cli_cmd_relog },
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800858 { "status", hostapd_cli_cmd_status },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700859 { "sta", hostapd_cli_cmd_sta },
860 { "all_sta", hostapd_cli_cmd_all_sta },
861 { "new_sta", hostapd_cli_cmd_new_sta },
862 { "deauthenticate", hostapd_cli_cmd_deauthenticate },
863 { "disassociate", hostapd_cli_cmd_disassociate },
864#ifdef CONFIG_IEEE80211W
865 { "sa_query", hostapd_cli_cmd_sa_query },
866#endif /* CONFIG_IEEE80211W */
867#ifdef CONFIG_WPS
868 { "wps_pin", hostapd_cli_cmd_wps_pin },
869 { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
870 { "wps_pbc", hostapd_cli_cmd_wps_pbc },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700871 { "wps_cancel", hostapd_cli_cmd_wps_cancel },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700872#ifdef CONFIG_WPS_NFC
873 { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
874 { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
875 { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800876 { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
Dmitry Shmidt04949592012-07-19 12:16:46 -0700877#endif /* CONFIG_WPS_NFC */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700878 { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
879 { "wps_config", hostapd_cli_cmd_wps_config },
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700880 { "wps_get_status", hostapd_cli_cmd_wps_get_status },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700881#endif /* CONFIG_WPS */
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800882 { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800883 { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700884 { "get_config", hostapd_cli_cmd_get_config },
885 { "help", hostapd_cli_cmd_help },
886 { "interface", hostapd_cli_cmd_interface },
887 { "level", hostapd_cli_cmd_level },
888 { "license", hostapd_cli_cmd_license },
889 { "quit", hostapd_cli_cmd_quit },
890 { "set", hostapd_cli_cmd_set },
891 { "get", hostapd_cli_cmd_get },
Dmitry Shmidt051af732013-10-22 13:52:46 -0700892 { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
893 { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700894 { NULL, NULL }
895};
896
897
898static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
899{
900 struct hostapd_cli_cmd *cmd, *match = NULL;
901 int count;
902
903 count = 0;
904 cmd = hostapd_cli_commands;
905 while (cmd->cmd) {
906 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
907 match = cmd;
908 if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
909 /* we have an exact match */
910 count = 1;
911 break;
912 }
913 count++;
914 }
915 cmd++;
916 }
917
918 if (count > 1) {
919 printf("Ambiguous command '%s'; possible commands:", argv[0]);
920 cmd = hostapd_cli_commands;
921 while (cmd->cmd) {
922 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
923 0) {
924 printf(" %s", cmd->cmd);
925 }
926 cmd++;
927 }
928 printf("\n");
929 } else if (count == 0) {
930 printf("Unknown command '%s'\n", argv[0]);
931 } else {
932 match->handler(ctrl, argc - 1, &argv[1]);
933 }
934}
935
936
937static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
938 int action_monitor)
939{
940 int first = 1;
941 if (ctrl_conn == NULL)
942 return;
943 while (wpa_ctrl_pending(ctrl)) {
944 char buf[256];
945 size_t len = sizeof(buf) - 1;
946 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
947 buf[len] = '\0';
948 if (action_monitor)
949 hostapd_cli_action_process(buf, len);
950 else {
951 if (in_read && first)
952 printf("\n");
953 first = 0;
954 printf("%s\n", buf);
955 }
956 } else {
957 printf("Could not read pending message.\n");
958 break;
959 }
960 }
961}
962
963
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800964#define max_args 10
965
966static int tokenize_cmd(char *cmd, char *argv[])
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700967{
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800968 char *pos;
969 int argc = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700970
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800971 pos = cmd;
972 for (;;) {
973 while (*pos == ' ')
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700974 pos++;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800975 if (*pos == '\0')
976 break;
977 argv[argc] = pos;
978 argc++;
979 if (argc == max_args)
980 break;
981 if (*pos == '"') {
982 char *pos2 = os_strrchr(pos, '"');
983 if (pos2)
984 pos = pos2 + 1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700985 }
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800986 while (*pos != '\0' && *pos != ' ')
987 pos++;
988 if (*pos == ' ')
989 *pos++ = '\0';
990 }
991
992 return argc;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700993}
994
995
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800996static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700997{
998 if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
999 printf("Connection to hostapd lost - trying to reconnect\n");
1000 hostapd_cli_close_connection();
1001 }
1002 if (!ctrl_conn) {
1003 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1004 if (ctrl_conn) {
1005 printf("Connection to hostapd re-established\n");
1006 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1007 hostapd_cli_attached = 1;
1008 } else {
1009 printf("Warning: Failed to attach to "
1010 "hostapd.\n");
1011 }
1012 }
1013 }
1014 if (ctrl_conn)
1015 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001016 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1017}
1018
1019
1020static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
1021{
1022 eloop_terminate();
1023}
1024
1025
1026static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
1027{
1028 char *argv[max_args];
1029 int argc;
1030 argc = tokenize_cmd(cmd, argv);
1031 if (argc)
1032 wpa_request(ctrl_conn, argc, argv);
1033}
1034
1035
1036static void hostapd_cli_edit_eof_cb(void *ctx)
1037{
1038 eloop_terminate();
1039}
1040
1041
1042static void hostapd_cli_interactive(void)
1043{
1044 printf("\nInteractive mode\n\n");
1045
1046 eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
1047 edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001048 NULL, NULL, NULL, NULL);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001049 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
1050
1051 eloop_run();
1052
1053 edit_deinit(NULL, NULL);
1054 eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
1055}
1056
1057
1058static void hostapd_cli_cleanup(void)
1059{
1060 hostapd_cli_close_connection();
1061 if (pid_file)
1062 os_daemonize_terminate(pid_file);
1063
1064 os_program_deinit();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001065}
1066
1067
1068static void hostapd_cli_action(struct wpa_ctrl *ctrl)
1069{
1070 fd_set rfds;
1071 int fd, res;
1072 struct timeval tv;
1073 char buf[256];
1074 size_t len;
1075
1076 fd = wpa_ctrl_get_fd(ctrl);
1077
1078 while (!hostapd_cli_quit) {
1079 FD_ZERO(&rfds);
1080 FD_SET(fd, &rfds);
1081 tv.tv_sec = ping_interval;
1082 tv.tv_usec = 0;
1083 res = select(fd + 1, &rfds, NULL, NULL, &tv);
1084 if (res < 0 && errno != EINTR) {
1085 perror("select");
1086 break;
1087 }
1088
1089 if (FD_ISSET(fd, &rfds))
1090 hostapd_cli_recv_pending(ctrl, 0, 1);
1091 else {
1092 len = sizeof(buf) - 1;
1093 if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
1094 hostapd_cli_action_process) < 0 ||
1095 len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
1096 printf("hostapd did not reply to PING "
1097 "command - exiting\n");
1098 break;
1099 }
1100 }
1101 }
1102}
1103
1104
1105int main(int argc, char *argv[])
1106{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001107 int warning_displayed = 0;
1108 int c;
1109 int daemonize = 0;
1110
1111 if (os_program_init())
1112 return -1;
1113
1114 for (;;) {
1115 c = getopt(argc, argv, "a:BhG:i:p:v");
1116 if (c < 0)
1117 break;
1118 switch (c) {
1119 case 'a':
1120 action_file = optarg;
1121 break;
1122 case 'B':
1123 daemonize = 1;
1124 break;
1125 case 'G':
1126 ping_interval = atoi(optarg);
1127 break;
1128 case 'h':
1129 usage();
1130 return 0;
1131 case 'v':
1132 printf("%s\n", hostapd_cli_version);
1133 return 0;
1134 case 'i':
1135 os_free(ctrl_ifname);
1136 ctrl_ifname = os_strdup(optarg);
1137 break;
1138 case 'p':
1139 ctrl_iface_dir = optarg;
1140 break;
1141 default:
1142 usage();
1143 return -1;
1144 }
1145 }
1146
1147 interactive = (argc == optind) && (action_file == NULL);
1148
1149 if (interactive) {
1150 printf("%s\n\n%s\n\n", hostapd_cli_version,
1151 hostapd_cli_license);
1152 }
1153
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001154 if (eloop_init())
1155 return -1;
1156
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001157 for (;;) {
1158 if (ctrl_ifname == NULL) {
1159 struct dirent *dent;
1160 DIR *dir = opendir(ctrl_iface_dir);
1161 if (dir) {
1162 while ((dent = readdir(dir))) {
1163 if (os_strcmp(dent->d_name, ".") == 0
1164 ||
1165 os_strcmp(dent->d_name, "..") == 0)
1166 continue;
1167 printf("Selected interface '%s'\n",
1168 dent->d_name);
1169 ctrl_ifname = os_strdup(dent->d_name);
1170 break;
1171 }
1172 closedir(dir);
1173 }
1174 }
1175 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1176 if (ctrl_conn) {
1177 if (warning_displayed)
1178 printf("Connection established.\n");
1179 break;
1180 }
1181
1182 if (!interactive) {
1183 perror("Failed to connect to hostapd - "
1184 "wpa_ctrl_open");
1185 return -1;
1186 }
1187
1188 if (!warning_displayed) {
1189 printf("Could not connect to hostapd - re-trying\n");
1190 warning_displayed = 1;
1191 }
1192 os_sleep(1, 0);
1193 continue;
1194 }
1195
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001196 if (interactive || action_file) {
1197 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1198 hostapd_cli_attached = 1;
1199 } else {
1200 printf("Warning: Failed to attach to hostapd.\n");
1201 if (action_file)
1202 return -1;
1203 }
1204 }
1205
1206 if (daemonize && os_daemonize(pid_file))
1207 return -1;
1208
1209 if (interactive)
1210 hostapd_cli_interactive();
1211 else if (action_file)
1212 hostapd_cli_action(ctrl_conn);
1213 else
1214 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1215
1216 os_free(ctrl_ifname);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001217 eloop_destroy();
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001218 hostapd_cli_cleanup();
1219 return 0;
1220}