]> git.ipfire.org Git - people/ms/network.git/blobdiff - src/networkd/config.c
config: Add string buffer type
[people/ms/network.git] / src / networkd / config.c
index 788308f8b3db4c59d421e418e9a113163c0a904e..c6281cb249021a4c1d25111b75f2d5af58d4d008 100644 (file)
@@ -24,7 +24,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/queue.h>
+#include <unistd.h>
 
+#include "address.h"
 #include "config.h"
 #include "logging.h"
 #include "string.h"
@@ -36,21 +38,38 @@ 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* value;
+       size_t length;
+
+       // Callbacks
+       nw_config_option_read_callback_t read_callback;
+       nw_config_option_write_callback_t write_callback;
+       void* data;
+};
+
 struct nw_config {
        int nrefs;
 
-       // The path to the configuration file
-       char path[PATH_MAX];
+       STAILQ_HEAD(config_entries, nw_config_entry) entries;
 
-       STAILQ_HEAD(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(
-               struct nw_config* config, const char* key) {
+               nw_config* config, const char* key) {
        int r;
 
        // Check input value
@@ -72,22 +91,35 @@ static struct nw_config_entry* nw_config_entry_create(
        // Append the new entry
        STAILQ_INSERT_TAIL(&config->entries, entry, nodes);
 
+       return entry;
+
 ERROR:
        nw_config_entry_free(entry);
        return NULL;
 }
 
-static void nw_config_free(struct nw_config* config) {
+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);
 }
 
-int nw_config_create(struct nw_config** config, const char* path) {
+int nw_config_create(nw_config** config, FILE* f) {
        int r;
 
-       struct nw_config* c = calloc(1, sizeof(*c));
+       nw_config* c = calloc(1, sizeof(*c));
        if (!c)
                return 1;
 
@@ -97,15 +129,13 @@ int nw_config_create(struct nw_config** config, const char* path) {
        // Initialise entries
        STAILQ_INIT(&c->entries);
 
-       // Store the path
-       if (path) {
-               r = nw_string_set(c->path, path);
-               if (r)
-                       goto ERROR;
+       // Initialise options
+       STAILQ_INIT(&c->options);
 
-               // Try to read the configuration from path
-               r = nw_config_read(c);
-               if (r)
+       // Read configuration
+       if (f) {
+               r = nw_config_read(c, f);
+               if (r < 0)
                        goto ERROR;
        }
 
@@ -119,13 +149,32 @@ ERROR:
        return r;
 }
 
-struct nw_config* nw_config_ref(struct nw_config* config) {
+int nw_config_open(nw_config** config, const char* path) {
+       FILE* f = NULL;
+       int r;
+
+       // Open path
+       f = fopen(path, "r");
+       if (!f)
+               return -errno;
+
+       // Create a new configuration
+       r = nw_config_create(config, f);
+
+ERROR:
+       if (f)
+               fclose(f);
+
+       return r;
+}
+
+nw_config* nw_config_ref(nw_config* config) {
        config->nrefs++;
 
        return config;
 }
 
-struct nw_config* nw_config_unref(struct nw_config* config) {
+nw_config* nw_config_unref(nw_config* config) {
        if (--config->nrefs > 0)
                return config;
 
@@ -133,14 +182,34 @@ struct nw_config* nw_config_unref(struct nw_config* config) {
        return NULL;
 }
 
-const char* nw_config_path(struct nw_config* config) {
-       if (*config->path)
-               return config->path;
+int nw_config_copy(nw_config* config, nw_config** copy) {
+       struct nw_config_entry* entry = NULL;
+       nw_config* c = NULL;
+       int r;
 
-       return NULL;
+       // 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;
 }
 
-int nw_config_flush(struct nw_config* config) {
+int nw_config_flush(nw_config* config) {
        struct nw_config_entry* entry = NULL;
 
        while (!STAILQ_EMPTY(&config->entries)) {
@@ -154,45 +223,53 @@ int nw_config_flush(struct nw_config* config) {
        return 0;
 }
 
-static int nw_config_readf(struct nw_config* config, FILE* f) {
-       // XXX TODO
+int nw_config_read(nw_config* config, FILE* f) {
+       char* line = NULL;
+       size_t length = 0;
+       int r;
 
-       return 0;
-}
+       ssize_t bytes_read = 0;
 
-int nw_config_read(struct nw_config* config) {
-       FILE* f = NULL;
-       int r;
+       char* key = NULL;
+       char* val = NULL;
 
-       // We cannot read if path is not set
-       if (!*config->path) {
-               errno = ENOTSUP;
-               return 1;
-       }
+       for (;;) {
+               // Read the next line
+               bytes_read = getline(&line, &length, f);
+               if (bytes_read < 0)
+                       break;
 
-       // Open the file
-       f = fopen(config->path, "r");
-       if (!f) {
-               // Silently ignore if the file does not exist
-               if (errno == ENOENT)
-                       return 0;
+               // Key starts at the beginning of the line
+               key = line;
 
-               ERROR("Could not read configuration file %s: %m\n", config->path);
-               r = 1;
-               goto ERROR;
-       }
+               // Value starts after '='
+               val = strchr(line, '=');
 
-       // Read from file
-       r = nw_config_readf(config, f);
+               // Invalid line without a '=' character
+               if (!val)
+                       continue;
 
-ERROR:
-       if (f)
-               fclose(f);
+               // Split the string
+               *val++ = '\0';
+
+               // Strip any whitespace from value
+               r = nw_string_strip(val);
+               if (r)
+                       break;
+
+               // Store the setting
+               r = nw_config_set(config, key, val);
+               if (r)
+                       break;
+       }
+
+       if (line)
+               free(line);
 
        return r;
 }
 
-static int nw_config_writef(struct nw_config* config, FILE* f) {
+int nw_config_write(nw_config* config, FILE* f) {
        struct nw_config_entry* entry = NULL;
        int r;
 
@@ -202,7 +279,7 @@ static int nw_config_writef(struct nw_config* config, FILE* f) {
                        continue;
 
                // Write the entry
-               r = fprintf(f, "%s=\"%s\"\n", entry->key, entry->value);
+               r = fprintf(f, "%s=%s\n", entry->key, entry->value);
                if (r < 0) {
                        ERROR("Failed to write configuration: %m\n");
                        return r;
@@ -212,33 +289,7 @@ static int nw_config_writef(struct nw_config* config, FILE* f) {
        return 0;
 }
 
-int nw_config_write(struct nw_config* config) {
-       int r;
-
-       // We cannot write if path is not set
-       if (!*config->path) {
-               errno = ENOTSUP;
-               return 1;
-       }
-
-       FILE* f = fopen(config->path, "w");
-       if (!f) {
-               ERROR("Failed to open %s for writing: %m\n", config->path);
-               r = 1;
-               goto ERROR;
-       }
-
-       // Write configuration
-       r = nw_config_writef(config, f);
-
-ERROR:
-       if (f)
-               fclose(f);
-
-       return r;
-}
-
-static struct nw_config_entry* nw_config_find(struct nw_config* config, const char* key) {
+static struct nw_config_entry* nw_config_find(nw_config* config, const char* key) {
        struct nw_config_entry* entry = NULL;
 
        STAILQ_FOREACH(entry, &config->entries, nodes) {
@@ -254,7 +305,7 @@ static struct nw_config_entry* nw_config_find(struct nw_config* config, const ch
        return NULL;
 }
 
-int nw_config_del(struct nw_config* config, const char* key) {
+int nw_config_del(nw_config* config, const char* key) {
        struct nw_config_entry* entry = NULL;
 
        // Find an entry matching the key
@@ -273,7 +324,7 @@ int nw_config_del(struct nw_config* config, const char* key) {
        return 0;
 }
 
-const char* nw_config_get(struct nw_config* config, const char* key) {
+const char* nw_config_get(nw_config* config, const char* key) {
        struct nw_config_entry* entry = nw_config_find(config, key);
 
        // Return the value if found and set
@@ -284,9 +335,12 @@ const char* nw_config_get(struct nw_config* config, const char* key) {
        return NULL;
 }
 
-int nw_config_set(struct nw_config* config, const char* key, const char* value) {
+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);
@@ -305,24 +359,251 @@ int nw_config_set(struct nw_config* config, const char* key, const char* value)
        return nw_string_set(entry->value, value);
 }
 
-int nw_config_get_int(struct nw_config* config, const char* key, const int __default) {
+int nw_config_get_int(nw_config* config, const char* key, const int __default) {
+       char* p = NULL;
+       int r;
+
        const char* value = nw_config_get(config, key);
 
        // Return zero if not set
        if (!value)
                return __default;
 
-       return strtoul(value, NULL, 10);
+       // Parse the input
+       r = strtoul(value, &p, 10);
+
+       // If we have characters following the input, we throw it away
+       if (p)
+               return __default;
+
+       return r;
 }
 
-int nw_config_set_int(struct nw_config* config, const char* key, const int value) {
+int nw_config_set_int(nw_config* config, const char* key, const int value) {
        char __value[1024];
        int r;
 
        // Format the value as string
-       r = nw_string_format(__value, "%d\n", value);
+       r = nw_string_format(__value, "%d", value);
        if (r)
                return r;
 
        return nw_config_set(config, key, __value);
 }
+
+static const char* nw_config_true[] = {
+       "true",
+       "yes",
+       "1",
+       NULL,
+};
+
+int nw_config_get_bool(nw_config* config, const char* key) {
+       const char* value = nw_config_get(config, key);
+
+       // No value indicates false
+       if (!value)
+               return 0;
+
+       // Check if we match any known true words
+       for (const char** s = nw_config_true; *s; s++) {
+               if (strcasecmp(value, *s) == 0)
+                       return 1;
+       }
+
+       // No match means false
+       return 0;
+}
+
+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->value, option->length, 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->value, option->length, option->data);
+               if (r < 0)
+                       return r;
+       }
+
+       return 0;
+}
+
+int nw_config_option_add(nw_config* config,
+               const char* key, void* value, const size_t length,
+               nw_config_option_read_callback_t read_callback,
+               nw_config_option_write_callback_t write_callback, void* data) {
+       // Check input
+       if (!key || !value || !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 value
+       option->value = value;
+       option->length = length;
+
+       // Set callbacks
+       option->read_callback = read_callback;
+       option->write_callback = write_callback;
+       option->data = data;
+
+       // 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* value, const size_t length, void* data) {
+       // Fetch the value
+       *(int*)value = nw_config_get_int(config, key, -1);
+
+       return 0;
+}
+
+int nw_config_write_int(nw_config* config,
+               const char* key, const void* value, const size_t length, void* data) {
+       return 0;
+}
+
+// String
+
+int nw_config_read_string(nw_config* config,
+               const char* key, void* value, const size_t length, void* data) {
+       // Fetch the value
+       const char* p = nw_config_get(config, key);
+       if (p)
+               *(const char**)value = p;
+
+       return 0;
+}
+
+int nw_config_write_string(nw_config* config,
+               const char* key, const void* value, const size_t length, void* data) {
+       return nw_config_set(config, key, *(const char**)value);
+}
+
+// String Buffer
+
+int nw_config_read_string_buffer(nw_config* config,
+               const char* key, void* value, const size_t length, void* data) {
+       char* string = (char*)value;
+
+       // Fetch the value
+       const char* p = nw_config_get(config, key);
+       if (p)
+               return __nw_string_set(string, length, p);
+
+       return 0;
+}
+
+// String Table
+
+int nw_config_read_string_table(nw_config* config,
+               const char* key, void* value, const size_t length, void* data) {
+       const char* s = NULL;
+       int* v = (int*)value;
+
+       const nw_string_table_t* table = (nw_string_table_t*)data;
+
+       // Fetch the string
+       s = nw_config_get(config, key);
+       if (!s)
+               return -errno;
+
+       // Lookup the string in the table
+       *v = nw_string_table_lookup_id(table, s);
+
+       // If the result is negative, nothing was found
+       if (*v < 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+int nw_config_write_string_table(nw_config* config,
+               const char* key, const void* value, const size_t length, void* data) {
+       int* v = (int*)value;
+
+       const nw_string_table_t* table = (nw_string_table_t*)data;
+
+       // Lookup the string
+       const char* s = nw_string_table_lookup_string(table, *v);
+       if (!s)
+               return -errno;
+
+       return nw_config_set(config, key, s);
+}
+
+// Address
+
+int nw_config_read_address(nw_config* config,
+               const char* key, void* value, const size_t length, void* data) {
+       nw_address_t* address = (nw_address_t*)value;
+       int r;
+
+       // Fetch the value
+       const char* p = nw_config_get(config, key);
+       if (!p)
+               return -EINVAL;
+
+       r = nw_address_from_string(address, p);
+       if (r < 0)
+               ERROR("Could not parse address: %s\n", p);
+
+       return r;
+}
+
+int nw_config_write_address(nw_config* config,
+               const char* key, const void* value, const size_t length, void* data) {
+       const nw_address_t* address = (nw_address_t*)value;
+       int r;
+
+       // Format the address to string
+       char* p = nw_address_to_string(address);
+       if (!p)
+               return -errno;
+
+       // Store the value
+       r = nw_config_set(config, key, p);
+       if (r < 0)
+               goto ERROR;
+
+       // Success
+       r = 0;
+
+ERROR:
+       if (p)
+               free(p);
+
+       return r;
+}