diff --git a/config.h b/config.h index 3f9b62cca..6555fac32 100644 --- a/config.h +++ b/config.h @@ -61,11 +61,3 @@ /** AXI Bus frequency for all components * except RTDS AXI Stream bridge which runs at RTDS_HZ (100 Mhz) */ #define FPGA_AXI_HZ 125000000 // 125 MHz - -/** Global configuration */ -struct settings { - int priority; /**< Process priority (lower is better) */ - int affinity; /**< Process affinity of the server and all created threads */ - int debug; /**< Debug log level */ - double stats; /**< Interval for path statistics. Set to 0 to disable themo disable them. */ -}; diff --git a/include/villas/cfg.h b/include/villas/cfg.h index 2065d6d95..ae6f51a2d 100644 --- a/include/villas/cfg.h +++ b/include/villas/cfg.h @@ -15,37 +15,53 @@ #include -/* Forward declarations */ -struct list; -struct settings; +#include "list.h" +#include "api.h" +#include "web.h" +#include "log.h" + +/** Global configuration */ +struct cfg { + int priority; /**< Process priority (lower is better) */ + int affinity; /**< Process affinity of the server and all created threads */ + double stats; /**< Interval for path statistics. Set to 0 to disable them. */ + + struct list nodes; + struct list paths; + struct list plugins; + + struct log log; + struct api api; + struct web web; + + config_t *cfg; /**< Pointer to configuration file */ + json_t *json; /**< JSON representation of the same config. */ +}; /* Compatibility with libconfig < 1.5 */ #if (LIBCONFIG_VER_MAJOR <= 1) && (LIBCONFIG_VER_MINOR < 5) #define config_setting_lookup config_lookup_from #endif -/** Simple wrapper around libconfig's config_init() - * - * This allows us to avoid an additional library dependency to libconfig - * for the excuctables. They only have to depend on libvillas. - */ -void cfg_init(config_t *cfg); +/** Inititalize configuration object before parsing the configuration. */ +int cfg_init_pre(struct cfg *cfg); -/** Simple wrapper around libconfig's config_init() */ -void cfg_destroy(config_t *cfg); +/** Initialize after parsing the configuration file. */ +int cfg_init_post(struct cfg *cfg); -/** Parse config file and store settings in supplied struct settings. +int cfg_deinit(struct cfg *cfg); + +/** Desctroy configuration object. */ +int cfg_destroy(struct cfg *cfg); + +/** Parse config file and store settings in supplied struct cfg. * + * @param cfg A configuration object. * @param filename The path to the configration file (relative or absolute) - * @param cfg A initialized libconfig object - * @param set The global configuration structure - * @param nodes A linked list of nodes which should be parsed - * @param paths A linked list of paths which should be parsed * @retval 0 Success. Everything went well. * @retval <0 Error. Something went wrong. */ -int cfg_parse(const char *filename, config_t *cfg, struct settings *set, - struct list *nodes, struct list *paths); +int cfg_parse(struct cfg *cfg, const char *uri); /** Parse the global section of a configuration file. * @@ -54,7 +70,7 @@ int cfg_parse(const char *filename, config_t *cfg, struct settings *set, * @retval 0 Success. Everything went well. * @retval <0 Error. Something went wrong. */ -int cfg_parse_global(config_setting_t *cfg, struct settings *set); +int cfg_parse_global(config_setting_t *cfg, struct cfg *set); /** Parse a single path and add it to the global configuration. * @@ -66,7 +82,7 @@ int cfg_parse_global(config_setting_t *cfg, struct settings *set); * @retval <0 Error. Something went wrong. */ int cfg_parse_path(config_setting_t *cfg, - struct list *paths, struct list *nodes, struct settings *set); + struct list *paths, struct list *nodes, struct cfg *set); /** Parse an array or single node and checks if they exist in the "nodes" section. * diff --git a/lib/cfg.c b/lib/cfg.c index 4584bc1a9..f2abd1c11 100644 --- a/lib/cfg.c +++ b/lib/cfg.c @@ -17,134 +17,208 @@ #include "node.h" #include "path.h" #include "hooks.h" +#include "advio.h" +#include "web.h" +#include "log.h" +#include "api.h" +#include "plugin.h" -void cfg_init(config_t *cfg) +#include "kernel/rt.h" + +int cfg_init_pre(struct cfg *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, - struct list *nodes, struct list *paths) -{ - int ret = CONFIG_FALSE; - char *filename_cpy, *include_dir; - - config_init(cfg); - - filename_cpy = strdup(filename); - include_dir = dirname(filename_cpy); - - /* Setup libconfig */ - config_set_auto_convert(cfg, 1); - config_set_include_dir(cfg, include_dir); - - free(filename_cpy); + config_init(cfg->cfg); - 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); + info("Inititliaze logging sub-system"); + log_init(&cfg->log); - if (ret != CONFIG_TRUE) { - error("Failed to parse configuration: %s in %s:%d", - config_error_text(cfg), - config_error_file(cfg) ? config_error_file(cfg) : filename, - config_error_line(cfg) - ); + list_init(&cfg->nodes); + list_init(&cfg->paths); + list_init(&cfg->plugins); + + return 0; +} + +int cfg_init_post(struct cfg *cfg) +{ + 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; +} + +int cfg_deinit(struct cfg *cfg) +{ + 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; +} - config_setting_t *cfg_root = config_root_setting(cfg); +int cfg_destroy(struct cfg *cfg) +{ + config_destroy(cfg->cfg); + + web_destroy(&cfg->web); + log_destroy(&cfg->log); + api_destroy(&cfg->api); + + 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; +} + +int cfg_parse(struct cfg *cfg, const char *uri) +{ + config_setting_t *cfg_root, *cfg_nodes, *cfg_paths, *cfg_plugins, *cfg_logging; + + int ret = CONFIG_FALSE; + + if (uri) { + /* Setup libconfig */ + config_set_auto_convert(cfg->cfg, 1); + + FILE *f; + AFILE *af; + + /* 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 + warn("No configuration file specified. Starting unconfigured. Use the API to configure this instance."); /* Parse global settings */ - if (set) { - if (!cfg_root || !config_setting_is_group(cfg_root)) - error("Missing global section in config file: %s", filename); + 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, set); + cfg_parse_global(cfg_root, cfg); + } + + /* 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); + } + + /* 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"); + + 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); + } } /* Parse nodes */ - 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); + 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."); for (int i = 0; i < config_setting_length(cfg_nodes); i++) { config_setting_t *cfg_node = config_setting_get_elem(cfg_nodes, i); - cfg_parse_node(cfg_node, nodes, set); + cfg_parse_node(cfg_node, &cfg->nodes, cfg); } } /* Parse paths */ - 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); + 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."); for (int i = 0; i < config_setting_length(cfg_paths); i++) { config_setting_t *cfg_path = config_setting_get_elem(cfg_paths, i); - cfg_parse_path(cfg_path, paths, nodes, set); + cfg_parse_path(cfg_path, &cfg->paths, &cfg->nodes, cfg); } } return 0; } -int cfg_parse_plugins(config_setting_t *cfg) +int cfg_parse_global(config_setting_t *cfg, struct cfg *set) { - 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; -} - -int cfg_parse_global(config_setting_t *cfg, struct settings *set) -{ - config_setting_t *cfg_plugins; - if (!config_setting_lookup_int(cfg, "affinity", &set->affinity)) set->affinity = 0; if (!config_setting_lookup_int(cfg, "priority", &set->priority)) set->priority = 0; - if (!config_setting_lookup_int(cfg, "debug", &set->debug)) - set->debug = V; - if (!config_setting_lookup_float(cfg, "stats", &set->stats)) set->stats = 0; - - cfg_plugins = config_setting_get_member(cfg, "plugins"); - if (cfg_plugins) - cfg_parse_plugins(cfg_plugins); - - log_setlevel(set->debug, -1); return 0; } int cfg_parse_path(config_setting_t *cfg, - struct list *paths, struct list *nodes, struct settings *set) + struct list *paths, struct list *nodes, struct cfg *set) { config_setting_t *cfg_out, *cfg_hook; const char *in; @@ -203,11 +277,6 @@ int cfg_parse_path(config_setting_t *cfg, if (!config_setting_lookup_float(cfg, "rate", &p->rate)) p->rate = 0; /* disabled */ - if (!IS_POW2(p->queuelen)) { - p->queuelen = LOG2_CEIL(p->queuelen); - warn("Queue length should always be a power of 2. Adjusting to %d", p->queuelen); - } - p->cfg = cfg; list_push(paths, p); @@ -276,9 +345,9 @@ int cfg_parse_nodelist(config_setting_t *cfg, struct list *list, struct list *al return list_length(list); } -int cfg_parse_node(config_setting_t *cfg, struct list *nodes, struct settings *set) +int cfg_parse_node(config_setting_t *cfg, struct list *nodes, struct cfg *set) { - const char *type, *name; + const char *type; int ret; struct node *n; @@ -287,16 +356,14 @@ int cfg_parse_node(config_setting_t *cfg, struct list *nodes, struct settings *s /* Required settings */ if (!config_setting_lookup_string(cfg, "type", &type)) cerror(cfg, "Missing node type"); - - 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->name = config_setting_name(cfg); n->cfg = cfg; ret = node_parse(n, cfg); diff --git a/src/fpga.c b/src/fpga.c index 5492b5d6a..221e41b81 100644 --- a/src/fpga.c +++ b/src/fpga.c @@ -26,7 +26,7 @@ int fpga_benchmarks(int argc, char *argv[], struct fpga *f); int fpga_tests(int argc, char *argv[], struct fpga *f); -struct settings settings; +struct cfg settings; void usage(char *name) { diff --git a/src/node.c b/src/node.c index 6dbac04e8..29528e072 100644 --- a/src/node.c +++ b/src/node.c @@ -23,32 +23,21 @@ #include "opal.h" #endif -struct list paths; /**< List of paths */ -struct list nodes; /**< List of nodes */ - -static config_t config; /**< libconfig handle */ -struct settings settings; /**< The global configuration */ +struct cfg config; static void quit() { info("Stopping paths"); - list_foreach(struct path *p, &paths) { INDENT + list_foreach(struct path *p, &config.paths) { INDENT path_stop(p); } info("Stopping nodes"); - list_foreach(struct node *n, &nodes) { INDENT + list_foreach(struct node *n, &config.nodes) { INDENT node_stop(n); } - info("De-initializing node types"); - list_foreach(struct node_type *vt, &node_types) { INDENT - node_deinit(vt); - } - - /* Freeing dynamically allocated memory */ - list_destroy(&paths, (dtor_cb_t) path_destroy, false); - list_destroy(&nodes, (dtor_cb_t) node_destroy, false); + cfg_deinit(&config); cfg_destroy(&config); info(GRN("Goodbye!")); @@ -103,9 +92,6 @@ int main(int argc, char *argv[]) #endif usage(argv[0]); - char *configfile = (argc == 2) ? argv[1] : "opal-shmem.conf"; - - log_init(); info("This is VILLASnode %s (built on %s, %s)", BLD(YEL(VERSION)), BLD(MAG(__DATE__)), BLD(MAG(__TIME__))); @@ -113,32 +99,26 @@ int main(int argc, char *argv[]) if (kernel_has_version(KERNEL_VERSION_MAJ, KERNEL_VERSION_MIN)) error("Your kernel version is to old: required >= %u.%u", KERNEL_VERSION_MAJ, KERNEL_VERSION_MIN); - /* Initialize lists */ - list_init(&paths); - list_init(&nodes); - - info("Parsing configuration"); - cfg_parse(configfile, &config, &settings, &nodes, &paths); - - info("Initialize real-time system"); - rt_init(settings.affinity, settings.priority); - info("Initialize signals"); signals_init(); - info("Initialize hook sub-system"); - hook_init(&nodes, &paths, &settings); + info("Parsing configuration"); + cfg_init_pre(&config); + + cfg_parse(&config, uri); + + cfg_init_post(&config); info("Initialize node types"); list_foreach(struct node_type *vt, &node_types) { INDENT int refs = list_length(&vt->instances); if (refs > 0) - node_init(vt, argc, argv, config_root_setting(&config)); + node_init(vt, argc, argv, config_root_setting(config.cfg)); } info("Starting nodes"); - list_foreach(struct node *n, &nodes) { INDENT - int refs = list_count(&paths, (cmp_cb_t) path_uses_node, n); + list_foreach(struct node *n, &config.nodes) { INDENT + int refs = list_count(&config.paths, (cmp_cb_t) path_uses_node, n); if (refs > 0) node_start(n); else @@ -146,7 +126,7 @@ int main(int argc, char *argv[]) } info("Starting paths"); - list_foreach(struct path *p, &paths) { INDENT + list_foreach(struct path *p, &config.paths) { INDENT if (p->enabled) { path_prepare(p); path_start(p); @@ -154,9 +134,8 @@ int main(int argc, char *argv[]) else warn("Path %s is disabled. Skipping...", path_name(p)); } - - /* Run! */ - if (settings.stats > 0) { + + if (config.stats > 0) hook_stats_header(); for (;;) { diff --git a/src/pipe.c b/src/pipe.c index 465a915e3..511deb87f 100644 --- a/src/pipe.c +++ b/src/pipe.c @@ -27,7 +27,7 @@ #include "config.h" static struct list nodes; /**< List of all nodes */ -static struct settings settings; /**< The global configuration */ +static struct cfg settings; /**< The global configuration */ static config_t config; struct dir { diff --git a/src/test.c b/src/test.c index ef95b2c65..318a13450 100644 --- a/src/test.c +++ b/src/test.c @@ -22,7 +22,7 @@ #include "timing.h" #include "pool.h" -struct settings settings; /**