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.
This commit is contained in:
parent
44cb17317c
commit
bcef77a7fb
14 changed files with 128 additions and 490 deletions
|
@ -3,6 +3,7 @@ project('neco', 'c', default_options : ['c_std=gnu11'])
|
||||||
cc = meson.get_compiler('c')
|
cc = meson.get_compiler('c')
|
||||||
libubox_dep = cc.find_library('ubox')
|
libubox_dep = cc.find_library('ubox')
|
||||||
|
|
||||||
|
libjson_c_dep = dependency('json-c', method : 'pkg-config')
|
||||||
libmnl_dep = dependency('libmnl', method : 'pkg-config')
|
libmnl_dep = dependency('libmnl', method : 'pkg-config')
|
||||||
|
|
||||||
subdir('src')
|
subdir('src')
|
||||||
|
|
140
src/config-ini.c
140
src/config-ini.c
|
@ -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, §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;
|
|
||||||
}
|
|
|
@ -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);
|
|
|
@ -14,110 +14,16 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.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);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
avl_insert(target_tree, &obj->node);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool read_config_dir(load_ctx_t *ctx, const char *path) {
|
|
||||||
DIR *dir = opendir(path);
|
|
||||||
if (!dir)
|
|
||||||
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) {
|
bool read_config(const char *path) {
|
||||||
load_ctx_t ctx;
|
struct json_object *config = json_object_from_file(path);
|
||||||
avl_init(&ctx.subtypes, avl_strcmp, true, NULL);
|
if (!config)
|
||||||
avl_init(&ctx.devices, avl_strcmp, true, NULL);
|
return false;
|
||||||
|
|
||||||
bool ret = read_config_dir(&ctx, path);
|
struct avl_tree *devices = config_process(config);
|
||||||
|
json_object_put(config);
|
||||||
|
|
||||||
struct avl_tree *subtypes = config_process_subtypes(&ctx.subtypes);
|
if (!devices)
|
||||||
struct avl_tree *devices = config_process_devices(&ctx.devices);
|
return false;
|
||||||
|
|
||||||
free(subtypes);
|
|
||||||
|
|
||||||
device_t *dev, *tmp;
|
device_t *dev, *tmp;
|
||||||
avl_for_each_element(devices, dev, node)
|
avl_for_each_element(devices, dev, node)
|
||||||
|
@ -131,5 +37,5 @@ bool read_config(const char *path) {
|
||||||
|
|
||||||
free(devices);
|
free(devices);
|
||||||
|
|
||||||
return ret;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#include "config-process.h"
|
#include "config-process.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "keywords.h"
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <libubox/avl-cmp.h>
|
#include <libubox/avl-cmp.h>
|
||||||
|
@ -9,6 +8,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
typedef struct _process_ctx {
|
typedef struct _process_ctx {
|
||||||
struct avl_tree *ret;
|
struct avl_tree *ret;
|
||||||
} process_ctx_t;
|
} process_ctx_t;
|
||||||
|
@ -17,52 +17,28 @@ typedef struct _config_subtype {
|
||||||
struct avl_node node;
|
struct avl_node node;
|
||||||
} config_subtype_t;
|
} 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) {
|
static device_t * config_process_device(const char *name, struct json_object *obj) {
|
||||||
switch (lookup_keyword(type)) {
|
const char *typename = NULL;
|
||||||
case KW_Properties:
|
struct json_object *device = neco_json_get_value(obj, "device", json_type_object);
|
||||||
case KW_Generate:
|
if (device)
|
||||||
case KW_Static:
|
typename = neco_json_get_string(obj, "type");
|
||||||
case KW_DHCP:
|
|
||||||
case KW_DHCPv6:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
const device_type_t *type = get_device_type(typename ?: "interface");
|
||||||
return avl_find(ctx->ret, type);
|
if (!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));
|
|
||||||
|
|
||||||
if (!subtype_supported(ctx, section->name))
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
config_subtype_t *ret = calloc(1, sizeof(*ret));
|
|
||||||
if (!ret)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
char *name = strdup(NODE_NAME(obj));
|
return type->process_config(name, obj);
|
||||||
if (!name) {
|
|
||||||
free(ret);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_NAME(ret) = name;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct avl_tree * config_process_subtypes(struct avl_tree *sub) {
|
struct avl_tree * config_process(struct json_object *config) {
|
||||||
|
if (!json_object_is_type(config, json_type_object))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
struct json_object *devices = neco_json_get_value(config, "devices", json_type_object);
|
||||||
|
if (!devices)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
process_ctx_t ctx;
|
process_ctx_t ctx;
|
||||||
ctx.ret = calloc(1, sizeof(*ctx.ret));
|
ctx.ret = calloc(1, sizeof(*ctx.ret));
|
||||||
if (!ctx.ret)
|
if (!ctx.ret)
|
||||||
|
@ -70,65 +46,12 @@ struct avl_tree * config_process_subtypes(struct avl_tree *sub) {
|
||||||
|
|
||||||
avl_init(ctx.ret, avl_strcmp, false, NULL);
|
avl_init(ctx.ret, avl_strcmp, false, NULL);
|
||||||
|
|
||||||
while (true) {
|
json_object_object_foreach(devices, name, obj) {
|
||||||
size_t processed = 0;
|
device_t *device = config_process_device(name, obj);
|
||||||
|
|
||||||
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 avl_tree * config_process_devices(struct avl_tree *devices) {
|
|
||||||
process_ctx_t ctx;
|
|
||||||
ctx.ret = calloc(1, sizeof(*ctx.ret));
|
|
||||||
if (!ctx.ret)
|
|
||||||
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(devices, obj, node, tmp) {
|
|
||||||
device_t *device = config_process_device(obj);
|
|
||||||
if (!device)
|
if (!device)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
avl_delete(devices, &obj->node);
|
|
||||||
config_object_free(obj);
|
|
||||||
|
|
||||||
avl_insert(ctx.ret, &device->node);
|
avl_insert(ctx.ret, &device->node);
|
||||||
|
|
||||||
processed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!processed)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx.ret;
|
return ctx.ret;
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "config-ini.h"
|
#include <json-c/json.h>
|
||||||
|
|
||||||
#include <libubox/avl.h>
|
#include <libubox/avl.h>
|
||||||
|
|
||||||
typedef struct _config_object {
|
struct avl_tree * config_process(struct json_object *config);
|
||||||
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);
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "device.h"
|
#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);
|
printf("Bridge: %s\n", name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "keywords.h"
|
|
||||||
#include "netlink.h"
|
#include "netlink.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
@ -109,22 +108,43 @@ static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_ho
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool process_section_device(device_interface_t *iface, const ini_section_t *section) {
|
static void interface_free(device_t *dev) {
|
||||||
ini_field_t *field;
|
device_interface_t *iface = container_of(dev, device_interface_t, device);
|
||||||
|
|
||||||
list_for_each_entry(field, §ion->fields, node) {
|
VECTOR_FREE(iface->addrs);
|
||||||
switch (lookup_keyword(field->key)) {
|
free(NODE_NAME(dev));
|
||||||
default:
|
free(iface);
|
||||||
fprintf(stderr, "interface: [Device]: unknown field %s\n", field->key);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
static bool process_section_device(device_interface_t *iface, struct json_object *section) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool process_section_static(device_interface_t *iface, const ini_section_t *section) {
|
static bool process_section_static(device_interface_t *iface, struct json_object *section) {
|
||||||
ini_field_t *field;
|
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) {
|
list_for_each_entry(field, §ion->fields, node) {
|
||||||
switch (lookup_keyword(field->key)) {
|
switch (lookup_keyword(field->key)) {
|
||||||
case KW_Address: {
|
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);
|
fprintf(stderr, "interface: [Static]: unknown field %s\n", field->key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void interface_free(device_t *dev) {
|
static device_t * interface_process_config(const char *name, struct json_object *config) {
|
||||||
device_interface_t *iface = container_of(dev, device_interface_t, device);
|
device_interface_t *iface = calloc(1, sizeof(*iface));
|
||||||
|
if (!iface)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
VECTOR_FREE(iface->addrs);
|
device_t *dev = &iface->device;
|
||||||
free(NODE_NAME(dev));
|
dev->type = &device_type_interface;
|
||||||
|
|
||||||
|
NODE_NAME(dev) = strdup(name);
|
||||||
|
if (!NODE_NAME(dev)) {
|
||||||
free(iface);
|
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) {
|
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);
|
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 = {
|
static device_type_t device_type_interface = {
|
||||||
.process_config = interface_process_config,
|
.process_config = interface_process_config,
|
||||||
.free = interface_free,
|
.free = interface_free,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "config-ini.h"
|
#include <json-c/json.h>
|
||||||
|
|
||||||
#include <libubox/avl.h>
|
#include <libubox/avl.h>
|
||||||
|
|
||||||
extern struct avl_tree device_types;
|
extern struct avl_tree device_types;
|
||||||
|
@ -15,7 +14,7 @@ typedef struct _device {
|
||||||
|
|
||||||
struct _device_type {
|
struct _device_type {
|
||||||
struct avl_node node;
|
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 (*free)(device_t *device);
|
||||||
|
|
||||||
void (*init)(device_t *device);
|
void (*init)(device_t *device);
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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)
|
|
|
@ -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);
|
|
|
@ -1,19 +1,18 @@
|
||||||
src = [
|
src = [
|
||||||
'neco.c',
|
'neco.c',
|
||||||
'config-ini.c',
|
|
||||||
'config-load.c',
|
'config-load.c',
|
||||||
'config-process.c',
|
'config-process.c',
|
||||||
'device.c',
|
'device.c',
|
||||||
'device-bridge.c',
|
'device-bridge.c',
|
||||||
'device-interface.c',
|
'device-interface.c',
|
||||||
'keywords.c',
|
|
||||||
'netlink.c',
|
'netlink.c',
|
||||||
'vector.c',
|
'vector.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
dep = [
|
dep = [
|
||||||
libubox_dep,
|
libjson_c_dep,
|
||||||
libmnl_dep,
|
libmnl_dep,
|
||||||
|
libubox_dep,
|
||||||
]
|
]
|
||||||
|
|
||||||
executable('neco', sources: src, dependencies: dep)
|
executable('neco', sources: src, dependencies: dep)
|
||||||
|
|
26
src/util.h
26
src/util.h
|
@ -1,3 +1,29 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <json-c/json.h>
|
||||||
|
|
||||||
|
|
||||||
#define NODE_NAME(c) (*(char **)&(c)->node.key)
|
#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);
|
||||||
|
}
|
||||||
|
|
Reference in a new issue