From: Michael Tremer Date: Sun, 11 Jun 2023 13:02:35 +0000 (+0000) Subject: networkd: Implement smarter handling of the configuration file hierarchy X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=25808f650e39dbcdf3e2d0ba42d079a9d5e43079;p=network.git networkd: Implement smarter handling of the configuration file hierarchy Signed-off-by: Michael Tremer --- diff --git a/src/networkd/config.c b/src/networkd/config.c index c6281cb2..53fd8e30 100644 --- a/src/networkd/config.c +++ b/src/networkd/config.c @@ -18,12 +18,16 @@ # # #############################################################################*/ +#include #include +#include #include #include #include #include #include +#include +#include #include #include "address.h" @@ -161,7 +165,6 @@ int nw_config_open(nw_config** config, const char* path) { // Create a new configuration r = nw_config_create(config, f); -ERROR: if (f) fclose(f); @@ -419,6 +422,208 @@ int nw_config_set_bool(nw_config* config, const char* key, const int value) { return nw_config_set(config, key, value ? "true" : "false"); } +/* + Directory +*/ + +struct nw_configd { + int nrefs; + + char path[PATH_MAX]; + int fd; +}; + +static void nw_configd_free(nw_configd* dir) { + if (dir->fd >= 0) + close(dir->fd); + + free(dir); +} + +static int __nw_configd_create(nw_configd** dir, int fd, const char* path) { + nw_configd* d = NULL; + int r; + + // Allocate a new object + d = calloc(1, sizeof(*d)); + if (!d) + return -errno; + + // Initialize the reference counter + d->nrefs = 1; + + // Store the file descriptor + d->fd = dup(fd); + if (d->fd < 0) { + r = -errno; + goto ERROR; + } + + // Store path + if (path) { + r = nw_string_set(d->path, path); + if (r < 0) + goto ERROR; + } + + *dir = d; + return 0; + +ERROR: + nw_configd_free(d); + return r; +} + +int nw_configd_create(nw_configd** dir, const char* path) { + int fd; + + // Open the directory + fd = open(path, O_DIRECTORY); + if (fd < 0) { + ERROR("Could not open %s: %m\n", path); + return -errno; + } + + return __nw_configd_create(dir, fd, path); +} + +nw_configd* nw_configd_ref(nw_configd* dir) { + dir->nrefs++; + + return dir; +} + +nw_configd* nw_configd_unref(nw_configd* dir) { + if (--dir->nrefs > 0) + return dir; + + nw_configd_free(dir); + return NULL; +} + +static int nw_configd_open(nw_configd* dir, const char* path, int flags) { + return openat(dir->fd, path, flags); +} + +FILE* nw_configd_fopen(nw_configd* dir, const char* path, const char* mode) { + int fd; + + // Open file + fd = nw_configd_open(dir, path, 0); + if (fd < 0) + return NULL; + + // Return a file handle + return fdopen(fd, mode); +} + +int nw_configd_open_config(nw_config** config, nw_configd* dir, const char* path) { + FILE* f = NULL; + int r; + + // Open the file + f = nw_configd_fopen(dir, path, "r"); + if (!f) + return -errno; + + // Create configuration + r = nw_config_create(config, f); + if (r < 0) + goto ERROR; + +ERROR: + if (f) + fclose(f); + + return r; +} + +int nw_configd_unlink(nw_configd* dir, const char* path, int flags) { + return unlinkat(dir->fd, path, flags); +} + +nw_configd* nw_configd_descend(nw_configd* dir, const char* path) { + nw_configd* d = NULL; + char p[PATH_MAX]; + int fd = -1; + int r; + + // Join paths + r = nw_path_join(p, dir->path, path); + if (r < 0) + goto ERROR; + + // Open directory + fd = nw_configd_open(dir, path, O_DIRECTORY); + if (fd < 0) { + ERROR("Could not open %s: %m\n", p); + goto ERROR; + } + + // Create a new config directory object + r = __nw_configd_create(&d, fd, p); + if (r < 0) + goto ERROR; + +ERROR: + if (fd >= 0) + close(fd); + + return d; +} + +int nw_configd_walk(nw_configd* dir, nw_configd_walk_callback callback, void* data) { + FILE* f = NULL; + DIR* d = NULL; + struct dirent* e = NULL; + int r; + + // Re-open the directory + d = fdopendir(dir->fd); + if (!d) { + r = -errno; + goto ERROR; + } + + // Walk trough everything + for (;;) { + // Read the next entry + e = readdir(d); + if (!e) + break; + + // Skip anything that is not a regular file + if (e->d_type != DT_REG) + continue; + + // Skip hidden files + if (e->d_name[0] == '.') + continue; + + // Open the file + f = nw_configd_fopen(dir, e->d_name, "r"); + if (!f) { + r = -errno; + goto ERROR; + } + + // Call the callback + r = callback(e, f, data); + fclose(f); + + if (r < 0) + goto ERROR; + } + + r = 0; + +ERROR: + if (d) + closedir(d); + + return r; +} + /* Options */ diff --git a/src/networkd/config.h b/src/networkd/config.h index 4b8bc01f..3e7c0977 100644 --- a/src/networkd/config.h +++ b/src/networkd/config.h @@ -21,6 +21,7 @@ #ifndef NETWORKD_CONFIG_H #define NETWORKD_CONFIG_H +#include #include #define NETWORK_CONFIG_KEY_MAX_LENGTH 128 @@ -52,6 +53,27 @@ int nw_config_set_int(nw_config* config, const char* key, const int value); int nw_config_get_bool(nw_config* config, const char* key); int nw_config_set_bool(nw_config* config, const char* key, const int value); +/* + Directory +*/ + +typedef struct nw_configd nw_configd; + +int nw_configd_create(nw_configd** dir, const char* path); + +nw_configd* nw_configd_ref(nw_configd* dir); +nw_configd* nw_configd_unref(nw_configd* dir); + +FILE* nw_configd_fopen(nw_configd* dir, const char* path, const char* mode); +int nw_configd_open_config(nw_config** config, nw_configd* dir, const char* path); +int nw_configd_unlink(nw_configd* dir, const char* path, int flags); + +nw_configd* nw_configd_descend(nw_configd* dir, const char* path); + +typedef int (*nw_configd_walk_callback)(struct dirent* entry, FILE* f, void* data); + +int nw_configd_walk(nw_configd* dir, nw_configd_walk_callback callback, void* data); + /* Options */ diff --git a/src/networkd/daemon.c b/src/networkd/daemon.c index a62e343f..dfbcc151 100644 --- a/src/networkd/daemon.c +++ b/src/networkd/daemon.c @@ -51,7 +51,7 @@ struct nw_daemon { int nrefs; // Configuration - int configfd; + nw_configd* configd; nw_config* config; // Event Loop @@ -103,36 +103,14 @@ static int __nw_daemon_reload(sd_event_source* source, const struct signalfd_sig */ static int nw_daemon_config_open(nw_daemon* daemon, const char* path) { - // Open the directory - daemon->configfd = open(path, O_DIRECTORY); - if (daemon->configfd < 0) { - ERROR("Could not open %s: %m\n", path); - return -errno; - } - - return 0; -} - -FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const char* mode) { - // Open the file - int fd = openat(daemon->configfd, path, 0); - if (fd < 0) { - ERROR("Could not open configuration file %s: %m\n", path); - return NULL; - } - - // Return a file handle - return fdopen(fd, mode); -} + int r; -DIR* nw_daemon_config_opendir(nw_daemon* daemon, const char* path) { - int fd = openat(daemon->configfd, path, O_DIRECTORY); - if (fd < 0) { - ERROR("Could not open configuration directory %s: %m\n", path); - return NULL; - } + // Open the configuration directory + r = nw_configd_create(&daemon->configd, path); + if (r < 0) + return r; - return fdopendir(fd); + return 0; } static int nw_daemon_parse_argv(nw_daemon* daemon, int argc, char* argv[]) { @@ -213,33 +191,17 @@ static int nw_daemon_setup_loop(nw_daemon* daemon) { } static int nw_daemon_load_config(nw_daemon* daemon) { - FILE* f = NULL; int r; // If no configuration path has been opened yet, we will open something - if (!daemon->configfd) { + if (!daemon->configd) { r = nw_daemon_config_open(daemon, CONFIG_DIR); if (r < 0) - goto ERROR; + return r; } // Open the configuration file - f = nw_daemon_config_fopen(daemon, "settings", "r"); - if (!f) { - r = -errno; - goto ERROR; - } - - // Create configuration - r = nw_config_create(&daemon->config, f); - if (r < 0) - goto ERROR; - -ERROR: - if (f) - fclose(f); - - return r; + return nw_configd_open_config(&daemon->config, daemon->configd, "settings"); } static int nw_start_device_monitor(nw_daemon* daemon) { @@ -540,8 +502,8 @@ static void nw_daemon_free(nw_daemon* daemon) { // Cleanup common objects nw_daemon_cleanup(daemon); - if (daemon->configfd > 0) - close(daemon->configfd); + if (daemon->configd) + nw_configd_unref(daemon->configd); if (daemon->stats_collector_event) sd_event_source_unref(daemon->stats_collector_event); if (daemon->bus) @@ -640,6 +602,16 @@ int nw_daemon_save(nw_daemon* daemon) { return 0; } +nw_configd* nw_daemon_configd(nw_daemon* daemon, const char* path) { + if (!daemon->configd) + return NULL; + + if (path) + return nw_configd_descend(daemon->configd, path); + + return nw_configd_ref(daemon->configd); +} + /* Bus */ diff --git a/src/networkd/daemon.h b/src/networkd/daemon.h index b03086c8..2d56d797 100644 --- a/src/networkd/daemon.h +++ b/src/networkd/daemon.h @@ -29,6 +29,7 @@ typedef struct nw_daemon nw_daemon; +#include "config.h" #include "link.h" #include "links.h" #include "port.h" @@ -47,11 +48,7 @@ int nw_daemon_reload(nw_daemon* daemon); int nw_daemon_save(nw_daemon* daemon); -/* - Configuration -*/ -FILE* nw_daemon_config_fopen(nw_daemon* daemon, const char* path, const char* mode); -DIR* nw_daemon_config_opendir(nw_daemon* daemon, const char* path); +nw_configd* nw_daemon_configd(nw_daemon* daemon, const char* path); /* Bus diff --git a/src/networkd/port.c b/src/networkd/port.c index f5479569..141bba59 100644 --- a/src/networkd/port.c +++ b/src/networkd/port.c @@ -222,24 +222,10 @@ ERROR: return r; } -int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name) { +int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name, FILE* f) { nw_config* config = NULL; - FILE* f = NULL; - char path[PATH_MAX]; int r; - // Make path - r = nw_string_format(path, "ports/%s", name); - if (r < 0) - goto ERROR; - - // Open the configuration file - f = nw_daemon_config_fopen(daemon, path, "r"); - if (!f) { - r = -errno; - goto ERROR; - } - // Initialize the configuration r = nw_config_create(&config, f); if (r < 0) @@ -248,7 +234,7 @@ int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name) { // Fetch the type const char* type = nw_config_get(config, "TYPE"); if (!type) { - ERROR("Port configuration %s has no TYPE\n", path); + ERROR("Port %s has no TYPE\n", name); r = -ENOTSUP; goto ERROR; } @@ -261,8 +247,6 @@ int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name) { ERROR: if (config) nw_config_unref(config); - if (f) - fclose(f); return r; } @@ -292,6 +276,7 @@ static void __nw_port_unref(void* data) { } int nw_port_destroy(nw_port* port) { + nw_configd* configd = NULL; int r; DEBUG("Destroying port %s\n", port->name); @@ -299,26 +284,33 @@ int nw_port_destroy(nw_port* port) { // Destroy the physical link (if exists) if (port->link) { r = nw_link_destroy(port->link); - if (r) - return r; + if (r < 0) + goto ERROR; } // Dereference the port from other ports r = nw_daemon_ports_walk(port->daemon, __nw_port_drop_port, port); - if (r) - return r; + if (r < 0) + goto ERROR; // Dereference the port from other zones r = nw_daemon_zones_walk(port->daemon, __nw_zone_drop_port, port); - if (r) - return r; + if (r < 0) + goto ERROR; - // Destroy the configuration - r = nw_config_destroy(port->config); - if (r) - return r; + // Fetch the configuration directory + configd = nw_daemon_configd(port->daemon, "ports"); + if (configd) { + r = nw_configd_unlink(configd, port->name, 0); + if (r < 0) + goto ERROR; + } - return 0; +ERROR: + if (configd) + nw_configd_unref(configd); + + return r; } int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) { @@ -345,17 +337,19 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) { } int nw_port_save(nw_port* port) { - char path[PATH_MAX]; + nw_configd* configd = NULL; FILE* f = NULL; int r; - // Compose path - r = nw_string_format(path, "ports/%s", port->name); - if (r < 0) - return r; + // Fetch configuration directory + configd = nw_daemon_configd(port->daemon, "ports"); + if (!configd) { + r = -errno; + goto ERROR; + } // Open file - f = nw_daemon_config_fopen(port->daemon, path, "w"); + f = nw_configd_fopen(configd, port->name, "w"); if (!f) { r = -errno; goto ERROR; @@ -372,6 +366,8 @@ int nw_port_save(nw_port* port) { goto ERROR; ERROR: + if (configd) + nw_configd_unref(configd); if (f) fclose(f); if (r) diff --git a/src/networkd/port.h b/src/networkd/port.h index f1cb3d28..3cbe4b09 100644 --- a/src/networkd/port.h +++ b/src/networkd/port.h @@ -105,7 +105,7 @@ struct nw_port { int nw_port_create(nw_port** port, nw_daemon* daemon, const nw_port_type_id_t type, const char* name, nw_config* config); -int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name); +int nw_port_open(nw_port** port, nw_daemon* daemon, const char* name, FILE* f); nw_port* nw_port_ref(nw_port* port); nw_port* nw_port_unref(nw_port* port); diff --git a/src/networkd/ports.c b/src/networkd/ports.c index 761e564a..95a13e37 100644 --- a/src/networkd/ports.c +++ b/src/networkd/ports.c @@ -130,12 +130,14 @@ static int nw_ports_add_port(nw_ports* ports, nw_port* port) { return 0; } -static int nw_ports_enumerate_port(nw_ports* ports, const char* name) { +static int __nw_ports_enumerate(struct dirent* entry, FILE* f, void* data) { nw_port* port = NULL; int r; + nw_ports* ports = (nw_ports*)data; + // Create a new port - r = nw_port_open(&port, ports->daemon, name); + r = nw_port_open(&port, ports->daemon, entry->d_name, f); if (r < 0 || r == 1) goto ERROR; @@ -152,45 +154,19 @@ ERROR: } int nw_ports_enumerate(nw_ports* ports) { - DIR* d = NULL; - struct dirent* entry = NULL; + nw_configd* configd = NULL; int r; - // Open the ports directory - d = nw_daemon_config_opendir(ports->daemon, "ports"); - if (!d) { - switch (errno) { - case ENOENT: - return 0; - - default: - return -errno; - } - } - - for (;;) { - // Read the next entry - entry = readdir(d); - if (!entry) - break; - - // Skip anything that is not a regular file - if (entry->d_type != DT_REG) - continue; + // Fetch ports configuration directory + configd = nw_daemon_configd(ports->daemon, "ports"); + if (!configd) + return 0; - // Skip hidden files - if (entry->d_name[0] == '.') - continue; + // Walk through all files + r = nw_configd_walk(configd, __nw_ports_enumerate, ports); - // Enumerate the port - r = nw_ports_enumerate_port(ports, entry->d_name); - if (r < 0) - goto ERROR; - } - -ERROR: - if (d) - closedir(d); + // Cleanup + nw_configd_unref(configd); return r; } diff --git a/src/networkd/zone.c b/src/networkd/zone.c index cc5fdaf5..19d221fb 100644 --- a/src/networkd/zone.c +++ b/src/networkd/zone.c @@ -32,6 +32,12 @@ #include "string.h" #include "zone.h" +static const nw_string_table_t nw_zone_type_id[] = { + { -1, NULL }, +}; + +NW_STRING_TABLE_LOOKUP(nw_zone_type_id_t, nw_zone_type_id) + struct nw_zone { nw_daemon* daemon; int nrefs; @@ -45,32 +51,6 @@ struct nw_zone { nw_config *config; }; -#define nw_zone_path(zone, path, format, ...) \ - __nw_zone_path(zone, path, sizeof(path), format, __VA_ARGS__) - -static int __nw_zone_path(nw_zone* zone, char* p, const size_t length, - const char* format, ...) { - char prefix[NAME_MAX]; - char suffix[NAME_MAX]; - va_list args; - int r; - - // Format the prefix - r = nw_string_format(prefix, "%s/zones/%s", CONFIG_DIR, zone->name); - if (r) - return r; - - // Format the suffix - va_start(args, format); - r = nw_string_vformat(suffix, format, args); - va_end(args); - if (r) - return r; - - // Join the two parts together - return __nw_path_join(p, length, prefix, suffix); -} - static void nw_zone_free(nw_zone* zone) { if (zone->link) nw_link_unref(zone->link); @@ -109,8 +89,7 @@ static int nw_zone_set_link(nw_zone* zone, nw_link* link) { static int nw_zone_setup(nw_zone* zone) { nw_link* link = NULL; - char path[PATH_MAX]; - int r; + int r = 0; // Find the link link = nw_daemon_get_link_by_name(zone->daemon, zone->name); @@ -120,18 +99,6 @@ static int nw_zone_setup(nw_zone* zone) { goto ERROR; } -#if 0 - // Compose the path to the main configuration file - r = nw_zone_path(zone, path, "%s", "settings"); - if (r) - goto ERROR; - - // Initialize the configuration - r = nw_config_create(&zone->config, path); - if (r) - goto ERROR; -#endif - ERROR: if (link) nw_link_unref(link); @@ -139,13 +106,15 @@ ERROR: return r; } -int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name) { +int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const nw_zone_type_id_t type, + const char* name, nw_config* config) { + nw_zone* z = NULL; int r; // Allocate a new object - nw_zone* z = calloc(1, sizeof(*z)); + z = calloc(1, sizeof(*z)); if (!z) - return 1; + return -errno; // Store a reference to the daemon z->daemon = nw_daemon_ref(daemon); @@ -158,6 +127,11 @@ int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name) { if (r) goto ERROR; + // Copy the configuration + r = nw_config_copy(config, &z->config); + if (r < 0) + goto ERROR; + // Setup the zone r = nw_zone_setup(z); if (r) @@ -171,6 +145,35 @@ ERROR: return r; } +int nw_zone_open(nw_zone** zone, nw_daemon* daemon, const char* name, FILE* f) { + nw_config* config = NULL; + int r; + + // Initialize the configuration + r = nw_config_create(&config, f); + if (r < 0) + goto ERROR; + + // Fetch the type + const char* type = nw_config_get(config, "TYPE"); + if (!type) { + ERROR("Zone %s has no TYPE\n", name); + r = -ENOTSUP; + goto ERROR; + } + + // Create a new zone + r = nw_zone_create(zone, daemon, nw_zone_type_id_from_string(type), name, config); + if (r < 0) + goto ERROR; + +ERROR: + if (config) + nw_config_unref(config); + + return r; +} + nw_zone* nw_zone_ref(nw_zone* zone) { zone->nrefs++; @@ -195,30 +198,41 @@ int __nw_zone_drop_port(nw_daemon* daemon, nw_zone* zone, void* data) { } int nw_zone_save(nw_zone* zone) { - char path[PATH_MAX]; + nw_configd* configd = NULL; FILE* f = NULL; int r; - // Compose path - r = nw_string_format(path, "zones/%s/settings", zone->name); - if (r < 0) + // Fetch configuration directory + configd = nw_daemon_configd(zone->daemon, "zones"); + if (!configd) { + r = -errno; goto ERROR; + } // Open file - f = nw_daemon_config_fopen(zone->daemon, path, "w"); + f = nw_configd_fopen(configd, zone->name, "w"); if (!f) { r = -errno; goto ERROR; } + // Write out the configuration + r = nw_config_options_write(zone->config); + if (r < 0) + goto ERROR; + // Write the configuration r = nw_config_write(zone->config, f); - if (r) + if (r < 0) goto ERROR; ERROR: + if (configd) + nw_configd_unref(configd); if (f) fclose(f); + if (r) + ERROR("Could not save configuration for zone %s: %s\n", zone->name, strerror(-r)); return r; } diff --git a/src/networkd/zone.h b/src/networkd/zone.h index 480440fb..6ab951b4 100644 --- a/src/networkd/zone.h +++ b/src/networkd/zone.h @@ -28,11 +28,18 @@ typedef struct nw_zone nw_zone; +typedef enum nw_zone_type_id { + __EMPTY +} nw_zone_type_id_t; + #include +#include "config.h" #include "daemon.h" -int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const char* name); +int nw_zone_create(nw_zone** zone, nw_daemon* daemon, const nw_zone_type_id_t type, + const char* name, nw_config* config); +int nw_zone_open(nw_zone** zone, nw_daemon* daemon, const char* name, FILE* f); nw_zone* nw_zone_ref(nw_zone* zone); nw_zone* nw_zone_unref(nw_zone* zone); diff --git a/src/networkd/zones.c b/src/networkd/zones.c index 84a6673e..654e637e 100644 --- a/src/networkd/zones.c +++ b/src/networkd/zones.c @@ -135,30 +135,15 @@ static int nw_zones_add_zone(nw_zones* zones, nw_zone* zone) { return 0; } -static int __nw_zones_enumerate(const char* path, const struct stat* s, void* data) { +static int __nw_zones_enumerate(struct dirent* entry, FILE* f, void* data) { nw_zone* zone = NULL; int r; nw_zones* zones = (nw_zones*)data; - // Skip anything that isn't a directory - if (!S_ISDIR(s->st_mode)) - return 0; - - // Find the basename of the file - const char* name = nw_path_basename(path); - - // Break on invalid paths - if (!name) - return 0; - - // Skip any hidden files - if (*name == '.') - return 0; - // Create a new zone - r = nw_zone_create(&zone, zones->daemon, name); - if (r) + r = nw_zone_open(&zone, zones->daemon, entry->d_name, f); + if (r < 0 || r == 1) goto ERROR; // Add the zone to the list @@ -174,7 +159,21 @@ ERROR: } int nw_zones_enumerate(nw_zones* zones) { - return nw_ftw(ZONE_CONFIG_DIR, ZONE_CONFIG_DIR "/*", __nw_zones_enumerate, zones); + nw_configd* configd = NULL; + int r; + + // Fetch zones configuration directory + configd = nw_daemon_configd(zones->daemon, "zones"); + if (!configd) + return 0; + + // Walk through all files + r = nw_configd_walk(configd, __nw_zones_enumerate, zones); + + // Cleanup + nw_configd_unref(configd); + + return r; } size_t nw_zones_num(nw_zones* zones) {