| /* |
| * |
| * 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 <config.h> |
| #endif |
| |
| #define _GNU_SOURCE |
| #include <errno.h> |
| #include <dirent.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <ell/ell.h> |
| |
| #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); |