]>
Commit | Line | Data |
---|---|---|
5c333467 JM |
1 | /* |
2 | * hostapd / main() | |
9e074973 | 3 | * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> |
5c333467 JM |
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 | ||
6226e38d | 15 | #include "utils/includes.h" |
5c333467 JM |
16 | #ifndef CONFIG_NATIVE_WINDOWS |
17 | #include <syslog.h> | |
18 | #endif /* CONFIG_NATIVE_WINDOWS */ | |
19 | ||
6226e38d JM |
20 | #include "utils/common.h" |
21 | #include "utils/eloop.h" | |
d47fa330 | 22 | #include "crypto/random.h" |
03da66bd | 23 | #include "crypto/tls.h" |
90973fb2 | 24 | #include "common/version.h" |
e5f2b59c | 25 | #include "drivers/driver.h" |
5c333467 JM |
26 | #include "eap_server/eap.h" |
27 | #include "eap_server/tncs.h" | |
1057d78e | 28 | #include "ap/hostapd.h" |
6226e38d | 29 | #include "ap/ap_config.h" |
41d719d6 | 30 | #include "config_file.h" |
a4f21109 JM |
31 | #include "eap_register.h" |
32 | #include "dump_state.h" | |
70db2ab3 | 33 | #include "ctrl_iface.h" |
5c333467 JM |
34 | |
35 | ||
36 | extern int wpa_debug_level; | |
37 | extern int wpa_debug_show_keys; | |
38 | extern int wpa_debug_timestamp; | |
39 | ||
40 | ||
41 | struct hapd_interfaces { | |
42 | size_t count; | |
43 | struct hostapd_iface **iface; | |
44 | }; | |
45 | ||
46 | ||
1b56c26c JM |
47 | static int hostapd_for_each_interface(struct hapd_interfaces *interfaces, |
48 | int (*cb)(struct hostapd_iface *iface, | |
49 | void *ctx), void *ctx) | |
5c333467 | 50 | { |
5c333467 JM |
51 | size_t i; |
52 | int ret; | |
53 | ||
54 | for (i = 0; i < interfaces->count; i++) { | |
55 | ret = cb(interfaces->iface[i], ctx); | |
56 | if (ret) | |
57 | return ret; | |
58 | } | |
59 | ||
60 | return 0; | |
61 | } | |
62 | ||
63 | ||
64 | #ifndef CONFIG_NO_HOSTAPD_LOGGER | |
65 | static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, | |
66 | int level, const char *txt, size_t len) | |
67 | { | |
68 | struct hostapd_data *hapd = ctx; | |
69 | char *format, *module_str; | |
70 | int maxlen; | |
71 | int conf_syslog_level, conf_stdout_level; | |
72 | unsigned int conf_syslog, conf_stdout; | |
73 | ||
74 | maxlen = len + 100; | |
75 | format = os_malloc(maxlen); | |
76 | if (!format) | |
77 | return; | |
78 | ||
79 | if (hapd && hapd->conf) { | |
80 | conf_syslog_level = hapd->conf->logger_syslog_level; | |
81 | conf_stdout_level = hapd->conf->logger_stdout_level; | |
82 | conf_syslog = hapd->conf->logger_syslog; | |
83 | conf_stdout = hapd->conf->logger_stdout; | |
84 | } else { | |
85 | conf_syslog_level = conf_stdout_level = 0; | |
86 | conf_syslog = conf_stdout = (unsigned int) -1; | |
87 | } | |
88 | ||
89 | switch (module) { | |
90 | case HOSTAPD_MODULE_IEEE80211: | |
91 | module_str = "IEEE 802.11"; | |
92 | break; | |
93 | case HOSTAPD_MODULE_IEEE8021X: | |
94 | module_str = "IEEE 802.1X"; | |
95 | break; | |
96 | case HOSTAPD_MODULE_RADIUS: | |
97 | module_str = "RADIUS"; | |
98 | break; | |
99 | case HOSTAPD_MODULE_WPA: | |
100 | module_str = "WPA"; | |
101 | break; | |
102 | case HOSTAPD_MODULE_DRIVER: | |
103 | module_str = "DRIVER"; | |
104 | break; | |
105 | case HOSTAPD_MODULE_IAPP: | |
106 | module_str = "IAPP"; | |
107 | break; | |
108 | case HOSTAPD_MODULE_MLME: | |
109 | module_str = "MLME"; | |
110 | break; | |
111 | default: | |
112 | module_str = NULL; | |
113 | break; | |
114 | } | |
115 | ||
116 | if (hapd && hapd->conf && addr) | |
117 | os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", | |
118 | hapd->conf->iface, MAC2STR(addr), | |
119 | module_str ? " " : "", module_str, txt); | |
120 | else if (hapd && hapd->conf) | |
121 | os_snprintf(format, maxlen, "%s:%s%s %s", | |
122 | hapd->conf->iface, module_str ? " " : "", | |
123 | module_str, txt); | |
124 | else if (addr) | |
125 | os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", | |
126 | MAC2STR(addr), module_str ? " " : "", | |
127 | module_str, txt); | |
128 | else | |
129 | os_snprintf(format, maxlen, "%s%s%s", | |
130 | module_str, module_str ? ": " : "", txt); | |
131 | ||
132 | if ((conf_stdout & module) && level >= conf_stdout_level) { | |
133 | wpa_debug_print_timestamp(); | |
134 | printf("%s\n", format); | |
135 | } | |
136 | ||
137 | #ifndef CONFIG_NATIVE_WINDOWS | |
138 | if ((conf_syslog & module) && level >= conf_syslog_level) { | |
139 | int priority; | |
140 | switch (level) { | |
141 | case HOSTAPD_LEVEL_DEBUG_VERBOSE: | |
142 | case HOSTAPD_LEVEL_DEBUG: | |
143 | priority = LOG_DEBUG; | |
144 | break; | |
145 | case HOSTAPD_LEVEL_INFO: | |
146 | priority = LOG_INFO; | |
147 | break; | |
148 | case HOSTAPD_LEVEL_NOTICE: | |
149 | priority = LOG_NOTICE; | |
150 | break; | |
151 | case HOSTAPD_LEVEL_WARNING: | |
152 | priority = LOG_WARNING; | |
153 | break; | |
154 | default: | |
155 | priority = LOG_INFO; | |
156 | break; | |
157 | } | |
158 | syslog(priority, "%s", format); | |
159 | } | |
160 | #endif /* CONFIG_NATIVE_WINDOWS */ | |
161 | ||
162 | os_free(format); | |
163 | } | |
164 | #endif /* CONFIG_NO_HOSTAPD_LOGGER */ | |
165 | ||
166 | ||
b6a7859d JM |
167 | /** |
168 | * hostapd_init - Allocate and initialize per-interface data | |
169 | * @config_file: Path to the configuration file | |
170 | * Returns: Pointer to the allocated interface data or %NULL on failure | |
171 | * | |
172 | * This function is used to allocate main data structures for per-interface | |
173 | * data. The allocated data buffer will be freed by calling | |
174 | * hostapd_cleanup_iface(). | |
175 | */ | |
176 | static struct hostapd_iface * hostapd_init(const char *config_file) | |
177 | { | |
178 | struct hostapd_iface *hapd_iface = NULL; | |
179 | struct hostapd_config *conf = NULL; | |
180 | struct hostapd_data *hapd; | |
181 | size_t i; | |
182 | ||
183 | hapd_iface = os_zalloc(sizeof(*hapd_iface)); | |
184 | if (hapd_iface == NULL) | |
185 | goto fail; | |
186 | ||
32da61d9 | 187 | hapd_iface->reload_config = hostapd_reload_config; |
41d719d6 | 188 | hapd_iface->config_read_cb = hostapd_config_read; |
b6a7859d JM |
189 | hapd_iface->config_fname = os_strdup(config_file); |
190 | if (hapd_iface->config_fname == NULL) | |
191 | goto fail; | |
70db2ab3 JM |
192 | hapd_iface->ctrl_iface_init = hostapd_ctrl_iface_init; |
193 | hapd_iface->ctrl_iface_deinit = hostapd_ctrl_iface_deinit; | |
1b56c26c | 194 | hapd_iface->for_each_interface = hostapd_for_each_interface; |
b6a7859d JM |
195 | |
196 | conf = hostapd_config_read(hapd_iface->config_fname); | |
197 | if (conf == NULL) | |
198 | goto fail; | |
199 | hapd_iface->conf = conf; | |
200 | ||
201 | hapd_iface->num_bss = conf->num_bss; | |
202 | hapd_iface->bss = os_zalloc(conf->num_bss * | |
203 | sizeof(struct hostapd_data *)); | |
204 | if (hapd_iface->bss == NULL) | |
205 | goto fail; | |
206 | ||
207 | for (i = 0; i < conf->num_bss; i++) { | |
208 | hapd = hapd_iface->bss[i] = | |
209 | hostapd_alloc_bss_data(hapd_iface, conf, | |
210 | &conf->bss[i]); | |
211 | if (hapd == NULL) | |
212 | goto fail; | |
bb437f28 | 213 | hapd->msg_ctx = hapd; |
b6a7859d JM |
214 | } |
215 | ||
216 | return hapd_iface; | |
217 | ||
218 | fail: | |
219 | if (conf) | |
220 | hostapd_config_free(conf); | |
221 | if (hapd_iface) { | |
b6a7859d JM |
222 | os_free(hapd_iface->config_fname); |
223 | os_free(hapd_iface->bss); | |
224 | os_free(hapd_iface); | |
225 | } | |
226 | return NULL; | |
227 | } | |
228 | ||
229 | ||
e5f2b59c JM |
230 | static int hostapd_driver_init(struct hostapd_iface *iface) |
231 | { | |
232 | struct wpa_init_params params; | |
233 | size_t i; | |
234 | struct hostapd_data *hapd = iface->bss[0]; | |
235 | struct hostapd_bss_config *conf = hapd->conf; | |
236 | u8 *b = conf->bssid; | |
2fee890a | 237 | struct wpa_driver_capa capa; |
e5f2b59c JM |
238 | |
239 | if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) { | |
240 | wpa_printf(MSG_ERROR, "No hostapd driver wrapper available"); | |
241 | return -1; | |
242 | } | |
243 | ||
244 | /* Initialize the driver interface */ | |
245 | if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5])) | |
246 | b = NULL; | |
247 | ||
248 | os_memset(¶ms, 0, sizeof(params)); | |
249 | params.bssid = b; | |
250 | params.ifname = hapd->conf->iface; | |
251 | params.ssid = (const u8 *) hapd->conf->ssid.ssid; | |
252 | params.ssid_len = hapd->conf->ssid.ssid_len; | |
253 | params.test_socket = hapd->conf->test_socket; | |
254 | params.use_pae_group_addr = hapd->conf->use_pae_group_addr; | |
255 | ||
256 | params.num_bridge = hapd->iface->num_bss; | |
257 | params.bridge = os_zalloc(hapd->iface->num_bss * sizeof(char *)); | |
258 | if (params.bridge == NULL) | |
259 | return -1; | |
260 | for (i = 0; i < hapd->iface->num_bss; i++) { | |
261 | struct hostapd_data *bss = hapd->iface->bss[i]; | |
262 | if (bss->conf->bridge[0]) | |
263 | params.bridge[i] = bss->conf->bridge; | |
264 | } | |
265 | ||
266 | params.own_addr = hapd->own_addr; | |
267 | ||
268 | hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms); | |
269 | os_free(params.bridge); | |
270 | if (hapd->drv_priv == NULL) { | |
271 | wpa_printf(MSG_ERROR, "%s driver initialization failed.", | |
272 | hapd->driver->name); | |
273 | hapd->driver = NULL; | |
274 | return -1; | |
275 | } | |
276 | ||
2fee890a JM |
277 | if (hapd->driver->get_capa && |
278 | hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) | |
279 | iface->drv_flags = capa.flags; | |
280 | ||
e5f2b59c JM |
281 | return 0; |
282 | } | |
283 | ||
284 | ||
f7c47833 JM |
285 | static void hostapd_interface_deinit_free(struct hostapd_iface *iface) |
286 | { | |
287 | const struct wpa_driver_ops *driver; | |
288 | void *drv_priv; | |
9078adfc JM |
289 | if (iface == NULL) |
290 | return; | |
f7c47833 JM |
291 | driver = iface->bss[0]->driver; |
292 | drv_priv = iface->bss[0]->drv_priv; | |
293 | hostapd_interface_deinit(iface); | |
294 | if (driver && driver->hapd_deinit) | |
295 | driver->hapd_deinit(drv_priv); | |
296 | hostapd_interface_free(iface); | |
297 | } | |
298 | ||
299 | ||
9969e5a4 JM |
300 | static struct hostapd_iface * |
301 | hostapd_interface_init(struct hapd_interfaces *interfaces, | |
302 | const char *config_fname, int debug) | |
5c333467 JM |
303 | { |
304 | struct hostapd_iface *iface; | |
305 | int k; | |
306 | ||
307 | wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname); | |
308 | iface = hostapd_init(config_fname); | |
309 | if (!iface) | |
310 | return NULL; | |
9969e5a4 | 311 | iface->interfaces = interfaces; |
5c333467 JM |
312 | |
313 | for (k = 0; k < debug; k++) { | |
314 | if (iface->bss[0]->conf->logger_stdout_level > 0) | |
315 | iface->bss[0]->conf->logger_stdout_level--; | |
316 | } | |
317 | ||
e5f2b59c JM |
318 | if (hostapd_driver_init(iface) || |
319 | hostapd_setup_interface(iface)) { | |
f7c47833 | 320 | hostapd_interface_deinit_free(iface); |
5c333467 JM |
321 | return NULL; |
322 | } | |
323 | ||
324 | return iface; | |
325 | } | |
326 | ||
327 | ||
328 | /** | |
329 | * handle_term - SIGINT and SIGTERM handler to terminate hostapd process | |
330 | */ | |
0456ea16 | 331 | static void handle_term(int sig, void *signal_ctx) |
5c333467 JM |
332 | { |
333 | wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig); | |
334 | eloop_terminate(); | |
335 | } | |
336 | ||
337 | ||
338 | #ifndef CONFIG_NATIVE_WINDOWS | |
a4f21109 JM |
339 | |
340 | static int handle_reload_iface(struct hostapd_iface *iface, void *ctx) | |
341 | { | |
342 | if (hostapd_reload_config(iface) < 0) { | |
343 | wpa_printf(MSG_WARNING, "Failed to read new configuration " | |
344 | "file - continuing with old."); | |
345 | } | |
346 | return 0; | |
347 | } | |
348 | ||
349 | ||
5c333467 JM |
350 | /** |
351 | * handle_reload - SIGHUP handler to reload configuration | |
352 | */ | |
0456ea16 | 353 | static void handle_reload(int sig, void *signal_ctx) |
5c333467 | 354 | { |
0456ea16 | 355 | struct hapd_interfaces *interfaces = signal_ctx; |
5c333467 JM |
356 | wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration", |
357 | sig); | |
9969e5a4 | 358 | hostapd_for_each_interface(interfaces, handle_reload_iface, NULL); |
5c333467 JM |
359 | } |
360 | ||
361 | ||
0456ea16 | 362 | static void handle_dump_state(int sig, void *signal_ctx) |
5c333467 JM |
363 | { |
364 | #ifdef HOSTAPD_DUMP_STATE | |
0456ea16 | 365 | struct hapd_interfaces *interfaces = signal_ctx; |
9969e5a4 | 366 | hostapd_for_each_interface(interfaces, handle_dump_state_iface, NULL); |
5c333467 JM |
367 | #endif /* HOSTAPD_DUMP_STATE */ |
368 | } | |
369 | #endif /* CONFIG_NATIVE_WINDOWS */ | |
370 | ||
371 | ||
372 | static int hostapd_global_init(struct hapd_interfaces *interfaces) | |
373 | { | |
374 | hostapd_logger_register_cb(hostapd_logger_cb); | |
375 | ||
376 | if (eap_server_register_methods()) { | |
377 | wpa_printf(MSG_ERROR, "Failed to register EAP methods"); | |
378 | return -1; | |
379 | } | |
380 | ||
0456ea16 | 381 | if (eloop_init()) { |
5c333467 JM |
382 | wpa_printf(MSG_ERROR, "Failed to initialize event loop"); |
383 | return -1; | |
384 | } | |
385 | ||
d47fa330 JM |
386 | random_init(); |
387 | ||
5c333467 | 388 | #ifndef CONFIG_NATIVE_WINDOWS |
0456ea16 JM |
389 | eloop_register_signal(SIGHUP, handle_reload, interfaces); |
390 | eloop_register_signal(SIGUSR1, handle_dump_state, interfaces); | |
5c333467 | 391 | #endif /* CONFIG_NATIVE_WINDOWS */ |
0456ea16 | 392 | eloop_register_signal_terminate(handle_term, interfaces); |
5c333467 JM |
393 | |
394 | #ifndef CONFIG_NATIVE_WINDOWS | |
395 | openlog("hostapd", 0, LOG_DAEMON); | |
396 | #endif /* CONFIG_NATIVE_WINDOWS */ | |
397 | ||
398 | return 0; | |
399 | } | |
400 | ||
401 | ||
402 | static void hostapd_global_deinit(const char *pid_file) | |
403 | { | |
404 | #ifdef EAP_SERVER_TNC | |
405 | tncs_global_deinit(); | |
406 | #endif /* EAP_SERVER_TNC */ | |
407 | ||
d47fa330 JM |
408 | random_deinit(); |
409 | ||
5c333467 JM |
410 | eloop_destroy(); |
411 | ||
412 | #ifndef CONFIG_NATIVE_WINDOWS | |
413 | closelog(); | |
414 | #endif /* CONFIG_NATIVE_WINDOWS */ | |
415 | ||
416 | eap_server_unregister_methods(); | |
417 | ||
418 | os_daemonize_terminate(pid_file); | |
419 | } | |
420 | ||
421 | ||
422 | static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize, | |
423 | const char *pid_file) | |
424 | { | |
425 | #ifdef EAP_SERVER_TNC | |
426 | int tnc = 0; | |
427 | size_t i, k; | |
428 | ||
429 | for (i = 0; !tnc && i < ifaces->count; i++) { | |
430 | for (k = 0; k < ifaces->iface[i]->num_bss; k++) { | |
431 | if (ifaces->iface[i]->bss[0]->conf->tnc) { | |
432 | tnc++; | |
433 | break; | |
434 | } | |
435 | } | |
436 | } | |
437 | ||
438 | if (tnc && tncs_global_init() < 0) { | |
439 | wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); | |
440 | return -1; | |
441 | } | |
442 | #endif /* EAP_SERVER_TNC */ | |
443 | ||
444 | if (daemonize && os_daemonize(pid_file)) { | |
445 | perror("daemon"); | |
446 | return -1; | |
447 | } | |
448 | ||
449 | eloop_run(); | |
450 | ||
451 | return 0; | |
452 | } | |
453 | ||
454 | ||
455 | static void show_version(void) | |
456 | { | |
457 | fprintf(stderr, | |
458 | "hostapd v" VERSION_STR "\n" | |
459 | "User space daemon for IEEE 802.11 AP management,\n" | |
460 | "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" | |
9e074973 | 461 | "Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> " |
5c333467 JM |
462 | "and contributors\n"); |
463 | } | |
464 | ||
465 | ||
466 | static void usage(void) | |
467 | { | |
468 | show_version(); | |
469 | fprintf(stderr, | |
470 | "\n" | |
471 | "usage: hostapd [-hdBKtv] [-P <PID file>] " | |
472 | "<configuration file(s)>\n" | |
473 | "\n" | |
474 | "options:\n" | |
475 | " -h show this usage\n" | |
476 | " -d show more debug messages (-dd for even more)\n" | |
477 | " -B run daemon in the background\n" | |
478 | " -P PID file\n" | |
479 | " -K include key data in debug messages\n" | |
b41a47c0 BG |
480 | #ifdef CONFIG_DEBUG_FILE |
481 | " -f log output to debug file instead of stdout\n" | |
482 | #endif /* CONFIG_DEBUG_FILE */ | |
5c333467 JM |
483 | " -t include timestamps in some debug messages\n" |
484 | " -v show hostapd version\n"); | |
485 | ||
486 | exit(1); | |
487 | } | |
488 | ||
489 | ||
379ff7b9 BG |
490 | static const char * hostapd_msg_ifname_cb(void *ctx) |
491 | { | |
492 | struct hostapd_data *hapd = ctx; | |
493 | if (hapd && hapd->iconf && hapd->iconf->bss) | |
494 | return hapd->iconf->bss->iface; | |
495 | return NULL; | |
496 | } | |
497 | ||
498 | ||
5c333467 JM |
499 | int main(int argc, char *argv[]) |
500 | { | |
501 | struct hapd_interfaces interfaces; | |
502 | int ret = 1; | |
503 | size_t i; | |
504 | int c, debug = 0, daemonize = 0; | |
cedf9473 | 505 | char *pid_file = NULL; |
b41a47c0 | 506 | const char *log_file = NULL; |
5c333467 | 507 | |
80d77c31 JM |
508 | if (os_program_init()) |
509 | return -1; | |
510 | ||
5c333467 | 511 | for (;;) { |
b41a47c0 | 512 | c = getopt(argc, argv, "Bdf:hKP:tv"); |
5c333467 JM |
513 | if (c < 0) |
514 | break; | |
515 | switch (c) { | |
516 | case 'h': | |
517 | usage(); | |
518 | break; | |
519 | case 'd': | |
520 | debug++; | |
521 | if (wpa_debug_level > 0) | |
522 | wpa_debug_level--; | |
523 | break; | |
524 | case 'B': | |
525 | daemonize++; | |
526 | break; | |
b41a47c0 BG |
527 | case 'f': |
528 | log_file = optarg; | |
529 | break; | |
5c333467 JM |
530 | case 'K': |
531 | wpa_debug_show_keys++; | |
532 | break; | |
533 | case 'P': | |
cedf9473 JM |
534 | os_free(pid_file); |
535 | pid_file = os_rel2abs_path(optarg); | |
5c333467 JM |
536 | break; |
537 | case 't': | |
538 | wpa_debug_timestamp++; | |
539 | break; | |
540 | case 'v': | |
541 | show_version(); | |
542 | exit(1); | |
543 | break; | |
544 | ||
545 | default: | |
546 | usage(); | |
547 | break; | |
548 | } | |
549 | } | |
550 | ||
551 | if (optind == argc) | |
552 | usage(); | |
553 | ||
379ff7b9 BG |
554 | wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb); |
555 | ||
b41a47c0 BG |
556 | if (log_file) |
557 | wpa_debug_open_file(log_file); | |
558 | ||
5c333467 | 559 | interfaces.count = argc - optind; |
5b73735b | 560 | interfaces.iface = os_zalloc(interfaces.count * |
5c333467 JM |
561 | sizeof(struct hostapd_iface *)); |
562 | if (interfaces.iface == NULL) { | |
5b73735b | 563 | wpa_printf(MSG_ERROR, "malloc failed"); |
5c333467 JM |
564 | return -1; |
565 | } | |
566 | ||
567 | if (hostapd_global_init(&interfaces)) | |
568 | return -1; | |
569 | ||
570 | /* Initialize interfaces */ | |
571 | for (i = 0; i < interfaces.count; i++) { | |
9969e5a4 JM |
572 | interfaces.iface[i] = hostapd_interface_init(&interfaces, |
573 | argv[optind + i], | |
5c333467 JM |
574 | debug); |
575 | if (!interfaces.iface[i]) | |
576 | goto out; | |
577 | } | |
578 | ||
579 | if (hostapd_global_run(&interfaces, daemonize, pid_file)) | |
580 | goto out; | |
581 | ||
582 | ret = 0; | |
583 | ||
584 | out: | |
585 | /* Deinitialize all interfaces */ | |
f7c47833 JM |
586 | for (i = 0; i < interfaces.count; i++) |
587 | hostapd_interface_deinit_free(interfaces.iface[i]); | |
5c333467 JM |
588 | os_free(interfaces.iface); |
589 | ||
590 | hostapd_global_deinit(pid_file); | |
dd745de3 | 591 | os_free(pid_file); |
5c333467 | 592 | |
b41a47c0 BG |
593 | if (log_file) |
594 | wpa_debug_close_file(); | |
595 | ||
80d77c31 JM |
596 | os_program_deinit(); |
597 | ||
5c333467 JM |
598 | return ret; |
599 | } |