controller-legacy/src/config.c

442 lines
10 KiB
C

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <config.h>
#include <constants.h>
#include <logger.h>
config_t *global_config;
static int
config_load_log_level(config_t *config, char *value)
{
if(strcmp(value, "debug") == 0)
{
setlogmask(LOG_UPTO(LOG_DEBUG));
config->logging.level = LOG_DEBUG;
return 0;
}
if(strcmp(value, "info") == 0)
{
setlogmask(LOG_UPTO(LOG_INFO));
config->logging.level = LOG_INFO;
return 0;
}
if(strcmp(value, "notice") == 0)
{
setlogmask(LOG_UPTO(LOG_NOTICE));
config->logging.level = LOG_NOTICE;
return 0;
}
if(strcmp(value, "warning") == 0)
{
setlogmask(LOG_UPTO(LOG_WARNING));
config->logging.level = LOG_WARNING;
return 0;
}
if(strcmp(value, "err") == 0)
{
setlogmask(LOG_UPTO(LOG_ERR));
config->logging.level = LOG_ERR;
return 0;
}
if(strcmp(value, "crit") == 0)
{
setlogmask(LOG_UPTO(LOG_CRIT));
config->logging.level = LOG_CRIT;
return 0;
}
if(strcmp(value, "emerg") == 0)
{
setlogmask(LOG_UPTO(LOG_EMERG));
config->logging.level = LOG_EMERG;
return 0;
}
LOGGER_WARNING("invalid log-level '%s'\n", value);
return 0;
}
static int
config_load_log_file(config_t *config, char *value)
{
if(strcmp(value, "stdout") == 0)
{
config->logging.file = stdout;
return 0;
}
if(strcmp(value, "stderr") == 0)
{
config->logging.file = stderr;
return 0;
}
config->logging.file = fopen(value, "a+");
return 0;
}
static void
config_init_relay_configs(config_t *config)
{
config->relay_configs = malloc(sizeof(config_relay_t) * config->relay_count);
for(uint8_t i = 0; i < config->relay_count; ++i)
{
config->relay_configs[i].driver = RELAY_DRIVER_NONE;
config->relay_configs[i].inverted = 0;
config->relay_configs[i].init = -1;
config->relay_configs[i].pin = 0;
config->relay_configs[i].pulse_duration = 0;
}
}
static void
config_load_section_controller(config_t *config, toml_table_t* controller)
{
toml_datum_t config_entry;
config_entry = toml_string_in(controller, "name");
if(config_entry.ok)
{
config_load_string(&config->name, config_entry.u.s);
free(config_entry.u.s);
}
config_entry = toml_string_in(controller, "database");
if(config_entry.ok)
{
config_load_string(&config->database, config_entry.u.s);
free(config_entry.u.s);
}
config_entry = toml_string_in(controller, "user");
if(config_entry.ok)
{
config_load_string(&config->user, config_entry.u.s);
free(config_entry.u.s);
}
config_entry = toml_string_in(controller, "group");
if(config_entry.ok)
{
config_load_string(&config->group, config_entry.u.s);
free(config_entry.u.s);
}
config_entry = toml_string_in(controller, "mqtt-host");
if(config_entry.ok)
{
config_load_string(&config->mqtt_host, config_entry.u.s);
free(config_entry.u.s);
}
config_entry = toml_int_in(controller, "relays-init");
if(config_entry.ok)
{
config->relays_init = config_entry.u.i;
}
config_entry = toml_string_in(controller, "include");
if(config_entry.ok)
{
config_load_string(&config->include, config_entry.u.s);
free(config_entry.u.s);
}
}
static void
config_load_section_logging(config_t *config, toml_table_t* logging)
{
toml_datum_t config_entry;
config_entry = toml_string_in(logging, "level");
if(config_entry.ok)
{
config_load_log_level(config, config_entry.u.s);
free(config_entry.u.s);
}
config_entry = toml_string_in(logging, "file");
if(config_entry.ok)
{
config_load_log_file(config, config_entry.u.s);
free(config_entry.u.s);
}
}
static void
config_load_section_ports(config_t *config, toml_table_t* ports)
{
toml_datum_t config_entry;
config_entry = toml_int_in(ports, "discovery");
if(config_entry.ok)
{
config->ports.discovery = config_entry.u.i;
}
config_entry = toml_int_in(ports, "mqtt");
if(config_entry.ok)
{
config->ports.mqtt = config_entry.u.i;
}
}
static void
config_load_section_relay(config_t *config, toml_table_t* relay, int relay_num)
{
toml_datum_t config_entry;
config_entry = toml_int_in(relay, "pin");
if(config_entry.ok)
{
config->relay_configs[relay_num].pin = config_entry.u.i;
}
config_entry = toml_int_in(relay, "inverted");
if(config_entry.ok)
{
config->relay_configs[relay_num].inverted = config_entry.u.i;
}
config_entry = toml_int_in(relay, "init");
if(config_entry.ok)
{
config->relay_configs[relay_num].init = config_entry.u.i;
}
config_entry = toml_int_in(relay, "pulse-duration");
if(config_entry.ok)
{
config->relay_configs[relay_num].pulse_duration = config_entry.u.i;
}
config_entry = toml_string_in(relay, "driver");
if(config_entry.ok)
{
for(int i = 0; config_entry.u.s[i] != '\0'; ++i)
{
config_entry.u.s[i] = tolower(config_entry.u.s[i]);
}
if(strcmp(config_entry.u.s, "gpio") == 0)
{
config->relay_configs[relay_num].driver = RELAY_DRIVER_GPIO;
}
else if(strcmp(config_entry.u.s, "piface") == 0)
{
config->relay_configs[relay_num].driver = RELAY_DRIVER_PIFACE;
}
else
{
LOGGER_WARNING("invalid driver '%s' in section for relay %d\n", config_entry.u.s, relay_num);
}
free(config_entry.u.s);
}
}
void
config_init()
{
global_config = calloc(1, sizeof(config_t));
config_load_string(&global_config->name, DEFAULT_CONTROLLER_NAME);
config_load_string(&global_config->database, DEFAULT_DATABASE_PATH);
config_load_string(&global_config->mqtt_host, DEFAULT_MQTT_HOST);
global_config->user = NULL;
global_config->group = NULL;
global_config->include = NULL;
global_config->relays_init = 0;
global_config->relay_count = 0;
config_load_string(&global_config->mqtt_host, "127.0.0.1");
global_config->ports.mqtt = DEFAULT_MQTT_PORT;
global_config->ports.discovery = DEFAULT_DISCOVERY_PORT;
global_config->logging.level = LOG_DEBUG;
global_config->logging.file = stdout;
}
void
config_free()
{
free(global_config->name);
free(global_config->database);
free(global_config->user);
free(global_config->group);
free(global_config->mqtt_host);
free(global_config->include);
free(global_config);
}
void
config_load_string(char **holder, const char *value)
{
if(*holder)
{
free(*holder);
}
size_t value_len = strlen(value);
char *new_holder = malloc(sizeof(char) * (value_len + 1));
strcpy(new_holder, value);
new_holder[value_len] = '\0';
*holder = new_holder;
}
static int
config_try_file(const char *path)
{
if(access(path, F_OK) != 0)
{
return 1;
}
if(access(path, R_OK) != 0)
{
return 1;
}
return 0;
}
void
config_load(config_t *config, const char *cli_config_file)
{
if(cli_config_file)
{
if(config_try_file(cli_config_file) == 0)
{
config_load_file(config, cli_config_file);
return;
}
LOGGER_CRIT("unable to open the passed config file '%s'\n", cli_config_file);
exit(1);
}
if(config_try_file(DEFAULT_CONFIG_PATH) == 0)
{
config_load_file(config, DEFAULT_CONFIG_PATH);
return;
}
if(config_try_file(DEFAULT_GLOBAL_CONFIG_PATH) == 0)
{
config_load_file(config, DEFAULT_GLOBAL_CONFIG_PATH);
return;
}
}
void
config_load_file(config_t *config, const char *file_name)
{
FILE *fp;
toml_table_t* config_toml;
char errbuf[256];
/* Open the file and parse content */
fp = fopen(file_name, "r");
if(fp == NULL) {
LOGGER_CRIT("unable to open config file '%s'\n", file_name);
exit(1);
}
config_toml = toml_parse_file(fp, errbuf, sizeof(errbuf));
fclose(fp);
if(config_toml == NULL) {
LOGGER_CRIT("unable to parse config file '%s': %s\n", file_name, errbuf);
exit(1);
}
toml_table_t* controller = toml_table_in(config_toml, "controller");
if(controller)
{
config_load_section_controller(config, controller);
}
toml_table_t* logging = toml_table_in(config_toml, "logging");
if(logging)
{
config_load_section_logging(config, logging);
}
toml_table_t* ports = toml_table_in(config_toml, "ports");
if(ports)
{
config_load_section_ports(config, ports);
}
toml_array_t* relays = toml_array_in(config_toml, "relays");
if(relays)
{
config->relay_count = toml_array_nelem(relays);
config_init_relay_configs(config);
for(int i = 0; i < config->relay_count; ++i)
{
config_load_section_relay(config, toml_table_at(relays, i), i);
}
}
toml_free(config_toml);
LOGGER_DEBUG("Loaded config from %s\n", file_name);
}
void
config_load_directory(config_t *config, const char *directory_name)
{
struct dirent *directory_entry;
DIR *directory;
(void)config;
directory = opendir(directory_name);
if(directory == NULL)
{
LOGGER_CRIT("cannot open directory '%s': %s\n", directory_name, strerror(errno));
exit(1);
}
while((directory_entry = readdir(directory)) != NULL)
{
struct stat sb;
const char *entry_name = directory_entry->d_name;
size_t copied = 0;
// Add 2 for '/' and '\0'.
size_t path_len = strlen(directory_name) + strlen(entry_name) + 1;
char *path = malloc(sizeof(char) * (path_len + 1));
path[0] = '\0';
strncat(path + copied, directory_name, path_len - copied);
copied = strlen(path);
if(path[copied - 1] != '/')
{
strncat(path + copied, "/", path_len - copied);
copied = strlen(path);
}
strncat(path + copied, entry_name, path_len - copied);
if(stat(path, &sb))
{
LOGGER_WARNING("failed to get info for '%s': %s\n", path, strerror(errno));
}
if(S_ISREG(sb.st_mode))
{
config_load_file(config, path);
}
free(path);
}
closedir(directory);
}