/* * * Ethernet daemon for Linux * * Copyright (C) 2017-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 #define _GNU_SOURCE #include #include #include #include #include #include "src/module.h" #include "wired/network.h" #define STORAGEFILE_SUFFIX ".8021x" #define STORAGEFILE_SUFFIX_LEN 6 struct network { char *name; }; static struct l_queue *network_list; static struct l_dir_watch *storage_watch; static char *storage_path; static char *network_name_from_filename(const char *filename) { if (!l_str_has_suffix(filename, STORAGEFILE_SUFFIX)) return NULL; return l_strndup(filename, strlen(filename) - STORAGEFILE_SUFFIX_LEN); } static struct network *network_new(const char *name) { struct network *net; l_debug("Creating network '%s'", name); net = l_new(struct network, 1); net->name = l_strdup(name); return net; } static void network_free(void *data) { struct network *net = data; l_debug("Freeing network '%s'", net->name); l_free(net->name); l_free(net); } static bool network_match(const void *a, const void *b) { const struct network *net = a; const char *name = b; return !strcmp(net->name, name); } static struct network *network_lookup(const char *name) { return l_queue_find(network_list, network_match, name); } static void network_create(const char *name) { struct network *net; net = network_lookup(name); if (net) { l_debug("Refresh network '%s'", net->name); return; } net = network_new(name); l_queue_push_tail(network_list, net); } static void network_remove(const char *name) { struct network *net; net = l_queue_remove_if(network_list, network_match, name); if (net) network_free(net); } static void network_reload(const char *name) { struct network *net; net = network_lookup(name); if (net) { l_debug("Refresh network '%s'", net->name); return; } } struct l_settings *network_lookup_security(const char *network) { struct l_settings *conf; char *path; path = l_strdup_printf("%s/%s%s", storage_path, network, STORAGEFILE_SUFFIX); l_debug("Loading %s", path); conf = l_settings_new(); l_settings_load_from_file(conf, path); l_free(path); return conf; } static void network_storage_watch_cb(const char *filename, enum l_dir_watch_event event, void *user_data) { char *name; name = network_name_from_filename(filename); if (!name) return; switch (event) { case L_DIR_WATCH_EVENT_CREATED: network_create(name); break; case L_DIR_WATCH_EVENT_REMOVED: network_remove(name); break; case L_DIR_WATCH_EVENT_MODIFIED: network_reload(name); break; case L_DIR_WATCH_EVENT_ACCESSED: case L_DIR_WATCH_EVENT_ATTRIB: break; } l_free(name); } static void network_storage_watch_destroy(void *user_data) { storage_watch = NULL; } static int network_init(void) { DIR *dir; struct dirent *dirent; const char *state_dir; char **state_dirs; state_dir = getenv("STATE_DIRECTORY"); if (!state_dir) state_dir = WIRED_STORAGEDIR; l_debug("Using state directory %s", state_dir); state_dirs = l_strsplit(state_dir, ':'); if (!state_dirs[0]) { l_strv_free(state_dirs); return -EINVAL; } storage_path = l_strdup(state_dirs[0]); l_strv_free(state_dirs); dir = opendir(storage_path); if (!dir) { l_info("Unable to open %s: %s", storage_path, strerror(errno)); return -EINVAL; } network_list = l_queue_new(); while ((dirent = readdir(dir))) { struct network *net; char *name; if (dirent->d_type != DT_REG && dirent->d_type != DT_LNK) continue; name = network_name_from_filename(dirent->d_name); if (!name) continue; net = network_new(name); l_queue_push_tail(network_list, net); l_free(name); } closedir(dir); storage_watch = l_dir_watch_new(storage_path, network_storage_watch_cb, NULL, network_storage_watch_destroy); return 0; } static void network_exit(void) { l_dir_watch_destroy(storage_watch); l_queue_destroy(network_list, network_free); network_list = NULL; l_free(storage_path); } IWD_MODULE(network, network_init, network_exit); IWD_MODULE_DEPENDS(network, eap);