summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Schiffer <mschiffer@universe-factory.net>2019-05-31 18:42:01 +0200
committerMatthias Schiffer <mschiffer@universe-factory.net>2019-05-31 18:42:01 +0200
commitbcef77a7fb3b73d2a2fbcea51012014b62755bb5 (patch)
tree3a5935014a6a2569e592f40086f7858d1207c66c
parent44cb17317c40fa9d39b3402f0826006f20387be5 (diff)
downloadneco-bcef77a7fb3b73d2a2fbcea51012014b62755bb5.tar
neco-bcef77a7fb3b73d2a2fbcea51012014b62755bb5.zip
Switch to a JSON-based config format
We can get rid of a lot of code by ditching out INI format parser. We also remove the support for "subtype" and "generator" templates for now; rather than implementing this in NeCo itself, templates could be implemented as a Lua DSL later.
-rw-r--r--meson.build1
-rw-r--r--src/config-ini.c140
-rw-r--r--src/config-ini.h24
-rw-r--r--src/config-load.c108
-rw-r--r--src/config-process.c115
-rw-r--r--src/config-process.h13
-rw-r--r--src/device-bridge.c4
-rw-r--r--src/device-interface.c117
-rw-r--r--src/device.h5
-rw-r--r--src/keywords.c31
-rw-r--r--src/keywords.def9
-rw-r--r--src/keywords.h12
-rw-r--r--src/meson.build5
-rw-r--r--src/util.h26
14 files changed, 124 insertions, 486 deletions
diff --git a/meson.build b/meson.build
index d7a554a..0cbd657 100644
--- a/meson.build
+++ b/meson.build
@@ -3,6 +3,7 @@ project('neco', 'c', default_options : ['c_std=gnu11'])
cc = meson.get_compiler('c')
libubox_dep = cc.find_library('ubox')
+libjson_c_dep = dependency('json-c', method : 'pkg-config')
libmnl_dep = dependency('libmnl', method : 'pkg-config')
subdir('src')
diff --git a/src/config-ini.c b/src/config-ini.c
deleted file mode 100644
index 0461e13..0000000
--- a/src/config-ini.c
+++ /dev/null
@@ -1,140 +0,0 @@
-#include "config-ini.h"
-
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-static void free_field(ini_field_t *field) {
- free(field->key);
- free(field->value);
- free(field);
-}
-
-static void free_section(ini_section_t *section) {
- ini_field_t *field, *tmp;
-
- list_for_each_entry_safe(field, tmp, &section->fields, node)
- free_field(field);
-
- free(section->name);
- free(section);
-}
-
-void free_ini_file(ini_file_t *file) {
- ini_section_t *section, *tmp;
-
- list_for_each_entry_safe(section, tmp, &file->sections, node)
- free_section(section);
-
- free(file);
-}
-
-static bool add_field(ini_section_t *section, const char *key, const char *value) {
- ini_field_t *field = calloc(1, sizeof(*field));
- if (!field)
- return false;
-
- field->key = strdup(key);
- field->value = strdup(value);
-
- if (!field->key || !field->value) {
- free_field(field);
- return false;
- }
-
- list_add_tail(&field->node, &section->fields);
-
- return true;
-}
-
-static ini_section_t * add_section(ini_file_t *file, const char *name) {
- ini_section_t *section = calloc(1, sizeof(*section));
- if (!section)
- return NULL;
-
- section->name = strdup(name);
- if (!section->name) {
- free(section);
- return false;
- }
-
- INIT_LIST_HEAD(&section->fields);
- list_add_tail(&section->node, &file->sections);
-
- return section;
-}
-
-ini_file_t * read_ini_file(FILE *f) {
- ini_file_t *file = calloc(1, sizeof(*file));
- if (!file)
- goto error;
-
- INIT_LIST_HEAD(&file->sections);
-
- ini_section_t *section = NULL;
- int err = 0;
-
- char *line = NULL;
- size_t n = 0;
-
- while (getline(&line, &n, f) >= 0) {
- char *input = line;
-
- while (isspace(input[0]))
- input++;
-
- if (input[0] == '#' || input[0] == '\0')
- continue;
-
- size_t len = strlen(input);
-
- while (isspace(input[len-1]))
- len--;
-
- if (input[0] == '[') {
- if (input[len-1] != ']') {
- err = EINVAL;
- goto error;
- }
-
- input[len-1] = '\0';
-
- section = add_section(file, input+1);
- if (!section)
- goto error;
- } else {
- if (!section) {
- err = EINVAL;
- goto error;
- }
-
- input[len] = '\0';
-
- char *delim = strchr(input, '=');
- if (!delim) {
- err = EINVAL;
- goto error;
- }
-
- *delim = '\0';
-
- if (!add_field(section, input, delim+1))
- goto error;
- }
- }
-
- if (ferror(f))
- err = EIO;
-
-error:
- free(line);
-
- if (err) {
- free_ini_file(file);
- errno = err;
- return NULL;
- }
-
- return file;
-}
diff --git a/src/config-ini.h b/src/config-ini.h
deleted file mode 100644
index 48400fd..0000000
--- a/src/config-ini.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include <libubox/list.h>
-
-#include <stdio.h>
-
-typedef struct _ini_field {
- struct list_head node;
- char *key;
- char *value;
-} ini_field_t;
-
-typedef struct _ini_section {
- struct list_head node;
- struct list_head fields;
- char *name;
-} ini_section_t;
-
-typedef struct _ini_file {
- struct list_head sections;
-} ini_file_t;
-
-ini_file_t * read_ini_file(FILE *f);
-void free_ini_file(ini_file_t *file);
diff --git a/src/config-load.c b/src/config-load.c
index 63a87b4..19b9d3a 100644
--- a/src/config-load.c
+++ b/src/config-load.c
@@ -14,110 +14,16 @@
#include <string.h>
#include <unistd.h>
-typedef struct _load_ctx {
- struct avl_tree subtypes;
- struct avl_tree devices;
-} load_ctx_t;
-
-static const char * extname(const char *filename) {
- const char *dot = strrchr(filename, '.');
- return dot ? (dot+1) : NULL;
-}
-
-static bool isfile(int fd) {
- struct stat buf;
- if (fstat(fd, &buf) < 0)
- return false;
-
- return (buf.st_mode & S_IFMT) == S_IFREG;
-}
-
-static bool read_config_file(load_ctx_t *ctx, int dirfd, const char *filename) {
- const char *ext = extname(filename);
- if (!ext)
- return true;
-
- struct avl_tree *target_tree;
- if (strcmp(ext, "sub") == 0 || strcmp(ext, "gen") == 0)
- target_tree = &ctx->subtypes;
- else if (get_device_type(ext))
- target_tree = &ctx->devices;
- else
- return true;
-
- int fd = openat(dirfd, filename, O_RDONLY);
- if (fd < 0)
- return false;
-
- if (!isfile(fd)) {
- close(fd);
- return true;
- }
-
- FILE *f = fdopen(fd, "r");
- if (!f) {
- close(fd);
- return false;
- }
-
- ini_file_t *data = read_ini_file(f);
- fclose(f);
-
- if (!data)
- return false;
-
- config_object_t *obj = calloc(1, sizeof(*obj));
- if (!obj) {
- free_ini_file(data);
- return false;
- }
- obj->data = data;
-
- char *name = strndup(filename, (ext - filename) - 1);
- if (!name) {
- config_object_free(obj);
- return false;
- }
- NODE_NAME(obj) = name;
-
- obj->type = strdup(ext);
- if (!obj->type) {
- config_object_free(obj);
+bool read_config(const char *path) {
+ struct json_object *config = json_object_from_file(path);
+ if (!config)
return false;
- }
- avl_insert(target_tree, &obj->node);
-
- return true;
-}
+ struct avl_tree *devices = config_process(config);
+ json_object_put(config);
-static bool read_config_dir(load_ctx_t *ctx, const char *path) {
- DIR *dir = opendir(path);
- if (!dir)
+ if (!devices)
return false;
-
- int fd = dirfd(dir);
-
- struct dirent *ent;
- while ((ent = readdir(dir)) != NULL)
- read_config_file(ctx, fd, ent->d_name);
-
- closedir(dir);
-
- return true;
-}
-
-bool read_config(const char *path) {
- load_ctx_t ctx;
- avl_init(&ctx.subtypes, avl_strcmp, true, NULL);
- avl_init(&ctx.devices, avl_strcmp, true, NULL);
-
- bool ret = read_config_dir(&ctx, path);
-
- struct avl_tree *subtypes = config_process_subtypes(&ctx.subtypes);
- struct avl_tree *devices = config_process_devices(&ctx.devices);
-
- free(subtypes);
device_t *dev, *tmp;
avl_for_each_element(devices, dev, node)
@@ -131,5 +37,5 @@ bool read_config(const char *path) {
free(devices);
- return ret;
+ return true;
}
diff --git a/src/config-process.c b/src/config-process.c
index e71b228..a0671cb 100644
--- a/src/config-process.c
+++ b/src/config-process.c
@@ -1,6 +1,5 @@
#include "config-process.h"
#include "device.h"
-#include "keywords.h"
#include "util.h"
#include <libubox/avl-cmp.h>
@@ -9,6 +8,7 @@
#include <stdlib.h>
#include <string.h>
+
typedef struct _process_ctx {
struct avl_tree *ret;
} process_ctx_t;
@@ -17,92 +17,28 @@ typedef struct _config_subtype {
struct avl_node node;
} config_subtype_t;
-void config_object_free(config_object_t *obj) {
- free_ini_file(obj->data);
- free(obj->type);
- free(NODE_NAME(obj));
- free(obj);
-}
-
-static bool subtype_supported(const process_ctx_t *ctx, const char *type) {
- switch (lookup_keyword(type)) {
- case KW_Properties:
- case KW_Generate:
- case KW_Static:
- case KW_DHCP:
- case KW_DHCPv6:
- return true;
-
- default:
- return avl_find(ctx->ret, type);
- }
-}
-static config_subtype_t * config_process_subtype(const process_ctx_t *ctx, config_object_t *obj) {
- ini_section_t *section;
- list_for_each_entry(section, &obj->data->sections, node) {
- printf("%s %s %i\n", NODE_NAME(obj), section->name, subtype_supported(ctx, section->name));
+static device_t * config_process_device(const char *name, struct json_object *obj) {
+ const char *typename = NULL;
+ struct json_object *device = neco_json_get_value(obj, "device", json_type_object);
+ if (device)
+ typename = neco_json_get_string(obj, "type");
- if (!subtype_supported(ctx, section->name))
- return NULL;
- }
-
- config_subtype_t *ret = calloc(1, sizeof(*ret));
- if (!ret)
+ const device_type_t *type = get_device_type(typename ?: "interface");
+ if (!type)
return NULL;
- char *name = strdup(NODE_NAME(obj));
- if (!name) {
- free(ret);
- return NULL;
- }
-
- NODE_NAME(ret) = name;
-
- return ret;
+ return type->process_config(name, obj);
}
-struct avl_tree * config_process_subtypes(struct avl_tree *sub) {
- process_ctx_t ctx;
- ctx.ret = calloc(1, sizeof(*ctx.ret));
- if (!ctx.ret)
+struct avl_tree * config_process(struct json_object *config) {
+ if (!json_object_is_type(config, json_type_object))
return NULL;
- avl_init(ctx.ret, avl_strcmp, false, NULL);
-
- while (true) {
- size_t processed = 0;
-
- config_object_t *obj, *tmp;
- avl_for_each_element_safe(sub, obj, node, tmp) {
- config_subtype_t *t = config_process_subtype(&ctx, obj);
- if (!t)
- continue;
-
- avl_delete(sub, &obj->node);
- config_object_free(obj);
-
- avl_insert(ctx.ret, &t->node);
-
- processed++;
- }
-
- if (!processed)
- break;
- }
-
- return ctx.ret;
-}
-
-static device_t * config_process_device(config_object_t *obj) {
- const device_type_t *type = get_device_type(obj->type);
-
- assert(type != NULL);
-
- return type->process_config(NODE_NAME(obj), obj->data);
-}
+ struct json_object *devices = neco_json_get_value(config, "devices", json_type_object);
+ if (!devices)
+ return NULL;
-struct avl_tree * config_process_devices(struct avl_tree *devices) {
process_ctx_t ctx;
ctx.ret = calloc(1, sizeof(*ctx.ret));
if (!ctx.ret)
@@ -110,25 +46,12 @@ struct avl_tree * config_process_devices(struct avl_tree *devices) {
avl_init(ctx.ret, avl_strcmp, false, NULL);
- while (true) {
- size_t processed = 0;
-
- config_object_t *obj, *tmp;
- avl_for_each_element_safe(devices, obj, node, tmp) {
- device_t *device = config_process_device(obj);
- if (!device)
- continue;
-
- avl_delete(devices, &obj->node);
- config_object_free(obj);
-
- avl_insert(ctx.ret, &device->node);
-
- processed++;
- }
+ json_object_object_foreach(devices, name, obj) {
+ device_t *device = config_process_device(name, obj);
+ if (!device)
+ continue;
- if (!processed)
- break;
+ avl_insert(ctx.ret, &device->node);
}
return ctx.ret;
diff --git a/src/config-process.h b/src/config-process.h
index 501e765..3903d78 100644
--- a/src/config-process.h
+++ b/src/config-process.h
@@ -1,15 +1,6 @@
#pragma once
-#include "config-ini.h"
-
+#include <json-c/json.h>
#include <libubox/avl.h>
-typedef struct _config_object {
- struct avl_node node;
- char *type;
- ini_file_t *data;
-} config_object_t;
-
-void config_object_free(config_object_t *obj);
-struct avl_tree * config_process_subtypes(struct avl_tree *sub);
-struct avl_tree * config_process_devices(struct avl_tree *devices);
+struct avl_tree * config_process(struct json_object *config);
diff --git a/src/device-bridge.c b/src/device-bridge.c
index f973dbf..4a8d24c 100644
--- a/src/device-bridge.c
+++ b/src/device-bridge.c
@@ -1,6 +1,8 @@
#include "device.h"
-static device_t * bridge_process_config(const char *name, const ini_file_t *config) {
+#include <stdio.h>
+
+static device_t * bridge_process_config(const char *name, struct json_object *config) {
printf("Bridge: %s\n", name);
return NULL;
}
diff --git a/src/device-interface.c b/src/device-interface.c
index a2aae14..ef85089 100644
--- a/src/device-interface.c
+++ b/src/device-interface.c
@@ -1,5 +1,4 @@
#include "device.h"
-#include "keywords.h"
#include "netlink.h"
#include "util.h"
#include "vector.h"
@@ -109,22 +108,43 @@ static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_ho
return true;
}
-static bool process_section_device(device_interface_t *iface, const ini_section_t *section) {
- ini_field_t *field;
+static void interface_free(device_t *dev) {
+ device_interface_t *iface = container_of(dev, device_interface_t, device);
- list_for_each_entry(field, &section->fields, node) {
- switch (lookup_keyword(field->key)) {
- default:
- fprintf(stderr, "interface: [Device]: unknown field %s\n", field->key);
- }
- }
+ VECTOR_FREE(iface->addrs);
+ free(NODE_NAME(dev));
+ free(iface);
+}
+static bool process_section_device(device_interface_t *iface, struct json_object *section) {
return true;
}
-static bool process_section_static(device_interface_t *iface, const ini_section_t *section) {
- ini_field_t *field;
+static bool process_section_static(device_interface_t *iface, struct json_object *section) {
+ struct json_object *addresses = neco_json_get_value(section, "addresses", json_type_array);
+ if (addresses) {
+ for (size_t i = 0; i < json_object_array_length(addresses); i++) {
+ struct json_object *address = json_object_array_get_idx(addresses, i);
+ if (!json_object_is_type(address, json_type_string)) {
+ fprintf(stderr, "interface: static: invalid address entry of type %s\n", json_type_to_name(json_object_get_type(address)));
+ continue;
+ }
+
+ ipaddr_prefix_t p;
+ if (!parse_prefix(&p, json_object_get_string(address), true)) {
+ fprintf(stderr, "interface: static: unable to parse Address %s\n", json_object_get_string(address));
+ break;
+ }
+ if (!VECTOR_ADD(iface->addrs, p)) {
+ fprintf(stderr, "interface: static: adding address failed\n");
+ return false;
+ }
+ }
+ }
+
+
+ /*
list_for_each_entry(field, &section->fields, node) {
switch (lookup_keyword(field->key)) {
case KW_Address: {
@@ -146,16 +166,42 @@ static bool process_section_static(device_interface_t *iface, const ini_section_
fprintf(stderr, "interface: [Static]: unknown field %s\n", field->key);
}
}
+ */
return true;
}
-static void interface_free(device_t *dev) {
- device_interface_t *iface = container_of(dev, device_interface_t, device);
+static device_t * interface_process_config(const char *name, struct json_object *config) {
+ device_interface_t *iface = calloc(1, sizeof(*iface));
+ if (!iface)
+ return NULL;
- VECTOR_FREE(iface->addrs);
- free(NODE_NAME(dev));
- free(iface);
+ device_t *dev = &iface->device;
+ dev->type = &device_type_interface;
+
+ NODE_NAME(dev) = strdup(name);
+ if (!NODE_NAME(dev)) {
+ free(iface);
+ return NULL;
+ }
+
+ struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object);
+ if (sec_device) {
+ if (!process_section_device(iface, sec_device))
+ goto err;
+ }
+
+ struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object);
+ if (sec_static) {
+ if (!process_section_static(iface, sec_static))
+ goto err;
+ }
+
+ return dev;
+
+err:
+ interface_free(dev);
+ return NULL;
}
static bool interface_set_link_flags(unsigned index, unsigned change, unsigned flags) {
@@ -281,45 +327,6 @@ static void interface_release(device_t *dev) {
interface_set_link_state(index, false);
}
-static device_t * interface_process_config(const char *name, const ini_file_t *config) {
- device_interface_t *iface = calloc(1, sizeof(*iface));
- if (!iface)
- return NULL;
-
- device_t *dev = &iface->device;
- dev->type = &device_type_interface;
-
- NODE_NAME(dev) = strdup(name);
- if (!NODE_NAME(dev)) {
- free(iface);
- return NULL;
- }
-
- const ini_section_t *section;
- list_for_each_entry(section, &config->sections, node) {
- switch (lookup_keyword(section->name)) {
- case KW_Device:
- if (!process_section_device(iface, section))
- goto err;
- break;
-
- case KW_Static:
- if (!process_section_static(iface, section))
- goto err;
- break;
-
- default:
- fprintf(stderr, "interface: unknown section %s\n", section->name);
- }
- }
-
- return dev;
-
-err:
- interface_free(dev);
- return NULL;
-}
-
static device_type_t device_type_interface = {
.process_config = interface_process_config,
.free = interface_free,
diff --git a/src/device.h b/src/device.h
index 33acfbb..e44c695 100644
--- a/src/device.h
+++ b/src/device.h
@@ -1,7 +1,6 @@
#pragma once
-#include "config-ini.h"
-
+#include <json-c/json.h>
#include <libubox/avl.h>
extern struct avl_tree device_types;
@@ -15,7 +14,7 @@ typedef struct _device {
struct _device_type {
struct avl_node node;
- device_t * (*process_config)(const char *name, const ini_file_t *config);
+ device_t * (*process_config)(const char *name, struct json_object *config);
void (*free)(device_t *device);
void (*init)(device_t *device);
diff --git a/src/keywords.c b/src/keywords.c
deleted file mode 100644
index 12393bd..0000000
--- a/src/keywords.c
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "keywords.h"
-
-#include <libubox/utils.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-static const char *const keywords[] = {
-
-#define KW(kw) #kw,
-#include "keywords.def"
-#undef KW
-
-};
-
-static int compare_keywords(const void *a, const void *b) {
- const char *const *ea = a, *const *eb = b;
- return strcmp(*ea, *eb);
-}
-
-keyword_t lookup_keyword(const char *keyword) {
- const char *const *entry = bsearch(
- &keyword, keywords, ARRAY_SIZE(keywords), sizeof(const char *),
- compare_keywords
- );
-
- if (!entry)
- return UNKNOWN_KEYWORD;
-
- return (keyword_t) (entry - keywords + 1);
-}
diff --git a/src/keywords.def b/src/keywords.def
deleted file mode 100644
index 0dbd866..0000000
--- a/src/keywords.def
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Config section and field names; keep sorted! */
-
-KW(Address)
-KW(DHCP)
-KW(DHCPv6)
-KW(Device)
-KW(Generate)
-KW(Properties)
-KW(Static)
diff --git a/src/keywords.h b/src/keywords.h
deleted file mode 100644
index 05d809e..0000000
--- a/src/keywords.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#pragma once
-
-typedef enum _keyword {
- UNKNOWN_KEYWORD = 0,
-
-#define KW(kw) KW_##kw,
-#include "keywords.def"
-#undef KW
-
-} keyword_t;
-
-keyword_t lookup_keyword(const char *keyword);
diff --git a/src/meson.build b/src/meson.build
index 9056cf3..b359ab4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,19 +1,18 @@
src = [
'neco.c',
- 'config-ini.c',
'config-load.c',
'config-process.c',
'device.c',
'device-bridge.c',
'device-interface.c',
- 'keywords.c',
'netlink.c',
'vector.c',
]
dep = [
- libubox_dep,
+ libjson_c_dep,
libmnl_dep,
+ libubox_dep,
]
executable('neco', sources: src, dependencies: dep)
diff --git a/src/util.h b/src/util.h
index 31b13b6..2e60467 100644
--- a/src/util.h
+++ b/src/util.h
@@ -1,3 +1,29 @@
#pragma once
+#include <json-c/json.h>
+
+
#define NODE_NAME(c) (*(char **)&(c)->node.key)
+
+
+static inline struct json_object * neco_json_get_value(
+ struct json_object *obj,
+ const char *key,
+ enum json_type type
+) {
+ struct json_object *value;
+ if (!json_object_object_get_ex(obj, key, &value))
+ return NULL;
+ if (!json_object_is_type(value, type))
+ return NULL;
+
+ return value;
+}
+
+static inline const char * neco_json_get_string(struct json_object *obj, const char *key) {
+ struct json_object *value = neco_json_get_value(obj, key, json_type_string);
+ if (!value)
+ return NULL;
+
+ return json_object_get_string(value);
+}