]> git.ipfire.org Git - people/ms/network.git/commitdiff
config: Extend the parser to easier read/write configs
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 8 Jun 2023 15:04:38 +0000 (15:04 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 8 Jun 2023 15:04:38 +0000 (15:04 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/networkd/address.h
src/networkd/config.c
src/networkd/config.h
src/networkd/port-bonding.c
src/networkd/port-vlan.c
src/networkd/port.c
src/networkd/port.h

index 7937d62cfc4459dd5c70846460a78d6a06a51125..afcbca150765f237392758d8bcd0dadc19b54b0f 100644 (file)
@@ -21,6 +21,7 @@
 #ifndef NETWORKD_ADDRESS_H
 #define NETWORKD_ADDRESS_H
 
+#include <errno.h>
 #include <netinet/ether.h>
 #include <string.h>
 #include <sys/random.h>
@@ -35,9 +36,12 @@ enum {
 };
 
 static inline int nw_address_from_string(nw_address_t* addr, const char* s) {
+       if (!s)
+               return -EINVAL;
+
        struct ether_addr* p = ether_aton_r(s, addr);
        if (!p)
-               return 1;
+               return -errno;
 
        return 0;
 }
index 78448df2ee641ee5ac6dd5382f9e93dd8f61b83d..6f22da2c706106519c84314600d26062074b2a4d 100644 (file)
@@ -26,6 +26,7 @@
 #include <sys/queue.h>
 #include <unistd.h>
 
+#include "address.h"
 #include "config.h"
 #include "logging.h"
 #include "string.h"
@@ -37,19 +38,37 @@ struct nw_config_entry {
        char value[NETWORK_CONFIG_KEY_MAX_LENGTH];
 };
 
+struct nw_config_option {
+       STAILQ_ENTRY(nw_config_option) nodes;
+
+       const char* key;
+       void* data;
+
+       // Callbacks
+       nw_config_option_read_callback_t read_callback;
+       nw_config_option_write_callback_t write_callback;
+};
+
 struct nw_config {
        int nrefs;
 
        // The path to the configuration file
        char path[PATH_MAX];
 
-       STAILQ_HEAD(entries, nw_config_entry) entries;
+       STAILQ_HEAD(config_entries, nw_config_entry) entries;
+
+       // Options
+       STAILQ_HEAD(parser_entries, nw_config_option) options;
 };
 
 static void nw_config_entry_free(struct nw_config_entry* entry) {
        free(entry);
 }
 
+static void nw_config_option_free(struct nw_config_option* option) {
+       free(option);
+}
+
 static struct nw_config_entry* nw_config_entry_create(
                nw_config* config, const char* key) {
        int r;
@@ -81,9 +100,20 @@ ERROR:
 }
 
 static void nw_config_free(nw_config* config) {
+       struct nw_config_option* option = NULL;
+
        // Flush all entries
        nw_config_flush(config);
 
+       // Free all options
+       while (!STAILQ_EMPTY(&config->options)) {
+               option = STAILQ_FIRST(&config->options);
+               STAILQ_REMOVE_HEAD(&config->options, nodes);
+
+               // Free the options
+               nw_config_option_free(option);
+       }
+
        free(config);
 }
 
@@ -100,6 +130,9 @@ int nw_config_create(nw_config** config, const char* path) {
        // Initialise entries
        STAILQ_INIT(&c->entries);
 
+       // Initialise options
+       STAILQ_INIT(&c->options);
+
        // Store the path
        if (path) {
                r = nw_string_set(c->path, path);
@@ -147,6 +180,33 @@ int nw_config_destroy(nw_config* config) {
        return unlink(config->path);
 }
 
+int nw_config_copy(nw_config* config, nw_config** copy) {
+       struct nw_config_entry* entry = NULL;
+       nw_config* c = NULL;
+       int r;
+
+       // Create a new configuration
+       r = nw_config_create(&c, NULL);
+       if (r)
+               return r;
+
+       // Copy everything
+       STAILQ_FOREACH(entry, &config->entries, nodes) {
+               r = nw_config_set(c, entry->key, entry->value);
+               if (r)
+                       goto ERROR;
+       }
+
+       *copy = c;
+       return 0;
+
+ERROR:
+       if (c)
+               nw_config_unref(c);
+
+       return r;
+}
+
 const char* nw_config_path(nw_config* config) {
        if (*config->path)
                return config->path;
@@ -341,6 +401,9 @@ const char* nw_config_get(nw_config* config, const char* key) {
 int nw_config_set(nw_config* config, const char* key, const char* value) {
        struct nw_config_entry* entry = NULL;
 
+       // Log the change
+       DEBUG("%p: Setting %s = %s\n", config, key, value);
+
        // Delete the entry if val is NULL
        if (!value)
                return nw_config_del(config, key);
@@ -418,3 +481,135 @@ 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) {
        return nw_config_set(config, key, value ? "true" : "false");
 }
+
+/*
+       Options
+*/
+
+int nw_config_options_read(nw_config* config) {
+       struct nw_config_option* option = NULL;
+       int r;
+
+       STAILQ_FOREACH(option, &config->options, nodes) {
+               r = option->read_callback(config, option->key, option->data);
+               if (r < 0)
+                       return r;
+       }
+
+       return 0;
+}
+
+int nw_config_options_write(nw_config* config) {
+       struct nw_config_option* option = NULL;
+       int r;
+
+       STAILQ_FOREACH(option, &config->options, nodes) {
+               r = option->write_callback(config, option->key, option->data);
+               if (r < 0)
+                       return r;
+       }
+
+       return 0;
+}
+
+int nw_config_option_add(nw_config* config, const char* key, void* data,
+               nw_config_option_read_callback_t read_callback,
+               nw_config_option_write_callback_t write_callback) {
+       // Check input
+       if (!key || !data || !read_callback || !write_callback)
+               return -EINVAL;
+
+       // Allocate a new option
+       struct nw_config_option* option = calloc(1, sizeof(*option));
+       if (!option)
+               return -errno;
+
+       // Set key
+       option->key = key;
+
+       // Set data
+       option->data = data;
+
+       // Set callbacks
+       option->read_callback = read_callback;
+       option->write_callback = write_callback;
+
+       // Append the new option
+       STAILQ_INSERT_TAIL(&config->options, option, nodes);
+
+       return 0;
+}
+
+int nw_config_read_int(nw_config* config, const char* key, void* data) {
+       int* p = (int*)data;
+
+       // Fetch the value
+       *p = nw_config_get_int(config, key, -1);
+
+       return 0;
+}
+
+int nw_config_write_int(nw_config* config, const char* key, const void* data) {
+       return 0;
+}
+
+// String
+
+int nw_config_read_string(nw_config* config, const char* key, void* data) {
+       const char** p = (const char**)data;
+
+       // Fetch the value
+       const char* value = nw_config_get(config, key);
+       if (value)
+               *p = value;
+
+       return 0;
+}
+
+int nw_config_write_string(nw_config* config, const char* key, const void* data) {
+       const char** value = (const char**)data;
+
+       return nw_config_set(config, key, *value);
+}
+
+// Address
+
+int nw_config_read_address(nw_config* config, const char* key, void* data) {
+       nw_address_t* address = (nw_address_t*)data;
+       int r;
+
+       // Fetch the value
+       const char* value = nw_config_get(config, key);
+       if (!value)
+               return -EINVAL;
+
+       r = nw_address_from_string(address, value);
+       if (r < 0)
+               ERROR("Could not parse address: %s\n", value);
+
+       return r;
+}
+
+int nw_config_write_address(nw_config* config, const char* key, const void* data) {
+       const nw_address_t* address = (nw_address_t*)data;
+       int r;
+
+       // Format the address to string
+       char* value = nw_address_to_string(address);
+       if (!value)
+               return -errno;
+
+       // Store the value
+       r = nw_config_set(config, key, value);
+       if (r < 0)
+               goto ERROR;
+
+       // Success
+       r = 0;
+
+ERROR:
+       if (value)
+               free(value);
+
+       return r;
+}
index e17c0167fa32dfca2c1c0ef433b28ff475601916..b5417e48d6b8c070875d81e94a3b8e71bca432c6 100644 (file)
@@ -34,6 +34,7 @@ nw_config* nw_config_ref(nw_config* config);
 nw_config* nw_config_unref(nw_config* config);
 
 int nw_config_destroy(nw_config* config);
+int nw_config_copy(nw_config* config, nw_config** copy);
 
 const char* nw_config_path(nw_config* config);
 
@@ -53,4 +54,41 @@ 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);
 
+/*
+       Options
+*/
+
+int nw_config_options_read(nw_config* config);
+int nw_config_options_write(nw_config* config);
+
+typedef int (*nw_config_option_read_callback_t)
+       (nw_config* config, const char* key, void* data);
+typedef int (*nw_config_option_write_callback_t)
+       (nw_config* config, const char* key, const void* data);
+
+int nw_config_option_add(nw_config* config, const char* key, void* value,
+       nw_config_option_read_callback_t read_callback,
+       nw_config_option_write_callback_t write_callback);
+
+#define NW_CONFIG_OPTION(config, key, data, read_callback, write_callback) \
+       nw_config_option_add(config, key, data, read_callback, write_callback)
+
+#define NW_CONFIG_OPTION_STRING(config, key, data) \
+       nw_config_option_add(config, key, data, nw_config_read_string, nw_config_write_string)
+
+int nw_config_read_string(nw_config* config, const char* key, void* data);
+int nw_config_write_string(nw_config* config, const char* key, const void* data);
+
+#define NW_CONFIG_OPTION_INT(config, key, data) \
+       nw_config_option_add(config, key, data, nw_config_read_int, nw_config_write_int)
+
+int nw_config_read_int(nw_config* config, const char* key, void* data);
+int nw_config_write_int(nw_config* config, const char* key, const void* data);
+
+#define NW_CONFIG_OPTION_ADDRESS(config, key, data) \
+       nw_config_option_add(config, key, data, nw_config_read_address, nw_config_write_address)
+
+int nw_config_read_address(nw_config* config, const char* key, void* data);
+int nw_config_write_address(nw_config* config, const char* key, const void* data);
+
 #endif /* NETWORKD_CONFIG_H */
index bbc3fb7a6b3f183a150fcf998b624883584d4770..a2e0fc3f84acb4e6001ea02058706f229308a6fb 100644 (file)
@@ -63,27 +63,32 @@ static const char* nw_port_bonding_mode_to_string(const int mode) {
        return NULL;
 }
 
-static int nw_port_bonding_config_read(nw_port* port) {
-       int r;
-
-       // Mode
-       const char* mode = nw_config_get(port->config, "BONDING_MODE");
-       if (mode) {
-               r = nw_port_bonding_set_mode(port, mode);
-               if (r)
-                       return r;
+static int nw_port_bonding_read_mode(nw_config* config, const char* key, void* data) {
+       int* mode = (int*)data;
+
+       const char* value = nw_config_get(config, key);
+       if (value) {
+               *mode = nw_port_bonding_mode_from_string(value);
+               if (!*mode)
+                       return -errno;
        }
 
        return 0;
 }
 
-static int nw_port_bonding_config_write(nw_port* port) {
+static int nw_port_bonding_write_mode(nw_config* config, const char* key, const void* data) {
+       const int* mode = (int*)data;
+
+       return nw_config_set(config, key, nw_port_bonding_mode_to_string(*mode));
+}
+
+static int nw_port_bonding_setup(nw_port* port) {
        int r;
 
        // Mode
-       r = nw_config_set(port->config, "BONDING_MODE",
-                       nw_port_bonding_mode_to_string(port->bonding.mode));
-       if (r)
+       r = NW_CONFIG_OPTION(port->config, "BONDING_MODE", &port->bonding.mode,
+                       nw_port_bonding_read_mode, nw_port_bonding_write_mode);
+       if (r < 0)
                return r;
 
        return 0;
@@ -119,8 +124,7 @@ const nw_port_info_t nw_port_info_bonding = {
        // Operations
        .ops = {
                // Configuration
-               .config_read = nw_port_bonding_config_read,
-               .config_write = nw_port_bonding_config_write,
+               .setup = nw_port_bonding_setup,
 
                // Link
                .create_link = nw_port_bonding_create_link,
index 6e13ba9a2b76722628e8506d5c9e442f420c594e..2d89a09c621388b1aa1f698ca0085ab0d3010d10 100644 (file)
@@ -38,54 +38,42 @@ const struct nw_string_table nw_port_vlan_proto[] = {
 
 NW_STRING_TABLE_LOOKUP(nw_port_vlan_proto_t, nw_port_vlan_proto)
 
-static int nw_port_vlan_config_read(nw_port* port) {
-       int r;
-
-       // VLAN ID
-       int id = nw_config_get_int(port->config, "VLAN_ID", NW_VLAN_ID_INVALID);
-       if (id) {
-               r = nw_port_set_vlan_id(port, id);
-               if (r)
-                       return r;
+static int nw_port_vlan_read_proto(nw_config* config, const char* key, void* data) {
+       nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)data;
+
+       const char* value = nw_config_get(config, key);
+       if (value) {
+               *proto = nw_port_vlan_proto_from_string(data);
+               if (!*proto)
+                       return -errno;
        }
 
-       // VLAN Protocol
-       const char* proto = nw_config_get(port->config, "VLAN_PROTO");
-       if (proto) {
-               r = nw_port_set_vlan_proto(port, nw_port_vlan_proto_from_string(proto));
-               if (r)
-                       return r;
-       }
+       return 0;
+}
 
-       // Parent Port
-       const char* parent = nw_config_get(port->config, "VLAN_PARENT");
-       if (parent) {
-               r = nw_string_set(port->vlan.__parent_name, parent);
-               if (r)
-                       return r;
-       }
+static int nw_port_vlan_write_proto(nw_config* config, const char* key, const void* data) {
+       const nw_port_vlan_proto_t* proto = (nw_port_vlan_proto_t*)data;
 
-       return 0;
+       return nw_config_set(config, key, nw_port_vlan_proto_to_string(*proto));
 }
 
-static int nw_port_vlan_config_write(nw_port* port) {
+static int nw_port_vlan_setup(nw_port* port) {
        int r;
 
        // VLAN ID
-       r = nw_config_set_int(port->config, "VLAN_ID", port->vlan.id);
-       if (r)
+       r = NW_CONFIG_OPTION_INT(port->config, "VLAN_ID", &port->vlan.id);
+       if (r < 0)
                return r;
 
        // VLAN Protocol
-       r = nw_config_set(port->config, "VLAN_PROTO",
-               nw_port_vlan_proto_to_string(port->vlan.proto));
-       if (r)
+       r = NW_CONFIG_OPTION(port->config, "VLAN_PROTO", &port->vlan.proto,
+                       nw_port_vlan_read_proto, nw_port_vlan_write_proto);
+       if (r < 0)
                return r;
 
        // Parent Port
-       r = nw_config_set(port->config, "VLAN_PARENT",
-               (port->vlan.parent) ? nw_port_name(port->vlan.parent) : port->vlan.__parent_name);
-       if (r)
+       r = NW_CONFIG_OPTION_STRING(port->config, "VLAN_PARENT", &port->vlan.__parent_name);
+       if (r < 0)
                return r;
 
        return 0;
@@ -143,8 +131,7 @@ const nw_port_info_t nw_port_info_vlan = {
        // Operations
        .ops = {
                // Configuration
-               .config_read = nw_port_vlan_config_read,
-               .config_write = nw_port_vlan_config_write,
+               .setup = nw_port_vlan_setup,
 
                .get_parent_port = nw_port_get_vlan_parent,
 
index a7fb826a8e764c0fda9eb4faf3dde36672700a53..cab8fc5f96003ff82704984284a84158fcdf132f 100644 (file)
@@ -69,64 +69,6 @@ static void nw_port_free(nw_port* port) {
        free(port);
 }
 
-static int nw_port_setup_address(nw_port* port) {
-       char* __address = NULL;
-       int r;
-
-       // Read ADDRESS from configuration
-       const char* s = nw_config_get(port->config, "ADDRESS");
-       if (!s) {
-               ERROR("Port %s: Address is not set\n", port->name);
-               goto ERROR;
-       }
-
-       // Parse the address
-       r = nw_address_from_string(&port->address, s);
-       if (r) {
-               ERROR("Port %s: Could not parse address: %m\n", port->name);
-               goto ERROR;
-       }
-
-       // Check if this address is usable
-       r = nw_address_is_multicast(&port->address);
-       if (r) {
-               DEBUG("Port %s: Multicast bit is set on Ethernet address\n", port->name);
-               goto ERROR;
-       }
-
-       return 0;
-
-ERROR:
-       // Generate a random Ethernet address
-       r = nw_address_generate(&port->address);
-       if (r) {
-               ERROR("Could not generate a random Ethernet address: %m\n");
-               return r;
-       }
-
-       // Format the generated address
-       __address = nw_address_to_string(&port->address);
-       if (__address) {
-               ERROR("Generated a random Ethernet address for %s: %s\n", port->name, __address);
-
-               // Free the address
-               free(__address);
-       }
-
-       return 0;
-}
-
-static int nw_port_setup_common(nw_port* port) {
-       int r;
-
-       // Address
-       r = nw_port_setup_address(port);
-       if (r)
-               return r;
-
-       return 0;
-}
-
 static int nw_port_set_link(nw_port* port, nw_link* link) {
        // Do nothing if the same link is being re-assigned
        if (port->link == link)
@@ -154,7 +96,6 @@ static int nw_port_set_link(nw_port* port, nw_link* link) {
 
 static int nw_port_setup(nw_port* port) {
        nw_link* link = NULL;
-       char path[PATH_MAX];
        int r;
 
        // Find the link
@@ -165,28 +106,32 @@ static int nw_port_setup(nw_port* port) {
                        goto ERROR;
        }
 
-       // Compose the path to the main configuration file
-       r = nw_path_join(path, PORT_CONFIG_DIR, port->name);
-       if (r)
-               goto ERROR;
-
-       // Initialize the configuration
-       r = nw_config_create(&port->config, path);
-       if (r)
+       // Generate a random Ethernet address
+       r = nw_address_generate(&port->address);
+       if (r < 0) {
+               ERROR("Could not generate an Ethernet address: %s\n", strerror(-r));
                goto ERROR;
+       }
 
-       // Perform some common initialization
-       r = nw_port_setup_common(port);
-       if (r)
+       // Setup options
+       r = NW_CONFIG_OPTION_ADDRESS(port->config, "ADDRESS", &port->address);
+       if (r < 0)
                goto ERROR;
 
        // Call any custom initialization
-       if (NW_PORT_OPS(port)->config_read) {
-               r = NW_PORT_OPS(port)->config_read(port);
-               if (r)
+       if (NW_PORT_OPS(port)->setup) {
+               r = NW_PORT_OPS(port)->setup(port);
+               if (r < 0)
                        goto ERROR;
        }
 
+       // Parse the configuration
+       r = nw_config_options_read(port->config);
+       if (r < 0) {
+               ERROR("Could not read configuration for port %s: %s\n", port->name, strerror(-r));
+               goto ERROR;
+       }
+
 ERROR:
        if (link)
                nw_link_unref(link);
@@ -194,7 +139,8 @@ ERROR:
        return r;
 }
 
-int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const char* name) {
+int nw_port_create(nw_port** port, nw_daemon* daemon,
+               nw_port_type_t type, const char* name, nw_config* config) {
        int r;
 
        // Allocate a new object
@@ -231,6 +177,11 @@ int nw_port_create(nw_port** port, nw_daemon* daemon, nw_port_type_t type, const
        if (r)
                goto ERROR;
 
+       // Copy the configuration
+       r = nw_config_copy(config, &p->config);
+       if (r)
+               goto ERROR;
+
        // Setup the port
        r = nw_port_setup(p);
        if (r)
@@ -263,7 +214,7 @@ int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
        }
 
        // Create a new port
-       r = nw_port_create(port, daemon, nw_port_type_from_string(type), name);
+       r = nw_port_create(port, daemon, nw_port_type_from_string(type), name, config);
        if (r)
                goto ERROR;
 
@@ -356,12 +307,10 @@ int __nw_port_drop_port(nw_daemon* daemon, nw_port* port, void* data) {
 int nw_port_save(nw_port* port) {
        int r;
 
-       // Call the custom handler
-       if (NW_PORT_OPS(port)->config_write) {
-               r = NW_PORT_OPS(port)->config_write(port);
-               if (r)
-                       goto ERROR;
-       }
+       // Write out the configuration
+       r = nw_config_options_write(port->config);
+       if (r < 0)
+               goto ERROR;
 
        // Write the configuration
        r = nw_config_write(port->config);
@@ -371,7 +320,7 @@ int nw_port_save(nw_port* port) {
        return 0;
 
 ERROR:
-       ERROR("Could not save configuration for port %s: %m\n", port->name);
+       ERROR("Could not save configuration for port %s: %s\n", port->name, strerror(-r));
 
        return 1;
 }
index e16b9572a5e52b40ea8ae2be5c4bf802ddda7d60..ef68a892c4a474628a42faad1f2b1eebe600278e 100644 (file)
@@ -59,8 +59,7 @@ struct nw_port_info {
 
        struct nw_port_ops {
                // Configuration
-               int (*config_read)(nw_port* port);
-               int (*config_write)(nw_port* port);
+               int (*setup)(nw_port* port);
 
                // Get Parent Port
                nw_port* (*get_parent_port)(nw_port* port);
@@ -104,7 +103,7 @@ struct nw_port {
 };
 
 int nw_port_create(nw_port** port, nw_daemon* daemon,
-       nw_port_type_t type, const char* name);
+       nw_port_type_t type, const char* name, nw_config* config);
 int nw_port_create_from_config(nw_port** port, nw_daemon* daemon,
        const char* name, const char* path);