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