1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-16 00:00:02 +01:00
VILLASnode/lib/cfg.c

433 lines
11 KiB
C
Raw Normal View History

/** Configuration parser.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2016, Institute for Automation of Complex Power Systems, EONERC
2015-06-02 21:53:04 +02:00
*********************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>
#include "utils.h"
#include "list.h"
#include "cfg.h"
#include "node.h"
#include "path.h"
#include "hooks.h"
2017-02-18 10:31:42 -05:00
#include "advio.h"
#include "web.h"
#include "log.h"
#include "api.h"
#include "plugin.h"
2017-02-18 10:31:42 -05:00
#include "kernel/rt.h"
int cfg_init_pre(struct cfg *cfg)
{
2017-02-18 10:31:42 -05:00
config_init(cfg->cfg);
info("Inititliaze logging sub-system");
log_init(&cfg->log);
list_init(&cfg->nodes);
list_init(&cfg->paths);
list_init(&cfg->plugins);
return 0;
}
2017-02-18 10:31:42 -05:00
int cfg_init_post(struct cfg *cfg)
{
2017-02-18 10:31:42 -05:00
info("Initialize real-time sub-system");
rt_init(cfg);
info("Initialize hook sub-system");
hook_init(cfg);
info("Initialize API sub-system");
api_init(&cfg->api, cfg);
info("Initialize web sub-system");
web_init(&cfg->web, &cfg->api);
return 0;
}
2017-02-18 10:31:42 -05:00
int cfg_deinit(struct cfg *cfg)
{
2017-02-18 10:31:42 -05:00
info("De-initializing node types");
list_foreach(struct node_type *vt, &node_types) { INDENT
node_deinit(vt);
}
info("De-initializing web interface");
web_deinit(&cfg->web);
info("De-initialize API");
api_deinit(&cfg->api);
return 0;
}
int cfg_destroy(struct cfg *cfg)
{
config_destroy(cfg->cfg);
2017-02-18 10:31:42 -05:00
web_destroy(&cfg->web);
log_destroy(&cfg->log);
api_destroy(&cfg->api);
2017-02-18 10:31:42 -05:00
list_destroy(&cfg->plugins, (dtor_cb_t) plugin_destroy, false);
list_destroy(&cfg->paths, (dtor_cb_t) path_destroy, true);
list_destroy(&cfg->nodes, (dtor_cb_t) node_destroy, true);
return 0;
}
2017-02-18 10:31:42 -05:00
int cfg_parse(struct cfg *cfg, const char *uri)
{
config_setting_t *cfg_root, *cfg_nodes, *cfg_paths, *cfg_plugins, *cfg_logging;
2017-02-18 10:31:42 -05:00
int ret = CONFIG_FALSE;
if (uri) {
/* Setup libconfig */
config_set_auto_convert(cfg->cfg, 1);
FILE *f;
AFILE *af;
2017-02-18 10:31:42 -05:00
/* Via stdin */
if (strcmp("-", uri) == 0) {
af = NULL;
f = stdin;
}
/* Local file? */
else if (access(uri, F_OK) != -1) {
/* Setup libconfig include path.
* This is only supported for local files */
char *uri_cpy = strdup(uri);
char *include_dir = dirname(uri_cpy);
config_set_include_dir(cfg->cfg, include_dir);
free(uri_cpy);
af = NULL;
f = fopen(uri, "r");
}
/* Use advio (libcurl) to fetch the config from a remote */
else {
af = afopen(uri, "r", ADVIO_MEM);
f = af ? af->file : NULL;
}
/* Check if file could be loaded / opened */
if (!f)
error("Failed to open configuration from: %s", uri);
/* Parse config */
ret = config_read(cfg->cfg, f);
if (ret != CONFIG_TRUE)
error("Failed to parse configuration: %s in %s:%d", config_error_text(cfg->cfg), uri, config_error_line(cfg->cfg));
/* Close configuration file */
if (af)
afclose(af);
else
fclose(f);
}
else
2017-02-18 10:31:42 -05:00
warn("No configuration file specified. Starting unconfigured. Use the API to configure this instance.");
/* Parse global settings */
cfg_root = config_root_setting(cfg->cfg);
if (cfg_root) {
if (!config_setting_is_group(cfg_root))
warn("Missing global section in config file.");
cfg_parse_global(cfg_root, cfg);
}
2015-08-07 01:11:43 +02:00
2017-02-18 10:31:42 -05:00
/* Parse logging settings */
cfg_logging = config_setting_get_member(cfg_root, "logging");
if (cfg_logging) {
if (!config_setting_is_group(cfg_logging))
cerror(cfg_logging, "Setting 'logging' must be a group.");
log_parse(&cfg->log, cfg_logging);
}
2017-02-18 10:31:42 -05:00
/* Parse plugins */
cfg_plugins = config_setting_get_member(cfg_root, "plugins");
if (cfg_plugins) {
if (!config_setting_is_array(cfg_plugins))
cerror(cfg_plugins, "Setting 'plugins' must be a list of strings");
2017-02-18 10:31:42 -05:00
for (int i = 0; i < config_setting_length(cfg_plugins); i++) {
struct config_setting_t *cfg_plugin = config_setting_get_elem(cfg_plugins, i);
struct plugin plugin;
plugin_parse(&plugin, cfg_plugin);
}
2014-12-05 12:39:52 +01:00
}
2015-08-07 01:11:43 +02:00
/* Parse nodes */
2017-02-18 10:31:42 -05:00
cfg_nodes = config_setting_get_member(cfg_root, "nodes");
if (cfg_nodes) {
if (!config_setting_is_group(cfg_nodes))
warn("Setting 'nodes' must be a group with node name => group mappings.");
2015-08-07 01:11:43 +02:00
2014-12-05 12:39:52 +01:00
for (int i = 0; i < config_setting_length(cfg_nodes); i++) {
config_setting_t *cfg_node = config_setting_get_elem(cfg_nodes, i);
2017-02-18 10:31:42 -05:00
cfg_parse_node(cfg_node, &cfg->nodes, cfg);
2014-12-05 12:39:52 +01:00
}
}
/* Parse paths */
2017-02-18 10:31:42 -05:00
cfg_paths = config_setting_get_member(cfg_root, "paths");
if (cfg_paths) {
if (!config_setting_is_list(cfg_paths))
warn("Setting 'paths' must be a list.");
2014-12-05 12:39:52 +01:00
for (int i = 0; i < config_setting_length(cfg_paths); i++) {
config_setting_t *cfg_path = config_setting_get_elem(cfg_paths, i);
2017-02-18 10:31:42 -05:00
cfg_parse_path(cfg_path, &cfg->paths, &cfg->nodes, cfg);
2014-12-05 12:39:52 +01:00
}
}
return 0;
}
2017-02-18 10:31:42 -05:00
int cfg_parse_global(config_setting_t *cfg, struct cfg *set)
{
2016-06-26 15:34:40 +02:00
if (!config_setting_lookup_int(cfg, "affinity", &set->affinity))
2016-07-11 11:36:23 +02:00
set->affinity = 0;
2016-06-26 15:34:40 +02:00
if (!config_setting_lookup_int(cfg, "priority", &set->priority))
2016-07-11 11:36:23 +02:00
set->priority = 0;
if (!config_setting_lookup_float(cfg, "stats", &set->stats))
set->stats = 0;
return 0;
}
int cfg_parse_path(config_setting_t *cfg,
2017-02-18 10:31:42 -05:00
struct list *paths, struct list *nodes, struct cfg *set)
{
2015-10-13 16:22:07 +02:00
config_setting_t *cfg_out, *cfg_hook;
const char *in;
2016-06-08 22:38:21 +02:00
int ret, reverse;
struct path *p;
2016-06-08 22:38:21 +02:00
/* Allocate memory and intialize path structure */
p = alloc(sizeof(struct path));
path_init(p);
/* Input node */
2016-06-26 15:34:40 +02:00
if (!config_setting_lookup_string(cfg, "in", &in) &&
!config_setting_lookup_string(cfg, "from", &in) &&
!config_setting_lookup_string(cfg, "src", &in) &&
!config_setting_lookup_string(cfg, "source", &in))
cerror(cfg, "Missing input node for path");
2015-08-07 01:11:43 +02:00
p->in = list_lookup(nodes, in);
if (!p->in)
2016-06-08 22:38:21 +02:00
cerror(cfg, "Invalid input node '%s'", in);
/* Output node(s) */
2016-06-26 15:34:40 +02:00
if (!(cfg_out = config_setting_get_member(cfg, "out")) &&
!(cfg_out = config_setting_get_member(cfg, "to")) &&
!(cfg_out = config_setting_get_member(cfg, "dst")) &&
!(cfg_out = config_setting_get_member(cfg, "dest")) &&
!(cfg_out = config_setting_get_member(cfg, "sink")))
2016-06-08 22:38:21 +02:00
cerror(cfg, "Missing output nodes for path");
ret = cfg_parse_nodelist(cfg_out, &p->destinations, nodes);
2016-06-08 22:38:21 +02:00
if (ret <= 0)
cerror(cfg_out, "Invalid output nodes");
/* Check if nodes are suitable */
if (p->in->_vt->read == NULL)
cerror(cfg, "Input node '%s' is not supported as a source.", node_name(p->in));
2016-06-08 22:38:21 +02:00
list_foreach(struct node *n, &p->destinations) {
if (n->_vt->write == NULL)
cerror(cfg_out, "Output node '%s' is not supported as a destination.", node_name(n));
}
/* Optional settings */
cfg_hook = config_setting_get_member(cfg, "hook");
if (cfg_hook)
cfg_parse_hooklist(cfg_hook, &p->hooks);
2015-08-07 01:11:43 +02:00
if (!config_setting_lookup_int(cfg, "values", &p->samplelen))
p->samplelen = DEFAULT_VALUES;
2016-06-08 22:38:21 +02:00
if (!config_setting_lookup_int(cfg, "queuelen", &p->queuelen))
p->queuelen = DEFAULT_QUEUELEN;
if (!config_setting_lookup_bool(cfg, "reverse", &reverse))
reverse = 0;
if (!config_setting_lookup_bool(cfg, "enabled", &p->enabled))
p->enabled = 1;
if (!config_setting_lookup_float(cfg, "rate", &p->rate))
p->rate = 0; /* disabled */
p->cfg = cfg;
list_push(paths, p);
2015-08-07 01:11:43 +02:00
if (reverse) {
if (list_length(&p->destinations) > 1)
2016-06-08 22:38:21 +02:00
cerror(cfg, "Can't reverse path with multiple destination nodes");
2016-06-08 22:38:21 +02:00
struct path *r = memdup(p, sizeof(struct path));
path_init(r);
2016-06-08 22:38:21 +02:00
/* Swap source and destination node */
r->in = list_first(&p->destinations);
list_push(&r->destinations, p->in);
if (cfg_hook)
cfg_parse_hooklist(cfg_hook, &r->hooks);
list_push(paths, r);
}
return 0;
}
int cfg_parse_nodelist(config_setting_t *cfg, struct list *list, struct list *all) {
const char *str;
struct node *node;
2015-08-07 01:11:43 +02:00
switch (config_setting_type(cfg)) {
case CONFIG_TYPE_STRING:
str = config_setting_get_string(cfg);
2015-09-19 15:28:28 +02:00
if (str) {
2015-09-19 18:46:04 +02:00
node = list_lookup(all, str);
2015-09-19 15:28:28 +02:00
if (node)
list_push(list, node);
else
cerror(cfg, "Unknown outgoing node '%s'", str);
}
else
cerror(cfg, "Invalid outgoing node");
break;
2015-08-07 01:11:43 +02:00
case CONFIG_TYPE_ARRAY:
2015-09-19 15:28:28 +02:00
for (int i = 0; i < config_setting_length(cfg); i++) {
config_setting_t *elm = config_setting_get_elem(cfg, i);
str = config_setting_get_string(elm);
if (str) {
node = list_lookup(all, str);
2016-06-08 22:38:21 +02:00
if (!node)
2015-09-19 15:28:28 +02:00
cerror(elm, "Unknown outgoing node '%s'", str);
2016-06-08 22:38:21 +02:00
else if (node->_vt->write == NULL)
cerror(cfg, "Output node '%s' is not supported as a sink.", node_name(node));
list_push(list, node);
2015-09-19 15:28:28 +02:00
}
else
cerror(cfg, "Invalid outgoing node");
}
break;
2015-08-07 01:11:43 +02:00
default:
cerror(cfg, "Invalid output node(s)");
}
2015-08-07 01:11:43 +02:00
2016-06-08 22:38:21 +02:00
return list_length(list);
}
2017-02-18 10:31:42 -05:00
int cfg_parse_node(config_setting_t *cfg, struct list *nodes, struct cfg *set)
2015-10-12 16:17:51 +02:00
{
2017-02-18 10:31:42 -05:00
const char *type;
2015-10-12 16:17:51 +02:00
int ret;
2015-10-13 15:05:48 +02:00
struct node *n;
struct node_type *vt;
2015-10-12 16:17:51 +02:00
/* Required settings */
if (!config_setting_lookup_string(cfg, "type", &type))
cerror(cfg, "Missing node type");
2015-10-13 15:05:48 +02:00
vt = list_lookup(&node_types, type);
if (!vt)
cerror(cfg, "Invalid type for node '%s'", config_setting_name(cfg));
n = node_create(vt);
2017-02-18 10:31:42 -05:00
n->name = config_setting_name(cfg);
2015-10-13 15:05:48 +02:00
n->cfg = cfg;
ret = node_parse(n, cfg);
if (ret)
cerror(cfg, "Failed to parse node '%s'", node_name(n));
2015-10-12 16:17:51 +02:00
if (config_setting_lookup_int(cfg, "vectorize", &n->vectorize)) {
2016-02-04 16:27:14 +01:00
config_setting_t *cfg_vectorize = config_setting_lookup(cfg, "vectorize");
if (n->vectorize <= 0)
cerror(cfg_vectorize, "Invalid value for `vectorize` %d. Must be natural number!", n->vectorize);
if (vt->vectorize && vt->vectorize < n->vectorize)
cerror(cfg_vectorize, "Invalid value for `vectorize`. Node type %s requires a number smaller than %d!",
node_name_type(n), vt->vectorize);
}
else
n->vectorize = 1;
2015-10-12 16:17:51 +02:00
2015-10-13 16:16:33 +02:00
if (!config_setting_lookup_int(cfg, "affinity", &n->affinity))
2015-10-12 16:17:51 +02:00
n->affinity = set->affinity;
2015-10-13 15:05:48 +02:00
list_push(nodes, n);
2015-10-12 16:17:51 +02:00
return ret;
}
int cfg_parse_hooklist(config_setting_t *cfg, struct list *list) {
switch (config_setting_type(cfg)) {
case CONFIG_TYPE_STRING:
cfg_parse_hook(cfg, list);
break;
2015-08-07 01:11:43 +02:00
case CONFIG_TYPE_ARRAY:
for (int i = 0; i < config_setting_length(cfg); i++)
cfg_parse_hook(config_setting_get_elem(cfg, i), list);
break;
2015-08-07 01:11:43 +02:00
default:
cerror(cfg, "Invalid hook functions");
}
2015-08-07 01:11:43 +02:00
2016-06-08 22:38:21 +02:00
return list_length(list);
}
int cfg_parse_hook(config_setting_t *cfg, struct list *list)
{
struct hook *hook, *copy;
2016-09-14 03:33:56 +02:00
char *name, *param;
const char *hookline = config_setting_get_string(cfg);
if (!hookline)
cerror(cfg, "Invalid hook function");
2016-09-14 03:33:56 +02:00
name = strtok((char *) hookline, ":");
param = strtok(NULL, "");
debug(3, "Hook: %s => %s", name, param);
hook = list_lookup(&hooks, name);
if (!hook)
cerror(cfg, "Unknown hook function '%s'", name);
copy = memdup(hook, sizeof(struct hook));
copy->parameter = param;
list_push(list, copy);
return 0;
}