2024-08-18 15:45:53 +03:00
|
|
|
#include <getopt.h>
|
|
|
|
#include <stdio.h>
|
2024-08-14 00:33:52 +03:00
|
|
|
#include "processing.h"
|
2024-08-15 18:35:51 +03:00
|
|
|
#include "hash_table.h"
|
2024-08-15 21:57:17 +03:00
|
|
|
#include "module_registry.h"
|
2024-08-14 23:23:21 +03:00
|
|
|
|
2024-08-18 15:45:53 +03:00
|
|
|
union __attribute__((transparent_union)) option_ident {
|
|
|
|
enum {
|
|
|
|
NCOPT_BASE = 0xFF,
|
|
|
|
NCOPT_MODULE_HELP,
|
|
|
|
} as_nonchar;
|
|
|
|
int as_int;
|
|
|
|
// No "as_char" field, because it can cause problems on big-endian CPUs
|
|
|
|
};
|
|
|
|
|
2024-08-14 00:33:52 +03:00
|
|
|
int
|
|
|
|
main(int argc, char ** argv)
|
|
|
|
{
|
2024-08-18 15:45:53 +03:00
|
|
|
const char* config_filename = "config.cfg";
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
static const struct option long_options [] = {
|
|
|
|
{"config", required_argument, NULL, 'c'},
|
|
|
|
{"help", no_argument, NULL, 'h'},
|
|
|
|
{"list-modules", no_argument, NULL, 'l'},
|
|
|
|
{"module-help", required_argument, NULL, NCOPT_MODULE_HELP},
|
|
|
|
{NULL, 0, NULL, 0}};
|
|
|
|
static const char help_fstring[] =
|
|
|
|
"Usage: %s <[options...]>\n"
|
|
|
|
"Options:\n"
|
|
|
|
"\t--config <filename>, -c <filename> read configuration from <filename>\n"
|
|
|
|
"\t instead of ./config.cfg\n"
|
|
|
|
"\t--help, -h show this message\n"
|
|
|
|
"\t--list-modules, -l list currently loaded node types\n"
|
|
|
|
"\t--module-help <name> print help information provided for node type <name>\n"
|
|
|
|
;
|
|
|
|
|
|
|
|
union option_ident opt = {.as_int = getopt_long(argc, argv, "c:hl", long_options, NULL)};
|
|
|
|
if (opt.as_int < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (opt.as_int) {
|
|
|
|
case 'c':
|
|
|
|
config_filename = optarg;
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
printf(help_fstring, argv[0]);
|
|
|
|
return 0;
|
|
|
|
case 'l':
|
|
|
|
{
|
|
|
|
const GraphNodeSpecificationRegistry * reg = get_graph_node_specification_registy();
|
|
|
|
for (size_t i = 0; i < reg->capacity; ++i) {
|
|
|
|
if (!reg->key_array[i].key.bytes) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
printf("%.*s\n", (int) reg->key_array[i].key.length, reg->key_array[i].key.bytes);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return 0;
|
|
|
|
case NCOPT_MODULE_HELP:
|
|
|
|
{
|
|
|
|
GraphNodeSpecification *spec = lookup_graph_node_specification(optarg);
|
|
|
|
if (!spec) {
|
|
|
|
fprintf(stderr, "Unknown node type \"%s\"\n", optarg);
|
|
|
|
return 1;
|
|
|
|
}
|
2024-08-18 16:26:54 +03:00
|
|
|
const char* module_help = spec->documentation;
|
2024-08-18 15:45:53 +03:00
|
|
|
if (module_help) {
|
2024-08-18 16:26:54 +03:00
|
|
|
printf("Help for node type \"%s\":\n", optarg);
|
2024-08-18 15:45:53 +03:00
|
|
|
printf("%s\n", module_help);
|
|
|
|
} else {
|
|
|
|
printf("No help provided for node type \"%s\"\n", optarg);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Unexpected option ");
|
|
|
|
if ((unsigned int) opt.as_int <= 0xFF) {
|
|
|
|
fprintf(stderr, "'%c'\n", (char) opt.as_int);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "%d\n", opt.as_int);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2024-08-18 21:23:57 +03:00
|
|
|
if (optind < argc) {
|
|
|
|
fprintf(stderr, "Unexpected positional argument at position %d: %s\n", optind, argv[optind]);
|
|
|
|
return 1;
|
|
|
|
}
|
2024-08-14 00:33:52 +03:00
|
|
|
|
|
|
|
ProcessingState state = (ProcessingState) {
|
|
|
|
.wait_delay = NULL,
|
2024-08-14 14:47:55 +03:00
|
|
|
.reached_time = get_current_time(),
|
2024-08-14 00:33:52 +03:00
|
|
|
};
|
|
|
|
io_subscription_list_init(&state.wait_input, 5);
|
|
|
|
io_subscription_list_init(&state.wait_output, 5);
|
|
|
|
|
2024-08-14 23:23:21 +03:00
|
|
|
config_t config_tree;
|
|
|
|
FullConfig loaded_config;
|
|
|
|
config_init(&config_tree);
|
2024-08-19 22:13:38 +03:00
|
|
|
if (config_read_file(&config_tree, config_filename) != CONFIG_TRUE) {
|
|
|
|
fprintf(stderr, "Config syntax error: %s:%d: %s\n", config_error_file(&config_tree), config_error_line(&config_tree), config_error_text(&config_tree));
|
|
|
|
exit(1);
|
|
|
|
}
|
2024-08-14 23:23:21 +03:00
|
|
|
config_set_auto_convert(&config_tree, CONFIG_TRUE);
|
|
|
|
if (!load_config(config_root_setting(&config_tree), &loaded_config)) {
|
|
|
|
perror("Failed to load config");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2024-08-15 17:53:02 +03:00
|
|
|
GraphNode **nodes = T_ALLOC(loaded_config.nodes.length, GraphNode*);
|
2024-08-15 18:35:51 +03:00
|
|
|
TYPED_HASH_TABLE(size_t) named_nodes;
|
|
|
|
hash_table_init(&named_nodes, NULL);
|
2024-08-14 23:23:21 +03:00
|
|
|
for (size_t i = 0; i < loaded_config.nodes.length; ++i) {
|
|
|
|
const char* type_name = loaded_config.nodes.items[i].type;
|
|
|
|
if (!type_name) {
|
|
|
|
fprintf(stderr, "No node type for node %ld \"%s\"\n", i, loaded_config.nodes.items[i].name);
|
|
|
|
exit(1);
|
|
|
|
}
|
2024-08-15 21:57:17 +03:00
|
|
|
GraphNodeSpecification *spec = lookup_graph_node_specification(type_name);
|
2024-08-14 23:23:21 +03:00
|
|
|
if (!spec) {
|
|
|
|
fprintf(stderr, "Unknown node type \"%s\" for node %ld \"%s\"\n", type_name, i, loaded_config.nodes.items[i].name);
|
|
|
|
exit(1);
|
|
|
|
}
|
2024-08-17 12:02:26 +03:00
|
|
|
if (!(nodes[i] = graph_node_new(spec, &loaded_config.nodes.items[i], &loaded_config.environment))) {
|
2024-08-14 23:23:21 +03:00
|
|
|
perror("Failed to create node");
|
2024-08-18 18:20:06 +03:00
|
|
|
fprintf(stderr, "Node %ld \"%s\"\n", i, loaded_config.nodes.items[i].name);
|
2024-08-14 23:23:21 +03:00
|
|
|
exit(1);
|
|
|
|
}
|
2024-08-15 18:35:51 +03:00
|
|
|
if (loaded_config.nodes.items[i].name) {
|
|
|
|
hash_table_insert(&named_nodes, hash_table_key_from_cstr(loaded_config.nodes.items[i].name), &i);
|
|
|
|
}
|
2024-08-14 23:23:21 +03:00
|
|
|
}
|
|
|
|
|
2024-08-15 17:53:02 +03:00
|
|
|
GraphChannel *channels = T_ALLOC(loaded_config.channels.length, GraphChannel);
|
2024-08-14 23:23:21 +03:00
|
|
|
for (size_t i = 0; i < loaded_config.channels.length; ++i) {
|
|
|
|
const char *node_names[2];
|
|
|
|
GraphNode *end_nodes[2] = {NULL, NULL};
|
|
|
|
node_names[0] = loaded_config.channels.items[i].from.name;
|
|
|
|
node_names[1] = loaded_config.channels.items[i].to.name;
|
|
|
|
for (int j = 0; j < 2; ++j) {
|
2024-08-15 18:35:51 +03:00
|
|
|
HashTableIndex k = hash_table_find(&named_nodes, hash_table_key_from_cstr(node_names[j]));
|
|
|
|
if (k < 0) {
|
|
|
|
perror("Errno");
|
2024-08-14 23:23:21 +03:00
|
|
|
fprintf(stderr, "No node named \"%s\"\n", node_names[j]);
|
|
|
|
exit(1);
|
|
|
|
}
|
2024-08-15 18:35:51 +03:00
|
|
|
end_nodes[j] = nodes[named_nodes.value_array[k]];
|
2024-08-14 23:23:21 +03:00
|
|
|
}
|
|
|
|
graph_channel_init(&channels[i],
|
|
|
|
end_nodes[0], loaded_config.channels.items[i].from.index,
|
|
|
|
end_nodes[1], loaded_config.channels.items[i].to.index
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < loaded_config.nodes.length; ++i) {
|
|
|
|
graph_node_register_io(nodes[i], &state);
|
|
|
|
}
|
2024-08-14 00:33:52 +03:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
process_iteration(&state);
|
|
|
|
}
|
|
|
|
|
2024-08-15 18:35:51 +03:00
|
|
|
hash_table_deinit(&named_nodes);
|
2024-08-14 23:23:21 +03:00
|
|
|
for (ssize_t i = loaded_config.nodes.length - 1; i >= 0; --i) {
|
|
|
|
graph_node_delete(nodes[i]);
|
|
|
|
}
|
2024-08-15 22:26:21 +03:00
|
|
|
event_destroy_all();
|
2024-08-14 23:23:21 +03:00
|
|
|
free(channels);
|
|
|
|
free(nodes);
|
|
|
|
|
2024-08-15 22:25:18 +03:00
|
|
|
reset_config(&loaded_config);
|
2024-08-16 23:46:11 +03:00
|
|
|
event_predicate_reset();
|
2024-08-14 23:23:21 +03:00
|
|
|
config_destroy(&config_tree);
|
2024-08-14 00:33:52 +03:00
|
|
|
|
|
|
|
io_subscription_list_deinit(&state.wait_output);
|
|
|
|
io_subscription_list_deinit(&state.wait_input);
|
|
|
|
return 0;
|
|
|
|
}
|