Compare commits

...

5 commits

35 changed files with 3104 additions and 5934 deletions

View file

@ -20,9 +20,7 @@ steps:
api_key: api_key:
from_secret: gitea_token from_secret: gitea_token
base_url: https://git.serguzim.me base_url: https://git.serguzim.me
files: title: ${DRONE_TAG}
- build/controller
- build/controller.ini
when: when:
event: tag event: tag

1
.envrc Normal file
View file

@ -0,0 +1 @@
use nix

5
.gitignore vendored
View file

@ -2,3 +2,8 @@ docs/
build/ build/
include/sql/*.h include/sql/*.h
# Added by cargo
/target

View file

@ -1,6 +1,6 @@
cmake_minimum_required (VERSION 3.7) cmake_minimum_required (VERSION 3.7)
project(controller project(controller
VERSION 0.3.7 VERSION 0.4.0
LANGUAGES C) LANGUAGES C)
add_executable(controller src/main.c) add_executable(controller src/main.c)
@ -12,11 +12,17 @@ target_link_libraries(controller -luuid)
option(WIRING_PI_DEBUG "Use WiringPi Debugging Tool (OFF)" OFF) option(WIRING_PI_DEBUG "Use WiringPi Debugging Tool (OFF)" OFF)
set(CMAKE_C_FLAGS "$ENV{CFLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} $ENV{CFLAGS}")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -std=gnu99 -Wpedantic -Werror -Wall -Wextra -ffile-prefix-map=${CMAKE_SOURCE_DIR}/src/=") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D'__FILENAME__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR}/src/)/,,$(abspath $<))\"'")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -std=gnu99 -Wpedantic -Werror -Wall -Wextra")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
file(GLOB_RECURSE ALL_SOURCE_FILES src/*.c)
if(WIRING_PI_DEBUG) if(WIRING_PI_DEBUG)
message("Showing wiringPi calls as debug") message("Showing wiringPi calls as debug")
add_definitions("-DWIRING_PI_DEBUG") add_definitions("-DWIRING_PI_DEBUG")
@ -32,7 +38,6 @@ aux_source_directory(vendor VENDOR_SRC)
add_dependencies(controller sql) add_dependencies(controller sql)
configure_file("controller.ini" "controller.ini" COPYONLY)
configure_file("version.h.in" "version.h" @ONLY) configure_file("version.h.in" "version.h" @ONLY)
@ -47,21 +52,43 @@ add_custom_target(sql
) )
add_custom_target(run add_custom_target(run
COMMAND ./controller start COMMAND ${CMAKE_BINARY_DIR}/controller
DEPENDS controller DEPENDS controller
WORKING_DIRECTORY ${CMAKE_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
add_custom_target(debug
COMMAND valgrind ./controller start
DEPENDS controller
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_target(debug-full
COMMAND valgrind --leak-check=full --show-leak-kinds=all ./controller start
DEPENDS controller
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
) )
add_custom_target(docs add_custom_target(docs
COMMAND doxygen COMMAND doxygen
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
) )
IF(CMAKE_BUILD_TYPE MATCHES Debug)
message(STATUS "loading debug targets")
add_custom_target(debug
COMMAND gdb ${CMAKE_BINARY_DIR}/controller
DEPENDS controller
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
add_custom_target(valgrind
COMMAND valgrind -s ${CMAKE_BINARY_DIR}/controller
DEPENDS controller
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
add_custom_target(valgrind-leak
COMMAND valgrind --leak-check=full --show-leak-kinds=all ${CMAKE_BINARY_DIR}/controller
DEPENDS controller
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
add_custom_target(
clang-tidy
COMMAND /usr/bin/clang-tidy
${ALL_SOURCE_FILES}
--header-filter=${CMAKE_SOURCE_DIR}/include/*
--
-std=gnu99
-I${CMAKE_SOURCE_DIR}/include
-I${CMAKE_SOURCE_DIR}/vendor
-I${CMAKE_BINARY_DIR}
)
ENDIF(CMAKE_BUILD_TYPE MATCHES Debug)

View file

@ -1,69 +0,0 @@
[controller]
name = new emgauwa device
: 4422 for testing; 4421 for dev-env; 4420 for testing-env; 4419 for prod-env
discovery-port = 4421
: 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
mqtt-port = 1885
mqtt-host = localhost
relay-count = 10
relays-init = 1
database = controller.sqlite
log-level = debug
log-file = stdout
[relay-0]
driver = piface
pin = 0
inverted = 0
init = 0
[relay-1]
driver = piface
pin = 1
inverted = 0
init = 1
[relay-2]
driver = gpio
pin = 5
inverted = 1
[relay-3]
driver = gpio
pin = 4
inverted = 1
[relay-4]
driver = gpio
pin = 3
inverted = 1
[relay-5]
driver = gpio
pin = 2
inverted = 1
[relay-6]
driver = gpio
pin = 1
inverted = 1
pulse-duration = 3
[relay-7]
driver = gpio
pin = 0
inverted = 1
pulse-duration = 3
[relay-8]
driver = gpio
pin = 16
inverted = 1
[relay-9]
driver = gpio
pin = 15
inverted = 1

66
emgauwa-controller.conf Normal file
View file

@ -0,0 +1,66 @@
[controller]
database = "emgauwa-controller.sqlite"
mqtt-host = "127.0.0.1"
[ports]
# 4422 for testing; 4421 for dev-env; 4420 for testing-env; 4419 for prod-env
discovery = 4421
# 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
mqtt = 1885
[logging]
level = "debug"
file = "stdout"
[[relays]]
driver = "piface"
pin = 0
inverted = 0
[[relays]]
driver = "piface"
pin = 1
inverted = 0
[[relays]]
driver = "gpio"
pin = 5
inverted = 1
[[relays]]
driver = "gpio"
pin = 4
inverted = 1
[[relays]]
driver = "gpio"
pin = 3
inverted = 1
[[relays]]
driver = "gpio"
pin = 2
inverted = 1
[[relays]]
driver = "gpio"
pin = 1
inverted = 1
pulse-duration = 3
[[relays]]
driver = "gpio"
pin = 0
inverted = 1
pulse-duration = 3
[[relays]]
driver = "gpio"
pin = 16
inverted = 1
[[relays]]
driver = "gpio"
pin = 15
inverted = 1

BIN
emgauwa-controller.sqlite Normal file

Binary file not shown.

13
include/cli.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef CONTROLLER_CLI_H
#define CONTROLLER_CLI_H
typedef struct cli
{
const char *config_file;
int demo_mode;
} cli_t;
void
cli_parse(int argc, const char **argv, cli_t *cli);
#endif /* CONTROLLER_CLI_H */

View file

@ -2,7 +2,8 @@
#define CONTROLLER_CONFIG_H #define CONTROLLER_CONFIG_H
#include <stdint.h> #include <stdint.h>
#include <confini.h>
#include <toml.h>
#include <constants.h> #include <constants.h>
#include <enums.h> #include <enums.h>
@ -16,27 +17,50 @@ typedef struct
uint8_t pulse_duration; uint8_t pulse_duration;
} config_relay_t; } config_relay_t;
int
config_load(IniDispatch *disp, void *config_void);
typedef struct typedef struct
{ {
char *file; char *name;
char database[256]; char *database;
char user[256]; char *user;
char group[256]; char *group;
int log_level; char *mqtt_host;
FILE *log_file; char *include;
run_type_t run_type;
char name[MAX_NAME_LENGTH + 1];
uint16_t discovery_port;
uint16_t mqtt_port;
char mqtt_host[256];
uint8_t relay_count;
int relays_init; int relays_init;
uint8_t relay_count;
struct
{
int level;
FILE *file;
} logging;
struct
{
uint16_t discovery;
uint16_t mqtt;
} ports;
config_relay_t *relay_configs; config_relay_t *relay_configs;
} config_t; } config_t;
extern config_t global_config; extern config_t *global_config;
void
config_init();
void
config_free();
void
config_load_string(char **holder, const char *value);
void
config_load(config_t *config, const char *cli_config_file);
void
config_load_file(config_t *config, const char *file_name);
void
config_load_directory(config_t *config, const char *directory_name);
#endif //CONTROLLER_CONFIG_H #endif //CONTROLLER_CONFIG_H

View file

@ -14,13 +14,6 @@
*/ */
#define MAX_NAME_LENGTH 128 #define MAX_NAME_LENGTH 128
/**
* @brief Maximum number of dbs for the databases for the MDB_env
*
* Used when calling mdb_env_set_maxdbs() in database_setup()
*/
#define MDB_MAXDBS 8
/** /**
* @brief How many milli seconds to wait until poll timeout in main loop * @brief How many milli seconds to wait until poll timeout in main loop
*/ */
@ -28,4 +21,12 @@
#define PIFACE_GPIO_BASE 200 #define PIFACE_GPIO_BASE 200
#define DEFAULT_CONTROLLER_NAME "emgauwa-controller"
#define DEFAULT_CONFIG_PATH "emgauwa-controller.conf"
#define DEFAULT_GLOBAL_CONFIG_PATH "/etc/emgauwa/controller.conf"
#define DEFAULT_DATABASE_PATH "emgauwa-controller.sqlite"
#define DEFAULT_DISCOVERY_PORT 4421
#define DEFAULT_MQTT_PORT 1885
#define DEFAULT_MQTT_HOST "127.0.0.1"
#endif /* CONTROLLER_CONTANTS_H */ #endif /* CONTROLLER_CONTANTS_H */

View file

@ -22,10 +22,4 @@ typedef enum
RELAY_DRIVER_PIFACE, RELAY_DRIVER_PIFACE,
} relay_driver_t; } relay_driver_t;
typedef enum
{
RUN_TYPE_START,
RUN_TYPE_TEST,
} run_type_t;
#endif /* CONTROLLER_ENUMS_H */ #endif /* CONTROLLER_ENUMS_H */

View file

@ -25,9 +25,6 @@ helper_get_port(int sock);
int int
helper_open_discovery_socket(uint16_t discovery_port); helper_open_discovery_socket(uint16_t discovery_port);
void
helper_parse_cli(int argc, const char **argv, config_t *config);
int int
helper_get_weekday(const struct tm *time_struct); helper_get_weekday(const struct tm *time_struct);

View file

@ -3,7 +3,6 @@
#include <uuid/uuid.h> #include <uuid/uuid.h>
#include <stdint.h> #include <stdint.h>
#include <lmdb.h>
#include <config.h> #include <config.h>
#include <models/relay.h> #include <models/relay.h>

View file

@ -3,7 +3,6 @@
#include <stdint.h> #include <stdint.h>
#include <time.h> #include <time.h>
#include <lmdb.h>
#include <constants.h> #include <constants.h>
#include <models/schedule.h> #include <models/schedule.h>

View file

@ -3,7 +3,6 @@
#include <stdint.h> #include <stdint.h>
#include <uuid/uuid.h> #include <uuid/uuid.h>
#include <lmdb.h>
#include <models/period.h> #include <models/period.h>

9
shell.nix Normal file
View file

@ -0,0 +1,9 @@
with import <nixpkgs> {};
mkShell {
nativeBuildInputs = [
cmake
sqlite
util-linux
wiringpi
];
}

View file

@ -3,30 +3,31 @@
#include <stdio.h> #include <stdio.h>
#include <argparse.h> #include <argparse.h>
#include <cli.h>
#include <config.h> #include <config.h>
#include <logger.h> #include <logger.h>
#include <helpers.h> #include <helpers.h>
#include <version.h> #include <version.h>
static const char *const usage[] = { static const char *const usage[] = {
"controller [options] [[--] args]",
"controller [options]", "controller [options]",
NULL, NULL,
}; };
#define PERM_READ (1<<0)
#define PERM_WRITE (1<<1)
#define PERM_EXEC (1<<2)
void void
helper_parse_cli(int argc, const char **argv, config_t *config) cli_parse(int argc, const char **argv, cli_t *cli)
{ {
cli->config_file = NULL;
cli->demo_mode = 0;
int version = 0; int version = 0;
struct argparse_option options[] = struct argparse_option options[] =
{ {
OPT_HELP(), OPT_HELP(),
OPT_GROUP("Basic options"), OPT_GROUP("Basic options"),
OPT_STRING('c', "config", &config->file, "path to config file", NULL, 0, OPT_NONEG), OPT_STRING('c', "config", &cli->config_file, "path to config file", NULL, 0, OPT_NONEG),
OPT_BOOLEAN('d', "demo", &cli->demo_mode, "demo mode", NULL, 0, OPT_NONEG),
OPT_BOOLEAN('v', "version", &version, "print version", NULL, 0, OPT_NONEG), OPT_BOOLEAN('v', "version", &version, "print version", NULL, 0, OPT_NONEG),
OPT_END(), OPT_END(),
}; };
@ -38,33 +39,11 @@ helper_parse_cli(int argc, const char **argv, config_t *config)
"\nA brief description of what the program does and how it works.", "\nA brief description of what the program does and how it works.",
"\nAdditional description of the program after the description of the arguments." "\nAdditional description of the program after the description of the arguments."
); );
argc = argparse_parse(&argparse, argc, argv); argparse_parse(&argparse, argc, argv);
if(version) if(version)
{ {
printf("%s\n", EMGAUWA_CONTROLLER_VERSION); printf("%s\n", EMGAUWA_CONTROLLER_VERSION);
exit(0); exit(0);
} }
if(argc == 1)
{
if(strcmp(argv[0], "start") == 0)
{
config->run_type = RUN_TYPE_START;
return;
}
if(strcmp(argv[0], "test") == 0)
{
config->run_type = RUN_TYPE_TEST;
return;
}
LOGGER_CRIT("bad action '%s' given ('start', 'test')\n", argv[0]);
exit(1);
}
else
{
LOGGER_CRIT("no action given ('start', 'test')\n");
exit(1);
}
return;
} }

View file

@ -1,191 +1,442 @@
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <helpers.h>
#include <config.h> #include <config.h>
#include <constants.h>
#include <logger.h> #include <logger.h>
#include <confini.h>
#define CONFINI_IS_KEY(SECTION, KEY) \ config_t *global_config;
(ini_array_match(SECTION, disp->append_to, '.', disp->format) && \
ini_string_match_ii(KEY, disp->data, disp->format))
static int static int
config_load_log_level(IniDispatch *disp, config_t *config) config_load_log_level(config_t *config, char *value)
{ {
if(strcasecmp(disp->value, "debug") == 0) if(strcmp(value, "debug") == 0)
{ {
setlogmask(LOG_UPTO(LOG_DEBUG)); setlogmask(LOG_UPTO(LOG_DEBUG));
config->log_level = LOG_DEBUG; config->logging.level = LOG_DEBUG;
return 0; return 0;
} }
if(strcasecmp(disp->value, "info") == 0) if(strcmp(value, "info") == 0)
{ {
setlogmask(LOG_UPTO(LOG_INFO)); setlogmask(LOG_UPTO(LOG_INFO));
config->log_level = LOG_INFO; config->logging.level = LOG_INFO;
return 0; return 0;
} }
if(strcasecmp(disp->value, "notice") == 0) if(strcmp(value, "notice") == 0)
{ {
setlogmask(LOG_UPTO(LOG_NOTICE)); setlogmask(LOG_UPTO(LOG_NOTICE));
config->log_level = LOG_NOTICE; config->logging.level = LOG_NOTICE;
return 0; return 0;
} }
if(strcasecmp(disp->value, "warning") == 0) if(strcmp(value, "warning") == 0)
{ {
setlogmask(LOG_UPTO(LOG_WARNING)); setlogmask(LOG_UPTO(LOG_WARNING));
config->log_level = LOG_WARNING; config->logging.level = LOG_WARNING;
return 0; return 0;
} }
if(strcasecmp(disp->value, "err") == 0) if(strcmp(value, "err") == 0)
{ {
setlogmask(LOG_UPTO(LOG_ERR)); setlogmask(LOG_UPTO(LOG_ERR));
config->log_level = LOG_ERR; config->logging.level = LOG_ERR;
return 0; return 0;
} }
if(strcasecmp(disp->value, "crit") == 0) if(strcmp(value, "crit") == 0)
{ {
setlogmask(LOG_UPTO(LOG_CRIT)); setlogmask(LOG_UPTO(LOG_CRIT));
config->log_level = LOG_CRIT; config->logging.level = LOG_CRIT;
return 0; return 0;
} }
if(strcasecmp(disp->value, "emerg") == 0) if(strcmp(value, "emerg") == 0)
{ {
setlogmask(LOG_UPTO(LOG_EMERG)); setlogmask(LOG_UPTO(LOG_EMERG));
config->log_level = LOG_EMERG; config->logging.level = LOG_EMERG;
return 0; return 0;
} }
LOGGER_WARNING("invalid log-level '%s'\n", disp->value); LOGGER_WARNING("invalid log-level '%s'\n", value);
return 0; return 0;
} }
static int static int
config_load_log_file(IniDispatch *disp, config_t *config) config_load_log_file(config_t *config, char *value)
{ {
if(strcasecmp(disp->value, "stdout") == 0) if(strcmp(value, "stdout") == 0)
{ {
config->log_file = stdout; config->logging.file = stdout;
return 0; return 0;
} }
if(strcasecmp(disp->value, "stderr") == 0) if(strcmp(value, "stderr") == 0)
{ {
config->log_file = stderr; config->logging.file = stderr;
return 0; return 0;
} }
config->log_file = fopen(disp->value, "a+"); config->logging.file = fopen(value, "a+");
return 0; return 0;
} }
int static void
config_load(IniDispatch *disp, void *config_void) config_init_relay_configs(config_t *config)
{ {
config_t *config = (config_t*)config_void; config->relay_configs = malloc(sizeof(config_relay_t) * config->relay_count);
char relay_section_name[10]; // "relay-255\0" is longest name for(uint8_t i = 0; i < config->relay_count; ++i)
if(disp->type == INI_KEY)
{ {
if(CONFINI_IS_KEY("controller", "name")) 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)
{ {
strncpy(config->name, disp->value, MAX_NAME_LENGTH); config_entry.u.s[i] = tolower(config_entry.u.s[i]);
config->name[MAX_NAME_LENGTH] = '\0';
return 0;
} }
if(CONFINI_IS_KEY("controller", "database"))
if(strcmp(config_entry.u.s, "gpio") == 0)
{ {
strcpy(config->database, disp->value); config->relay_configs[relay_num].driver = RELAY_DRIVER_GPIO;
return 0;
} }
if(CONFINI_IS_KEY("controller", "user")) else if(strcmp(config_entry.u.s, "piface") == 0)
{ {
strcpy(config->user, disp->value); config->relay_configs[relay_num].driver = RELAY_DRIVER_PIFACE;
return 0;
} }
if(CONFINI_IS_KEY("controller", "group")) else
{ {
strcpy(config->group, disp->value); LOGGER_WARNING("invalid driver '%s' in section for relay %d\n", config_entry.u.s, relay_num);
return 0;
}
if(CONFINI_IS_KEY("controller", "log-level"))
{
return config_load_log_level(disp, config);
}
if(CONFINI_IS_KEY("controller", "log-file"))
{
return config_load_log_file(disp, config);
}
if(CONFINI_IS_KEY("controller", "discovery-port"))
{
config->discovery_port = atoi(disp->value);
return 0;
}
if(CONFINI_IS_KEY("controller", "mqtt-port"))
{
config->mqtt_port = atoi(disp->value);
return 0;
}
if(CONFINI_IS_KEY("controller", "mqtt-host"))
{
strcpy(config->mqtt_host, disp->value);
return 0;
}
if(CONFINI_IS_KEY("controller", "relays-init"))
{
config->relays_init = atoi(disp->value);
return 0;
}
if(CONFINI_IS_KEY("controller", "relay-count"))
{
config->relay_count = atoi(disp->value);
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;
}
return 0;
}
for(uint8_t i = 0; i < config->relay_count; ++i)
{
sprintf(relay_section_name, "relay-%u", i);
if(CONFINI_IS_KEY(relay_section_name, "pin"))
{
config->relay_configs[i].pin = atoi(disp->value);
return 0;
}
if(CONFINI_IS_KEY(relay_section_name, "inverted"))
{
config->relay_configs[i].inverted = atoi(disp->value);
return 0;
}
if(CONFINI_IS_KEY(relay_section_name, "init"))
{
config->relay_configs[i].init = atoi(disp->value);
return 0;
}
if(CONFINI_IS_KEY(relay_section_name, "pulse-duration"))
{
config->relay_configs[i].pulse_duration = atoi(disp->value);
return 0;
}
if(CONFINI_IS_KEY(relay_section_name, "driver"))
{
if(strcasecmp(disp->value, "gpio") == 0)
{
config->relay_configs[i].driver = RELAY_DRIVER_GPIO;
return 0;
}
if(strcasecmp(disp->value, "piface") == 0)
{
config->relay_configs[i].driver = RELAY_DRIVER_PIFACE;
return 0;
}
LOGGER_WARNING("invalid driver '%s' in section '%s'\n", disp->value, relay_section_name);
return 0;
}
} }
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; 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);
}

View file

@ -11,7 +11,7 @@ struct mg_connection*
connection_discovery_bind(struct mg_mgr *mgr) connection_discovery_bind(struct mg_mgr *mgr)
{ {
char address[32]; char address[32];
sprintf(address, "udp://0.0.0.0:%u", global_config.discovery_port); sprintf(address, "udp://0.0.0.0:%u", global_config->ports.discovery);
struct mg_connection *c = mg_bind(mgr, address, handler_discovery); struct mg_connection *c = mg_bind(mgr, address, handler_discovery);
return c; return c;
} }
@ -29,7 +29,7 @@ struct mg_connection*
connection_mqtt_connect(struct mg_mgr *mgr) connection_mqtt_connect(struct mg_mgr *mgr)
{ {
char address[512]; char address[512];
sprintf(address, "tcp://%s:%u", global_config.mqtt_host, global_config.mqtt_port); sprintf(address, "tcp://%s:%u", global_config->mqtt_host, global_config->ports.mqtt);
struct mg_connection *c = mg_connect(mgr, address, handler_mqtt); struct mg_connection *c = mg_connect(mgr, address, handler_mqtt);
return c; return c;
} }

View file

@ -14,7 +14,7 @@ static int in_transaction;
void void
database_init() database_init()
{ {
int rc = sqlite3_open(global_config.database, &global_database); int rc = sqlite3_open(global_config->database, &global_database);
if(rc) if(rc)
{ {

View file

@ -53,9 +53,9 @@ handler_command_relay_pulse(mpack_node_t map)
{ {
uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM)); uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM));
if(relay_num > global_config.relay_count) if(relay_num > global_config->relay_count)
{ {
LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config.relay_count); LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config->relay_count);
return; return;
} }
@ -65,7 +65,7 @@ handler_command_relay_pulse(mpack_node_t map)
int duration = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_PULSE_DURATION)); int duration = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_PULSE_DURATION));
if(duration == 0) if(duration == 0)
{ {
duration = global_config.relay_configs[relay_num].pulse_duration; duration = global_config->relay_configs[relay_num].pulse_duration;
} }
target_relay->pulse_timer = duration; target_relay->pulse_timer = duration;
LOGGER_DEBUG("pulsing relay %d for %ds\n", relay_num, duration); LOGGER_DEBUG("pulsing relay %d for %ds\n", relay_num, duration);
@ -87,9 +87,9 @@ handler_command_relay_name_set(mpack_node_t map)
uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM)); uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM));
const char *relay_name = mpack_node_str(mpack_node_map_uint(map, COMMAND_MAPPING_NAME)); const char *relay_name = mpack_node_str(mpack_node_map_uint(map, COMMAND_MAPPING_NAME));
if(relay_num > global_config.relay_count) if(relay_num > global_config->relay_count)
{ {
LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config.relay_count); LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config->relay_count);
return; return;
} }
relay_set_name(global_controller->relays[relay_num], relay_name); relay_set_name(global_controller->relays[relay_num], relay_name);
@ -120,7 +120,7 @@ handler_command_schedule_update(mpack_node_t map)
int *relay_ids = junction_relay_schedule_get_relay_ids_with_schedule(new_schedule->id); int *relay_ids = junction_relay_schedule_get_relay_ids_with_schedule(new_schedule->id);
for(int i = 0; i < global_config.relay_count; ++i) for(int i = 0; i < global_config->relay_count; ++i)
{ {
for(int j = 0; relay_ids[j] != 0; ++j) for(int j = 0; relay_ids[j] != 0; ++j)
{ {
@ -140,9 +140,9 @@ handler_command_relay_schedules_set(mpack_node_t map)
{ {
uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM)); uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM));
if(relay_num > global_config.relay_count) if(relay_num > global_config->relay_count)
{ {
LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config.relay_count); LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config->relay_count);
return; return;
} }

View file

@ -53,7 +53,7 @@ handler_discovery(struct mg_connection *c, int ev, void *ev_data)
mpack_write_uint(&writer, DISCOVERY_MAPPING_COMMAND_PORT); mpack_write_uint(&writer, DISCOVERY_MAPPING_COMMAND_PORT);
mpack_write_u16(&writer, global_controller->command_port); mpack_write_u16(&writer, global_controller->command_port);
mpack_write_uint(&writer, DISCOVERY_MAPPING_RELAY_COUNT); mpack_write_uint(&writer, DISCOVERY_MAPPING_RELAY_COUNT);
mpack_write_u8(&writer, global_config.relay_count); mpack_write_u8(&writer, global_config->relay_count);
mpack_write_uint(&writer, DISCOVERY_MAPPING_NAME); mpack_write_uint(&writer, DISCOVERY_MAPPING_NAME);
mpack_write_cstr(&writer, global_controller->name); mpack_write_cstr(&writer, global_controller->name);
mpack_finish_map(&writer); mpack_finish_map(&writer);

View file

@ -28,13 +28,13 @@ handler_loop(struct mg_connection *c_mqtt)
localtime_r(&timestamp, &time_last); localtime_r(&timestamp, &time_last);
timestamp = time(NULL); timestamp = time(NULL);
localtime_r(&timestamp, &time_now); localtime_r(&timestamp, &time_now);
for(uint_fast8_t i = 0; i < global_config.relay_count; ++i) for(uint_fast8_t i = 0; i < global_config->relay_count; ++i)
{ {
relay_t *relay = global_controller->relays[i]; relay_t *relay = global_controller->relays[i];
int is_on = 0; int is_on = 0;
int is_on_schedule = relay_is_on_schedule(relay, &time_now); int is_on_schedule = relay_is_on_schedule(relay, &time_now);
int pulse_relay = global_config.relay_configs[i].pulse_duration; int pulse_relay = global_config->relay_configs[i].pulse_duration;
if(is_on_schedule) if(is_on_schedule)
{ {
@ -73,17 +73,17 @@ handler_loop(struct mg_connection *c_mqtt)
} }
relay->is_on = is_on; relay->is_on = is_on;
if(global_config.relay_configs[i].inverted) if(global_config->relay_configs[i].inverted)
{ {
is_on = !is_on; is_on = !is_on;
} }
switch(global_config.relay_configs[i].driver) switch(global_config->relay_configs[i].driver)
{ {
case RELAY_DRIVER_GPIO: case RELAY_DRIVER_GPIO:
driver_gpio_set(global_config.relay_configs[i].pin, is_on); driver_gpio_set(global_config->relay_configs[i].pin, is_on);
break; break;
case RELAY_DRIVER_PIFACE: case RELAY_DRIVER_PIFACE:
driver_piface_set(global_config.relay_configs[i].pin, is_on); driver_piface_set(global_config->relay_configs[i].pin, is_on);
break; break;
default: default:
LOGGER_WARNING("relay %d is not using a driver\n", i); LOGGER_WARNING("relay %d is not using a driver\n", i);

View file

@ -59,8 +59,8 @@ get_gid_for_group(char *group)
int int
helper_drop_privileges() helper_drop_privileges()
{ {
uid_t uid = get_uid_for_user(global_config.user); uid_t uid = get_uid_for_user(global_config->user);
gid_t gid = get_gid_for_group(global_config.group); gid_t gid = get_gid_for_group(global_config->group);
LOGGER_DEBUG("drop privileges to %lu:%lu\n", uid, gid); LOGGER_DEBUG("drop privileges to %lu:%lu\n", uid, gid);

View file

@ -17,10 +17,11 @@ const char *COLOR_EMERG = COLOR_MAGENTA;
void void
logger_log(int level, const char *filename, int line, const char *func, const char *msg, ...) logger_log(int level, const char *filename, int line, const char *func, const char *msg, ...)
{ {
if(global_config.log_level < level) if(global_config->logging.level < level)
{ {
return; return;
} }
va_list args;
const char *level_str; const char *level_str;
const char *color; const char *color;
@ -62,24 +63,34 @@ logger_log(int level, const char *filename, int line, const char *func, const ch
time_t rawtime; time_t rawtime;
time(&rawtime); time(&rawtime);
strftime(timestamp_str, 32, "%Y-%m-%d %H:%M:%S", localtime(&rawtime)); strftime(timestamp_str, 32, "%Y-%m-%d %H:%M:%S", localtime(&rawtime));
size_t timestamp_len = strlen(timestamp_str);
char *buffer = malloc(sizeof(char) * (128 + strlen(msg))); size_t buffer_size = 128;
sprintf(buffer, "%s[%5s] %s:%d:%s " COLOR_NONE "%s", color, level_str, filename, line, func, msg); buffer_size += timestamp_len;
buffer_size += strlen(filename);
buffer_size += strlen(func);
buffer_size += strlen(msg);
//fprintf(stream, "%s %s:%d:%s " COLOR_NONE, timestamp_str, filename, line, func); char *buffer = malloc(sizeof(char) * (buffer_size));
sprintf(buffer, "%s %s[%5s] %s:%d:%s " COLOR_NONE "%s", timestamp_str, color, level_str, filename, line, func, msg);
va_list args; // start arg va_list and find log_len
va_start(args, msg); va_start(args, msg);
vsyslog(level, buffer, args); size_t log_len = vsnprintf(NULL, 0, buffer, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
va_end(args); va_end(args);
char *buffer_timed = malloc(sizeof(char) * (strlen(timestamp_str) + strlen(buffer) + 2)); char *log_line = malloc(sizeof(char) * (log_len + 1));
sprintf(buffer_timed, "%s %s", timestamp_str, buffer);
// start arg va_list again and write log_line
va_start(args, msg); va_start(args, msg);
vfprintf(global_config.log_file, buffer_timed, args); vsprintf(log_line, buffer, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
fflush(global_config.log_file);
va_end(args); va_end(args);
syslog(level, "%s", log_line + timestamp_len + 1);
fprintf(global_config->logging.file, "%s", log_line);
fflush(global_config->logging.file);
free(buffer); free(buffer);
free(buffer_timed); free(log_line);
} }

View file

@ -2,10 +2,10 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#include <lmdb.h>
#include <signal.h> #include <signal.h>
#include <syslog.h> #include <syslog.h>
#include <cli.h>
#include <logger.h> #include <logger.h>
#include <mongoose.h> #include <mongoose.h>
#include <models/controller.h> #include <models/controller.h>
@ -21,9 +21,6 @@
#include <wiringPi.h> #include <wiringPi.h>
#include <piFace.h> #include <piFace.h>
#include <wiring_debug.h> #include <wiring_debug.h>
#include <confini.h>
config_t global_config;
static struct mg_mgr mgr; static struct mg_mgr mgr;
@ -43,7 +40,7 @@ terminate(int signum)
controller_free(global_controller); controller_free(global_controller);
LOGGER_DEBUG("freeing relay configs config\n"); LOGGER_DEBUG("freeing relay configs config\n");
free(global_config.relay_configs); free(global_config->relay_configs);
exit(signum); exit(signum);
} }
@ -66,45 +63,28 @@ main(int argc, const char** argv)
signal(SIGABRT, terminate); signal(SIGABRT, terminate);
signal(SIGTERM, terminate); signal(SIGTERM, terminate);
openlog("emgauwa-controller", 0, LOG_USER);
setlogmask(LOG_UPTO(LOG_INFO)); setlogmask(LOG_UPTO(LOG_INFO));
/******************** LOAD CONFIG ********************/ /******************** LOAD CONFIG ********************/
global_config.file = "controller.ini"; config_init();
global_config.discovery_port = 4421;
global_config.mqtt_port = 1885;
global_config.relay_count = 0; cli_t cli;
global_config.relays_init = -1; cli_parse(argc, argv, &cli);
global_config.log_level = LOG_INFO; config_load(global_config, cli.config_file);
global_config.log_file = stdout;
strcpy(global_config.user, ""); if(global_config->logging.file == NULL)
strcpy(global_config.group, "");
helper_parse_cli(argc, argv, &global_config);
FILE * const ini_file = fopen(global_config.file, "rb");
if(ini_file == NULL)
{ {
LOGGER_CRIT("config file '%s' was not found\n", global_config.file); global_config->logging.file = stdout;
exit(1);
} }
if(load_ini_file( ini_file, INI_DEFAULT_FORMAT, NULL, config_load, &global_config))
{
LOGGER_CRIT("unable to parse ini file\n");
exit(1);
}
fclose(ini_file);
if(global_config.log_file == NULL) if(global_config->include)
{ {
global_config.log_file = stdout; config_load_directory(global_config, global_config->include);
} }
openlog("emgauwa-controller", 0, LOG_USER);
if(sizeof(time_t) < 8) if(sizeof(time_t) < 8)
{ {
@ -146,38 +126,38 @@ main(int argc, const char** argv)
int piface_setup = 0; int piface_setup = 0;
for(uint_fast8_t i = 0; i < global_config.relay_count; ++i) for(uint_fast8_t i = 0; i < global_config->relay_count; ++i)
{ {
int relay_default = global_config.relay_configs[i].init; int relay_default = global_config->relay_configs[i].init;
if(relay_default == -1) if(relay_default == -1)
{ {
relay_default = global_config.relays_init; relay_default = global_config->relays_init;
} }
if(relay_default == -1) if(relay_default == -1)
{ {
relay_default = global_config.relay_configs[i].inverted; relay_default = global_config->relay_configs[i].inverted;
} }
if(global_config.relay_configs[i].driver == RELAY_DRIVER_GPIO) if(global_config->relay_configs[i].driver == RELAY_DRIVER_GPIO)
{ {
pinMode(global_config.relay_configs[i].pin, OUTPUT); pinMode(global_config->relay_configs[i].pin, OUTPUT);
driver_gpio_set(global_config.relay_configs[i].pin, relay_default); driver_gpio_set(global_config->relay_configs[i].pin, relay_default);
} }
if(global_config.relay_configs[i].driver == RELAY_DRIVER_PIFACE) if(global_config->relay_configs[i].driver == RELAY_DRIVER_PIFACE)
{ {
if(!piface_setup) if(!piface_setup)
{ {
piFaceSetup(PIFACE_GPIO_BASE); piFaceSetup(PIFACE_GPIO_BASE);
piface_setup = 1; piface_setup = 1;
} }
driver_piface_set(global_config.relay_configs[i].pin, relay_default); driver_piface_set(global_config->relay_configs[i].pin, relay_default);
} }
} }
/******************** CHECK FOR TESTING RUN ********************/ /******************** CHECK FOR TESTING RUN ********************/
if(global_config.run_type == RUN_TYPE_TEST) if(cli.demo_mode)
{ {
runner_test(global_controller); runner_test(global_controller);
terminate(0); terminate(0);

View file

@ -56,9 +56,9 @@ controller_db_select_mapper(sqlite3_stmt *stmt)
break; break;
} }
} }
new_controller->relays = malloc(sizeof(relay_t) * global_config.relay_count); new_controller->relays = malloc(sizeof(relay_t) * global_config->relay_count);
uint8_t i; uint8_t i;
for(i = 0; i < global_config.relay_count; ++i) for(i = 0; i < global_config->relay_count; ++i)
{ {
new_controller->relays[i] = relay_load(i); new_controller->relays[i] = relay_load(i);
if(!new_controller->relays[i]) if(!new_controller->relays[i])
@ -198,14 +198,14 @@ controller_create(void)
new_controller->id = 0; new_controller->id = 0;
uuid_generate(new_controller->uid); uuid_generate(new_controller->uid);
strncpy(new_controller->name, global_config.name, MAX_NAME_LENGTH); strncpy(new_controller->name, global_config->name, MAX_NAME_LENGTH);
new_controller->name[MAX_NAME_LENGTH] = '\0'; new_controller->name[MAX_NAME_LENGTH] = '\0';
new_controller->command_port = 0; new_controller->command_port = 0;
new_controller->relays = malloc(sizeof(relay_t) * global_config.relay_count); new_controller->relays = malloc(sizeof(relay_t) * global_config->relay_count);
uint8_t i; uint8_t i;
for(i = 0; i < global_config.relay_count; ++i) for(i = 0; i < global_config->relay_count; ++i)
{ {
new_controller->relays[i] = relay_load(i); new_controller->relays[i] = relay_load(i);
if(!new_controller->relays[i]) if(!new_controller->relays[i])
@ -228,7 +228,7 @@ controller_set_name(controller_t *controller, const char *name)
void void
controller_free(controller_t *controller) controller_free(controller_t *controller)
{ {
for(int i = 0; i < global_config.relay_count; ++i) for(int i = 0; i < global_config->relay_count; ++i)
{ {
relay_free(controller->relays[i]); relay_free(controller->relays[i]);
} }
@ -249,7 +249,7 @@ controller_debug(controller_t *controller)
LOGGER_DEBUG("(1/3) %s @ %p\n", uuid_str, (void*)controller); LOGGER_DEBUG("(1/3) %s @ %p\n", uuid_str, (void*)controller);
LOGGER_DEBUG("(2/3) name: %s\n", controller->name); LOGGER_DEBUG("(2/3) name: %s\n", controller->name);
LOGGER_DEBUG("(3/3) relays @ %p:\n", (void*)controller->relays); LOGGER_DEBUG("(3/3) relays @ %p:\n", (void*)controller->relays);
for(int i = 0; i < global_config.relay_count; ++i) for(int i = 0; i < global_config->relay_count; ++i)
{ {
relay_debug(controller->relays[i]); relay_debug(controller->relays[i]);
} }

View file

@ -24,12 +24,12 @@ junction_relay_schedule_insert(uint8_t weekday, int relay_id, int schedule_id)
if (rc != SQLITE_DONE) if (rc != SQLITE_DONE)
{ {
LOGGER_ERR("error inserting data: %s\n", sqlite3_errmsg(global_database)); LOGGER_ERR("error inserting data: %s\n", sqlite3_errmsg(global_database));
return false; return 0;
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return true; return 1;
} }
int int
@ -67,12 +67,12 @@ junction_relay_schedule_insert_weekdays(int relay_id, int *schedule_ids)
if (rc != SQLITE_DONE) if (rc != SQLITE_DONE)
{ {
LOGGER_ERR("error inserting data: %s", sqlite3_errmsg(global_database)); LOGGER_ERR("error inserting data: %s", sqlite3_errmsg(global_database));
return false; return 0;
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return true; return 1;
} }
int int

View file

@ -73,7 +73,7 @@ relay_db_select(sqlite3_stmt *stmt)
int row = 0; int row = 0;
while(true) for(;;)
{ {
int s; int s;

View file

@ -9,22 +9,22 @@ void
runner_test() runner_test()
{ {
// from x down to 0 to turn all relays off in the end // from x down to 0 to turn all relays off in the end
for(uint_fast8_t i = 0; i < global_config.relay_count; ++i) for(uint_fast8_t i = 0; i < global_config->relay_count; ++i)
{ {
for(int test_run = 2; test_run >= 0; --test_run) for(int test_run = 2; test_run >= 0; --test_run)
{ {
int is_active = test_run % 2; int is_active = test_run % 2;
if(global_config.relay_configs[i].inverted) if(global_config->relay_configs[i].inverted)
{ {
is_active = !is_active; is_active = !is_active;
} }
switch(global_config.relay_configs[i].driver) switch(global_config->relay_configs[i].driver)
{ {
case RELAY_DRIVER_GPIO: case RELAY_DRIVER_GPIO:
driver_gpio_set(global_config.relay_configs[i].pin, is_active); driver_gpio_set(global_config->relay_configs[i].pin, is_active);
break; break;
case RELAY_DRIVER_PIFACE: case RELAY_DRIVER_PIFACE:
driver_piface_set(global_config.relay_configs[i].pin, is_active); driver_piface_set(global_config->relay_configs[i].pin, is_active);
break; break;
default: default:
LOGGER_WARNING("relay %d is not using a driver\n", i); LOGGER_WARNING("relay %d is not using a driver\n", i);

5016
vendor/confini.c vendored

File diff suppressed because it is too large Load diff

547
vendor/confini.h vendored
View file

@ -1,547 +0,0 @@
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
/**
@file confini.h
@brief libconfini header
@author Stefano Gioffr&eacute;
@copyright GNU General Public License, version 3 or any later version
@version 1.14.0
@date 2016-2020
@see https://madmurphy.github.io/libconfini
**/
#ifndef _LIBCONFINI_HEADER_
#define _LIBCONFINI_HEADER_
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* PRIVATE (HEADER-SCOPED) MACROS */
#define __INIFORMAT_TABLE_CB_FIELDS__(NAME, OFFSET, SIZE, DEFVAL) \
unsigned char NAME:SIZE;
#define __INIFORMAT_TABLE_CB_DEFAULT__(NAME, OFFSET, SIZE, DEFVAL) DEFVAL,
#define __INIFORMAT_TABLE_CB_ZERO__(NAME, OFFSET, SIZE, DEFVAL) 0,
#define _LIBCONFINI_INIFORMAT_TYPE_ \
struct IniFormat { INIFORMAT_TABLE_AS(__INIFORMAT_TABLE_CB_FIELDS__) }
#define _LIBCONFINI_DEFAULT_FORMAT_ \
{ INIFORMAT_TABLE_AS(__INIFORMAT_TABLE_CB_DEFAULT__) }
#define _LIBCONFINI_UNIXLIKE_FORMAT_ \
{ INIFORMAT_TABLE_AS(__INIFORMAT_TABLE_CB_ZERO__) }
/* PUBLIC MACROS */
/**
@brief Calls a user-given macro (that accepts four arguments) for each row
of the table
**/
/*
NOTE: The following table and the order of its rows **define** (and link
together) both the #IniFormat and #IniFormatNum data types declared in this
header
*/
#define INIFORMAT_TABLE_AS(_____) /* IniFormat table *\
NAME BIT SIZE DEFAULT
*/\
_____( delimiter_symbol, 0, 7, INI_EQUALS ) \
_____( case_sensitive, 7, 1, false )/*
*/\
_____( semicolon_marker, 8, 2, INI_DISABLED_OR_COMMENT ) \
_____( hash_marker, 10, 2, INI_DISABLED_OR_COMMENT ) \
_____( section_paths, 12, 2, INI_ABSOLUTE_AND_RELATIVE ) \
_____( multiline_nodes, 14, 2, INI_MULTILINE_EVERYWHERE )/*
*/\
_____( no_single_quotes, 16, 1, false ) \
_____( no_double_quotes, 17, 1, false ) \
_____( no_spaces_in_names, 18, 1, false ) \
_____( implicit_is_not_empty, 19, 1, false ) \
_____( do_not_collapse_values, 20, 1, false ) \
_____( preserve_empty_quotes, 21, 1, false ) \
_____( disabled_after_space, 22, 1, false ) \
_____( disabled_can_be_implicit, 23, 1, false )
/**
@brief Checks whether a format does **not** support escape sequences
**/
#define INIFORMAT_HAS_NO_ESC(FORMAT) \
(FORMAT.multiline_nodes == INI_NO_MULTILINE && \
FORMAT.no_double_quotes && FORMAT.no_single_quotes)
/* PUBLIC TYPEDEFS */
/**
@brief 24-bit bitfield representing the format of an INI file (INI
dialect)
**/
typedef _LIBCONFINI_INIFORMAT_TYPE_ IniFormat;
/**
@brief Global statistics about an INI file
**/
typedef struct IniStatistics {
const IniFormat format;
const size_t bytes;
const size_t members;
} IniStatistics;
/**
@brief Dispatch of a single INI node
**/
typedef struct IniDispatch {
const IniFormat format;
uint8_t type;
char * data;
char * value;
const char * append_to;
size_t d_len;
size_t v_len;
size_t at_len;
size_t dispatch_id;
} IniDispatch;
/**
@brief The unique ID of an INI format (24-bit maximum)
**/
typedef uint32_t IniFormatNum;
/**
@brief Callback function for handling an #IniStatistics structure
**/
typedef int (* IniStatsHandler) (
IniStatistics * statistics,
void * user_data
);
/**
@brief Callback function for handling an #IniDispatch structure
**/
typedef int (* IniDispHandler) (
IniDispatch * dispatch,
void * user_data
);
/**
@brief Callback function for handling an INI string belonging to a
sequence of INI strings
**/
typedef int (* IniStrHandler) (
char * ini_string,
size_t string_length,
size_t string_num,
IniFormat format,
void * user_data
);
/**
@brief Callback function for handling a selected fragment of an INI string
**/
typedef int (* IniSubstrHandler) (
const char * ini_string,
size_t fragm_offset,
size_t fragm_length,
size_t fragm_num,
IniFormat format,
void * user_data
);
/* PUBLIC FUNCTIONS */
extern int strip_ini_cache (
register char * const ini_source,
const size_t ini_length,
const IniFormat format,
const IniStatsHandler f_init,
const IniDispHandler f_foreach,
void * const user_data
);
extern int load_ini_file (
FILE * const ini_file,
const IniFormat format,
const IniStatsHandler f_init,
const IniDispHandler f_foreach,
void * const user_data
);
extern int load_ini_path (
const char * const path,
const IniFormat format,
const IniStatsHandler f_init,
const IniDispHandler f_foreach,
void * const user_data
);
extern bool ini_string_match_ss (
const char * const simple_string_a,
const char * const simple_string_b,
const IniFormat format
);
extern bool ini_string_match_si (
const char * const simple_string,
const char * const ini_string,
const IniFormat format
);
extern bool ini_string_match_ii (
const char * const ini_string_a,
const char * const ini_string_b,
const IniFormat format
);
extern bool ini_array_match (
const char * const ini_string_a,
const char * const ini_string_b,
const char delimiter,
const IniFormat format
);
extern size_t ini_unquote (
char * const ini_string,
const IniFormat format
);
extern size_t ini_string_parse (
char * const ini_string,
const IniFormat format
);
extern size_t ini_array_get_length (
const char * const ini_string,
const char delimiter,
const IniFormat format
);
extern int ini_array_foreach (
const char * const ini_string,
const char delimiter,
const IniFormat format,
const IniSubstrHandler f_foreach,
void * const user_data
);
extern size_t ini_array_shift (
const char ** const ini_strptr,
const char delimiter,
const IniFormat format
);
extern size_t ini_array_collapse (
char * const ini_string,
const char delimiter,
const IniFormat format
);
extern char * ini_array_break (
char * const ini_string,
const char delimiter,
const IniFormat format
);
extern char * ini_array_release (
char ** const ini_strptr,
const char delimiter,
const IniFormat format
);
extern int ini_array_split (
char * const ini_string,
const char delimiter,
const IniFormat format,
const IniStrHandler f_foreach,
void * const user_data
);
extern void ini_global_set_lowercase_mode (
const bool lowercase
);
extern void ini_global_set_implicit_value (
char * const implicit_value,
const size_t implicit_v_len
);
extern IniFormatNum ini_fton (
const IniFormat format
);
extern IniFormat ini_ntof (
const IniFormatNum format_id
);
extern int ini_get_bool (
const char * const ini_string,
const int when_fail
);
/* PUBLIC LINKS */
extern int (* const ini_get_int) (
const char * ini_string
);
extern long int (* const ini_get_lint) (
const char * ini_string
);
extern long long int (* const ini_get_llint) (
const char * ini_string
);
extern double (* const ini_get_double) (
const char * ini_string
);
/**
@brief Legacy support, soon to be replaced with a `float` data type --
please **do not use `ini_get_float()`!**
**/
#define ini_get_float \
_Pragma("GCC warning \"function `ini_get_float()` is deprecated for parsing a `double` data type; use `ini_get_double()` instead\"") \
ini_get_double
/* PUBLIC CONSTANTS AND VARIABLES */
/**
@brief Error mask (flags not present in user-generated interruptions)
**/
#define CONFINI_ERROR 252
/**
@brief Error codes
**/
enum ConfiniInterruptNo {
CONFINI_SUCCESS = 0, /**< There have been no interruptions, everything
went well [value=0] **/
CONFINI_IINTR = 1, /**< Interrupted by the user during `f_init()`
[value=1] **/
CONFINI_FEINTR = 2, /**< Interrupted by the user during `f_foreach()`
[value=2] **/
CONFINI_ENOENT = 4, /**< File inaccessible [value=4] **/
CONFINI_ENOMEM = 5, /**< Error allocating virtual memory [value=5] **/
CONFINI_EIO = 6, /**< Error reading the file [value=6] **/
CONFINI_EOOR = 7, /**< Out-of-range error: callbacks are more than
expected [value=7] **/
CONFINI_EBADF = 8, /**< The stream specified is not a seekable stream
[value=8] **/
CONFINI_EFBIG = 9, /**< File too large [value=9] **/
CONFINI_EROADDR = 10 /**< Address is read-only [value=10] **/
};
/**
@brief INI node types
**/
enum IniNodeType {
INI_UNKNOWN = 0, /**< This is a node impossible to categorize
[value=0] **/
INI_VALUE = 1, /**< Not used by **libconfini** (values are
dispatched together with keys) -- but
available for user's implementations
[value=1] **/
INI_KEY = 2, /**< This is a key [value=2] **/
INI_SECTION = 3, /**< This is a section or a section path
[value=3] **/
INI_COMMENT = 4, /**< This is a comment [value=4] **/
INI_INLINE_COMMENT = 5, /**< This is an inline comment [value=5] **/
INI_DISABLED_KEY = 6, /**< This is a disabled key [value=6] **/
INI_DISABLED_SECTION = 7 /**< This is a disabled section path
[value=7] **/
};
/**
@brief Common array and key-value delimiters (but a delimiter may also be
any other ASCII character not present in this list)
**/
enum IniDelimiters {
INI_ANY_SPACE = 0, /**< In multi-line INIs:
`/(?:\\(?:\n\r?|\r\n?)|[\t \v\f])+/`, in
non-multi-line INIs: `/[\t \v\f])+/` **/
INI_EQUALS = '=', /**< Equals character (`=`) **/
INI_COLON = ':', /**< Colon character (`:`) **/
INI_DOT = '.', /**< Dot character (`.`) **/
INI_COMMA = ',' /**< Comma character (`,`) **/
};
/**
@brief Possible values of #IniFormat::semicolon_marker and
#IniFormat::hash_marker (i.e., meaning of `/\s+;/` and `/\s+#/` in
respect to a format)
**/
enum IniCommentMarker {
INI_DISABLED_OR_COMMENT = 0, /**< This marker opens a comment or a
disabled entry **/
INI_ONLY_COMMENT = 1, /**< This marker opens a comment **/
INI_IGNORE = 2, /**< This marker opens a comment that has
been marked for deletion and must not
be dispatched or counted **/
INI_IS_NOT_A_MARKER = 3 /**< This is not a marker at all, but a
normal character instead **/
};
/**
@brief Possible values of #IniFormat::section_paths
**/
enum IniSectionPaths {
INI_ABSOLUTE_AND_RELATIVE = 0, /**< Section paths starting with a dot
express nesting to the current parent,
to root otherwise **/
INI_ABSOLUTE_ONLY = 1, /**< Section paths starting with a dot will
be cleaned of their leading dot and
appended to root **/
INI_ONE_LEVEL_ONLY = 2, /**< Format supports sections, but the dot
does not express nesting and is not a
meta-character **/
INI_NO_SECTIONS = 3 /**< Format does *not* support sections --
`/\[[^\]]*\]/g`, if any, will be
treated as keys! **/
};
/**
@brief Possible values of #IniFormat::multiline_nodes
**/
enum IniMultiline {
INI_MULTILINE_EVERYWHERE = 0, /**< Comments, section paths and keys
-- disabled or not -- are allowed
to be multi-line **/
INI_BUT_COMMENTS = 1, /**< Only section paths and keys --
disabled or not -- are allowed to
be multi-line **/
INI_BUT_DISABLED_AND_COMMENTS = 2, /**< Only active section paths and
active keys are allowed to be
multi-line **/
INI_NO_MULTILINE = 3 /**< Multi-line escape sequences are
disabled **/
};
/**
@brief A model format for standard INI files
**/
static const IniFormat INI_DEFAULT_FORMAT = _LIBCONFINI_DEFAULT_FORMAT_;
/**
@brief A model format for Unix-like .conf files (where space characters
are delimiters between keys and values)
**/
/* All fields are set to `0` here. */
static const IniFormat INI_UNIXLIKE_FORMAT = _LIBCONFINI_UNIXLIKE_FORMAT_;
/**
@brief If set to `true`, key and section names in case-insensitive INI
formats will be dispatched lowercase, verbatim otherwise (default
value: `false`)
**/
extern bool INI_GLOBAL_LOWERCASE_MODE;
/**
@brief Value to be assigned to implicit keys (default value: `NULL`)
**/
extern char * INI_GLOBAL_IMPLICIT_VALUE;
/**
@brief Length of the value assigned to implicit keys (default value: `0`)
**/
extern size_t INI_GLOBAL_IMPLICIT_V_LEN;
/* CLEAN THE PRIVATE ENVIRONMENT */
#undef _LIBCONFINI_UNIXLIKE_FORMAT_
#undef _LIBCONFINI_DEFAULT_FORMAT_
#undef _LIBCONFINI_INIFORMAT_TYPE_
#undef __INIFORMAT_TABLE_CB_ZERO__
#undef __INIFORMAT_TABLE_CB_DEFAULT__
#undef __INIFORMAT_TABLE_CB_FIELDS__
/* END OF `_LIBCONFINI_HEADER_` */
#ifdef __cplusplus
}
#endif
#endif
/* EOF */

2
vendor/mongoose.c vendored
View file

@ -1108,7 +1108,7 @@ void cs_md5_update(cs_md5_ctx *ctx, const unsigned char *buf, size_t len) {
memcpy(ctx->in, buf, len); memcpy(ctx->in, buf, len);
} }
void cs_md5_final(unsigned char digest[16], cs_md5_ctx *ctx) { void cs_md5_final(unsigned char *digest, cs_md5_ctx *ctx) {
unsigned count; unsigned count;
unsigned char *p; unsigned char *p;
uint32_t *a; uint32_t *a;

2274
vendor/toml.c vendored Normal file

File diff suppressed because it is too large Load diff

175
vendor/toml.h vendored Normal file
View file

@ -0,0 +1,175 @@
/*
MIT License
Copyright (c) 2017 - 2019 CK Tan
https://github.com/cktan/tomlc99
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef TOML_H
#define TOML_H
#include <stdio.h>
#include <stdint.h>
#ifdef __cplusplus
#define TOML_EXTERN extern "C"
#else
#define TOML_EXTERN extern
#endif
typedef struct toml_timestamp_t toml_timestamp_t;
typedef struct toml_table_t toml_table_t;
typedef struct toml_array_t toml_array_t;
typedef struct toml_datum_t toml_datum_t;
/* Parse a file. Return a table on success, or 0 otherwise.
* Caller must toml_free(the-return-value) after use.
*/
TOML_EXTERN toml_table_t* toml_parse_file(FILE* fp,
char* errbuf,
int errbufsz);
/* Parse a string containing the full config.
* Return a table on success, or 0 otherwise.
* Caller must toml_free(the-return-value) after use.
*/
TOML_EXTERN toml_table_t* toml_parse(char* conf, /* NUL terminated, please. */
char* errbuf,
int errbufsz);
/* Free the table returned by toml_parse() or toml_parse_file(). Once
* this function is called, any handles accessed through this tab
* directly or indirectly are no longer valid.
*/
TOML_EXTERN void toml_free(toml_table_t* tab);
/* Timestamp types. The year, month, day, hour, minute, second, z
* fields may be NULL if they are not relevant. e.g. In a DATE
* type, the hour, minute, second and z fields will be NULLs.
*/
struct toml_timestamp_t {
struct { /* internal. do not use. */
int year, month, day;
int hour, minute, second, millisec;
char z[10];
} __buffer;
int *year, *month, *day;
int *hour, *minute, *second, *millisec;
char* z;
};
/*-----------------------------------------------------------------
* Enhanced access methods
*/
struct toml_datum_t {
int ok;
union {
toml_timestamp_t* ts; /* ts must be freed after use */
char* s; /* string value. s must be freed after use */
int b; /* bool value */
int64_t i; /* int value */
double d; /* double value */
} u;
};
/* on arrays: */
/* ... retrieve size of array. */
TOML_EXTERN int toml_array_nelem(const toml_array_t* arr);
/* ... retrieve values using index. */
TOML_EXTERN toml_datum_t toml_string_at(const toml_array_t* arr, int idx);
TOML_EXTERN toml_datum_t toml_bool_at(const toml_array_t* arr, int idx);
TOML_EXTERN toml_datum_t toml_int_at(const toml_array_t* arr, int idx);
TOML_EXTERN toml_datum_t toml_double_at(const toml_array_t* arr, int idx);
TOML_EXTERN toml_datum_t toml_timestamp_at(const toml_array_t* arr, int idx);
/* ... retrieve array or table using index. */
TOML_EXTERN toml_array_t* toml_array_at(const toml_array_t* arr, int idx);
TOML_EXTERN toml_table_t* toml_table_at(const toml_array_t* arr, int idx);
/* on tables: */
/* ... retrieve the key in table at keyidx. Return 0 if out of range. */
TOML_EXTERN const char* toml_key_in(const toml_table_t* tab, int keyidx);
/* ... retrieve values using key. */
TOML_EXTERN toml_datum_t toml_string_in(const toml_table_t* arr, const char* key);
TOML_EXTERN toml_datum_t toml_bool_in(const toml_table_t* arr, const char* key);
TOML_EXTERN toml_datum_t toml_int_in(const toml_table_t* arr, const char* key);
TOML_EXTERN toml_datum_t toml_double_in(const toml_table_t* arr, const char* key);
TOML_EXTERN toml_datum_t toml_timestamp_in(const toml_table_t* arr, const char* key);
/* .. retrieve array or table using key. */
TOML_EXTERN toml_array_t* toml_array_in(const toml_table_t* tab,
const char* key);
TOML_EXTERN toml_table_t* toml_table_in(const toml_table_t* tab,
const char* key);
/*-----------------------------------------------------------------
* lesser used
*/
/* Return the array kind: 't'able, 'a'rray, 'v'alue */
TOML_EXTERN char toml_array_kind(const toml_array_t* arr);
/* For array kind 'v'alue, return the type of values
i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp
0 if unknown
*/
TOML_EXTERN char toml_array_type(const toml_array_t* arr);
/* Return the key of an array */
TOML_EXTERN const char* toml_array_key(const toml_array_t* arr);
/* Return the number of key-values in a table */
TOML_EXTERN int toml_table_nkval(const toml_table_t* tab);
/* Return the number of arrays in a table */
TOML_EXTERN int toml_table_narr(const toml_table_t* tab);
/* Return the number of sub-tables in a table */
TOML_EXTERN int toml_table_ntab(const toml_table_t* tab);
/* Return the key of a table*/
TOML_EXTERN const char* toml_table_key(const toml_table_t* tab);
/*--------------------------------------------------------------
* misc
*/
TOML_EXTERN int toml_utf8_to_ucs(const char* orig, int len, int64_t* ret);
TOML_EXTERN int toml_ucs_to_utf8(int64_t code, char buf[6]);
TOML_EXTERN void toml_set_memutil(void* (*xxmalloc)(size_t),
void (*xxfree)(void*));
/*--------------------------------------------------------------
* deprecated
*/
/* A raw value, must be processed by toml_rto* before using. */
typedef const char* toml_raw_t;
TOML_EXTERN toml_raw_t toml_raw_in(const toml_table_t* tab, const char* key);
TOML_EXTERN toml_raw_t toml_raw_at(const toml_array_t* arr, int idx);
TOML_EXTERN int toml_rtos(toml_raw_t s, char** ret);
TOML_EXTERN int toml_rtob(toml_raw_t s, int* ret);
TOML_EXTERN int toml_rtoi(toml_raw_t s, int64_t* ret);
TOML_EXTERN int toml_rtod(toml_raw_t s, double* ret);
TOML_EXTERN int toml_rtod_ex(toml_raw_t s, double* ret, char* buf, int buflen);
TOML_EXTERN int toml_rtots(toml_raw_t s, toml_timestamp_t* ret);
#endif /* TOML_H */