From bcef77a7fb3b73d2a2fbcea51012014b62755bb5 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 31 May 2019 18:42:01 +0200 Subject: 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. --- meson.build | 1 + src/config-ini.c | 140 ------------------------------------------------- src/config-ini.h | 24 --------- src/config-load.c | 108 +++----------------------------------- src/config-process.c | 115 +++++++--------------------------------- src/config-process.h | 13 +---- src/device-bridge.c | 4 +- src/device-interface.c | 117 ++++++++++++++++++++++------------------- src/device.h | 5 +- src/keywords.c | 31 ----------- src/keywords.def | 9 ---- src/keywords.h | 12 ----- src/meson.build | 5 +- src/util.h | 26 +++++++++ 14 files changed, 124 insertions(+), 486 deletions(-) delete mode 100644 src/config-ini.c delete mode 100644 src/config-ini.h delete mode 100644 src/keywords.c delete mode 100644 src/keywords.def delete mode 100644 src/keywords.h 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 -#include -#include -#include - -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, §ion->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, §ion->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(§ion->fields); - list_add_tail(§ion->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 - -#include - -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 #include -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 @@ -9,6 +8,7 @@ #include #include + 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 #include -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 + +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, §ion->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, §ion->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 #include 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 - -#include -#include - -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 + + #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); +} -- cgit v1.2.3