diff --git a/Makefile.am b/Makefile.am index 3b4fd720..0d498ed1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -214,6 +214,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \ src/resolve.h src/resolve.c\ src/hotspot.h src/hotspot.c \ src/p2putil.h src/p2putil.c \ + src/module.c \ $(eap_sources) \ $(builtin_sources) diff --git a/src/hotspot.c b/src/hotspot.c index e25a78b7..8348b86e 100644 --- a/src/hotspot.c +++ b/src/hotspot.c @@ -360,3 +360,4 @@ static void hotspot_exit(void) } IWD_MODULE(hotspot, hotspot_init, hotspot_exit) +IWD_MODULE_DEPENDS(hotspot, known_networks) diff --git a/src/iwd.h b/src/iwd.h index dba62fd7..8bb4f64f 100644 --- a/src/iwd.h +++ b/src/iwd.h @@ -44,8 +44,25 @@ struct iwd_module_desc { bool active; } __attribute__((aligned(8))); +struct iwd_module_depends { + const char *self; + const char *target; +}; + #define IWD_MODULE(name, init, exit) \ static struct iwd_module_desc __iwd_module_ ## name \ __attribute__((used, section("__iwd_module"), aligned(8))) = {\ #name, init, exit \ }; + +#define IWD_MODULE_DEPENDS(name, dep) \ + static struct iwd_module_depends \ + __iwd_module__##name_##dep \ + __attribute__((used, section("__iwd_module_dep"), \ + aligned(8))) = { \ + .self = #name, \ + .target = #dep, \ + }; + +int iwd_modules_init(); +void iwd_modules_exit(); diff --git a/src/main.c b/src/main.c index d529052d..aaa182a4 100644 --- a/src/main.c +++ b/src/main.c @@ -159,44 +159,6 @@ static void nl80211_appeared(const struct l_genl_family_info *info, netdev_set_nl80211(nl80211); } -extern struct iwd_module_desc __start___iwd_module[]; -extern struct iwd_module_desc __stop___iwd_module[]; - -static int iwd_modules_init() -{ - struct iwd_module_desc *desc; - int r; - - l_debug(""); - - for (desc = __start___iwd_module; desc < __stop___iwd_module; desc++) { - r = desc->init(); - if (r < 0) - return r; - - l_debug("Initialized module: %s", desc->name); - desc->active = true; - } - - return 0; -} - -static void iwd_modules_exit() -{ - struct iwd_module_desc *desc; - - l_debug(""); - - for (desc = __stop___iwd_module - 1; - desc >= __start___iwd_module; desc--) { - if (!desc->active) - continue; - l_debug("Removing module: %s", desc->name); - desc->exit(); - desc->active = false; - } -} - static void request_name_callback(struct l_dbus *dbus, bool success, bool queued, void *user_data) { diff --git a/src/module.c b/src/module.c new file mode 100644 index 00000000..c027b9d0 --- /dev/null +++ b/src/module.c @@ -0,0 +1,184 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "src/iwd.h" + +struct dependency { + struct dependency *next; + struct module *module; +}; + +struct module { + struct iwd_module_desc *desc; + struct dependency *depends; + bool visited : 1; + bool processed : 1; +}; + +extern struct iwd_module_desc __start___iwd_module[]; +extern struct iwd_module_desc __stop___iwd_module[]; +extern struct iwd_module_depends __start___iwd_module_dep[]; +extern struct iwd_module_depends __stop___iwd_module_dep[]; + +struct iwd_module_desc **modules_sorted; + +static struct module *module_find(struct module *modules, size_t count, + const char *name) +{ + unsigned int i; + + for (i = 0; i < count; i++) + if (!strcmp(modules[i].desc->name, name)) + return &modules[i]; + + return NULL; +} + +static int module_topological_order(struct module *module, + struct iwd_module_desc **sorted, + size_t *offset) +{ + struct dependency *d; + int r; + + module->visited = true; + + for (d = module->depends; d; d = d->next) { + if (d->module->processed) + continue; + + if (d->module->visited) { + l_error("Circular dependency between %s and %s", + module->desc->name, + d->module->desc->name); + return -EINVAL; + } + + r = module_topological_order(d->module, sorted, offset); + if (r < 0) + return r; + } + + module->processed = true; + sorted[*offset] = module->desc; + *offset += 1; + return 0; +} + +int iwd_modules_init() +{ + struct iwd_module_desc *desc; + struct iwd_module_depends *dep; + L_AUTO_FREE_VAR(struct module *, modules) = NULL; + L_AUTO_FREE_VAR(struct dependency *, deps) = NULL; + L_AUTO_FREE_VAR(struct iwd_module_desc **, sorted) = NULL; + unsigned int i = 0; + size_t n_modules; + size_t n_deps; + size_t offset; + int r; + + l_debug(""); + + n_modules = (__stop___iwd_module - __start___iwd_module); + modules = l_new(struct module, n_modules); + + for (desc = __start___iwd_module; desc < __stop___iwd_module; desc++) + modules[i++].desc = desc; + + n_deps = (__stop___iwd_module_dep - __start___iwd_module_dep); + deps = l_new(struct dependency, n_deps); + + for (dep = __start___iwd_module_dep, i = 0; + dep < __stop___iwd_module_dep; dep++, i++) { + struct module *src; + struct module *dst; + + src = module_find(modules, n_modules, dep->self); + dst = module_find(modules, n_modules, dep->target); + if (!src || !dst) { + l_error("Module dependency %s->%s not found", + dep->self, dep->target); + return -EINVAL; + } + + deps[i].next = src->depends; + deps[i].module = dst; + src->depends = &deps[i]; + } + + sorted = l_new(struct iwd_module_desc *, n_modules); + + for (i = 0, offset = 0; i < n_modules; i++) { + if (modules[i].processed) + continue; + + if (module_topological_order(&modules[i], sorted, + &offset) < 0) + return -EINVAL; + } + + modules_sorted = sorted; + sorted = NULL; + + for (i = 0; i < n_modules; i++) { + desc = modules_sorted[i]; + r = desc->init(); + + if (r < 0) + return r; + + desc->active = true; + } + + return 0; +} + +void iwd_modules_exit() +{ + struct iwd_module_desc *desc; + unsigned int i; + size_t n_modules = (__stop___iwd_module - __start___iwd_module); + + l_debug(""); + + if (!modules_sorted) + return; + + for (i = 0; i < n_modules; i++) { + desc = modules_sorted[i]; + if (!desc->active) + continue; + + desc->exit(); + desc->active = false; + } + + l_free(modules_sorted); + modules_sorted = NULL; +}