blob: 05b4b3bc882750ac6d04dd025ec055a59f6de5fe [file] [log] [blame]
Jouni Malinen6fc68792008-02-27 17:34:43 -08001/*
2 * hostapd - command line interface for hostapd daemon
Jouni Malinen9e074972011-02-27 12:50:00 +02003 * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
Jouni Malinen6fc68792008-02-27 17:34:43 -08004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15#include "includes.h"
16#include <dirent.h>
17
Jouni Malinen90973fb2009-11-29 17:51:55 +020018#include "common/wpa_ctrl.h"
Jouni Malinen42838052011-07-16 17:37:18 +030019#include "utils/common.h"
20#include "utils/eloop.h"
21#include "utils/edit.h"
Jouni Malinen90973fb2009-11-29 17:51:55 +020022#include "common/version.h"
Jouni Malinen6fc68792008-02-27 17:34:43 -080023
24
25static const char *hostapd_cli_version =
26"hostapd_cli v" VERSION_STR "\n"
Jouni Malinen57d38dd2012-01-01 18:59:16 +020027"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors";
Jouni Malinen6fc68792008-02-27 17:34:43 -080028
29
30static const char *hostapd_cli_license =
Jouni Malinen331f89f2012-02-11 14:04:12 +020031"This software may be distributed under the terms of the BSD license.\n"
32"See README for more details.\n";
Jouni Malinen6fc68792008-02-27 17:34:43 -080033
34static const char *hostapd_cli_full_license =
Jouni Malinen331f89f2012-02-11 14:04:12 +020035"This software may be distributed under the terms of the BSD license.\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -080036"\n"
37"Redistribution and use in source and binary forms, with or without\n"
38"modification, are permitted provided that the following conditions are\n"
39"met:\n"
40"\n"
41"1. Redistributions of source code must retain the above copyright\n"
42" notice, this list of conditions and the following disclaimer.\n"
43"\n"
44"2. Redistributions in binary form must reproduce the above copyright\n"
45" notice, this list of conditions and the following disclaimer in the\n"
46" documentation and/or other materials provided with the distribution.\n"
47"\n"
48"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
49" names of its contributors may be used to endorse or promote products\n"
50" derived from this software without specific prior written permission.\n"
51"\n"
52"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
53"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
54"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
55"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
56"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
57"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
58"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
59"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
60"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
61"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
62"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
63"\n";
64
65static const char *commands_help =
66"Commands:\n"
67" mib get MIB variables (dot1x, dot11, radius)\n"
68" sta <addr> get MIB variables for one station\n"
69" all_sta get MIB variables for all stations\n"
70" new_sta <addr> add a new station\n"
Jouni Malinen488d0932010-04-11 21:00:16 +030071" deauthenticate <addr> deauthenticate a station\n"
72" disassociate <addr> disassociate a station\n"
Jouni Malinen88b4b422008-12-30 18:04:29 +020073#ifdef CONFIG_IEEE80211W
74" sa_query <addr> send SA Query to a station\n"
75#endif /* CONFIG_IEEE80211W */
Jouni Malinenad08c362008-11-23 19:34:26 +020076#ifdef CONFIG_WPS
Jouni Malinen31fcea92009-12-12 16:40:10 +020077" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n"
Jouni Malinen3981cb32010-09-23 10:30:52 -070078" wps_check_pin <PIN> verify PIN checksum\n"
Jouni Malinenad08c362008-11-23 19:34:26 +020079" wps_pbc indicate button pushed to initiate PBC\n"
Jouni Malinen116f7bb2009-02-26 22:10:21 +020080#ifdef CONFIG_WPS_OOB
Masashi Honma46bdb832009-02-26 21:57:38 +020081" wps_oob <type> <path> <method> use WPS with out-of-band (UFD)\n"
Jouni Malinen116f7bb2009-02-26 22:10:21 +020082#endif /* CONFIG_WPS_OOB */
Jouni Malinen5a1cc302010-08-24 16:35:37 +030083" wps_ap_pin <cmd> [params..] enable/disable AP PIN\n"
Jouni Malinen450eddc2010-10-21 16:49:41 +030084" wps_config <SSID> <auth> <encr> <key> configure AP\n"
Jouni Malinenad08c362008-11-23 19:34:26 +020085#endif /* CONFIG_WPS */
Jouni Malinen403b96f2010-09-23 12:02:28 -070086" get_config show current configuration\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -080087" help show this usage help\n"
88" interface [ifname] show interfaces/select interface\n"
89" level <debug level> change debug level\n"
90" license show full hostapd_cli license\n"
91" quit exit hostapd_cli\n";
92
93static struct wpa_ctrl *ctrl_conn;
94static int hostapd_cli_quit = 0;
95static int hostapd_cli_attached = 0;
96static const char *ctrl_iface_dir = "/var/run/hostapd";
97static char *ctrl_ifname = NULL;
Gregory Detalbae92172010-04-07 11:14:54 +030098static const char *pid_file = NULL;
99static const char *action_file = NULL;
Jouni Malinen1cc84c12009-01-20 21:12:00 +0200100static int ping_interval = 5;
Jouni Malinen42838052011-07-16 17:37:18 +0300101static int interactive = 0;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800102
103
104static void usage(void)
105{
106 fprintf(stderr, "%s\n", hostapd_cli_version);
Gregory Detalbae92172010-04-07 11:14:54 +0300107 fprintf(stderr,
108 "\n"
109 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
110 "[-a<path>] \\\n"
111 " [-G<ping interval>] [command..]\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -0800112 "\n"
113 "Options:\n"
114 " -h help (show this usage text)\n"
115 " -v shown version information\n"
116 " -p<path> path to find control sockets (default: "
117 "/var/run/hostapd)\n"
Gregory Detalbae92172010-04-07 11:14:54 +0300118 " -a<file> run in daemon mode executing the action file "
119 "based on events\n"
120 " from hostapd\n"
121 " -B run a daemon in the background\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -0800122 " -i<ifname> Interface to listen on (default: first "
123 "interface found in the\n"
124 " socket path)\n\n"
125 "%s",
126 commands_help);
127}
128
129
130static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
131{
132 char *cfile;
133 int flen;
134
135 if (ifname == NULL)
136 return NULL;
137
138 flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
139 cfile = malloc(flen);
140 if (cfile == NULL)
141 return NULL;
142 snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
143
144 ctrl_conn = wpa_ctrl_open(cfile);
145 free(cfile);
146 return ctrl_conn;
147}
148
149
150static void hostapd_cli_close_connection(void)
151{
152 if (ctrl_conn == NULL)
153 return;
154
155 if (hostapd_cli_attached) {
156 wpa_ctrl_detach(ctrl_conn);
157 hostapd_cli_attached = 0;
158 }
159 wpa_ctrl_close(ctrl_conn);
160 ctrl_conn = NULL;
161}
162
163
164static void hostapd_cli_msg_cb(char *msg, size_t len)
165{
166 printf("%s\n", msg);
167}
168
169
170static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
171{
172 char buf[4096];
173 size_t len;
174 int ret;
175
176 if (ctrl_conn == NULL) {
177 printf("Not connected to hostapd - command dropped.\n");
178 return -1;
179 }
180 len = sizeof(buf) - 1;
181 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
182 hostapd_cli_msg_cb);
183 if (ret == -2) {
184 printf("'%s' command timed out.\n", cmd);
185 return -2;
186 } else if (ret < 0) {
187 printf("'%s' command failed.\n", cmd);
188 return -1;
189 }
190 if (print) {
191 buf[len] = '\0';
192 printf("%s", buf);
193 }
194 return 0;
195}
196
197
198static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
199{
200 return _wpa_ctrl_command(ctrl, cmd, 1);
201}
202
203
204static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
205{
206 return wpa_ctrl_command(ctrl, "PING");
207}
208
209
Ben Greearb41a47c2011-02-06 20:24:16 +0200210static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
211{
212 return wpa_ctrl_command(ctrl, "RELOG");
213}
214
215
Jouni Malinen6fc68792008-02-27 17:34:43 -0800216static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
217{
218 return wpa_ctrl_command(ctrl, "MIB");
219}
220
221
Gregory Detalbae92172010-04-07 11:14:54 +0300222static int hostapd_cli_exec(const char *program, const char *arg1,
223 const char *arg2)
224{
225 char *cmd;
226 size_t len;
227 int res;
228 int ret = 0;
229
230 len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
231 cmd = os_malloc(len);
232 if (cmd == NULL)
233 return -1;
234 res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
235 if (res < 0 || (size_t) res >= len) {
236 os_free(cmd);
237 return -1;
238 }
239 cmd[len - 1] = '\0';
240#ifndef _WIN32_WCE
241 if (system(cmd) < 0)
242 ret = -1;
243#endif /* _WIN32_WCE */
244 os_free(cmd);
245
246 return ret;
247}
248
249
250static void hostapd_cli_action_process(char *msg, size_t len)
251{
252 const char *pos;
253
254 pos = msg;
255 if (*pos == '<') {
256 pos = os_strchr(pos, '>');
257 if (pos)
258 pos++;
259 else
260 pos = msg;
261 }
262
263 hostapd_cli_exec(action_file, ctrl_ifname, pos);
264}
265
266
Jouni Malinen6fc68792008-02-27 17:34:43 -0800267static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
268{
269 char buf[64];
270 if (argc != 1) {
271 printf("Invalid 'sta' command - exactly one argument, STA "
272 "address, is required.\n");
273 return -1;
274 }
275 snprintf(buf, sizeof(buf), "STA %s", argv[0]);
276 return wpa_ctrl_command(ctrl, buf);
277}
278
279
280static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
281 char *argv[])
282{
283 char buf[64];
284 if (argc != 1) {
285 printf("Invalid 'new_sta' command - exactly one argument, STA "
286 "address, is required.\n");
287 return -1;
288 }
289 snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
290 return wpa_ctrl_command(ctrl, buf);
291}
292
293
Jouni Malinen90a32062010-03-29 11:14:57 -0700294static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
295 char *argv[])
296{
297 char buf[64];
Jouni Malinenb91ab762010-03-29 12:01:40 -0700298 if (argc < 1) {
Jouni Malinen90a32062010-03-29 11:14:57 -0700299 printf("Invalid 'deauthenticate' command - exactly one "
300 "argument, STA address, is required.\n");
301 return -1;
302 }
Jouni Malinenb91ab762010-03-29 12:01:40 -0700303 if (argc > 1)
304 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
305 argv[0], argv[1]);
306 else
307 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
Jouni Malinen90a32062010-03-29 11:14:57 -0700308 return wpa_ctrl_command(ctrl, buf);
309}
310
311
312static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
313 char *argv[])
314{
315 char buf[64];
Jouni Malinenb91ab762010-03-29 12:01:40 -0700316 if (argc < 1) {
Jouni Malinen90a32062010-03-29 11:14:57 -0700317 printf("Invalid 'disassociate' command - exactly one "
318 "argument, STA address, is required.\n");
319 return -1;
320 }
Jouni Malinenb91ab762010-03-29 12:01:40 -0700321 if (argc > 1)
322 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
323 argv[0], argv[1]);
324 else
325 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
Jouni Malinen90a32062010-03-29 11:14:57 -0700326 return wpa_ctrl_command(ctrl, buf);
327}
328
329
Jouni Malinen88b4b422008-12-30 18:04:29 +0200330#ifdef CONFIG_IEEE80211W
331static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
332 char *argv[])
333{
334 char buf[64];
335 if (argc != 1) {
336 printf("Invalid 'sa_query' command - exactly one argument, "
337 "STA address, is required.\n");
338 return -1;
339 }
340 snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
341 return wpa_ctrl_command(ctrl, buf);
342}
343#endif /* CONFIG_IEEE80211W */
344
345
Jouni Malinenad08c362008-11-23 19:34:26 +0200346#ifdef CONFIG_WPS
347static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
348 char *argv[])
349{
Jouni Malinen31fcea92009-12-12 16:40:10 +0200350 char buf[256];
Jouni Malinen077a7812009-05-26 17:44:44 +0300351 if (argc < 2) {
352 printf("Invalid 'wps_pin' command - at least two arguments, "
Jouni Malinenad08c362008-11-23 19:34:26 +0200353 "UUID and PIN, are required.\n");
354 return -1;
355 }
Jouni Malinen31fcea92009-12-12 16:40:10 +0200356 if (argc > 3)
357 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
358 argv[0], argv[1], argv[2], argv[3]);
359 else if (argc > 2)
Jouni Malinen077a7812009-05-26 17:44:44 +0300360 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
361 argv[0], argv[1], argv[2]);
362 else
363 snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
Jouni Malinenad08c362008-11-23 19:34:26 +0200364 return wpa_ctrl_command(ctrl, buf);
365}
366
367
Jouni Malinen3981cb32010-09-23 10:30:52 -0700368static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
369 char *argv[])
370{
371 char cmd[256];
372 int res;
373
374 if (argc != 1 && argc != 2) {
375 printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
376 "- PIN to be verified\n");
377 return -1;
378 }
379
380 if (argc == 2)
381 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
382 argv[0], argv[1]);
383 else
384 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
385 argv[0]);
386 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
387 printf("Too long WPS_CHECK_PIN command.\n");
388 return -1;
389 }
390 return wpa_ctrl_command(ctrl, cmd);
391}
392
393
Jouni Malinenad08c362008-11-23 19:34:26 +0200394static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
395 char *argv[])
396{
397 return wpa_ctrl_command(ctrl, "WPS_PBC");
398}
Masashi Honma46bdb832009-02-26 21:57:38 +0200399
400
Jouni Malinen116f7bb2009-02-26 22:10:21 +0200401#ifdef CONFIG_WPS_OOB
Masashi Honma46bdb832009-02-26 21:57:38 +0200402static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
403 char *argv[])
404{
405 char cmd[256];
406 int res;
407
Masashi Honmae1ee6b62009-03-06 16:16:22 +0200408 if (argc != 3 && argc != 4) {
409 printf("Invalid WPS_OOB command: need three or four "
410 "arguments:\n"
411 "- DEV_TYPE: use 'ufd' or 'nfc'\n"
412 "- PATH: path of OOB device like '/mnt'\n"
413 "- METHOD: OOB method 'pin-e' or 'pin-r', "
414 "'cred'\n"
415 "- DEV_NAME: (only for NFC) device name like "
416 "'pn531'\n");
Masashi Honma46bdb832009-02-26 21:57:38 +0200417 return -1;
418 }
419
Masashi Honmae1ee6b62009-03-06 16:16:22 +0200420 if (argc == 3)
421 res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
422 argv[0], argv[1], argv[2]);
423 else
424 res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
425 argv[0], argv[1], argv[2], argv[3]);
Masashi Honma46bdb832009-02-26 21:57:38 +0200426 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
427 printf("Too long WPS_OOB command.\n");
428 return -1;
429 }
430 return wpa_ctrl_command(ctrl, cmd);
431}
Jouni Malinen116f7bb2009-02-26 22:10:21 +0200432#endif /* CONFIG_WPS_OOB */
Jouni Malinen5a1cc302010-08-24 16:35:37 +0300433
434
435static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
436 char *argv[])
437{
438 char buf[64];
439 if (argc < 1) {
440 printf("Invalid 'wps_ap_pin' command - at least one argument "
441 "is required.\n");
442 return -1;
443 }
444 if (argc > 2)
445 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
446 argv[0], argv[1], argv[2]);
447 else if (argc > 1)
448 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
449 argv[0], argv[1]);
450 else
451 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
452 return wpa_ctrl_command(ctrl, buf);
453}
Jouni Malinen450eddc2010-10-21 16:49:41 +0300454
455
456static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
457 char *argv[])
458{
459 char buf[256];
460 char ssid_hex[2 * 32 + 1];
461 char key_hex[2 * 64 + 1];
462 int i;
463
464 if (argc < 1) {
465 printf("Invalid 'wps_config' command - at least two arguments "
466 "are required.\n");
467 return -1;
468 }
469
470 ssid_hex[0] = '\0';
471 for (i = 0; i < 32; i++) {
472 if (argv[0][i] == '\0')
473 break;
474 os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
475 }
476
477 key_hex[0] = '\0';
478 if (argc > 3) {
479 for (i = 0; i < 64; i++) {
480 if (argv[3][i] == '\0')
481 break;
482 os_snprintf(&key_hex[i * 2], 3, "%02x",
483 argv[3][i]);
484 }
485 }
486
487 if (argc > 3)
488 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
489 ssid_hex, argv[1], argv[2], key_hex);
490 else if (argc > 2)
491 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
492 ssid_hex, argv[1], argv[2]);
493 else
494 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
495 ssid_hex, argv[1]);
496 return wpa_ctrl_command(ctrl, buf);
497}
Jouni Malinenad08c362008-11-23 19:34:26 +0200498#endif /* CONFIG_WPS */
499
500
Jouni Malinen71269b32011-10-07 13:33:04 +0300501static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
502 char *argv[])
503{
504 char buf[300];
505 int res;
506
507 if (argc < 2) {
508 printf("Invalid 'ess_disassoc' command - two arguments (STA "
509 "addr and URL) are needed\n");
510 return -1;
511 }
512
513 res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s",
514 argv[0], argv[1]);
515 if (res < 0 || res >= (int) sizeof(buf))
516 return -1;
517 return wpa_ctrl_command(ctrl, buf);
518}
519
520
Jouni Malinen403b96f2010-09-23 12:02:28 -0700521static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
522 char *argv[])
523{
524 return wpa_ctrl_command(ctrl, "GET_CONFIG");
525}
526
527
Jouni Malinen6fc68792008-02-27 17:34:43 -0800528static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
529 char *addr, size_t addr_len)
530{
531 char buf[4096], *pos;
532 size_t len;
533 int ret;
534
535 if (ctrl_conn == NULL) {
536 printf("Not connected to hostapd - command dropped.\n");
537 return -1;
538 }
539 len = sizeof(buf) - 1;
540 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
541 hostapd_cli_msg_cb);
542 if (ret == -2) {
543 printf("'%s' command timed out.\n", cmd);
544 return -2;
545 } else if (ret < 0) {
546 printf("'%s' command failed.\n", cmd);
547 return -1;
548 }
549
550 buf[len] = '\0';
551 if (memcmp(buf, "FAIL", 4) == 0)
552 return -1;
553 printf("%s", buf);
554
555 pos = buf;
556 while (*pos != '\0' && *pos != '\n')
557 pos++;
558 *pos = '\0';
559 os_strlcpy(addr, buf, addr_len);
560 return 0;
561}
562
563
564static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
565 char *argv[])
566{
567 char addr[32], cmd[64];
568
569 if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
570 return 0;
571 do {
572 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
573 } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
574
575 return -1;
576}
577
578
579static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
580{
581 printf("%s", commands_help);
582 return 0;
583}
584
585
586static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
587 char *argv[])
588{
589 printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
590 return 0;
591}
592
593
594static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
595{
596 hostapd_cli_quit = 1;
Jouni Malinen42838052011-07-16 17:37:18 +0300597 if (interactive)
598 eloop_terminate();
Jouni Malinen6fc68792008-02-27 17:34:43 -0800599 return 0;
600}
601
602
603static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
604{
605 char cmd[256];
606 if (argc != 1) {
607 printf("Invalid LEVEL command: needs one argument (debug "
608 "level)\n");
609 return 0;
610 }
611 snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
612 return wpa_ctrl_command(ctrl, cmd);
613}
614
615
616static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
617{
618 struct dirent *dent;
619 DIR *dir;
620
621 dir = opendir(ctrl_iface_dir);
622 if (dir == NULL) {
623 printf("Control interface directory '%s' could not be "
624 "openned.\n", ctrl_iface_dir);
625 return;
626 }
627
628 printf("Available interfaces:\n");
629 while ((dent = readdir(dir))) {
630 if (strcmp(dent->d_name, ".") == 0 ||
631 strcmp(dent->d_name, "..") == 0)
632 continue;
633 printf("%s\n", dent->d_name);
634 }
635 closedir(dir);
636}
637
638
639static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
640 char *argv[])
641{
642 if (argc < 1) {
643 hostapd_cli_list_interfaces(ctrl);
644 return 0;
645 }
646
647 hostapd_cli_close_connection();
648 free(ctrl_ifname);
649 ctrl_ifname = strdup(argv[0]);
650
651 if (hostapd_cli_open_connection(ctrl_ifname)) {
652 printf("Connected to interface '%s.\n", ctrl_ifname);
653 if (wpa_ctrl_attach(ctrl_conn) == 0) {
654 hostapd_cli_attached = 1;
655 } else {
656 printf("Warning: Failed to attach to "
657 "hostapd.\n");
658 }
659 } else {
660 printf("Could not connect to interface '%s' - re-trying\n",
661 ctrl_ifname);
662 }
663 return 0;
664}
665
666
Jouni Malinenb4e34f22010-09-21 19:51:23 -0700667static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
668{
669 char cmd[256];
670 int res;
671
672 if (argc != 2) {
673 printf("Invalid SET command: needs two arguments (variable "
674 "name and value)\n");
675 return -1;
676 }
677
678 res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
679 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
680 printf("Too long SET command.\n");
681 return -1;
682 }
683 return wpa_ctrl_command(ctrl, cmd);
684}
685
686
Jouni Malinenacec8d32010-10-31 17:07:31 +0200687static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
688{
689 char cmd[256];
690 int res;
691
692 if (argc != 1) {
693 printf("Invalid GET command: needs one argument (variable "
694 "name)\n");
695 return -1;
696 }
697
698 res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
699 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
700 printf("Too long GET command.\n");
701 return -1;
702 }
703 return wpa_ctrl_command(ctrl, cmd);
704}
705
706
Jouni Malinen6fc68792008-02-27 17:34:43 -0800707struct hostapd_cli_cmd {
708 const char *cmd;
709 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
710};
711
712static struct hostapd_cli_cmd hostapd_cli_commands[] = {
713 { "ping", hostapd_cli_cmd_ping },
714 { "mib", hostapd_cli_cmd_mib },
Ben Greearb41a47c2011-02-06 20:24:16 +0200715 { "relog", hostapd_cli_cmd_relog },
Jouni Malinen6fc68792008-02-27 17:34:43 -0800716 { "sta", hostapd_cli_cmd_sta },
717 { "all_sta", hostapd_cli_cmd_all_sta },
718 { "new_sta", hostapd_cli_cmd_new_sta },
Jouni Malinen90a32062010-03-29 11:14:57 -0700719 { "deauthenticate", hostapd_cli_cmd_deauthenticate },
720 { "disassociate", hostapd_cli_cmd_disassociate },
Jouni Malinen88b4b422008-12-30 18:04:29 +0200721#ifdef CONFIG_IEEE80211W
722 { "sa_query", hostapd_cli_cmd_sa_query },
723#endif /* CONFIG_IEEE80211W */
Jouni Malinenad08c362008-11-23 19:34:26 +0200724#ifdef CONFIG_WPS
725 { "wps_pin", hostapd_cli_cmd_wps_pin },
Jouni Malinen3981cb32010-09-23 10:30:52 -0700726 { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
Jouni Malinenad08c362008-11-23 19:34:26 +0200727 { "wps_pbc", hostapd_cli_cmd_wps_pbc },
Jouni Malinen116f7bb2009-02-26 22:10:21 +0200728#ifdef CONFIG_WPS_OOB
Masashi Honma46bdb832009-02-26 21:57:38 +0200729 { "wps_oob", hostapd_cli_cmd_wps_oob },
Jouni Malinen116f7bb2009-02-26 22:10:21 +0200730#endif /* CONFIG_WPS_OOB */
Jouni Malinen5a1cc302010-08-24 16:35:37 +0300731 { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
Jouni Malinen450eddc2010-10-21 16:49:41 +0300732 { "wps_config", hostapd_cli_cmd_wps_config },
Jouni Malinenad08c362008-11-23 19:34:26 +0200733#endif /* CONFIG_WPS */
Jouni Malinen71269b32011-10-07 13:33:04 +0300734 { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
Jouni Malinen403b96f2010-09-23 12:02:28 -0700735 { "get_config", hostapd_cli_cmd_get_config },
Jouni Malinen6fc68792008-02-27 17:34:43 -0800736 { "help", hostapd_cli_cmd_help },
737 { "interface", hostapd_cli_cmd_interface },
738 { "level", hostapd_cli_cmd_level },
739 { "license", hostapd_cli_cmd_license },
740 { "quit", hostapd_cli_cmd_quit },
Jouni Malinenb4e34f22010-09-21 19:51:23 -0700741 { "set", hostapd_cli_cmd_set },
Jouni Malinenacec8d32010-10-31 17:07:31 +0200742 { "get", hostapd_cli_cmd_get },
Jouni Malinen6fc68792008-02-27 17:34:43 -0800743 { NULL, NULL }
744};
745
746
747static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
748{
749 struct hostapd_cli_cmd *cmd, *match = NULL;
750 int count;
751
752 count = 0;
753 cmd = hostapd_cli_commands;
754 while (cmd->cmd) {
755 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
756 match = cmd;
Jouni Malinenacec8d32010-10-31 17:07:31 +0200757 if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
758 /* we have an exact match */
759 count = 1;
760 break;
761 }
Jouni Malinen6fc68792008-02-27 17:34:43 -0800762 count++;
763 }
764 cmd++;
765 }
766
767 if (count > 1) {
768 printf("Ambiguous command '%s'; possible commands:", argv[0]);
769 cmd = hostapd_cli_commands;
770 while (cmd->cmd) {
771 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
772 0) {
773 printf(" %s", cmd->cmd);
774 }
775 cmd++;
776 }
777 printf("\n");
778 } else if (count == 0) {
779 printf("Unknown command '%s'\n", argv[0]);
780 } else {
781 match->handler(ctrl, argc - 1, &argv[1]);
782 }
783}
784
785
Gregory Detalbae92172010-04-07 11:14:54 +0300786static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
787 int action_monitor)
Jouni Malinen6fc68792008-02-27 17:34:43 -0800788{
789 int first = 1;
790 if (ctrl_conn == NULL)
791 return;
792 while (wpa_ctrl_pending(ctrl)) {
793 char buf[256];
794 size_t len = sizeof(buf) - 1;
795 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
796 buf[len] = '\0';
Gregory Detalbae92172010-04-07 11:14:54 +0300797 if (action_monitor)
798 hostapd_cli_action_process(buf, len);
799 else {
800 if (in_read && first)
801 printf("\n");
802 first = 0;
803 printf("%s\n", buf);
804 }
Jouni Malinen6fc68792008-02-27 17:34:43 -0800805 } else {
806 printf("Could not read pending message.\n");
807 break;
808 }
809 }
810}
811
812
Jouni Malinen42838052011-07-16 17:37:18 +0300813#define max_args 10
814
815static int tokenize_cmd(char *cmd, char *argv[])
Jouni Malinen6fc68792008-02-27 17:34:43 -0800816{
Jouni Malinen42838052011-07-16 17:37:18 +0300817 char *pos;
818 int argc = 0;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800819
Jouni Malinen42838052011-07-16 17:37:18 +0300820 pos = cmd;
821 for (;;) {
822 while (*pos == ' ')
Jouni Malinen6fc68792008-02-27 17:34:43 -0800823 pos++;
Jouni Malinen42838052011-07-16 17:37:18 +0300824 if (*pos == '\0')
825 break;
826 argv[argc] = pos;
827 argc++;
828 if (argc == max_args)
829 break;
830 if (*pos == '"') {
831 char *pos2 = os_strrchr(pos, '"');
832 if (pos2)
833 pos = pos2 + 1;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800834 }
Jouni Malinen42838052011-07-16 17:37:18 +0300835 while (*pos != '\0' && *pos != ' ')
836 pos++;
837 if (*pos == ' ')
838 *pos++ = '\0';
839 }
840
841 return argc;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800842}
843
844
Jouni Malinen42838052011-07-16 17:37:18 +0300845static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
Jouni Malinen6fc68792008-02-27 17:34:43 -0800846{
847 if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
848 printf("Connection to hostapd lost - trying to reconnect\n");
849 hostapd_cli_close_connection();
850 }
851 if (!ctrl_conn) {
852 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
853 if (ctrl_conn) {
854 printf("Connection to hostapd re-established\n");
855 if (wpa_ctrl_attach(ctrl_conn) == 0) {
856 hostapd_cli_attached = 1;
857 } else {
858 printf("Warning: Failed to attach to "
859 "hostapd.\n");
860 }
861 }
862 }
863 if (ctrl_conn)
Gregory Detalbae92172010-04-07 11:14:54 +0300864 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
Jouni Malinen42838052011-07-16 17:37:18 +0300865 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
866}
867
868
869static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
870{
871 eloop_terminate();
872}
873
874
875static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
876{
877 char *argv[max_args];
878 int argc;
879 argc = tokenize_cmd(cmd, argv);
880 if (argc)
881 wpa_request(ctrl_conn, argc, argv);
882}
883
884
885static void hostapd_cli_edit_eof_cb(void *ctx)
886{
887 eloop_terminate();
888}
889
890
891static void hostapd_cli_interactive(void)
892{
893 printf("\nInteractive mode\n\n");
894
895 eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
896 edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
897 NULL, NULL, NULL);
898 eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
899
900 eloop_run();
901
902 edit_deinit(NULL, NULL);
903 eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
904}
905
906
907static void hostapd_cli_cleanup(void)
908{
909 hostapd_cli_close_connection();
910 if (pid_file)
911 os_daemonize_terminate(pid_file);
912
913 os_program_deinit();
Jouni Malinen6fc68792008-02-27 17:34:43 -0800914}
915
916
Gregory Detalbae92172010-04-07 11:14:54 +0300917static void hostapd_cli_action(struct wpa_ctrl *ctrl)
918{
919 fd_set rfds;
920 int fd, res;
921 struct timeval tv;
922 char buf[256];
923 size_t len;
924
925 fd = wpa_ctrl_get_fd(ctrl);
926
927 while (!hostapd_cli_quit) {
928 FD_ZERO(&rfds);
929 FD_SET(fd, &rfds);
930 tv.tv_sec = ping_interval;
931 tv.tv_usec = 0;
932 res = select(fd + 1, &rfds, NULL, NULL, &tv);
933 if (res < 0 && errno != EINTR) {
934 perror("select");
935 break;
936 }
937
938 if (FD_ISSET(fd, &rfds))
939 hostapd_cli_recv_pending(ctrl, 0, 1);
940 else {
941 len = sizeof(buf) - 1;
942 if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
943 hostapd_cli_action_process) < 0 ||
944 len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
945 printf("hostapd did not reply to PING "
946 "command - exiting\n");
947 break;
948 }
949 }
950 }
951}
952
953
Jouni Malinen6fc68792008-02-27 17:34:43 -0800954int main(int argc, char *argv[])
955{
Jouni Malinen6fc68792008-02-27 17:34:43 -0800956 int warning_displayed = 0;
957 int c;
Gregory Detalbae92172010-04-07 11:14:54 +0300958 int daemonize = 0;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800959
Jouni Malinen3433ed82009-12-19 22:26:36 +0200960 if (os_program_init())
961 return -1;
962
Jouni Malinen6fc68792008-02-27 17:34:43 -0800963 for (;;) {
Gregory Detalbae92172010-04-07 11:14:54 +0300964 c = getopt(argc, argv, "a:BhG:i:p:v");
Jouni Malinen6fc68792008-02-27 17:34:43 -0800965 if (c < 0)
966 break;
967 switch (c) {
Gregory Detalbae92172010-04-07 11:14:54 +0300968 case 'a':
969 action_file = optarg;
970 break;
971 case 'B':
972 daemonize = 1;
973 break;
Jouni Malinen1cc84c12009-01-20 21:12:00 +0200974 case 'G':
975 ping_interval = atoi(optarg);
976 break;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800977 case 'h':
978 usage();
979 return 0;
980 case 'v':
981 printf("%s\n", hostapd_cli_version);
982 return 0;
983 case 'i':
Jouni Malinenb242d392010-04-07 11:40:34 +0300984 os_free(ctrl_ifname);
985 ctrl_ifname = os_strdup(optarg);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800986 break;
987 case 'p':
988 ctrl_iface_dir = optarg;
989 break;
990 default:
991 usage();
992 return -1;
993 }
994 }
995
Gregory Detalbae92172010-04-07 11:14:54 +0300996 interactive = (argc == optind) && (action_file == NULL);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800997
998 if (interactive) {
999 printf("%s\n\n%s\n\n", hostapd_cli_version,
1000 hostapd_cli_license);
1001 }
1002
Jouni Malinen42838052011-07-16 17:37:18 +03001003 if (eloop_init())
1004 return -1;
1005
Jouni Malinen6fc68792008-02-27 17:34:43 -08001006 for (;;) {
1007 if (ctrl_ifname == NULL) {
1008 struct dirent *dent;
1009 DIR *dir = opendir(ctrl_iface_dir);
1010 if (dir) {
1011 while ((dent = readdir(dir))) {
Jouni Malinenb242d392010-04-07 11:40:34 +03001012 if (os_strcmp(dent->d_name, ".") == 0
1013 ||
1014 os_strcmp(dent->d_name, "..") == 0)
Jouni Malinen6fc68792008-02-27 17:34:43 -08001015 continue;
1016 printf("Selected interface '%s'\n",
1017 dent->d_name);
Jouni Malinenb242d392010-04-07 11:40:34 +03001018 ctrl_ifname = os_strdup(dent->d_name);
Jouni Malinen6fc68792008-02-27 17:34:43 -08001019 break;
1020 }
1021 closedir(dir);
1022 }
1023 }
1024 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
1025 if (ctrl_conn) {
1026 if (warning_displayed)
1027 printf("Connection established.\n");
1028 break;
1029 }
1030
1031 if (!interactive) {
1032 perror("Failed to connect to hostapd - "
1033 "wpa_ctrl_open");
1034 return -1;
1035 }
1036
1037 if (!warning_displayed) {
1038 printf("Could not connect to hostapd - re-trying\n");
1039 warning_displayed = 1;
1040 }
Jouni Malinenb242d392010-04-07 11:40:34 +03001041 os_sleep(1, 0);
Jouni Malinen6fc68792008-02-27 17:34:43 -08001042 continue;
1043 }
1044
Gregory Detalbae92172010-04-07 11:14:54 +03001045 if (interactive || action_file) {
Jouni Malinen6fc68792008-02-27 17:34:43 -08001046 if (wpa_ctrl_attach(ctrl_conn) == 0) {
1047 hostapd_cli_attached = 1;
1048 } else {
1049 printf("Warning: Failed to attach to hostapd.\n");
Gregory Detalbae92172010-04-07 11:14:54 +03001050 if (action_file)
1051 return -1;
Jouni Malinen6fc68792008-02-27 17:34:43 -08001052 }
Gregory Detalbae92172010-04-07 11:14:54 +03001053 }
1054
1055 if (daemonize && os_daemonize(pid_file))
1056 return -1;
1057
1058 if (interactive)
Jouni Malinen6fc68792008-02-27 17:34:43 -08001059 hostapd_cli_interactive();
Gregory Detalbae92172010-04-07 11:14:54 +03001060 else if (action_file)
1061 hostapd_cli_action(ctrl_conn);
1062 else
Jouni Malinen6fc68792008-02-27 17:34:43 -08001063 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1064
Gregory Detalbae92172010-04-07 11:14:54 +03001065 os_free(ctrl_ifname);
Jouni Malinen42838052011-07-16 17:37:18 +03001066 eloop_destroy();
Gregory Detalbae92172010-04-07 11:14:54 +03001067 hostapd_cli_cleanup();
Jouni Malinen6fc68792008-02-27 17:34:43 -08001068 return 0;
1069}