1/* 2 * hostapd / main() 3 * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9#include "utils/includes.h" 10#ifndef CONFIG_NATIVE_WINDOWS 11#include <syslog.h> 12#include <grp.h> 13#endif /* CONFIG_NATIVE_WINDOWS */ 14 15#include "utils/common.h" 16#include "utils/eloop.h" 17#include "utils/uuid.h" 18#include "crypto/random.h" 19#include "crypto/tls.h" 20#include "common/version.h" 21#include "drivers/driver.h" 22#include "eap_server/eap.h" 23#include "eap_server/tncs.h" 24#include "ap/hostapd.h" 25#include "ap/ap_config.h" 26#include "ap/ap_drv_ops.h" 27#include "config_file.h" 28#include "eap_register.h" 29#include "ctrl_iface.h" 30 31struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers, 32 struct wpa_driver_capa *capa); 33 34struct hapd_global { 35 void **drv_priv; 36 size_t drv_count; 37}; 38 39static struct hapd_global global; 40 41 42#ifndef CONFIG_NO_HOSTAPD_LOGGER 43static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, 44 int level, const char *txt, size_t len) 45{ 46 struct hostapd_data *hapd = ctx; 47 char *format, *module_str; 48 int maxlen; 49 int conf_syslog_level, conf_stdout_level; 50 unsigned int conf_syslog, conf_stdout; 51 52 maxlen = len + 100; 53 format = os_malloc(maxlen); 54 if (!format) 55 return; 56 57 if (hapd && hapd->conf) { 58 conf_syslog_level = hapd->conf->logger_syslog_level; 59 conf_stdout_level = hapd->conf->logger_stdout_level; 60 conf_syslog = hapd->conf->logger_syslog; 61 conf_stdout = hapd->conf->logger_stdout; 62 } else { 63 conf_syslog_level = conf_stdout_level = 0; 64 conf_syslog = conf_stdout = (unsigned int) -1; 65 } 66 67 switch (module) { 68 case HOSTAPD_MODULE_IEEE80211: 69 module_str = "IEEE 802.11"; 70 break; 71 case HOSTAPD_MODULE_IEEE8021X: 72 module_str = "IEEE 802.1X"; 73 break; 74 case HOSTAPD_MODULE_RADIUS: 75 module_str = "RADIUS"; 76 break; 77 case HOSTAPD_MODULE_WPA: 78 module_str = "WPA"; 79 break; 80 case HOSTAPD_MODULE_DRIVER: 81 module_str = "DRIVER"; 82 break; 83 case HOSTAPD_MODULE_IAPP: 84 module_str = "IAPP"; 85 break; 86 case HOSTAPD_MODULE_MLME: 87 module_str = "MLME"; 88 break; 89 default: 90 module_str = NULL; 91 break; 92 } 93 94 if (hapd && hapd->conf && addr) 95 os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", 96 hapd->conf->iface, MAC2STR(addr), 97 module_str ? " " : "", module_str ? module_str : "", 98 txt); 99 else if (hapd && hapd->conf) 100 os_snprintf(format, maxlen, "%s:%s%s %s", 101 hapd->conf->iface, module_str ? " " : "", 102 module_str ? module_str : "", txt); 103 else if (addr) 104 os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", 105 MAC2STR(addr), module_str ? " " : "", 106 module_str ? module_str : "", txt); 107 else 108 os_snprintf(format, maxlen, "%s%s%s", 109 module_str ? module_str : "", 110 module_str ? ": " : "", txt); 111 112 if ((conf_stdout & module) && level >= conf_stdout_level) { 113 wpa_debug_print_timestamp(); 114 wpa_printf(MSG_INFO, "%s", format); 115 } 116 117#ifndef CONFIG_NATIVE_WINDOWS 118 if ((conf_syslog & module) && level >= conf_syslog_level) { 119 int priority; 120 switch (level) { 121 case HOSTAPD_LEVEL_DEBUG_VERBOSE: 122 case HOSTAPD_LEVEL_DEBUG: 123 priority = LOG_DEBUG; 124 break; 125 case HOSTAPD_LEVEL_INFO: 126 priority = LOG_INFO; 127 break; 128 case HOSTAPD_LEVEL_NOTICE: 129 priority = LOG_NOTICE; 130 break; 131 case HOSTAPD_LEVEL_WARNING: 132 priority = LOG_WARNING; 133 break; 134 default: 135 priority = LOG_INFO; 136 break; 137 } 138 syslog(priority, "%s", format); 139 } 140#endif /* CONFIG_NATIVE_WINDOWS */ 141 142 os_free(format); 143} 144#endif /* CONFIG_NO_HOSTAPD_LOGGER */ 145 146 147/** 148 * hostapd_driver_init - Preparate driver interface 149 */ 150static int hostapd_driver_init(struct hostapd_iface *iface) 151{ 152 struct wpa_init_params params; 153 size_t i; 154 struct hostapd_data *hapd = iface->bss[0]; 155 struct hostapd_bss_config *conf = hapd->conf; 156 u8 *b = conf->bssid; 157 struct wpa_driver_capa capa; 158 159 if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) { 160 wpa_printf(MSG_ERROR, "No hostapd driver wrapper available"); 161 return -1; 162 } 163 164 /* Initialize the driver interface */ 165 if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5])) 166 b = NULL; 167 168 os_memset(¶ms, 0, sizeof(params)); 169 for (i = 0; wpa_drivers[i]; i++) { 170 if (wpa_drivers[i] != hapd->driver) 171 continue; 172 173 if (global.drv_priv[i] == NULL && 174 wpa_drivers[i]->global_init) { 175 global.drv_priv[i] = wpa_drivers[i]->global_init(); 176 if (global.drv_priv[i] == NULL) { 177 wpa_printf(MSG_ERROR, "Failed to initialize " 178 "driver '%s'", 179 wpa_drivers[i]->name); 180 return -1; 181 } 182 } 183 184 params.global_priv = global.drv_priv[i]; 185 break; 186 } 187 params.bssid = b; 188 params.ifname = hapd->conf->iface; 189 params.ssid = hapd->conf->ssid.ssid; 190 params.ssid_len = hapd->conf->ssid.ssid_len; 191 params.test_socket = hapd->conf->test_socket; 192 params.use_pae_group_addr = hapd->conf->use_pae_group_addr; 193 194 params.num_bridge = hapd->iface->num_bss; 195 params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *)); 196 if (params.bridge == NULL) 197 return -1; 198 for (i = 0; i < hapd->iface->num_bss; i++) { 199 struct hostapd_data *bss = hapd->iface->bss[i]; 200 if (bss->conf->bridge[0]) 201 params.bridge[i] = bss->conf->bridge; 202 } 203 204 params.own_addr = hapd->own_addr; 205 206 hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms); 207 os_free(params.bridge); 208 if (hapd->drv_priv == NULL) { 209 wpa_printf(MSG_ERROR, "%s driver initialization failed.", 210 hapd->driver->name); 211 hapd->driver = NULL; 212 return -1; 213 } 214 215 if (hapd->driver->get_capa && 216 hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) { 217 struct wowlan_triggers *triggs; 218 219 iface->drv_flags = capa.flags; 220 iface->probe_resp_offloads = capa.probe_resp_offloads; 221 iface->extended_capa = capa.extended_capa; 222 iface->extended_capa_mask = capa.extended_capa_mask; 223 iface->extended_capa_len = capa.extended_capa_len; 224 iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; 225 226 triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa); 227 if (triggs && hapd->driver->set_wowlan) { 228 if (hapd->driver->set_wowlan(hapd->drv_priv, triggs)) 229 wpa_printf(MSG_ERROR, "set_wowlan failed"); 230 } 231 os_free(triggs); 232 } 233 234 return 0; 235} 236 237 238/** 239 * hostapd_interface_init - Read configuration file and init BSS data 240 * 241 * This function is used to parse configuration file for a full interface (one 242 * or more BSSes sharing the same radio) and allocate memory for the BSS 243 * interfaces. No actiual driver operations are started. 244 */ 245static struct hostapd_iface * 246hostapd_interface_init(struct hapd_interfaces *interfaces, 247 const char *config_fname, int debug) 248{ 249 struct hostapd_iface *iface; 250 int k; 251 252 wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname); 253 iface = hostapd_init(interfaces, config_fname); 254 if (!iface) 255 return NULL; 256 iface->interfaces = interfaces; 257 258 for (k = 0; k < debug; k++) { 259 if (iface->bss[0]->conf->logger_stdout_level > 0) 260 iface->bss[0]->conf->logger_stdout_level--; 261 } 262 263 if (iface->conf->bss[0]->iface[0] == '\0' && 264 !hostapd_drv_none(iface->bss[0])) { 265 wpa_printf(MSG_ERROR, "Interface name not specified in %s", 266 config_fname); 267 hostapd_interface_deinit_free(iface); 268 return NULL; 269 } 270 271 return iface; 272} 273 274 275/** 276 * handle_term - SIGINT and SIGTERM handler to terminate hostapd process 277 */ 278static void handle_term(int sig, void *signal_ctx) 279{ 280 wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig); 281 eloop_terminate(); 282} 283 284 285#ifndef CONFIG_NATIVE_WINDOWS 286 287static int handle_reload_iface(struct hostapd_iface *iface, void *ctx) 288{ 289 if (hostapd_reload_config(iface) < 0) { 290 wpa_printf(MSG_WARNING, "Failed to read new configuration " 291 "file - continuing with old."); 292 } 293 return 0; 294} 295 296 297/** 298 * handle_reload - SIGHUP handler to reload configuration 299 */ 300static void handle_reload(int sig, void *signal_ctx) 301{ 302 struct hapd_interfaces *interfaces = signal_ctx; 303 wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration", 304 sig); 305 hostapd_for_each_interface(interfaces, handle_reload_iface, NULL); 306} 307 308 309static void handle_dump_state(int sig, void *signal_ctx) 310{ 311 /* Not used anymore - ignore signal */ 312} 313#endif /* CONFIG_NATIVE_WINDOWS */ 314 315 316static int hostapd_global_init(struct hapd_interfaces *interfaces, 317 const char *entropy_file) 318{ 319 int i; 320 321 os_memset(&global, 0, sizeof(global)); 322 323 hostapd_logger_register_cb(hostapd_logger_cb); 324 325 if (eap_server_register_methods()) { 326 wpa_printf(MSG_ERROR, "Failed to register EAP methods"); 327 return -1; 328 } 329 330 if (eloop_init()) { 331 wpa_printf(MSG_ERROR, "Failed to initialize event loop"); 332 return -1; 333 } 334 335 random_init(entropy_file); 336 337#ifndef CONFIG_NATIVE_WINDOWS 338 eloop_register_signal(SIGHUP, handle_reload, interfaces); 339 eloop_register_signal(SIGUSR1, handle_dump_state, interfaces); 340#endif /* CONFIG_NATIVE_WINDOWS */ 341 eloop_register_signal_terminate(handle_term, interfaces); 342 343#ifndef CONFIG_NATIVE_WINDOWS 344 openlog("hostapd", 0, LOG_DAEMON); 345#endif /* CONFIG_NATIVE_WINDOWS */ 346 347 for (i = 0; wpa_drivers[i]; i++) 348 global.drv_count++; 349 if (global.drv_count == 0) { 350 wpa_printf(MSG_ERROR, "No drivers enabled"); 351 return -1; 352 } 353 global.drv_priv = os_calloc(global.drv_count, sizeof(void *)); 354 if (global.drv_priv == NULL) 355 return -1; 356 357 return 0; 358} 359 360 361static void hostapd_global_deinit(const char *pid_file) 362{ 363 int i; 364 365 for (i = 0; wpa_drivers[i] && global.drv_priv; i++) { 366 if (!global.drv_priv[i]) 367 continue; 368 wpa_drivers[i]->global_deinit(global.drv_priv[i]); 369 } 370 os_free(global.drv_priv); 371 global.drv_priv = NULL; 372 373#ifdef EAP_SERVER_TNC 374 tncs_global_deinit(); 375#endif /* EAP_SERVER_TNC */ 376 377 random_deinit(); 378 379 eloop_destroy(); 380 381#ifndef CONFIG_NATIVE_WINDOWS 382 closelog(); 383#endif /* CONFIG_NATIVE_WINDOWS */ 384 385 eap_server_unregister_methods(); 386 387 os_daemonize_terminate(pid_file); 388} 389 390 391static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize, 392 const char *pid_file) 393{ 394#ifdef EAP_SERVER_TNC 395 int tnc = 0; 396 size_t i, k; 397 398 for (i = 0; !tnc && i < ifaces->count; i++) { 399 for (k = 0; k < ifaces->iface[i]->num_bss; k++) { 400 if (ifaces->iface[i]->bss[0]->conf->tnc) { 401 tnc++; 402 break; 403 } 404 } 405 } 406 407 if (tnc && tncs_global_init() < 0) { 408 wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); 409 return -1; 410 } 411#endif /* EAP_SERVER_TNC */ 412 413 if (daemonize && os_daemonize(pid_file)) { 414 perror("daemon"); 415 return -1; 416 } 417 418 eloop_run(); 419 420 return 0; 421} 422 423 424static void show_version(void) 425{ 426 fprintf(stderr, 427 "hostapd v" VERSION_STR "\n" 428 "User space daemon for IEEE 802.11 AP management,\n" 429 "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" 430 "Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> " 431 "and contributors\n"); 432} 433 434 435static void usage(void) 436{ 437 show_version(); 438 fprintf(stderr, 439 "\n" 440 "usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] " 441 "\\\n" 442 " [-g <global ctrl_iface>] [-G <group>] \\\n" 443 " <configuration file(s)>\n" 444 "\n" 445 "options:\n" 446 " -h show this usage\n" 447 " -d show more debug messages (-dd for even more)\n" 448 " -B run daemon in the background\n" 449 " -e entropy file\n" 450 " -g global control interface path\n" 451 " -G group for control interfaces\n" 452 " -P PID file\n" 453 " -K include key data in debug messages\n" 454#ifdef CONFIG_DEBUG_FILE 455 " -f log output to debug file instead of stdout\n" 456#endif /* CONFIG_DEBUG_FILE */ 457#ifdef CONFIG_DEBUG_LINUX_TRACING 458 " -T = record to Linux tracing in addition to logging\n" 459 " (records all messages regardless of debug verbosity)\n" 460#endif /* CONFIG_DEBUG_LINUX_TRACING */ 461 " -t include timestamps in some debug messages\n" 462 " -v show hostapd version\n"); 463 464 exit(1); 465} 466 467 468static const char * hostapd_msg_ifname_cb(void *ctx) 469{ 470 struct hostapd_data *hapd = ctx; 471 if (hapd && hapd->iconf && hapd->iconf->bss && 472 hapd->iconf->num_bss > 0 && hapd->iconf->bss[0]) 473 return hapd->iconf->bss[0]->iface; 474 return NULL; 475} 476 477 478static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, 479 const char *path) 480{ 481 char *pos; 482 os_free(interfaces->global_iface_path); 483 interfaces->global_iface_path = os_strdup(path); 484 if (interfaces->global_iface_path == NULL) 485 return -1; 486 pos = os_strrchr(interfaces->global_iface_path, '/'); 487 if (pos == NULL) { 488 wpa_printf(MSG_ERROR, "No '/' in the global control interface " 489 "file"); 490 os_free(interfaces->global_iface_path); 491 interfaces->global_iface_path = NULL; 492 return -1; 493 } 494 495 *pos = '\0'; 496 interfaces->global_iface_name = pos + 1; 497 498 return 0; 499} 500 501 502static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces, 503 const char *group) 504{ 505#ifndef CONFIG_NATIVE_WINDOWS 506 struct group *grp; 507 grp = getgrnam(group); 508 if (grp == NULL) { 509 wpa_printf(MSG_ERROR, "Unknown group '%s'", group); 510 return -1; 511 } 512 interfaces->ctrl_iface_group = grp->gr_gid; 513#endif /* CONFIG_NATIVE_WINDOWS */ 514 return 0; 515} 516 517 518#ifdef CONFIG_WPS 519static int gen_uuid(const char *txt_addr) 520{ 521 u8 addr[ETH_ALEN]; 522 u8 uuid[UUID_LEN]; 523 char buf[100]; 524 525 if (hwaddr_aton(txt_addr, addr) < 0) 526 return -1; 527 528 uuid_gen_mac_addr(addr, uuid); 529 if (uuid_bin2str(uuid, buf, sizeof(buf)) < 0) 530 return -1; 531 532 printf("%s\n", buf); 533 534 return 0; 535} 536#endif /* CONFIG_WPS */ 537 538 539int main(int argc, char *argv[]) 540{ 541 struct hapd_interfaces interfaces; 542 int ret = 1; 543 size_t i, j; 544 int c, debug = 0, daemonize = 0; 545 char *pid_file = NULL; 546 const char *log_file = NULL; 547 const char *entropy_file = NULL; 548 char **bss_config = NULL, **tmp_bss; 549 size_t num_bss_configs = 0; 550#ifdef CONFIG_DEBUG_LINUX_TRACING 551 int enable_trace_dbg = 0; 552#endif /* CONFIG_DEBUG_LINUX_TRACING */ 553 554 if (os_program_init()) 555 return -1; 556 557 os_memset(&interfaces, 0, sizeof(interfaces)); 558 interfaces.reload_config = hostapd_reload_config; 559 interfaces.config_read_cb = hostapd_config_read; 560 interfaces.for_each_interface = hostapd_for_each_interface; 561 interfaces.ctrl_iface_init = hostapd_ctrl_iface_init; 562 interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit; 563 interfaces.driver_init = hostapd_driver_init; 564 interfaces.global_iface_path = NULL; 565 interfaces.global_iface_name = NULL; 566 interfaces.global_ctrl_sock = -1; 567 568 for (;;) { 569 c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:"); 570 if (c < 0) 571 break; 572 switch (c) { 573 case 'h': 574 usage(); 575 break; 576 case 'd': 577 debug++; 578 if (wpa_debug_level > 0) 579 wpa_debug_level--; 580 break; 581 case 'B': 582 daemonize++; 583 break; 584 case 'e': 585 entropy_file = optarg; 586 break; 587 case 'f': 588 log_file = optarg; 589 break; 590 case 'K': 591 wpa_debug_show_keys++; 592 break; 593 case 'P': 594 os_free(pid_file); 595 pid_file = os_rel2abs_path(optarg); 596 break; 597 case 't': 598 wpa_debug_timestamp++; 599 break; 600#ifdef CONFIG_DEBUG_LINUX_TRACING 601 case 'T': 602 enable_trace_dbg = 1; 603 break; 604#endif /* CONFIG_DEBUG_LINUX_TRACING */ 605 case 'v': 606 show_version(); 607 exit(1); 608 break; 609 case 'g': 610 if (hostapd_get_global_ctrl_iface(&interfaces, optarg)) 611 return -1; 612 break; 613 case 'G': 614 if (hostapd_get_ctrl_iface_group(&interfaces, optarg)) 615 return -1; 616 break; 617 case 'b': 618 tmp_bss = os_realloc_array(bss_config, 619 num_bss_configs + 1, 620 sizeof(char *)); 621 if (tmp_bss == NULL) 622 goto out; 623 bss_config = tmp_bss; 624 bss_config[num_bss_configs++] = optarg; 625 break; 626#ifdef CONFIG_WPS 627 case 'u': 628 return gen_uuid(optarg); 629#endif /* CONFIG_WPS */ 630 default: 631 usage(); 632 break; 633 } 634 } 635 636 if (optind == argc && interfaces.global_iface_path == NULL && 637 num_bss_configs == 0) 638 usage(); 639 640 wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb); 641 642 if (log_file) 643 wpa_debug_open_file(log_file); 644#ifdef CONFIG_DEBUG_LINUX_TRACING 645 if (enable_trace_dbg) { 646 int tret = wpa_debug_open_linux_tracing(); 647 if (tret) { 648 wpa_printf(MSG_ERROR, "Failed to enable trace logging"); 649 return -1; 650 } 651 } 652#endif /* CONFIG_DEBUG_LINUX_TRACING */ 653 654 interfaces.count = argc - optind; 655 if (interfaces.count || num_bss_configs) { 656 interfaces.iface = os_calloc(interfaces.count + num_bss_configs, 657 sizeof(struct hostapd_iface *)); 658 if (interfaces.iface == NULL) { 659 wpa_printf(MSG_ERROR, "malloc failed"); 660 return -1; 661 } 662 } 663 664 if (hostapd_global_init(&interfaces, entropy_file)) { 665 wpa_printf(MSG_ERROR, "Failed to initilize global context"); 666 return -1; 667 } 668 669 /* Allocate and parse configuration for full interface files */ 670 for (i = 0; i < interfaces.count; i++) { 671 interfaces.iface[i] = hostapd_interface_init(&interfaces, 672 argv[optind + i], 673 debug); 674 if (!interfaces.iface[i]) { 675 wpa_printf(MSG_ERROR, "Failed to initialize interface"); 676 goto out; 677 } 678 } 679 680 /* Allocate and parse configuration for per-BSS files */ 681 for (i = 0; i < num_bss_configs; i++) { 682 struct hostapd_iface *iface; 683 char *fname; 684 685 wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]); 686 fname = os_strchr(bss_config[i], ':'); 687 if (fname == NULL) { 688 wpa_printf(MSG_ERROR, 689 "Invalid BSS config identifier '%s'", 690 bss_config[i]); 691 goto out; 692 } 693 *fname++ = '\0'; 694 iface = hostapd_interface_init_bss(&interfaces, bss_config[i], 695 fname, debug); 696 if (iface == NULL) 697 goto out; 698 for (j = 0; j < interfaces.count; j++) { 699 if (interfaces.iface[j] == iface) 700 break; 701 } 702 if (j == interfaces.count) { 703 struct hostapd_iface **tmp; 704 tmp = os_realloc_array(interfaces.iface, 705 interfaces.count + 1, 706 sizeof(struct hostapd_iface *)); 707 if (tmp == NULL) { 708 hostapd_interface_deinit_free(iface); 709 goto out; 710 } 711 interfaces.iface = tmp; 712 interfaces.iface[interfaces.count++] = iface; 713 } 714 } 715 716 /* 717 * Enable configured interfaces. Depending on channel configuration, 718 * this may complete full initialization before returning or use a 719 * callback mechanism to complete setup in case of operations like HT 720 * co-ex scans, ACS, or DFS are needed to determine channel parameters. 721 * In such case, the interface will be enabled from eloop context within 722 * hostapd_global_run(). 723 */ 724 interfaces.terminate_on_error = interfaces.count; 725 for (i = 0; i < interfaces.count; i++) { 726 if (hostapd_driver_init(interfaces.iface[i]) || 727 hostapd_setup_interface(interfaces.iface[i])) 728 goto out; 729 } 730 731 hostapd_global_ctrl_iface_init(&interfaces); 732 733 if (hostapd_global_run(&interfaces, daemonize, pid_file)) { 734 wpa_printf(MSG_ERROR, "Failed to start eloop"); 735 goto out; 736 } 737 738 ret = 0; 739 740 out: 741 hostapd_global_ctrl_iface_deinit(&interfaces); 742 /* Deinitialize all interfaces */ 743 for (i = 0; i < interfaces.count; i++) { 744 if (!interfaces.iface[i]) 745 continue; 746 interfaces.iface[i]->driver_ap_teardown = 747 !!(interfaces.iface[i]->drv_flags & 748 WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); 749 hostapd_interface_deinit_free(interfaces.iface[i]); 750 } 751 os_free(interfaces.iface); 752 753 hostapd_global_deinit(pid_file); 754 os_free(pid_file); 755 756 if (log_file) 757 wpa_debug_close_file(); 758 wpa_debug_close_linux_tracing(); 759 760 os_free(bss_config); 761 762 os_program_deinit(); 763 764 return ret; 765} 766