blob: 64dcb4be18fd95bc8297b6088941b500a3ae8240 [file] [log] [blame]
/*
*
* 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);