442 lines
10 KiB
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);
|
|
}
|