9c3e65b2a690848fd54148c70608bcde4a8717d0
[mech_eap.git] / hostapd / hostapd_cli.c
1 /*
2  * hostapd - command line interface for hostapd daemon
3  * Copyright (c) 2004-2009, 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 "wpa_ctrl.h"
19 #include "common.h"
20 #include "version.h"
21
22
23 static const char *hostapd_cli_version =
24 "hostapd_cli v" VERSION_STR "\n"
25 "Copyright (c) 2004-2009, 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 #ifdef CONFIG_IEEE80211W
87 "   sa_query <addr>      send SA Query to a station\n"
88 #endif /* CONFIG_IEEE80211W */
89 #ifdef CONFIG_WPS
90 "   wps_pin <uuid> <pin> add WPS Enrollee PIN (Device Password)\n"
91 "   wps_pbc              indicate button pushed to initiate PBC\n"
92 #endif /* CONFIG_WPS */
93 "   help                 show this usage help\n"
94 "   interface [ifname]   show interfaces/select interface\n"
95 "   level <debug level>  change debug level\n"
96 "   license              show full hostapd_cli license\n"
97 "   quit                 exit hostapd_cli\n";
98
99 static struct wpa_ctrl *ctrl_conn;
100 static int hostapd_cli_quit = 0;
101 static int hostapd_cli_attached = 0;
102 static const char *ctrl_iface_dir = "/var/run/hostapd";
103 static char *ctrl_ifname = NULL;
104
105
106 static void usage(void)
107 {
108         fprintf(stderr, "%s\n", hostapd_cli_version);
109         fprintf(stderr, 
110                 "\n"    
111                 "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hv] "
112                 "[command..]\n"
113                 "\n"
114                 "Options:\n"
115                 "   -h           help (show this usage text)\n"
116                 "   -v           shown version information\n"
117                 "   -p<path>     path to find control sockets (default: "
118                 "/var/run/hostapd)\n"
119                 "   -i<ifname>   Interface to listen on (default: first "
120                 "interface found in the\n"
121                 "                socket path)\n\n"
122                 "%s",
123                 commands_help);
124 }
125
126
127 static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
128 {
129         char *cfile;
130         int flen;
131
132         if (ifname == NULL)
133                 return NULL;
134
135         flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
136         cfile = malloc(flen);
137         if (cfile == NULL)
138                 return NULL;
139         snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
140
141         ctrl_conn = wpa_ctrl_open(cfile);
142         free(cfile);
143         return ctrl_conn;
144 }
145
146
147 static void hostapd_cli_close_connection(void)
148 {
149         if (ctrl_conn == NULL)
150                 return;
151
152         if (hostapd_cli_attached) {
153                 wpa_ctrl_detach(ctrl_conn);
154                 hostapd_cli_attached = 0;
155         }
156         wpa_ctrl_close(ctrl_conn);
157         ctrl_conn = NULL;
158 }
159
160
161 static void hostapd_cli_msg_cb(char *msg, size_t len)
162 {
163         printf("%s\n", msg);
164 }
165
166
167 static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
168 {
169         char buf[4096];
170         size_t len;
171         int ret;
172
173         if (ctrl_conn == NULL) {
174                 printf("Not connected to hostapd - command dropped.\n");
175                 return -1;
176         }
177         len = sizeof(buf) - 1;
178         ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
179                                hostapd_cli_msg_cb);
180         if (ret == -2) {
181                 printf("'%s' command timed out.\n", cmd);
182                 return -2;
183         } else if (ret < 0) {
184                 printf("'%s' command failed.\n", cmd);
185                 return -1;
186         }
187         if (print) {
188                 buf[len] = '\0';
189                 printf("%s", buf);
190         }
191         return 0;
192 }
193
194
195 static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
196 {
197         return _wpa_ctrl_command(ctrl, cmd, 1);
198 }
199
200
201 static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
202 {
203         return wpa_ctrl_command(ctrl, "PING");
204 }
205
206
207 static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
208 {
209         return wpa_ctrl_command(ctrl, "MIB");
210 }
211
212
213 static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
214 {
215         char buf[64];
216         if (argc != 1) {
217                 printf("Invalid 'sta' command - exactly one argument, STA "
218                        "address, is required.\n");
219                 return -1;
220         }
221         snprintf(buf, sizeof(buf), "STA %s", argv[0]);
222         return wpa_ctrl_command(ctrl, buf);
223 }
224
225
226 static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
227                                    char *argv[])
228 {
229         char buf[64];
230         if (argc != 1) {
231                 printf("Invalid 'new_sta' command - exactly one argument, STA "
232                        "address, is required.\n");
233                 return -1;
234         }
235         snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
236         return wpa_ctrl_command(ctrl, buf);
237 }
238
239
240 #ifdef CONFIG_IEEE80211W
241 static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
242                                     char *argv[])
243 {
244         char buf[64];
245         if (argc != 1) {
246                 printf("Invalid 'sa_query' command - exactly one argument, "
247                        "STA address, is required.\n");
248                 return -1;
249         }
250         snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
251         return wpa_ctrl_command(ctrl, buf);
252 }
253 #endif /* CONFIG_IEEE80211W */
254
255
256 #ifdef CONFIG_WPS
257 static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
258                                    char *argv[])
259 {
260         char buf[64];
261         if (argc != 2) {
262                 printf("Invalid 'wps_pin' command - exactly two arguments, "
263                        "UUID and PIN, are required.\n");
264                 return -1;
265         }
266         snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
267         return wpa_ctrl_command(ctrl, buf);
268 }
269
270
271 static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
272                                    char *argv[])
273 {
274         return wpa_ctrl_command(ctrl, "WPS_PBC");
275 }
276 #endif /* CONFIG_WPS */
277
278
279 static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
280                                 char *addr, size_t addr_len)
281 {
282         char buf[4096], *pos;
283         size_t len;
284         int ret;
285
286         if (ctrl_conn == NULL) {
287                 printf("Not connected to hostapd - command dropped.\n");
288                 return -1;
289         }
290         len = sizeof(buf) - 1;
291         ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
292                                hostapd_cli_msg_cb);
293         if (ret == -2) {
294                 printf("'%s' command timed out.\n", cmd);
295                 return -2;
296         } else if (ret < 0) {
297                 printf("'%s' command failed.\n", cmd);
298                 return -1;
299         }
300
301         buf[len] = '\0';
302         if (memcmp(buf, "FAIL", 4) == 0)
303                 return -1;
304         printf("%s", buf);
305
306         pos = buf;
307         while (*pos != '\0' && *pos != '\n')
308                 pos++;
309         *pos = '\0';
310         os_strlcpy(addr, buf, addr_len);
311         return 0;
312 }
313
314
315 static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
316                                    char *argv[])
317 {
318         char addr[32], cmd[64];
319
320         if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
321                 return 0;
322         do {
323                 snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
324         } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
325
326         return -1;
327 }
328
329
330 static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
331 {
332         printf("%s", commands_help);
333         return 0;
334 }
335
336
337 static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
338                                    char *argv[])
339 {
340         printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
341         return 0;
342 }
343
344
345 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
346 {
347         hostapd_cli_quit = 1;
348         return 0;
349 }
350
351
352 static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
353 {
354         char cmd[256];
355         if (argc != 1) {
356                 printf("Invalid LEVEL command: needs one argument (debug "
357                        "level)\n");
358                 return 0;
359         }
360         snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
361         return wpa_ctrl_command(ctrl, cmd);
362 }
363
364
365 static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
366 {
367         struct dirent *dent;
368         DIR *dir;
369
370         dir = opendir(ctrl_iface_dir);
371         if (dir == NULL) {
372                 printf("Control interface directory '%s' could not be "
373                        "openned.\n", ctrl_iface_dir);
374                 return;
375         }
376
377         printf("Available interfaces:\n");
378         while ((dent = readdir(dir))) {
379                 if (strcmp(dent->d_name, ".") == 0 ||
380                     strcmp(dent->d_name, "..") == 0)
381                         continue;
382                 printf("%s\n", dent->d_name);
383         }
384         closedir(dir);
385 }
386
387
388 static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
389                                      char *argv[])
390 {
391         if (argc < 1) {
392                 hostapd_cli_list_interfaces(ctrl);
393                 return 0;
394         }
395
396         hostapd_cli_close_connection();
397         free(ctrl_ifname);
398         ctrl_ifname = strdup(argv[0]);
399
400         if (hostapd_cli_open_connection(ctrl_ifname)) {
401                 printf("Connected to interface '%s.\n", ctrl_ifname);
402                 if (wpa_ctrl_attach(ctrl_conn) == 0) {
403                         hostapd_cli_attached = 1;
404                 } else {
405                         printf("Warning: Failed to attach to "
406                                "hostapd.\n");
407                 }
408         } else {
409                 printf("Could not connect to interface '%s' - re-trying\n",
410                         ctrl_ifname);
411         }
412         return 0;
413 }
414
415
416 struct hostapd_cli_cmd {
417         const char *cmd;
418         int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
419 };
420
421 static struct hostapd_cli_cmd hostapd_cli_commands[] = {
422         { "ping", hostapd_cli_cmd_ping },
423         { "mib", hostapd_cli_cmd_mib },
424         { "sta", hostapd_cli_cmd_sta },
425         { "all_sta", hostapd_cli_cmd_all_sta },
426         { "new_sta", hostapd_cli_cmd_new_sta },
427 #ifdef CONFIG_IEEE80211W
428         { "sa_query", hostapd_cli_cmd_sa_query },
429 #endif /* CONFIG_IEEE80211W */
430 #ifdef CONFIG_WPS
431         { "wps_pin", hostapd_cli_cmd_wps_pin },
432         { "wps_pbc", hostapd_cli_cmd_wps_pbc },
433 #endif /* CONFIG_WPS */
434         { "help", hostapd_cli_cmd_help },
435         { "interface", hostapd_cli_cmd_interface },
436         { "level", hostapd_cli_cmd_level },
437         { "license", hostapd_cli_cmd_license },
438         { "quit", hostapd_cli_cmd_quit },
439         { NULL, NULL }
440 };
441
442
443 static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
444 {
445         struct hostapd_cli_cmd *cmd, *match = NULL;
446         int count;
447
448         count = 0;
449         cmd = hostapd_cli_commands;
450         while (cmd->cmd) {
451                 if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
452                         match = cmd;
453                         count++;
454                 }
455                 cmd++;
456         }
457
458         if (count > 1) {
459                 printf("Ambiguous command '%s'; possible commands:", argv[0]);
460                 cmd = hostapd_cli_commands;
461                 while (cmd->cmd) {
462                         if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
463                             0) {
464                                 printf(" %s", cmd->cmd);
465                         }
466                         cmd++;
467                 }
468                 printf("\n");
469         } else if (count == 0) {
470                 printf("Unknown command '%s'\n", argv[0]);
471         } else {
472                 match->handler(ctrl, argc - 1, &argv[1]);
473         }
474 }
475
476
477 static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read)
478 {
479         int first = 1;
480         if (ctrl_conn == NULL)
481                 return;
482         while (wpa_ctrl_pending(ctrl)) {
483                 char buf[256];
484                 size_t len = sizeof(buf) - 1;
485                 if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
486                         buf[len] = '\0';
487                         if (in_read && first)
488                                 printf("\n");
489                         first = 0;
490                         printf("%s\n", buf);
491                 } else {
492                         printf("Could not read pending message.\n");
493                         break;
494                 }
495         }
496 }
497
498
499 static void hostapd_cli_interactive(void)
500 {
501         const int max_args = 10;
502         char cmd[256], *res, *argv[max_args], *pos;
503         int argc;
504
505         printf("\nInteractive mode\n\n");
506
507         do {
508                 hostapd_cli_recv_pending(ctrl_conn, 0);
509                 printf("> ");
510                 alarm(1);
511                 res = fgets(cmd, sizeof(cmd), stdin);
512                 alarm(0);
513                 if (res == NULL)
514                         break;
515                 pos = cmd;
516                 while (*pos != '\0') {
517                         if (*pos == '\n') {
518                                 *pos = '\0';
519                                 break;
520                         }
521                         pos++;
522                 }
523                 argc = 0;
524                 pos = cmd;
525                 for (;;) {
526                         while (*pos == ' ')
527                                 pos++;
528                         if (*pos == '\0')
529                                 break;
530                         argv[argc] = pos;
531                         argc++;
532                         if (argc == max_args)
533                                 break;
534                         while (*pos != '\0' && *pos != ' ')
535                                 pos++;
536                         if (*pos == ' ')
537                                 *pos++ = '\0';
538                 }
539                 if (argc)
540                         wpa_request(ctrl_conn, argc, argv);
541         } while (!hostapd_cli_quit);
542 }
543
544
545 static void hostapd_cli_terminate(int sig)
546 {
547         hostapd_cli_close_connection();
548         exit(0);
549 }
550
551
552 static void hostapd_cli_alarm(int sig)
553 {
554         if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
555                 printf("Connection to hostapd lost - trying to reconnect\n");
556                 hostapd_cli_close_connection();
557         }
558         if (!ctrl_conn) {
559                 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
560                 if (ctrl_conn) {
561                         printf("Connection to hostapd re-established\n");
562                         if (wpa_ctrl_attach(ctrl_conn) == 0) {
563                                 hostapd_cli_attached = 1;
564                         } else {
565                                 printf("Warning: Failed to attach to "
566                                        "hostapd.\n");
567                         }
568                 }
569         }
570         if (ctrl_conn)
571                 hostapd_cli_recv_pending(ctrl_conn, 1);
572         alarm(1);
573 }
574
575
576 int main(int argc, char *argv[])
577 {
578         int interactive;
579         int warning_displayed = 0;
580         int c;
581
582         for (;;) {
583                 c = getopt(argc, argv, "hi:p:v");
584                 if (c < 0)
585                         break;
586                 switch (c) {
587                 case 'h':
588                         usage();
589                         return 0;
590                 case 'v':
591                         printf("%s\n", hostapd_cli_version);
592                         return 0;
593                 case 'i':
594                         free(ctrl_ifname);
595                         ctrl_ifname = strdup(optarg);
596                         break;
597                 case 'p':
598                         ctrl_iface_dir = optarg;
599                         break;
600                 default:
601                         usage();
602                         return -1;
603                 }
604         }
605
606         interactive = argc == optind;
607
608         if (interactive) {
609                 printf("%s\n\n%s\n\n", hostapd_cli_version,
610                        hostapd_cli_license);
611         }
612
613         for (;;) {
614                 if (ctrl_ifname == NULL) {
615                         struct dirent *dent;
616                         DIR *dir = opendir(ctrl_iface_dir);
617                         if (dir) {
618                                 while ((dent = readdir(dir))) {
619                                         if (strcmp(dent->d_name, ".") == 0 ||
620                                             strcmp(dent->d_name, "..") == 0)
621                                                 continue;
622                                         printf("Selected interface '%s'\n",
623                                                dent->d_name);
624                                         ctrl_ifname = strdup(dent->d_name);
625                                         break;
626                                 }
627                                 closedir(dir);
628                         }
629                 }
630                 ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
631                 if (ctrl_conn) {
632                         if (warning_displayed)
633                                 printf("Connection established.\n");
634                         break;
635                 }
636
637                 if (!interactive) {
638                         perror("Failed to connect to hostapd - "
639                                "wpa_ctrl_open");
640                         return -1;
641                 }
642
643                 if (!warning_displayed) {
644                         printf("Could not connect to hostapd - re-trying\n");
645                         warning_displayed = 1;
646                 }
647                 sleep(1);
648                 continue;
649         }
650
651         signal(SIGINT, hostapd_cli_terminate);
652         signal(SIGTERM, hostapd_cli_terminate);
653         signal(SIGALRM, hostapd_cli_alarm);
654
655         if (interactive) {
656                 if (wpa_ctrl_attach(ctrl_conn) == 0) {
657                         hostapd_cli_attached = 1;
658                 } else {
659                         printf("Warning: Failed to attach to hostapd.\n");
660                 }
661                 hostapd_cli_interactive();
662         } else
663                 wpa_request(ctrl_conn, argc - optind, &argv[optind]);
664
665         free(ctrl_ifname);
666         hostapd_cli_close_connection();
667         return 0;
668 }