Compare commits
5 commits
Author | SHA1 | Date | |
---|---|---|---|
5d98a0579b | |||
c49ada88c3 | |||
943dd8e0d1 | |||
5d3c94ef8c | |||
79b1f3b2cf |
35 changed files with 3104 additions and 5934 deletions
|
@ -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
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use nix
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -2,3 +2,8 @@ docs/
|
||||||
build/
|
build/
|
||||||
|
|
||||||
include/sql/*.h
|
include/sql/*.h
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
66
emgauwa-controller.conf
Normal 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
BIN
emgauwa-controller.sqlite
Normal file
Binary file not shown.
13
include/cli.h
Normal file
13
include/cli.h
Normal 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 */
|
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
9
shell.nix
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
with import <nixpkgs> {};
|
||||||
|
mkShell {
|
||||||
|
nativeBuildInputs = [
|
||||||
|
cmake
|
||||||
|
sqlite
|
||||||
|
util-linux
|
||||||
|
wiringpi
|
||||||
|
];
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
}
|
505
src/config.c
505
src/config.c
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -28,13 +28,13 @@ handler_loop(struct mg_connection *c_mqtt)
|
||||||
localtime_r(×tamp, &time_last);
|
localtime_r(×tamp, &time_last);
|
||||||
timestamp = time(NULL);
|
timestamp = time(NULL);
|
||||||
localtime_r(×tamp, &time_now);
|
localtime_r(×tamp, &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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
33
src/logger.c
33
src/logger.c
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
62
src/main.c
62
src/main.c
|
@ -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);
|
||||||
|
|
|
@ -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]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -73,7 +73,7 @@ relay_db_select(sqlite3_stmt *stmt)
|
||||||
|
|
||||||
int row = 0;
|
int row = 0;
|
||||||
|
|
||||||
while(true)
|
for(;;)
|
||||||
{
|
{
|
||||||
int s;
|
int s;
|
||||||
|
|
||||||
|
|
|
@ -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
5016
vendor/confini.c
vendored
File diff suppressed because it is too large
Load diff
547
vendor/confini.h
vendored
547
vendor/confini.h
vendored
|
@ -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é
|
|
||||||
@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
2
vendor/mongoose.c
vendored
|
@ -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
2274
vendor/toml.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
175
vendor/toml.h
vendored
Normal file
175
vendor/toml.h
vendored
Normal 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 */
|
Loading…
Reference in a new issue