blob: 3fdaa159616de784684a9c8893db0f68c2a83bf7 [file] [log] [blame]
Jouni Malinen6fc68792008-02-27 17:34:43 -08001/*
2 * hostapd - command line interface for hostapd daemon
Jouni Malinendff0f702010-01-16 19:04:38 +02003 * Copyright (c) 2004-2010, 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 Malinen6fc68792008-02-27 17:34:43 -080019#include "common.h"
Jouni Malinen90973fb2009-11-29 17:51:55 +020020#include "common/version.h"
Jouni Malinen6fc68792008-02-27 17:34:43 -080021
22
23static const char *hostapd_cli_version =
24"hostapd_cli v" VERSION_STR "\n"
Jouni Malinendff0f702010-01-16 19:04:38 +020025"Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
Jouni Malinen6fc68792008-02-27 17:34:43 -080026
27
28static const char *hostapd_cli_license =
29"This program is free software. You can distribute it and/or modify it\n"
30"under the terms of the GNU General Public License version 2.\n"
31"\n"
32"Alternatively, this software may be distributed under the terms of the\n"
33"BSD license. See README and COPYING for more details.\n";
34
35static const char *hostapd_cli_full_license =
36"This program is free software; you can redistribute it and/or modify\n"
37"it under the terms of the GNU General Public License version 2 as\n"
38"published by the Free Software Foundation.\n"
39"\n"
40"This program is distributed in the hope that it will be useful,\n"
41"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
42"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
43"GNU General Public License for more details.\n"
44"\n"
45"You should have received a copy of the GNU General Public License\n"
46"along with this program; if not, write to the Free Software\n"
47"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n"
48"\n"
49"Alternatively, this software may be distributed under the terms of the\n"
50"BSD license.\n"
51"\n"
52"Redistribution and use in source and binary forms, with or without\n"
53"modification, are permitted provided that the following conditions are\n"
54"met:\n"
55"\n"
56"1. Redistributions of source code must retain the above copyright\n"
57" notice, this list of conditions and the following disclaimer.\n"
58"\n"
59"2. Redistributions in binary form must reproduce the above copyright\n"
60" notice, this list of conditions and the following disclaimer in the\n"
61" documentation and/or other materials provided with the distribution.\n"
62"\n"
63"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
64" names of its contributors may be used to endorse or promote products\n"
65" derived from this software without specific prior written permission.\n"
66"\n"
67"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
68"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
69"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
70"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
71"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
72"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
73"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
74"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
75"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
76"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
77"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
78"\n";
79
80static const char *commands_help =
81"Commands:\n"
82" mib get MIB variables (dot1x, dot11, radius)\n"
83" sta <addr> get MIB variables for one station\n"
84" all_sta get MIB variables for all stations\n"
85" new_sta <addr> add a new station\n"
Jouni Malinen488d0932010-04-11 21:00:16 +030086" deauthenticate <addr> deauthenticate a station\n"
87" disassociate <addr> disassociate a station\n"
Jouni Malinen88b4b422008-12-30 18:04:29 +020088#ifdef CONFIG_IEEE80211W
89" sa_query <addr> send SA Query to a station\n"
90#endif /* CONFIG_IEEE80211W */
Jouni Malinenad08c362008-11-23 19:34:26 +020091#ifdef CONFIG_WPS
Jouni Malinen31fcea92009-12-12 16:40:10 +020092" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n"
Jouni Malinen3981cb32010-09-23 10:30:52 -070093" wps_check_pin <PIN> verify PIN checksum\n"
Jouni Malinenad08c362008-11-23 19:34:26 +020094" wps_pbc indicate button pushed to initiate PBC\n"
Jouni Malinen116f7bb2009-02-26 22:10:21 +020095#ifdef CONFIG_WPS_OOB
Masashi Honma46bdb832009-02-26 21:57:38 +020096" wps_oob <type> <path> <method> use WPS with out-of-band (UFD)\n"
Jouni Malinen116f7bb2009-02-26 22:10:21 +020097#endif /* CONFIG_WPS_OOB */
Jouni Malinen5a1cc302010-08-24 16:35:37 +030098" wps_ap_pin <cmd> [params..] enable/disable AP PIN\n"
Jouni Malinen450eddc2010-10-21 16:49:41 +030099" wps_config <SSID> <auth> <encr> <key> configure AP\n"
Jouni Malinenad08c362008-11-23 19:34:26 +0200100#endif /* CONFIG_WPS */
Jouni Malinen403b96f2010-09-23 12:02:28 -0700101" get_config show current configuration\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -0800102" help show this usage help\n"
103" interface [ifname] show interfaces/select interface\n"
104" level <debug level> change debug level\n"
105" license show full hostapd_cli license\n"
106" quit exit hostapd_cli\n";
107
108static struct wpa_ctrl *ctrl_conn;
109static int hostapd_cli_quit = 0;
110static int hostapd_cli_attached = 0;
111static const char *ctrl_iface_dir = "/var/run/hostapd";
112static char *ctrl_ifname = NULL;
Gregory Detalbae92172010-04-07 11:14:54 +0300113static const char *pid_file = NULL;
114static const char *action_file = NULL;
Jouni Malinen1cc84c12009-01-20 21:12:00 +0200115static int ping_interval = 5;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800116
117
118static void usage(void)
119{
120 fprintf(stderr, "%s\n", hostapd_cli_version);
Gregory Detalbae92172010-04-07 11:14:54 +0300121 fprintf(stderr,
122 "\n"
123 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
124 "[-a<path>] \\\n"
125 " [-G<ping interval>] [command..]\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -0800126 "\n"
127 "Options:\n"
128 " -h help (show this usage text)\n"
129 " -v shown version information\n"
130 " -p<path> path to find control sockets (default: "
131 "/var/run/hostapd)\n"
Gregory Detalbae92172010-04-07 11:14:54 +0300132 " -a<file> run in daemon mode executing the action file "
133 "based on events\n"
134 " from hostapd\n"
135 " -B run a daemon in the background\n"
Jouni Malinen6fc68792008-02-27 17:34:43 -0800136 " -i<ifname> Interface to listen on (default: first "
137 "interface found in the\n"
138 " socket path)\n\n"
139 "%s",
140 commands_help);
141}
142
143
144static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
145{
146 char *cfile;
147 int flen;
148
149 if (ifname == NULL)
150 return NULL;
151
152 flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
153 cfile = malloc(flen);
154 if (cfile == NULL)
155 return NULL;
156 snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
157
158 ctrl_conn = wpa_ctrl_open(cfile);
159 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_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
225{
226 return wpa_ctrl_command(ctrl, "MIB");
227}
228
229
Gregory Detalbae92172010-04-07 11:14:54 +0300230static int hostapd_cli_exec(const char *program, const char *arg1,
231 const char *arg2)
232{
233 char *cmd;
234 size_t len;
235 int res;
236 int ret = 0;
237
238 len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
239 cmd = os_malloc(len);
240 if (cmd == NULL)
241 return -1;
242 res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
243 if (res < 0 || (size_t) res >= len) {
244 os_free(cmd);
245 return -1;
246 }
247 cmd[len - 1] = '\0';
248#ifndef _WIN32_WCE
249 if (system(cmd) < 0)
250 ret = -1;
251#endif /* _WIN32_WCE */
252 os_free(cmd);
253
254 return ret;
255}
256
257
258static void hostapd_cli_action_process(char *msg, size_t len)
259{
260 const char *pos;
261
262 pos = msg;
263 if (*pos == '<') {
264 pos = os_strchr(pos, '>');
265 if (pos)
266 pos++;
267 else
268 pos = msg;
269 }
270
271 hostapd_cli_exec(action_file, ctrl_ifname, pos);
272}
273
274
Jouni Malinen6fc68792008-02-27 17:34:43 -0800275static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
276{
277 char buf[64];
278 if (argc != 1) {
279 printf("Invalid 'sta' command - exactly one argument, STA "
280 "address, is required.\n");
281 return -1;
282 }
283 snprintf(buf, sizeof(buf), "STA %s", argv[0]);
284 return wpa_ctrl_command(ctrl, buf);
285}
286
287
288static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
289 char *argv[])
290{
291 char buf[64];
292 if (argc != 1) {
293 printf("Invalid 'new_sta' command - exactly one argument, STA "
294 "address, is required.\n");
295 return -1;
296 }
297 snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
298 return wpa_ctrl_command(ctrl, buf);
299}
300
301
Jouni Malinen90a32062010-03-29 11:14:57 -0700302static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
303 char *argv[])
304{
305 char buf[64];
Jouni Malinenb91ab762010-03-29 12:01:40 -0700306 if (argc < 1) {
Jouni Malinen90a32062010-03-29 11:14:57 -0700307 printf("Invalid 'deauthenticate' command - exactly one "
308 "argument, STA address, is required.\n");
309 return -1;
310 }
Jouni Malinenb91ab762010-03-29 12:01:40 -0700311 if (argc > 1)
312 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
313 argv[0], argv[1]);
314 else
315 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
Jouni Malinen90a32062010-03-29 11:14:57 -0700316 return wpa_ctrl_command(ctrl, buf);
317}
318
319
320static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
321 char *argv[])
322{
323 char buf[64];
Jouni Malinenb91ab762010-03-29 12:01:40 -0700324 if (argc < 1) {
Jouni Malinen90a32062010-03-29 11:14:57 -0700325 printf("Invalid 'disassociate' command - exactly one "
326 "argument, STA address, is required.\n");
327 return -1;
328 }
Jouni Malinenb91ab762010-03-29 12:01:40 -0700329 if (argc > 1)
330 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
331 argv[0], argv[1]);
332 else
333 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
Jouni Malinen90a32062010-03-29 11:14:57 -0700334 return wpa_ctrl_command(ctrl, buf);
335}
336
337
Jouni Malinen88b4b422008-12-30 18:04:29 +0200338#ifdef CONFIG_IEEE80211W
339static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
340 char *argv[])
341{
342 char buf[64];
343 if (argc != 1) {
344 printf("Invalid 'sa_query' command - exactly one argument, "
345 "STA address, is required.\n");
346 return -1;
347 }
348 snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
349 return wpa_ctrl_command(ctrl, buf);
350}
351#endif /* CONFIG_IEEE80211W */
352
353
Jouni Malinenad08c362008-11-23 19:34:26 +0200354#ifdef CONFIG_WPS
355static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
356 char *argv[])
357{
Jouni Malinen31fcea92009-12-12 16:40:10 +0200358 char buf[256];
Jouni Malinen077a7812009-05-26 17:44:44 +0300359 if (argc < 2) {
360 printf("Invalid 'wps_pin' command - at least two arguments, "
Jouni Malinenad08c362008-11-23 19:34:26 +0200361 "UUID and PIN, are required.\n");
362 return -1;
363 }
Jouni Malinen31fcea92009-12-12 16:40:10 +0200364 if (argc > 3)
365 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
366 argv[0], argv[1], argv[2], argv[3]);
367 else if (argc > 2)
Jouni Malinen077a7812009-05-26 17:44:44 +0300368 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
369 argv[0], argv[1], argv[2]);
370 else
371 snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
Jouni Malinenad08c362008-11-23 19:34:26 +0200372 return wpa_ctrl_command(ctrl, buf);
373}
374
375
Jouni Malinen3981cb32010-09-23 10:30:52 -0700376static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
377 char *argv[])
378{
379 char cmd[256];
380 int res;
381
382 if (argc != 1 && argc != 2) {
383 printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
384 "- PIN to be verified\n");
385 return -1;
386 }
387
388 if (argc == 2)
389 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
390 argv[0], argv[1]);
391 else
392 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
393 argv[0]);
394 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
395 printf("Too long WPS_CHECK_PIN command.\n");
396 return -1;
397 }
398 return wpa_ctrl_command(ctrl, cmd);
399}
400
401
Jouni Malinenad08c362008-11-23 19:34:26 +0200402static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
403 char *argv[])
404{
405 return wpa_ctrl_command(ctrl, "WPS_PBC");
406}
Masashi Honma46bdb832009-02-26 21:57:38 +0200407
408
Jouni Malinen116f7bb2009-02-26 22:10:21 +0200409#ifdef CONFIG_WPS_OOB
Masashi Honma46bdb832009-02-26 21:57:38 +0200410static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
411 char *argv[])
412{
413 char cmd[256];
414 int res;
415
Masashi Honmae1ee6b62009-03-06 16:16:22 +0200416 if (argc != 3 && argc != 4) {
417 printf("Invalid WPS_OOB command: need three or four "
418 "arguments:\n"
419 "- DEV_TYPE: use 'ufd' or 'nfc'\n"
420 "- PATH: path of OOB device like '/mnt'\n"
421 "- METHOD: OOB method 'pin-e' or 'pin-r', "
422 "'cred'\n"
423 "- DEV_NAME: (only for NFC) device name like "
424 "'pn531'\n");
Masashi Honma46bdb832009-02-26 21:57:38 +0200425 return -1;
426 }
427
Masashi Honmae1ee6b62009-03-06 16:16:22 +0200428 if (argc == 3)
429 res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
430 argv[0], argv[1], argv[2]);
431 else
432 res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
433 argv[0], argv[1], argv[2], argv[3]);
Masashi Honma46bdb832009-02-26 21:57:38 +0200434 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
435 printf("Too long WPS_OOB command.\n");
436 return -1;
437 }
438 return wpa_ctrl_command(ctrl, cmd);
439}
Jouni Malinen116f7bb2009-02-26 22:10:21 +0200440#endif /* CONFIG_WPS_OOB */
Jouni Malinen5a1cc302010-08-24 16:35:37 +0300441
442
443static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
444 char *argv[])
445{
446 char buf[64];
447 if (argc < 1) {
448 printf("Invalid 'wps_ap_pin' command - at least one argument "
449 "is required.\n");
450 return -1;
451 }
452 if (argc > 2)
453 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
454 argv[0], argv[1], argv[2]);
455 else if (argc > 1)
456 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
457 argv[0], argv[1]);
458 else
459 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
460 return wpa_ctrl_command(ctrl, buf);
461}
Jouni Malinen450eddc2010-10-21 16:49:41 +0300462
463
464static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
465 char *argv[])
466{
467 char buf[256];
468 char ssid_hex[2 * 32 + 1];
469 char key_hex[2 * 64 + 1];
470 int i;
471
472 if (argc < 1) {
473 printf("Invalid 'wps_config' command - at least two arguments "
474 "are required.\n");
475 return -1;
476 }
477
478 ssid_hex[0] = '\0';
479 for (i = 0; i < 32; i++) {
480 if (argv[0][i] == '\0')
481 break;
482 os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
483 }
484
485 key_hex[0] = '\0';
486 if (argc > 3) {
487 for (i = 0; i < 64; i++) {
488 if (argv[3][i] == '\0')
489 break;
490 os_snprintf(&key_hex[i * 2], 3, "%02x",
491 argv[3][i]);
492 }
493 }
494
495 if (argc > 3)
496 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
497 ssid_hex, argv[1], argv[2], key_hex);
498 else if (argc > 2)
499 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
500 ssid_hex, argv[1], argv[2]);
501 else
502 snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
503 ssid_hex, argv[1]);
504 return wpa_ctrl_command(ctrl, buf);
505}
Jouni Malinenad08c362008-11-23 19:34:26 +0200506#endif /* CONFIG_WPS */
507
508
Jouni Malinen403b96f2010-09-23 12:02:28 -0700509static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
510 char *argv[])
511{
512 return wpa_ctrl_command(ctrl, "GET_CONFIG");
513}
514
515
Jouni Malinen6fc68792008-02-27 17:34:43 -0800516static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
517 char *addr, size_t addr_len)
518{
519 char buf[4096], *pos;
520 size_t len;
521 int ret;
522
523 if (ctrl_conn == NULL) {
524 printf("Not connected to hostapd - command dropped.\n");
525 return -1;
526 }
527 len = sizeof(buf) - 1;
528 ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
529 hostapd_cli_msg_cb);
530 if (ret == -2) {
531 printf("'%s' command timed out.\n", cmd);
532 return -2;
533 } else if (ret < 0) {
534 printf("'%s' command failed.\n", cmd);
535 return -1;
536 }
537
538 buf[len] = '\0';
539 if (memcmp(buf, "FAIL", 4) == 0)
540 return -1;
541 printf("%s", buf);
542
543 pos = buf;
544 while (*pos != '\0' && *pos != '\n')
545 pos++;
546 *pos = '\0';
547 os_strlcpy(addr, buf, addr_len);
548 return 0;
549}
550
551
552static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
553 char *argv[])
554{
555 char addr[32], cmd[64];
556
557 if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
558 return 0;
559 do {
560 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
561 } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
562
563 return -1;
564}
565
566
567static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
568{
569 printf("%s", commands_help);
570 return 0;
571}
572
573
574static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
575 char *argv[])
576{
577 printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
578 return 0;
579}
580
581
582static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
583{
584 hostapd_cli_quit = 1;
585 return 0;
586}
587
588
589static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
590{
591 char cmd[256];
592 if (argc != 1) {
593 printf("Invalid LEVEL command: needs one argument (debug "
594 "level)\n");
595 return 0;
596 }
597 snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
598 return wpa_ctrl_command(ctrl, cmd);
599}
600
601
602static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
603{
604 struct dirent *dent;
605 DIR *dir;
606
607 dir = opendir(ctrl_iface_dir);
608 if (dir == NULL) {
609 printf("Control interface directory '%s' could not be "
610 "openned.\n", ctrl_iface_dir);
611 return;
612 }
613
614 printf("Available interfaces:\n");
615 while ((dent = readdir(dir))) {
616 if (strcmp(dent->d_name, ".") == 0 ||
617 strcmp(dent->d_name, "..") == 0)
618 continue;
619 printf("%s\n", dent->d_name);
620 }
621 closedir(dir);
622}
623
624
625static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
626 char *argv[])
627{
628 if (argc < 1) {
629 hostapd_cli_list_interfaces(ctrl);
630 return 0;
631 }
632
633 hostapd_cli_close_connection();
634 free(ctrl_ifname);
635 ctrl_ifname = strdup(argv[0]);
636
637 if (hostapd_cli_open_connection(ctrl_ifname)) {
638 printf("Connected to interface '%s.\n", ctrl_ifname);
639 if (wpa_ctrl_attach(ctrl_conn) == 0) {
640 hostapd_cli_attached = 1;
641 } else {
642 printf("Warning: Failed to attach to "
643 "hostapd.\n");
644 }
645 } else {
646 printf("Could not connect to interface '%s' - re-trying\n",
647 ctrl_ifname);
648 }
649 return 0;
650}
651
652
Jouni Malinenb4e34f22010-09-21 19:51:23 -0700653static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
654{
655 char cmd[256];
656 int res;
657
658 if (argc != 2) {
659 printf("Invalid SET command: needs two arguments (variable "
660 "name and value)\n");
661 return -1;
662 }
663
664 res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
665 if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
666 printf("Too long SET command.\n");
667 return -1;
668 }
669 return wpa_ctrl_command(ctrl, cmd);
670}
671
672
Jouni Malinen6fc68792008-02-27 17:34:43 -0800673struct hostapd_cli_cmd {
674 const char *cmd;
675 int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
676};
677
678static struct hostapd_cli_cmd hostapd_cli_commands[] = {
679 { "ping", hostapd_cli_cmd_ping },
680 { "mib", hostapd_cli_cmd_mib },
681 { "sta", hostapd_cli_cmd_sta },
682 { "all_sta", hostapd_cli_cmd_all_sta },
683 { "new_sta", hostapd_cli_cmd_new_sta },
Jouni Malinen90a32062010-03-29 11:14:57 -0700684 { "deauthenticate", hostapd_cli_cmd_deauthenticate },
685 { "disassociate", hostapd_cli_cmd_disassociate },
Jouni Malinen88b4b422008-12-30 18:04:29 +0200686#ifdef CONFIG_IEEE80211W
687 { "sa_query", hostapd_cli_cmd_sa_query },
688#endif /* CONFIG_IEEE80211W */
Jouni Malinenad08c362008-11-23 19:34:26 +0200689#ifdef CONFIG_WPS
690 { "wps_pin", hostapd_cli_cmd_wps_pin },
Jouni Malinen3981cb32010-09-23 10:30:52 -0700691 { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
Jouni Malinenad08c362008-11-23 19:34:26 +0200692 { "wps_pbc", hostapd_cli_cmd_wps_pbc },
Jouni Malinen116f7bb2009-02-26 22:10:21 +0200693#ifdef CONFIG_WPS_OOB
Masashi Honma46bdb832009-02-26 21:57:38 +0200694 { "wps_oob", hostapd_cli_cmd_wps_oob },
Jouni Malinen116f7bb2009-02-26 22:10:21 +0200695#endif /* CONFIG_WPS_OOB */
Jouni Malinen5a1cc302010-08-24 16:35:37 +0300696 { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
Jouni Malinen450eddc2010-10-21 16:49:41 +0300697 { "wps_config", hostapd_cli_cmd_wps_config },
Jouni Malinenad08c362008-11-23 19:34:26 +0200698#endif /* CONFIG_WPS */
Jouni Malinen403b96f2010-09-23 12:02:28 -0700699 { "get_config", hostapd_cli_cmd_get_config },
Jouni Malinen6fc68792008-02-27 17:34:43 -0800700 { "help", hostapd_cli_cmd_help },
701 { "interface", hostapd_cli_cmd_interface },
702 { "level", hostapd_cli_cmd_level },
703 { "license", hostapd_cli_cmd_license },
704 { "quit", hostapd_cli_cmd_quit },
Jouni Malinenb4e34f22010-09-21 19:51:23 -0700705 { "set", hostapd_cli_cmd_set },
Jouni Malinen6fc68792008-02-27 17:34:43 -0800706 { NULL, NULL }
707};
708
709
710static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
711{
712 struct hostapd_cli_cmd *cmd, *match = NULL;
713 int count;
714
715 count = 0;
716 cmd = hostapd_cli_commands;
717 while (cmd->cmd) {
718 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
719 match = cmd;
720 count++;
721 }
722 cmd++;
723 }
724
725 if (count > 1) {
726 printf("Ambiguous command '%s'; possible commands:", argv[0]);
727 cmd = hostapd_cli_commands;
728 while (cmd->cmd) {
729 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
730 0) {
731 printf(" %s", cmd->cmd);
732 }
733 cmd++;
734 }
735 printf("\n");
736 } else if (count == 0) {
737 printf("Unknown command '%s'\n", argv[0]);
738 } else {
739 match->handler(ctrl, argc - 1, &argv[1]);
740 }
741}
742
743
Gregory Detalbae92172010-04-07 11:14:54 +0300744static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
745 int action_monitor)
Jouni Malinen6fc68792008-02-27 17:34:43 -0800746{
747 int first = 1;
748 if (ctrl_conn == NULL)
749 return;
750 while (wpa_ctrl_pending(ctrl)) {
751 char buf[256];
752 size_t len = sizeof(buf) - 1;
753 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
754 buf[len] = '\0';
Gregory Detalbae92172010-04-07 11:14:54 +0300755 if (action_monitor)
756 hostapd_cli_action_process(buf, len);
757 else {
758 if (in_read && first)
759 printf("\n");
760 first = 0;
761 printf("%s\n", buf);
762 }
Jouni Malinen6fc68792008-02-27 17:34:43 -0800763 } else {
764 printf("Could not read pending message.\n");
765 break;
766 }
767 }
768}
769
770
771static void hostapd_cli_interactive(void)
772{
773 const int max_args = 10;
774 char cmd[256], *res, *argv[max_args], *pos;
775 int argc;
776
777 printf("\nInteractive mode\n\n");
778
779 do {
Gregory Detalbae92172010-04-07 11:14:54 +0300780 hostapd_cli_recv_pending(ctrl_conn, 0, 0);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800781 printf("> ");
Jouni Malinen1cc84c12009-01-20 21:12:00 +0200782 alarm(ping_interval);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800783 res = fgets(cmd, sizeof(cmd), stdin);
784 alarm(0);
785 if (res == NULL)
786 break;
787 pos = cmd;
788 while (*pos != '\0') {
789 if (*pos == '\n') {
790 *pos = '\0';
791 break;
792 }
793 pos++;
794 }
795 argc = 0;
796 pos = cmd;
797 for (;;) {
798 while (*pos == ' ')
799 pos++;
800 if (*pos == '\0')
801 break;
802 argv[argc] = pos;
803 argc++;
804 if (argc == max_args)
805 break;
806 while (*pos != '\0' && *pos != ' ')
807 pos++;
808 if (*pos == ' ')
809 *pos++ = '\0';
810 }
811 if (argc)
812 wpa_request(ctrl_conn, argc, argv);
813 } while (!hostapd_cli_quit);
814}
815
816
Gregory Detalbae92172010-04-07 11:14:54 +0300817static void hostapd_cli_cleanup(void)
Jouni Malinen6fc68792008-02-27 17:34:43 -0800818{
819 hostapd_cli_close_connection();
Gregory Detalbae92172010-04-07 11:14:54 +0300820 if (pid_file)
821 os_daemonize_terminate(pid_file);
822
823 os_program_deinit();
824}
825
826
827static void hostapd_cli_terminate(int sig)
828{
829 hostapd_cli_cleanup();
Jouni Malinen6fc68792008-02-27 17:34:43 -0800830 exit(0);
831}
832
833
834static void hostapd_cli_alarm(int sig)
835{
836 if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
837 printf("Connection to hostapd lost - trying to reconnect\n");
838 hostapd_cli_close_connection();
839 }
840 if (!ctrl_conn) {
841 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
842 if (ctrl_conn) {
843 printf("Connection to hostapd re-established\n");
844 if (wpa_ctrl_attach(ctrl_conn) == 0) {
845 hostapd_cli_attached = 1;
846 } else {
847 printf("Warning: Failed to attach to "
848 "hostapd.\n");
849 }
850 }
851 }
852 if (ctrl_conn)
Gregory Detalbae92172010-04-07 11:14:54 +0300853 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
Jouni Malinen1cc84c12009-01-20 21:12:00 +0200854 alarm(ping_interval);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800855}
856
857
Gregory Detalbae92172010-04-07 11:14:54 +0300858static void hostapd_cli_action(struct wpa_ctrl *ctrl)
859{
860 fd_set rfds;
861 int fd, res;
862 struct timeval tv;
863 char buf[256];
864 size_t len;
865
866 fd = wpa_ctrl_get_fd(ctrl);
867
868 while (!hostapd_cli_quit) {
869 FD_ZERO(&rfds);
870 FD_SET(fd, &rfds);
871 tv.tv_sec = ping_interval;
872 tv.tv_usec = 0;
873 res = select(fd + 1, &rfds, NULL, NULL, &tv);
874 if (res < 0 && errno != EINTR) {
875 perror("select");
876 break;
877 }
878
879 if (FD_ISSET(fd, &rfds))
880 hostapd_cli_recv_pending(ctrl, 0, 1);
881 else {
882 len = sizeof(buf) - 1;
883 if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
884 hostapd_cli_action_process) < 0 ||
885 len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
886 printf("hostapd did not reply to PING "
887 "command - exiting\n");
888 break;
889 }
890 }
891 }
892}
893
894
Jouni Malinen6fc68792008-02-27 17:34:43 -0800895int main(int argc, char *argv[])
896{
897 int interactive;
898 int warning_displayed = 0;
899 int c;
Gregory Detalbae92172010-04-07 11:14:54 +0300900 int daemonize = 0;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800901
Jouni Malinen3433ed82009-12-19 22:26:36 +0200902 if (os_program_init())
903 return -1;
904
Jouni Malinen6fc68792008-02-27 17:34:43 -0800905 for (;;) {
Gregory Detalbae92172010-04-07 11:14:54 +0300906 c = getopt(argc, argv, "a:BhG:i:p:v");
Jouni Malinen6fc68792008-02-27 17:34:43 -0800907 if (c < 0)
908 break;
909 switch (c) {
Gregory Detalbae92172010-04-07 11:14:54 +0300910 case 'a':
911 action_file = optarg;
912 break;
913 case 'B':
914 daemonize = 1;
915 break;
Jouni Malinen1cc84c12009-01-20 21:12:00 +0200916 case 'G':
917 ping_interval = atoi(optarg);
918 break;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800919 case 'h':
920 usage();
921 return 0;
922 case 'v':
923 printf("%s\n", hostapd_cli_version);
924 return 0;
925 case 'i':
Jouni Malinenb242d392010-04-07 11:40:34 +0300926 os_free(ctrl_ifname);
927 ctrl_ifname = os_strdup(optarg);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800928 break;
929 case 'p':
930 ctrl_iface_dir = optarg;
931 break;
932 default:
933 usage();
934 return -1;
935 }
936 }
937
Gregory Detalbae92172010-04-07 11:14:54 +0300938 interactive = (argc == optind) && (action_file == NULL);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800939
940 if (interactive) {
941 printf("%s\n\n%s\n\n", hostapd_cli_version,
942 hostapd_cli_license);
943 }
944
945 for (;;) {
946 if (ctrl_ifname == NULL) {
947 struct dirent *dent;
948 DIR *dir = opendir(ctrl_iface_dir);
949 if (dir) {
950 while ((dent = readdir(dir))) {
Jouni Malinenb242d392010-04-07 11:40:34 +0300951 if (os_strcmp(dent->d_name, ".") == 0
952 ||
953 os_strcmp(dent->d_name, "..") == 0)
Jouni Malinen6fc68792008-02-27 17:34:43 -0800954 continue;
955 printf("Selected interface '%s'\n",
956 dent->d_name);
Jouni Malinenb242d392010-04-07 11:40:34 +0300957 ctrl_ifname = os_strdup(dent->d_name);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800958 break;
959 }
960 closedir(dir);
961 }
962 }
963 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
964 if (ctrl_conn) {
965 if (warning_displayed)
966 printf("Connection established.\n");
967 break;
968 }
969
970 if (!interactive) {
971 perror("Failed to connect to hostapd - "
972 "wpa_ctrl_open");
973 return -1;
974 }
975
976 if (!warning_displayed) {
977 printf("Could not connect to hostapd - re-trying\n");
978 warning_displayed = 1;
979 }
Jouni Malinenb242d392010-04-07 11:40:34 +0300980 os_sleep(1, 0);
Jouni Malinen6fc68792008-02-27 17:34:43 -0800981 continue;
982 }
983
984 signal(SIGINT, hostapd_cli_terminate);
985 signal(SIGTERM, hostapd_cli_terminate);
986 signal(SIGALRM, hostapd_cli_alarm);
987
Gregory Detalbae92172010-04-07 11:14:54 +0300988 if (interactive || action_file) {
Jouni Malinen6fc68792008-02-27 17:34:43 -0800989 if (wpa_ctrl_attach(ctrl_conn) == 0) {
990 hostapd_cli_attached = 1;
991 } else {
992 printf("Warning: Failed to attach to hostapd.\n");
Gregory Detalbae92172010-04-07 11:14:54 +0300993 if (action_file)
994 return -1;
Jouni Malinen6fc68792008-02-27 17:34:43 -0800995 }
Gregory Detalbae92172010-04-07 11:14:54 +0300996 }
997
998 if (daemonize && os_daemonize(pid_file))
999 return -1;
1000
1001 if (interactive)
Jouni Malinen6fc68792008-02-27 17:34:43 -08001002 hostapd_cli_interactive();
Gregory Detalbae92172010-04-07 11:14:54 +03001003 else if (action_file)
1004 hostapd_cli_action(ctrl_conn);
1005 else
Jouni Malinen6fc68792008-02-27 17:34:43 -08001006 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
1007
Gregory Detalbae92172010-04-07 11:14:54 +03001008 os_free(ctrl_ifname);
1009 hostapd_cli_cleanup();
Jouni Malinen6fc68792008-02-27 17:34:43 -08001010 return 0;
1011}