5ca793b49647516538c6ff188e730b79a87f325a
[libeap.git] / hostapd / hostapd_cli.c
1 /*
2  * hostapd - command line interface for hostapd daemon
3  * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
4  *
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
18 #include "common/wpa_ctrl.h"
19 #include "common.h"
20 #include "common/version.h"
21
22
23 static const char *hostapd_cli_version =
24 "hostapd_cli v" VERSION_STR "\n"
25 "Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
26
27
28 static 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
35 static 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
80 static 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"
86 "   deauthenticate <addr>  deauthenticate a station\n"
87 "   disassociate <addr>  disassociate a station\n"
88 #ifdef CONFIG_IEEE80211W
89 "   sa_query <addr>      send SA Query to a station\n"
90 #endif /* CONFIG_IEEE80211W */
91 #ifdef CONFIG_WPS
92 "   wps_pin <uuid> <pin> [timeout] [addr]  add WPS Enrollee PIN\n"
93 "   wps_check_pin <PIN>  verify PIN checksum\n"
94 "   wps_pbc              indicate button pushed to initiate PBC\n"
95 #ifdef CONFIG_WPS_OOB
96 "   wps_oob <type> <path> <method>  use WPS with out-of-band (UFD)\n"
97 #endif /* CONFIG_WPS_OOB */
98 "   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
99 #endif /* CONFIG_WPS */
100 "   help                 show this usage help\n"
101 "   interface [ifname]   show interfaces/select interface\n"
102 "   level <debug level>  change debug level\n"
103 "   license              show full hostapd_cli license\n"
104 "   quit                 exit hostapd_cli\n";
105
106 static struct wpa_ctrl *ctrl_conn;
107 static int hostapd_cli_quit = 0;
108 static int hostapd_cli_attached = 0;
109 static const char *ctrl_iface_dir = "/var/run/hostapd";
110 static char *ctrl_ifname = NULL;
111 static const char *pid_file = NULL;
112 static const char *action_file = NULL;
113 static int ping_interval = 5;
114
115
116 static void usage(void)
117 {
118         fprintf(stderr, "%s\n", hostapd_cli_version);
119         fprintf(stderr,
120                 "\n"
121                 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
122                 "[-a<path>] \\\n"
123                 "                   [-G<ping interval>] [command..]\n"
124                 "\n"
125                 "Options:\n"
126                 "   -h           help (show this usage text)\n"
127                 "   -v           shown version information\n"
128                 "   -p<path>     path to find control sockets (default: "
129                 "/var/run/hostapd)\n"
130                 "   -a<file>     run in daemon mode executing the action file "
131                 "based on events\n"
132                 "                from hostapd\n"
133                 "   -B           run a daemon in the background\n"
134                 "   -i<ifname>   Interface to listen on (default: first "
135                 "interface found in the\n"
136                 "                socket path)\n\n"
137                 "%s",
138                 commands_help);
139 }
140
141
142 static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
143 {
144         char *cfile;
145         int flen;
146
147         if (ifname == NULL)
148                 return NULL;
149
150         flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
151         cfile = malloc(flen);
152         if (cfile == NULL)
153                 return NULL;
154         snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
155
156         ctrl_conn = wpa_ctrl_open(cfile);
157         free(cfile);
158         return ctrl_conn;
159 }
160
161
162 static void hostapd_cli_close_connection(void)
163 {
164         if (ctrl_conn == NULL)
165                 return;
166
167         if (hostapd_cli_attached) {
168                 wpa_ctrl_detach(ctrl_conn);
169                 hostapd_cli_attached = 0;
170         }
171         wpa_ctrl_close(ctrl_conn);
172         ctrl_conn = NULL;
173 }
174
175
176 static void hostapd_cli_msg_cb(char *msg, size_t len)
177 {
178         printf("%s\n", msg);
179 }
180
181
182 static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
183 {
184         char buf[4096];
185         size_t len;
186         int ret;
187
188         if (ctrl_conn == NULL) {
189                 printf("Not connected to hostapd - command dropped.\n");
190                 return -1;
191         }
192         len = sizeof(buf) - 1;
193         ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
194                                hostapd_cli_msg_cb);
195         if (ret == -2) {
196                 printf("'%s' command timed out.\n", cmd);
197                 return -2;
198         } else if (ret < 0) {
199                 printf("'%s' command failed.\n", cmd);
200                 return -1;
201         }
202         if (print) {
203                 buf[len] = '\0';
204                 printf("%s", buf);
205         }
206         return 0;
207 }
208
209
210 static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
211 {
212         return _wpa_ctrl_command(ctrl, cmd, 1);
213 }
214
215
216 static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
217 {
218         return wpa_ctrl_command(ctrl, "PING");
219 }
220
221
222 static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
223 {
224         return wpa_ctrl_command(ctrl, "MIB");
225 }
226
227
228 static int hostapd_cli_exec(const char *program, const char *arg1,
229                             const char *arg2)
230 {
231         char *cmd;
232         size_t len;
233         int res;
234         int ret = 0;
235
236         len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
237         cmd = os_malloc(len);
238         if (cmd == NULL)
239                 return -1;
240         res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
241         if (res < 0 || (size_t) res >= len) {
242                 os_free(cmd);
243                 return -1;
244         }
245         cmd[len - 1] = '\0';
246 #ifndef _WIN32_WCE
247         if (system(cmd) < 0)
248                 ret = -1;
249 #endif /* _WIN32_WCE */
250         os_free(cmd);
251
252         return ret;
253 }
254
255
256 static void hostapd_cli_action_process(char *msg, size_t len)
257 {
258         const char *pos;
259
260         pos = msg;
261         if (*pos == '<') {
262                 pos = os_strchr(pos, '>');
263                 if (pos)
264                         pos++;
265                 else
266                         pos = msg;
267         }
268
269         hostapd_cli_exec(action_file, ctrl_ifname, pos);
270 }
271
272
273 static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
274 {
275         char buf[64];
276         if (argc != 1) {
277                 printf("Invalid 'sta' command - exactly one argument, STA "
278                        "address, is required.\n");
279                 return -1;
280         }
281         snprintf(buf, sizeof(buf), "STA %s", argv[0]);
282         return wpa_ctrl_command(ctrl, buf);
283 }
284
285
286 static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
287                                    char *argv[])
288 {
289         char buf[64];
290         if (argc != 1) {
291                 printf("Invalid 'new_sta' command - exactly one argument, STA "
292                        "address, is required.\n");
293                 return -1;
294         }
295         snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
296         return wpa_ctrl_command(ctrl, buf);
297 }
298
299
300 static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
301                                           char *argv[])
302 {
303         char buf[64];
304         if (argc < 1) {
305                 printf("Invalid 'deauthenticate' command - exactly one "
306                        "argument, STA address, is required.\n");
307                 return -1;
308         }
309         if (argc > 1)
310                 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
311                             argv[0], argv[1]);
312         else
313                 os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
314         return wpa_ctrl_command(ctrl, buf);
315 }
316
317
318 static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
319                                         char *argv[])
320 {
321         char buf[64];
322         if (argc < 1) {
323                 printf("Invalid 'disassociate' command - exactly one "
324                        "argument, STA address, is required.\n");
325                 return -1;
326         }
327         if (argc > 1)
328                 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
329                             argv[0], argv[1]);
330         else
331                 os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
332         return wpa_ctrl_command(ctrl, buf);
333 }
334
335
336 #ifdef CONFIG_IEEE80211W
337 static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
338                                     char *argv[])
339 {
340         char buf[64];
341         if (argc != 1) {
342                 printf("Invalid 'sa_query' command - exactly one argument, "
343                        "STA address, is required.\n");
344                 return -1;
345         }
346         snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
347         return wpa_ctrl_command(ctrl, buf);
348 }
349 #endif /* CONFIG_IEEE80211W */
350
351
352 #ifdef CONFIG_WPS
353 static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
354                                    char *argv[])
355 {
356         char buf[256];
357         if (argc < 2) {
358                 printf("Invalid 'wps_pin' command - at least two arguments, "
359                        "UUID and PIN, are required.\n");
360                 return -1;
361         }
362         if (argc > 3)
363                 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
364                          argv[0], argv[1], argv[2], argv[3]);
365         else if (argc > 2)
366                 snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
367                          argv[0], argv[1], argv[2]);
368         else
369                 snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
370         return wpa_ctrl_command(ctrl, buf);
371 }
372
373
374 static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
375                                          char *argv[])
376 {
377         char cmd[256];
378         int res;
379
380         if (argc != 1 && argc != 2) {
381                 printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
382                        "- PIN to be verified\n");
383                 return -1;
384         }
385
386         if (argc == 2)
387                 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
388                                   argv[0], argv[1]);
389         else
390                 res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
391                                   argv[0]);
392         if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
393                 printf("Too long WPS_CHECK_PIN command.\n");
394                 return -1;
395         }
396         return wpa_ctrl_command(ctrl, cmd);
397 }
398
399
400 static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
401                                    char *argv[])
402 {
403         return wpa_ctrl_command(ctrl, "WPS_PBC");
404 }
405
406
407 #ifdef CONFIG_WPS_OOB
408 static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
409                                    char *argv[])
410 {
411         char cmd[256];
412         int res;
413
414         if (argc != 3 && argc != 4) {
415                 printf("Invalid WPS_OOB command: need three or four "
416                        "arguments:\n"
417                        "- DEV_TYPE: use 'ufd' or 'nfc'\n"
418                        "- PATH: path of OOB device like '/mnt'\n"
419                        "- METHOD: OOB method 'pin-e' or 'pin-r', "
420                        "'cred'\n"
421                        "- DEV_NAME: (only for NFC) device name like "
422                        "'pn531'\n");
423                 return -1;
424         }
425
426         if (argc == 3)
427                 res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
428                                   argv[0], argv[1], argv[2]);
429         else
430                 res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
431                                   argv[0], argv[1], argv[2], argv[3]);
432         if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
433                 printf("Too long WPS_OOB command.\n");
434                 return -1;
435         }
436         return wpa_ctrl_command(ctrl, cmd);
437 }
438 #endif /* CONFIG_WPS_OOB */
439
440
441 static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
442                                       char *argv[])
443 {
444         char buf[64];
445         if (argc < 1) {
446                 printf("Invalid 'wps_ap_pin' command - at least one argument "
447                        "is required.\n");
448                 return -1;
449         }
450         if (argc > 2)
451                 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
452                          argv[0], argv[1], argv[2]);
453         else if (argc > 1)
454                 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
455                          argv[0], argv[1]);
456         else
457                 snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
458         return wpa_ctrl_command(ctrl, buf);
459 }
460 #endif /* CONFIG_WPS */
461
462
463 static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
464                                 char *addr, size_t addr_len)
465 {
466         char buf[4096], *pos;
467         size_t len;
468         int ret;
469
470         if (ctrl_conn == NULL) {
471                 printf("Not connected to hostapd - command dropped.\n");
472                 return -1;
473         }
474         len = sizeof(buf) - 1;
475         ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
476                                hostapd_cli_msg_cb);
477         if (ret == -2) {
478                 printf("'%s' command timed out.\n", cmd);
479                 return -2;
480         } else if (ret < 0) {
481                 printf("'%s' command failed.\n", cmd);
482                 return -1;
483         }
484
485         buf[len] = '\0';
486         if (memcmp(buf, "FAIL", 4) == 0)
487                 return -1;
488         printf("%s", buf);
489
490         pos = buf;
491         while (*pos != '\0' && *pos != '\n')
492                 pos++;
493         *pos = '\0';
494         os_strlcpy(addr, buf, addr_len);
495         return 0;
496 }
497
498
499 static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
500                                    char *argv[])
501 {
502         char addr[32], cmd[64];
503
504         if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
505                 return 0;
506         do {
507                 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
508         } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
509
510         return -1;
511 }
512
513
514 static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
515 {
516         printf("%s", commands_help);
517         return 0;
518 }
519
520
521 static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
522                                    char *argv[])
523 {
524         printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
525         return 0;
526 }
527
528
529 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
530 {
531         hostapd_cli_quit = 1;
532         return 0;
533 }
534
535
536 static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
537 {
538         char cmd[256];
539         if (argc != 1) {
540                 printf("Invalid LEVEL command: needs one argument (debug "
541                        "level)\n");
542                 return 0;
543         }
544         snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
545         return wpa_ctrl_command(ctrl, cmd);
546 }
547
548
549 static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
550 {
551         struct dirent *dent;
552         DIR *dir;
553
554         dir = opendir(ctrl_iface_dir);
555         if (dir == NULL) {
556                 printf("Control interface directory '%s' could not be "
557                        "openned.\n", ctrl_iface_dir);
558                 return;
559         }
560
561         printf("Available interfaces:\n");
562         while ((dent = readdir(dir))) {
563                 if (strcmp(dent->d_name, ".") == 0 ||
564                     strcmp(dent->d_name, "..") == 0)
565                         continue;
566                 printf("%s\n", dent->d_name);
567         }
568         closedir(dir);
569 }
570
571
572 static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
573                                      char *argv[])
574 {
575         if (argc < 1) {
576                 hostapd_cli_list_interfaces(ctrl);
577                 return 0;
578         }
579
580         hostapd_cli_close_connection();
581         free(ctrl_ifname);
582         ctrl_ifname = strdup(argv[0]);
583
584         if (hostapd_cli_open_connection(ctrl_ifname)) {
585                 printf("Connected to interface '%s.\n", ctrl_ifname);
586                 if (wpa_ctrl_attach(ctrl_conn) == 0) {
587                         hostapd_cli_attached = 1;
588                 } else {
589                         printf("Warning: Failed to attach to "
590                                "hostapd.\n");
591                 }
592         } else {
593                 printf("Could not connect to interface '%s' - re-trying\n",
594                         ctrl_ifname);
595         }
596         return 0;
597 }
598
599
600 static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
601 {
602         char cmd[256];
603         int res;
604
605         if (argc != 2) {
606                 printf("Invalid SET command: needs two arguments (variable "
607                        "name and value)\n");
608                 return -1;
609         }
610
611         res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
612         if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
613                 printf("Too long SET command.\n");
614                 return -1;
615         }
616         return wpa_ctrl_command(ctrl, cmd);
617 }
618
619
620 struct hostapd_cli_cmd {
621         const char *cmd;
622         int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
623 };
624
625 static struct hostapd_cli_cmd hostapd_cli_commands[] = {
626         { "ping", hostapd_cli_cmd_ping },
627         { "mib", hostapd_cli_cmd_mib },
628         { "sta", hostapd_cli_cmd_sta },
629         { "all_sta", hostapd_cli_cmd_all_sta },
630         { "new_sta", hostapd_cli_cmd_new_sta },
631         { "deauthenticate", hostapd_cli_cmd_deauthenticate },
632         { "disassociate", hostapd_cli_cmd_disassociate },
633 #ifdef CONFIG_IEEE80211W
634         { "sa_query", hostapd_cli_cmd_sa_query },
635 #endif /* CONFIG_IEEE80211W */
636 #ifdef CONFIG_WPS
637         { "wps_pin", hostapd_cli_cmd_wps_pin },
638         { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
639         { "wps_pbc", hostapd_cli_cmd_wps_pbc },
640 #ifdef CONFIG_WPS_OOB
641         { "wps_oob", hostapd_cli_cmd_wps_oob },
642 #endif /* CONFIG_WPS_OOB */
643         { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
644 #endif /* CONFIG_WPS */
645         { "help", hostapd_cli_cmd_help },
646         { "interface", hostapd_cli_cmd_interface },
647         { "level", hostapd_cli_cmd_level },
648         { "license", hostapd_cli_cmd_license },
649         { "quit", hostapd_cli_cmd_quit },
650         { "set", hostapd_cli_cmd_set },
651         { NULL, NULL }
652 };
653
654
655 static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
656 {
657         struct hostapd_cli_cmd *cmd, *match = NULL;
658         int count;
659
660         count = 0;
661         cmd = hostapd_cli_commands;
662         while (cmd->cmd) {
663                 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
664                         match = cmd;
665                         count++;
666                 }
667                 cmd++;
668         }
669
670         if (count > 1) {
671                 printf("Ambiguous command '%s'; possible commands:", argv[0]);
672                 cmd = hostapd_cli_commands;
673                 while (cmd->cmd) {
674                         if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
675                             0) {
676                                 printf(" %s", cmd->cmd);
677                         }
678                         cmd++;
679                 }
680                 printf("\n");
681         } else if (count == 0) {
682                 printf("Unknown command '%s'\n", argv[0]);
683         } else {
684                 match->handler(ctrl, argc - 1, &argv[1]);
685         }
686 }
687
688
689 static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
690                                      int action_monitor)
691 {
692         int first = 1;
693         if (ctrl_conn == NULL)
694                 return;
695         while (wpa_ctrl_pending(ctrl)) {
696                 char buf[256];
697                 size_t len = sizeof(buf) - 1;
698                 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
699                         buf[len] = '\0';
700                         if (action_monitor)
701                                 hostapd_cli_action_process(buf, len);
702                         else {
703                                 if (in_read && first)
704                                         printf("\n");
705                                 first = 0;
706                                 printf("%s\n", buf);
707                         }
708                 } else {
709                         printf("Could not read pending message.\n");
710                         break;
711                 }
712         }
713 }
714
715
716 static void hostapd_cli_interactive(void)
717 {
718         const int max_args = 10;
719         char cmd[256], *res, *argv[max_args], *pos;
720         int argc;
721
722         printf("\nInteractive mode\n\n");
723
724         do {
725                 hostapd_cli_recv_pending(ctrl_conn, 0, 0);
726                 printf("> ");
727                 alarm(ping_interval);
728                 res = fgets(cmd, sizeof(cmd), stdin);
729                 alarm(0);
730                 if (res == NULL)
731                         break;
732                 pos = cmd;
733                 while (*pos != '\0') {
734                         if (*pos == '\n') {
735                                 *pos = '\0';
736                                 break;
737                         }
738                         pos++;
739                 }
740                 argc = 0;
741                 pos = cmd;
742                 for (;;) {
743                         while (*pos == ' ')
744                                 pos++;
745                         if (*pos == '\0')
746                                 break;
747                         argv[argc] = pos;
748                         argc++;
749                         if (argc == max_args)
750                                 break;
751                         while (*pos != '\0' && *pos != ' ')
752                                 pos++;
753                         if (*pos == ' ')
754                                 *pos++ = '\0';
755                 }
756                 if (argc)
757                         wpa_request(ctrl_conn, argc, argv);
758         } while (!hostapd_cli_quit);
759 }
760
761
762 static void hostapd_cli_cleanup(void)
763 {
764         hostapd_cli_close_connection();
765         if (pid_file)
766                 os_daemonize_terminate(pid_file);
767
768         os_program_deinit();
769 }
770
771
772 static void hostapd_cli_terminate(int sig)
773 {
774         hostapd_cli_cleanup();
775         exit(0);
776 }
777
778
779 static void hostapd_cli_alarm(int sig)
780 {
781         if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
782                 printf("Connection to hostapd lost - trying to reconnect\n");
783                 hostapd_cli_close_connection();
784         }
785         if (!ctrl_conn) {
786                 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
787                 if (ctrl_conn) {
788                         printf("Connection to hostapd re-established\n");
789                         if (wpa_ctrl_attach(ctrl_conn) == 0) {
790                                 hostapd_cli_attached = 1;
791                         } else {
792                                 printf("Warning: Failed to attach to "
793                                        "hostapd.\n");
794                         }
795                 }
796         }
797         if (ctrl_conn)
798                 hostapd_cli_recv_pending(ctrl_conn, 1, 0);
799         alarm(ping_interval);
800 }
801
802
803 static void hostapd_cli_action(struct wpa_ctrl *ctrl)
804 {
805         fd_set rfds;
806         int fd, res;
807         struct timeval tv;
808         char buf[256];
809         size_t len;
810
811         fd = wpa_ctrl_get_fd(ctrl);
812
813         while (!hostapd_cli_quit) {
814                 FD_ZERO(&rfds);
815                 FD_SET(fd, &rfds);
816                 tv.tv_sec = ping_interval;
817                 tv.tv_usec = 0;
818                 res = select(fd + 1, &rfds, NULL, NULL, &tv);
819                 if (res < 0 && errno != EINTR) {
820                         perror("select");
821                         break;
822                 }
823
824                 if (FD_ISSET(fd, &rfds))
825                         hostapd_cli_recv_pending(ctrl, 0, 1);
826                 else {
827                         len = sizeof(buf) - 1;
828                         if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
829                                              hostapd_cli_action_process) < 0 ||
830                             len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
831                                 printf("hostapd did not reply to PING "
832                                        "command - exiting\n");
833                                 break;
834                         }
835                 }
836         }
837 }
838
839
840 int main(int argc, char *argv[])
841 {
842         int interactive;
843         int warning_displayed = 0;
844         int c;
845         int daemonize = 0;
846
847         if (os_program_init())
848                 return -1;
849
850         for (;;) {
851                 c = getopt(argc, argv, "a:BhG:i:p:v");
852                 if (c < 0)
853                         break;
854                 switch (c) {
855                 case 'a':
856                         action_file = optarg;
857                         break;
858                 case 'B':
859                         daemonize = 1;
860                         break;
861                 case 'G':
862                         ping_interval = atoi(optarg);
863                         break;
864                 case 'h':
865                         usage();
866                         return 0;
867                 case 'v':
868                         printf("%s\n", hostapd_cli_version);
869                         return 0;
870                 case 'i':
871                         os_free(ctrl_ifname);
872                         ctrl_ifname = os_strdup(optarg);
873                         break;
874                 case 'p':
875                         ctrl_iface_dir = optarg;
876                         break;
877                 default:
878                         usage();
879                         return -1;
880                 }
881         }
882
883         interactive = (argc == optind) && (action_file == NULL);
884
885         if (interactive) {
886                 printf("%s\n\n%s\n\n", hostapd_cli_version,
887                        hostapd_cli_license);
888         }
889
890         for (;;) {
891                 if (ctrl_ifname == NULL) {
892                         struct dirent *dent;
893                         DIR *dir = opendir(ctrl_iface_dir);
894                         if (dir) {
895                                 while ((dent = readdir(dir))) {
896                                         if (os_strcmp(dent->d_name, ".") == 0
897                                             ||
898                                             os_strcmp(dent->d_name, "..") == 0)
899                                                 continue;
900                                         printf("Selected interface '%s'\n",
901                                                dent->d_name);
902                                         ctrl_ifname = os_strdup(dent->d_name);
903                                         break;
904                                 }
905                                 closedir(dir);
906                         }
907                 }
908                 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
909                 if (ctrl_conn) {
910                         if (warning_displayed)
911                                 printf("Connection established.\n");
912                         break;
913                 }
914
915                 if (!interactive) {
916                         perror("Failed to connect to hostapd - "
917                                "wpa_ctrl_open");
918                         return -1;
919                 }
920
921                 if (!warning_displayed) {
922                         printf("Could not connect to hostapd - re-trying\n");
923                         warning_displayed = 1;
924                 }
925                 os_sleep(1, 0);
926                 continue;
927         }
928
929         signal(SIGINT, hostapd_cli_terminate);
930         signal(SIGTERM, hostapd_cli_terminate);
931         signal(SIGALRM, hostapd_cli_alarm);
932
933         if (interactive || action_file) {
934                 if (wpa_ctrl_attach(ctrl_conn) == 0) {
935                         hostapd_cli_attached = 1;
936                 } else {
937                         printf("Warning: Failed to attach to hostapd.\n");
938                         if (action_file)
939                                 return -1;
940                 }
941         }
942
943         if (daemonize && os_daemonize(pid_file))
944                 return -1;
945
946         if (interactive)
947                 hostapd_cli_interactive();
948         else if (action_file)
949                 hostapd_cli_action(ctrl_conn);
950         else
951                 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
952
953         os_free(ctrl_ifname);
954         hostapd_cli_cleanup();
955         return 0;
956 }