From: Michael Tremer Date: Thu, 8 Jun 2023 15:04:38 +0000 (+0000) Subject: config: Extend the parser to easier read/write configs X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=082d81a3b4115422a0d58fe8c3cf504350c76bfe;p=network.git config: Extend the parser to easier read/write configs Signed-off-by: Michael Tremer --- diff --git a/src/networkd/address.h b/src/networkd/address.h index 7937d62c..afcbca15 100644 --- a/src/networkd/address.h +++ b/src/networkd/address.h @@ -21,6 +21,7 @@ #ifndef NETWORKD_ADDRESS_H #define NETWORKD_ADDRESS_H +#include #include #include #include @@ -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; } diff --git a/src/networkd/config.c b/src/networkd/config.c index 78448df2..6f22da2c 100644 --- a/src/networkd/config.c +++ b/src/networkd/config.c @@ -26,6 +26,7 @@ #include #include +#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; +} diff --git a/src/networkd/config.h b/src/networkd/config.h index e17c0167..b5417e48 100644 --- a/src/networkd/config.h +++ b/src/networkd/config.h @@ -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 */ diff --git a/src/networkd/port-bonding.c b/src/networkd/port-bonding.c index bbc3fb7a..a2e0fc3f 100644 --- a/src/networkd/port-bonding.c +++ b/src/networkd/port-bonding.c @@ -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, diff --git a/src/networkd/port-vlan.c b/src/networkd/port-vlan.c index 6e13ba9a..2d89a09c 100644 --- a/src/networkd/port-vlan.c +++ b/src/networkd/port-vlan.c @@ -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, diff --git a/src/networkd/port.c b/src/networkd/port.c index a7fb826a..cab8fc5f 100644 --- a/src/networkd/port.c +++ b/src/networkd/port.c @@ -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; } diff --git a/src/networkd/port.h b/src/networkd/port.h index e16b9572..ef68a892 100644 --- a/src/networkd/port.h +++ b/src/networkd/port.h @@ -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);