#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); }