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')
|
||||
libubox_dep = cc.find_library('ubox')
|
||||
|
||||
libjson_c_dep = dependency('json-c', method : 'pkg-config')
|
||||
libmnl_dep = dependency('libmnl', method : 'pkg-config')
|
||||
|
||||
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 <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) {
|
||||
load_ctx_t ctx;
|
||||
avl_init(&ctx.subtypes, avl_strcmp, true, NULL);
|
||||
avl_init(&ctx.devices, avl_strcmp, true, NULL);
|
||||
struct json_object *config = json_object_from_file(path);
|
||||
if (!config)
|
||||
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);
|
||||
struct avl_tree *devices = config_process_devices(&ctx.devices);
|
||||
|
||||
free(subtypes);
|
||||
if (!devices)
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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,52 +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;
|
||||
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");
|
||||
|
||||
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));
|
||||
|
||||
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 type->process_config(name, obj);
|
||||
}
|
||||
|
||||
struct avl_tree * config_process(struct json_object *config) {
|
||||
if (!json_object_is_type(config, json_type_object))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NODE_NAME(ret) = name;
|
||||
struct json_object *devices = neco_json_get_value(config, "devices", json_type_object);
|
||||
if (!devices)
|
||||
return NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct avl_tree * config_process_subtypes(struct avl_tree *sub) {
|
||||
process_ctx_t ctx;
|
||||
ctx.ret = calloc(1, sizeof(*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);
|
||||
|
||||
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 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);
|
||||
json_object_object_foreach(devices, name, obj) {
|
||||
device_t *device = config_process_device(name, obj);
|
||||
if (!device)
|
||||
continue;
|
||||
|
||||
avl_delete(devices, &obj->node);
|
||||
config_object_free(obj);
|
||||
|
||||
avl_insert(ctx.ret, &device->node);
|
||||
|
||||
processed++;
|
||||
}
|
||||
|
||||
if (!processed)
|
||||
break;
|
||||
}
|
||||
|
||||
return ctx.ret;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = [
|
||||
'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)
|
||||
|
|
26
src/util.h
26
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);
|
||||
}
|
||||
|
|
Reference in a new issue