2014-07-14 11:49:44 +00:00
|
|
|
/** Configuration parser.
|
2014-06-05 09:34:32 +00:00
|
|
|
*
|
2014-06-05 09:35:41 +00:00
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
2016-02-09 05:33:19 +01:00
|
|
|
* @copyright 2014-2016, Institute for Automation of Complex Power Systems, EONERC
|
2016-06-08 23:21:42 +02:00
|
|
|
* This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential.
|
2015-08-07 01:11:43 +02:00
|
|
|
* Unauthorized copying of this file, via any medium is strictly prohibited.
|
2015-06-02 21:53:04 +02:00
|
|
|
*********************************************************************************/
|
2014-06-05 09:34:32 +00:00
|
|
|
|
2014-06-10 19:44:21 +00:00
|
|
|
#include <stdlib.h>
|
2014-12-05 12:34:45 +01:00
|
|
|
#include <string.h>
|
2016-02-04 17:11:36 +01:00
|
|
|
#include <libgen.h>
|
2016-06-26 15:31:31 +02:00
|
|
|
#include <dlfcn.h>
|
2016-07-11 16:02:55 +02:00
|
|
|
#include <unistd.h>
|
2014-06-05 09:34:32 +00:00
|
|
|
|
2015-03-18 15:50:02 +01:00
|
|
|
#include "utils.h"
|
|
|
|
#include "list.h"
|
2014-06-05 09:34:50 +00:00
|
|
|
#include "cfg.h"
|
2014-06-05 09:35:23 +00:00
|
|
|
#include "node.h"
|
|
|
|
#include "path.h"
|
2014-08-31 14:43:28 +00:00
|
|
|
#include "hooks.h"
|
2014-06-05 09:34:32 +00:00
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
void cfg_init(config_t *cfg)
|
|
|
|
{
|
|
|
|
config_init(cfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cfg_destroy(config_t *cfg)
|
|
|
|
{
|
|
|
|
config_destroy(cfg);
|
|
|
|
}
|
|
|
|
|
|
|
|
int cfg_parse(const char *filename, config_t *cfg, struct settings *set,
|
2015-03-21 15:19:41 +01:00
|
|
|
struct list *nodes, struct list *paths)
|
2014-06-05 09:34:32 +00:00
|
|
|
{
|
2016-07-26 15:42:11 +02:00
|
|
|
int ret = CONFIG_FALSE;
|
2016-06-19 19:23:19 +02:00
|
|
|
char *filename_cpy, *include_dir;
|
2016-06-15 20:05:09 +02:00
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
config_init(cfg);
|
|
|
|
|
2016-06-15 20:05:09 +02:00
|
|
|
filename_cpy = strdup(filename);
|
|
|
|
include_dir = dirname(filename_cpy);
|
2016-02-04 17:11:36 +01:00
|
|
|
|
|
|
|
/* Setup libconfig */
|
2014-07-04 09:44:09 +00:00
|
|
|
config_set_auto_convert(cfg, 1);
|
2016-02-04 17:11:36 +01:00
|
|
|
config_set_include_dir(cfg, include_dir);
|
2014-07-04 09:44:09 +00:00
|
|
|
|
2016-06-26 15:34:40 +02:00
|
|
|
free(filename_cpy);
|
2016-07-11 16:02:55 +02:00
|
|
|
|
|
|
|
if (strcmp("-", filename) == 0)
|
|
|
|
ret = config_read(cfg, stdin);
|
|
|
|
else if (access(filename, F_OK) != -1)
|
|
|
|
ret = config_read_file(cfg, filename);
|
|
|
|
else
|
|
|
|
error("Invalid configuration file name: %s", filename);
|
2016-06-26 15:34:40 +02:00
|
|
|
|
2016-02-04 17:11:36 +01:00
|
|
|
if (ret != CONFIG_TRUE) {
|
2014-06-05 09:34:50 +00:00
|
|
|
error("Failed to parse configuration: %s in %s:%d",
|
2015-03-20 18:55:14 +01:00
|
|
|
config_error_text(cfg),
|
|
|
|
config_error_file(cfg) ? config_error_file(cfg) : filename,
|
2014-06-05 09:34:50 +00:00
|
|
|
config_error_line(cfg)
|
|
|
|
);
|
2016-02-04 17:11:36 +01:00
|
|
|
}
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2014-06-10 16:57:40 +00:00
|
|
|
config_setting_t *cfg_root = config_root_setting(cfg);
|
2014-06-05 09:34:50 +00:00
|
|
|
|
2014-06-10 18:47:25 +00:00
|
|
|
/* Parse global settings */
|
2014-12-05 12:39:52 +01:00
|
|
|
if (set) {
|
|
|
|
if (!cfg_root || !config_setting_is_group(cfg_root))
|
|
|
|
error("Missing global section in config file: %s", filename);
|
2014-06-10 16:57:40 +00:00
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
cfg_parse_global(cfg_root, set);
|
2014-12-05 12:39:52 +01:00
|
|
|
}
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2014-06-10 18:47:25 +00:00
|
|
|
/* Parse nodes */
|
2014-12-05 12:39:52 +01:00
|
|
|
if (nodes) {
|
|
|
|
config_setting_t *cfg_nodes = config_setting_get_member(cfg_root, "nodes");
|
|
|
|
if (!cfg_nodes || !config_setting_is_group(cfg_nodes))
|
|
|
|
error("Missing node section in config file: %s", filename);
|
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);
|
2016-07-14 09:47:00 +02:00
|
|
|
cfg_parse_node(cfg_node, nodes, set);
|
2014-12-05 12:39:52 +01:00
|
|
|
}
|
2014-06-05 09:34:32 +00:00
|
|
|
}
|
|
|
|
|
2014-06-05 09:34:50 +00:00
|
|
|
/* Parse paths */
|
2014-12-05 12:39:52 +01:00
|
|
|
if (paths) {
|
|
|
|
config_setting_t *cfg_paths = config_setting_get_member(cfg_root, "paths");
|
|
|
|
if (!cfg_paths || !config_setting_is_list(cfg_paths))
|
|
|
|
error("Missing path section in config file: %s", filename);
|
|
|
|
|
|
|
|
for (int i = 0; i < config_setting_length(cfg_paths); i++) {
|
|
|
|
config_setting_t *cfg_path = config_setting_get_elem(cfg_paths, i);
|
2016-07-14 09:47:00 +02:00
|
|
|
cfg_parse_path(cfg_path, paths, nodes, set);
|
2014-12-05 12:39:52 +01:00
|
|
|
}
|
2014-06-05 09:34:32 +00:00
|
|
|
}
|
|
|
|
|
2015-03-20 18:55:14 +01:00
|
|
|
return 0;
|
2014-06-05 09:34:32 +00:00
|
|
|
}
|
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
int cfg_parse_plugins(config_setting_t *cfg)
|
2016-07-11 11:36:23 +02:00
|
|
|
{
|
|
|
|
if (!config_setting_is_array(cfg))
|
|
|
|
cerror(cfg, "Setting 'plugins' must be a list of strings");
|
|
|
|
|
|
|
|
for (int i = 0; i < config_setting_length(cfg); i++) {
|
|
|
|
void *handle;
|
|
|
|
const char *path;
|
|
|
|
|
|
|
|
path = config_setting_get_string_elem(cfg, i);
|
|
|
|
if (!path)
|
|
|
|
cerror(cfg, "Setting 'plugins' must be a list of strings");
|
|
|
|
|
|
|
|
handle = dlopen(path, RTLD_NOW);
|
|
|
|
if (!handle)
|
|
|
|
error("Failed to load plugin %s", dlerror());
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
int cfg_parse_global(config_setting_t *cfg, struct settings *set)
|
2014-06-05 09:34:32 +00:00
|
|
|
{
|
2016-06-26 15:34:40 +02:00
|
|
|
config_setting_t *cfg_plugins;
|
|
|
|
|
|
|
|
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_int(cfg, "debug", &set->debug))
|
|
|
|
set->debug = V;
|
2016-06-26 15:34:40 +02:00
|
|
|
|
2016-07-11 11:36:23 +02:00
|
|
|
if (!config_setting_lookup_float(cfg, "stats", &set->stats))
|
|
|
|
set->stats = 0;
|
2016-06-26 15:31:31 +02:00
|
|
|
|
|
|
|
cfg_plugins = config_setting_get_member(cfg, "plugins");
|
2016-07-11 11:36:23 +02:00
|
|
|
if (cfg_plugins)
|
2016-07-14 09:47:00 +02:00
|
|
|
cfg_parse_plugins(cfg_plugins);
|
2014-09-07 16:28:50 +00:00
|
|
|
|
2016-06-08 22:29:30 +02:00
|
|
|
log_setlevel(set->debug, -1);
|
2014-06-05 09:35:41 +00:00
|
|
|
|
2015-03-20 18:55:14 +01:00
|
|
|
return 0;
|
2014-06-05 09:34:32 +00:00
|
|
|
}
|
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
int cfg_parse_path(config_setting_t *cfg,
|
2015-09-17 01:33:19 +02:00
|
|
|
struct list *paths, struct list *nodes, struct settings *set)
|
2014-06-05 09:34:32 +00:00
|
|
|
{
|
2015-10-13 16:22:07 +02:00
|
|
|
config_setting_t *cfg_out, *cfg_hook;
|
2015-03-18 15:50:02 +01:00
|
|
|
const char *in;
|
2016-11-07 22:17:45 -05:00
|
|
|
int ret, reverse, samplelen, queuelen;
|
2016-06-08 22:38:21 +02:00
|
|
|
struct path *p;
|
2016-11-07 22:17:45 -05:00
|
|
|
|
|
|
|
struct node *source;
|
|
|
|
struct list destinations;
|
2016-01-14 22:59:57 +01:00
|
|
|
|
2016-06-08 22:38:21 +02:00
|
|
|
/* Allocate memory and intialize path structure */
|
|
|
|
p = alloc(sizeof(struct path));
|
2014-06-25 01:53:35 +00:00
|
|
|
|
2015-03-18 15:50:02 +01:00
|
|
|
/* 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))
|
2015-10-13 15:40:34 +02:00
|
|
|
cerror(cfg, "Missing input node for path");
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2016-11-07 22:17:45 -05:00
|
|
|
source = list_lookup(nodes, in);
|
|
|
|
if (!source)
|
2016-06-08 22:38:21 +02:00
|
|
|
cerror(cfg, "Invalid input node '%s'", in);
|
2014-06-05 09:34:50 +00:00
|
|
|
|
2015-03-18 15:50:02 +01:00
|
|
|
/* 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");
|
|
|
|
|
2016-11-07 22:17:45 -05:00
|
|
|
list_init(&destinations);
|
|
|
|
ret = cfg_parse_nodelist(cfg_out, &destinations, nodes);
|
2016-06-08 22:38:21 +02:00
|
|
|
if (ret <= 0)
|
|
|
|
cerror(cfg_out, "Invalid output nodes");
|
2014-06-05 09:34:50 +00:00
|
|
|
|
2014-06-25 17:50:30 +00:00
|
|
|
/* Optional settings */
|
2016-11-07 22:17:45 -05:00
|
|
|
list_init(&p->hooks);
|
2015-10-13 15:40:34 +02:00
|
|
|
cfg_hook = config_setting_get_member(cfg, "hook");
|
2015-03-18 16:13:18 +01:00
|
|
|
if (cfg_hook)
|
2016-07-14 09:47:00 +02:00
|
|
|
cfg_parse_hooklist(cfg_hook, &p->hooks);
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-05-06 11:49:13 +02:00
|
|
|
if (!config_setting_lookup_bool(cfg, "reverse", &reverse))
|
|
|
|
reverse = 0;
|
2016-01-14 22:59:57 +01:00
|
|
|
if (!config_setting_lookup_bool(cfg, "enabled", &p->enabled))
|
|
|
|
p->enabled = 1;
|
2016-11-07 22:17:45 -05:00
|
|
|
if (!config_setting_lookup_int(cfg, "values", &samplelen))
|
|
|
|
samplelen = DEFAULT_VALUES;
|
|
|
|
if (!config_setting_lookup_int(cfg, "queuelen", &queuelen))
|
|
|
|
queuelen = DEFAULT_QUEUELEN;
|
|
|
|
|
|
|
|
if (!IS_POW2(queuelen)) {
|
|
|
|
queuelen = LOG2_CEIL(queuelen);
|
|
|
|
warn("Queue length should always be a power of 2. Adjusting to %d", queuelen);
|
2016-10-30 15:21:46 -04:00
|
|
|
}
|
|
|
|
|
2014-09-11 14:40:43 +00:00
|
|
|
p->cfg = cfg;
|
2016-11-07 22:17:45 -05:00
|
|
|
|
|
|
|
/* Check if nodes are suitable */
|
|
|
|
if (source->_vt->read == NULL)
|
|
|
|
cerror(cfg, "Input node '%s' is not supported as a source.", node_name(source));
|
2014-06-05 09:34:50 +00:00
|
|
|
|
2016-11-07 22:17:45 -05:00
|
|
|
p->source = alloc(sizeof(struct path_source));
|
|
|
|
p->source->node = source;
|
|
|
|
p->source->samplelen = samplelen;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2016-11-07 22:17:45 -05:00
|
|
|
list_foreach(struct node *n, &destinations) {
|
|
|
|
if (n->_vt->write == NULL)
|
|
|
|
cerror(cfg_out, "Output node '%s' is not supported as a destination.", node_name(n));
|
|
|
|
|
|
|
|
struct path_destination *pd = alloc(sizeof(struct path_destination));
|
|
|
|
|
|
|
|
pd->node = n;
|
|
|
|
pd->queuelen = queuelen;
|
|
|
|
|
|
|
|
list_push(&p->destinations, pd);
|
|
|
|
}
|
2014-09-11 14:40:48 +00:00
|
|
|
|
2016-11-07 22:17:45 -05:00
|
|
|
list_push(paths, p);
|
2014-06-05 09:34:50 +00:00
|
|
|
|
2016-11-07 22:17:45 -05:00
|
|
|
if (reverse) {
|
|
|
|
struct path *r = alloc(sizeof(struct path));
|
|
|
|
|
|
|
|
ret = path_reverse(p, r);
|
|
|
|
if (ret)
|
|
|
|
cerror(cfg, "Failed to reverse path %s", path_name(p));
|
2015-09-22 12:58:37 +02:00
|
|
|
|
2015-11-23 16:32:24 +01:00
|
|
|
list_push(paths, r);
|
2014-06-25 01:53:35 +00:00
|
|
|
}
|
2016-11-07 22:17:45 -05:00
|
|
|
|
|
|
|
list_destroy(&destinations, NULL, false);
|
2014-06-25 01:53:35 +00:00
|
|
|
|
|
|
|
return 0;
|
2014-06-05 09:34:50 +00:00
|
|
|
}
|
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
int cfg_parse_nodelist(config_setting_t *cfg, struct list *list, struct list *all) {
|
2015-03-18 15:50:02 +01:00
|
|
|
const char *str;
|
|
|
|
struct node *node;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-18 15:50:02 +01: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");
|
2015-03-18 15:50:02 +01:00
|
|
|
break;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-18 15:50:02 +01: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");
|
2015-03-18 15:50:02 +01:00
|
|
|
}
|
|
|
|
break;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-18 15:50:02 +01: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);
|
2015-03-18 15:50:02 +01:00
|
|
|
}
|
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
int cfg_parse_node(config_setting_t *cfg, struct list *nodes, struct settings *set)
|
2015-10-12 16:17:51 +02:00
|
|
|
{
|
2015-10-13 15:05:48 +02:00
|
|
|
const char *type, *name;
|
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
|
|
|
|
|
|
|
name = config_setting_name(cfg);
|
|
|
|
|
|
|
|
vt = list_lookup(&node_types, type);
|
|
|
|
if (!vt)
|
|
|
|
cerror(cfg, "Invalid type for node '%s'", config_setting_name(cfg));
|
|
|
|
|
|
|
|
n = node_create(vt);
|
|
|
|
|
|
|
|
n->name = name;
|
|
|
|
n->cfg = cfg;
|
|
|
|
|
|
|
|
ret = node_parse(n, cfg);
|
|
|
|
if (ret)
|
2015-11-29 22:45:46 +01:00
|
|
|
cerror(cfg, "Failed to parse node '%s'", node_name(n));
|
2015-10-12 16:17:51 +02:00
|
|
|
|
2016-01-14 22:54:08 +01: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");
|
2016-01-14 22:54:08 +01:00
|
|
|
|
|
|
|
if (n->vectorize <= 0)
|
2016-07-17 01:01:43 +02:00
|
|
|
cerror(cfg_vectorize, "Invalid value for `vectorize` %d. Must be natural number!", n->vectorize);
|
2016-01-14 22:54:08 +01:00
|
|
|
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
|
2015-12-19 16:48:20 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
int cfg_parse_hooklist(config_setting_t *cfg, struct list *list) {
|
2015-03-18 16:13:18 +01:00
|
|
|
switch (config_setting_type(cfg)) {
|
|
|
|
case CONFIG_TYPE_STRING:
|
2016-07-14 09:47:00 +02:00
|
|
|
cfg_parse_hook(cfg, list);
|
2015-03-18 16:13:18 +01:00
|
|
|
break;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-18 16:13:18 +01:00
|
|
|
case CONFIG_TYPE_ARRAY:
|
2015-10-09 13:30:41 +02:00
|
|
|
for (int i = 0; i < config_setting_length(cfg); i++)
|
2016-07-14 09:47:00 +02:00
|
|
|
cfg_parse_hook(config_setting_get_elem(cfg, i), list);
|
2015-03-18 16:13:18 +01:00
|
|
|
break;
|
2015-08-07 01:11:43 +02:00
|
|
|
|
2015-03-18 16:13:18 +01: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);
|
2015-03-18 16:13:18 +01:00
|
|
|
}
|
|
|
|
|
2016-07-14 09:47:00 +02:00
|
|
|
int cfg_parse_hook(config_setting_t *cfg, struct list *list)
|
2015-10-09 13:30:41 +02:00
|
|
|
{
|
|
|
|
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)
|
2015-10-09 13:30:41 +02:00
|
|
|
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);
|
|
|
|
|
2015-10-09 13:30:41 +02:00
|
|
|
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;
|
|
|
|
}
|