Compare commits
42 commits
Author | SHA1 | Date | |
---|---|---|---|
173b9d41e8 | |||
ea6b653121 | |||
aa3253b190 | |||
8c5ad5a374 | |||
b2b71d6742 | |||
20f80a223f | |||
3c04173fbf | |||
87b3f06092 | |||
750002daf2 | |||
e2bb965cc0 | |||
19a4a14cb9 | |||
1f315d2b92 | |||
3f11431df6 | |||
2fe784dd78 | |||
688e2e71ee | |||
a3ce2bfac9 | |||
1bbaeacc84 | |||
ce59bb364f | |||
0c13c03a73 | |||
5796f88e05 | |||
97a19135ce | |||
c7cafa94c4 | |||
5747a65664 | |||
1d6e8ff037 | |||
f97b149376 | |||
fca35ade9e | |||
fad3d80f39 | |||
ead52a0f47 | |||
b7d3f2ad0b | |||
78f98010aa | |||
22b8ff4d6a | |||
011b9f4c14 | |||
b38c3d61d8 | |||
4b39631765 | |||
d3fd48b35a | |||
01ffb1d58d | |||
9d2c48d645 | |||
6a2b94ef1c | |||
0103b0b2ff | |||
f67b7e9e0f | |||
7275d66c86 | |||
4dd8329484 |
83 changed files with 5367 additions and 6683 deletions
2
.clang-tidy
Normal file
2
.clang-tidy
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
---
|
||||||
|
Checks: 'clang-diagnostics-*,clang-analyzer-*,linuxkernel-*,modernize-*,readability-*,-readability-magic-numbers,portability-*'
|
17
.drone.yml
17
.drone.yml
|
@ -29,12 +29,12 @@ steps:
|
||||||
image: serguzim/emgauwa-builder
|
image: serguzim/emgauwa-builder
|
||||||
pull: always
|
pull: always
|
||||||
commands:
|
commands:
|
||||||
- tar xzf emgauwa-controller.tar.gz
|
- tar xzf emgauwa-controller.tar.gz
|
||||||
- cd controller
|
- cd controller
|
||||||
- mkdir build
|
- mkdir build
|
||||||
- cd build
|
- cd build
|
||||||
- cmake -DWIRING_PI_DEBUG=on ..
|
- cmake -DWIRING_PI_DEBUG=on ..
|
||||||
- make
|
- make
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
image: serguzim/emgauwa-builder
|
image: serguzim/emgauwa-builder
|
||||||
|
@ -53,9 +53,8 @@ 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/core
|
|
||||||
- build/core.ini
|
|
||||||
when:
|
when:
|
||||||
event: tag
|
event: tag
|
||||||
|
|
||||||
|
|
|
@ -10,3 +10,7 @@ end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use nix
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,7 +1,11 @@
|
||||||
build/
|
build/
|
||||||
docs/
|
docs/
|
||||||
|
|
||||||
tests/testing_latest/
|
tests/testing/
|
||||||
tests/testing_bak/
|
|
||||||
|
|
||||||
include/sql/*.h
|
include/sql/*.h
|
||||||
|
|
||||||
|
emgauwa-core.conf.d
|
||||||
|
|
||||||
|
emgauwa-core.sqlite
|
||||||
|
vgcore.*
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
cmake_minimum_required (VERSION 3.7)
|
cmake_minimum_required (VERSION 3.7)
|
||||||
project(core
|
project(core
|
||||||
VERSION 0.3.4
|
VERSION 0.4.5
|
||||||
LANGUAGES C)
|
LANGUAGES C)
|
||||||
|
|
||||||
add_executable(core src/main.c)
|
add_executable(core src/main.c)
|
||||||
|
@ -8,11 +8,18 @@ add_executable(core src/main.c)
|
||||||
target_link_libraries(core -lsqlite3)
|
target_link_libraries(core -lsqlite3)
|
||||||
target_link_libraries(core -luuid)
|
target_link_libraries(core -luuid)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
|
||||||
add_definitions("-DMG_ENABLE_EXTRA_ERRORS_DESC -DMG_ENABLE_MQTT_BROKER")
|
add_definitions("-DMG_ENABLE_EXTRA_ERRORS_DESC -DMG_ENABLE_MQTT_BROKER")
|
||||||
|
|
||||||
aux_source_directory(src/ SRC_DIR)
|
aux_source_directory(src/ SRC_DIR)
|
||||||
|
@ -24,7 +31,6 @@ aux_source_directory(vendor VENDOR_SRC)
|
||||||
|
|
||||||
add_dependencies(core sql)
|
add_dependencies(core sql)
|
||||||
|
|
||||||
configure_file("core.ini" "core.ini" COPYONLY)
|
|
||||||
configure_file("version.h.in" "version.h" @ONLY)
|
configure_file("version.h.in" "version.h" @ONLY)
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,27 +45,8 @@ add_custom_target(sql
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(run
|
add_custom_target(run
|
||||||
COMMAND ./core start
|
COMMAND ${CMAKE_BINARY_DIR}/core
|
||||||
DEPENDS core
|
DEPENDS core
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
)
|
|
||||||
add_custom_target(debug
|
|
||||||
COMMAND valgrind -s ./core start
|
|
||||||
DEPENDS core
|
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
)
|
|
||||||
add_custom_target(debug-leak
|
|
||||||
COMMAND valgrind --leak-check=full --show-leak-kinds=all ./core start
|
|
||||||
DEPENDS core
|
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
)
|
|
||||||
add_custom_target(debug-callgrind
|
|
||||||
COMMAND valgrind --tool=callgrind ./core start
|
|
||||||
DEPENDS core
|
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
|
||||||
)
|
|
||||||
add_custom_target(docs
|
|
||||||
COMMAND doxygen
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,13 +55,57 @@ add_custom_target(test
|
||||||
DEPENDS core
|
DEPENDS core
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||||
)
|
)
|
||||||
add_custom_target(test-callgrind
|
|
||||||
COMMAND ./run_tests.sh ${CMAKE_BINARY_DIR}/core "--tool=callgrind"
|
add_custom_target(docs
|
||||||
DEPENDS core
|
COMMAND doxygen
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
|
||||||
)
|
|
||||||
add_custom_target(coverage
|
|
||||||
COMMAND gcovr -s --root ${CMAKE_SOURCE_DIR} -e ${CMAKE_SOURCE_DIR}/vendor --html-details ${CMAKE_BINARY_DIR}/coverage.html --html-title "Emgauwa Core Coverage" ${CMAKE_BINARY_DIR}/CMakeFiles/core.dir
|
|
||||||
DEPENDS test
|
|
||||||
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}/core
|
||||||
|
DEPENDS core
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
add_custom_target(valgrind
|
||||||
|
COMMAND valgrind -s ${CMAKE_BINARY_DIR}/core
|
||||||
|
DEPENDS core
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
add_custom_target(valgrind-leak
|
||||||
|
COMMAND valgrind --leak-check=full --show-leak-kinds=all ${CMAKE_BINARY_DIR}/core
|
||||||
|
DEPENDS core
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
add_custom_target(valgrind-callgrind
|
||||||
|
COMMAND valgrind --tool=callgrind ${CMAKE_BINARY_DIR}/core
|
||||||
|
DEPENDS core
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
add_custom_target(coverage
|
||||||
|
COMMAND gcovr -s --root ${CMAKE_SOURCE_DIR} -e ${CMAKE_SOURCE_DIR}/vendor --html-details ${CMAKE_BINARY_DIR}/coverage.html --html-title "Emgauwa Core Coverage" ${CMAKE_BINARY_DIR}/CMakeFiles/core.dir
|
||||||
|
DEPENDS test
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(test-callgrind
|
||||||
|
COMMAND ./run_tests.sh ${CMAKE_BINARY_DIR}/core "--tool=callgrind"
|
||||||
|
DEPENDS core
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||||
|
)
|
||||||
|
|
||||||
|
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}
|
||||||
|
-DMG_ENABLE_EXTRA_ERRORS_DESC
|
||||||
|
-DMG_ENABLE_MQTT_BROKER
|
||||||
|
)
|
||||||
|
ENDIF(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||||
|
|
81
Doxyfile
81
Doxyfile
|
@ -1,4 +1,4 @@
|
||||||
# Doxyfile 1.8.17
|
# Doxyfile 1.8.20
|
||||||
|
|
||||||
# This file describes the settings to be used by the documentation system
|
# This file describes the settings to be used by the documentation system
|
||||||
# doxygen (www.doxygen.org) for a project.
|
# doxygen (www.doxygen.org) for a project.
|
||||||
|
@ -227,6 +227,14 @@ QT_AUTOBRIEF = NO
|
||||||
|
|
||||||
MULTILINE_CPP_IS_BRIEF = NO
|
MULTILINE_CPP_IS_BRIEF = NO
|
||||||
|
|
||||||
|
# By default Python docstrings are displayed as preformatted text and doxygen's
|
||||||
|
# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
|
||||||
|
# doxygen's special commands can be used and the contents of the docstring
|
||||||
|
# documentation blocks is shown as doxygen documentation.
|
||||||
|
# The default value is: YES.
|
||||||
|
|
||||||
|
PYTHON_DOCSTRING = YES
|
||||||
|
|
||||||
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
|
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
|
||||||
# documentation from any documented member that it re-implements.
|
# documentation from any documented member that it re-implements.
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
|
@ -263,12 +271,6 @@ TAB_SIZE = 4
|
||||||
|
|
||||||
ALIASES =
|
ALIASES =
|
||||||
|
|
||||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
|
||||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
|
||||||
# will allow you to use the command class in the itcl::class meaning.
|
|
||||||
|
|
||||||
TCL_SUBST =
|
|
||||||
|
|
||||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||||
# only. Doxygen will then generate output that is more tailored for C. For
|
# only. Doxygen will then generate output that is more tailored for C. For
|
||||||
# instance, some of the names that are used will be different. The list of all
|
# instance, some of the names that are used will be different. The list of all
|
||||||
|
@ -310,13 +312,13 @@ OPTIMIZE_OUTPUT_SLICE = NO
|
||||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||||
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
|
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
|
||||||
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice,
|
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
|
||||||
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||||
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
|
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
|
||||||
# tries to guess whether the code is fixed or free formatted code, this is the
|
# tries to guess whether the code is fixed or free formatted code, this is the
|
||||||
# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat
|
# default for Fortran type files). For instance to make doxygen treat .inc files
|
||||||
# .inc files as Fortran files (default is PHP), and .f files as C (default is
|
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
|
||||||
# Fortran), use: inc=Fortran f=C.
|
# use: inc=Fortran f=C.
|
||||||
#
|
#
|
||||||
# Note: For files without extension you can use no_extension as a placeholder.
|
# Note: For files without extension you can use no_extension as a placeholder.
|
||||||
#
|
#
|
||||||
|
@ -455,6 +457,19 @@ TYPEDEF_HIDES_STRUCT = NO
|
||||||
|
|
||||||
LOOKUP_CACHE_SIZE = 0
|
LOOKUP_CACHE_SIZE = 0
|
||||||
|
|
||||||
|
# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
|
||||||
|
# during processing. When set to 0 doxygen will based this on the number of
|
||||||
|
# cores available in the system. You can set it explicitly to a value larger
|
||||||
|
# than 0 to get more control over the balance between CPU load and processing
|
||||||
|
# speed. At this moment only the input processing can be done using multiple
|
||||||
|
# threads. Since this is still an experimental feature the default is set to 1,
|
||||||
|
# which efficively disables parallel processing. Please report any issues you
|
||||||
|
# encounter. Generating dot graphs in parallel is controlled by the
|
||||||
|
# DOT_NUM_THREADS setting.
|
||||||
|
# Minimum value: 0, maximum value: 32, default value: 1.
|
||||||
|
|
||||||
|
NUM_PROC_THREADS = 1
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Build related configuration options
|
# Build related configuration options
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
@ -559,7 +574,7 @@ INTERNAL_DOCS = NO
|
||||||
# names in lower-case letters. If set to YES, upper-case letters are also
|
# names in lower-case letters. If set to YES, upper-case letters are also
|
||||||
# allowed. This is useful if you have classes or files whose names only differ
|
# allowed. This is useful if you have classes or files whose names only differ
|
||||||
# in case and if your file system supports case sensitive file names. Windows
|
# in case and if your file system supports case sensitive file names. Windows
|
||||||
# (including Cygwin) ands Mac users are advised to set this option to NO.
|
# (including Cygwin) and Mac users are advised to set this option to NO.
|
||||||
# The default value is: system dependent.
|
# The default value is: system dependent.
|
||||||
|
|
||||||
CASE_SENSE_NAMES = NO
|
CASE_SENSE_NAMES = NO
|
||||||
|
@ -829,7 +844,8 @@ WARN_LOGFILE =
|
||||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# Note: If this tag is empty the current directory is searched.
|
||||||
|
|
||||||
INPUT = .
|
INPUT = src/ \
|
||||||
|
include/
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||||
|
@ -853,7 +869,7 @@ INPUT_ENCODING = UTF-8
|
||||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||||
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
|
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
|
||||||
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
|
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
|
||||||
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd,
|
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||||
|
|
||||||
FILE_PATTERNS = *.c \
|
FILE_PATTERNS = *.c \
|
||||||
|
@ -1378,7 +1394,7 @@ CHM_FILE =
|
||||||
HHC_LOCATION =
|
HHC_LOCATION =
|
||||||
|
|
||||||
# The GENERATE_CHI flag controls if a separate .chi index file is generated
|
# The GENERATE_CHI flag controls if a separate .chi index file is generated
|
||||||
# (YES) or that it should be included in the master .chm file (NO).
|
# (YES) or that it should be included in the main .chm file (NO).
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||||
|
|
||||||
|
@ -1514,7 +1530,7 @@ DISABLE_INDEX = NO
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
GENERATE_TREEVIEW = NO
|
GENERATE_TREEVIEW = YES
|
||||||
|
|
||||||
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
|
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
|
||||||
# doxygen will group on one line in the generated HTML documentation.
|
# doxygen will group on one line in the generated HTML documentation.
|
||||||
|
@ -1540,6 +1556,17 @@ TREEVIEW_WIDTH = 250
|
||||||
|
|
||||||
EXT_LINKS_IN_WINDOW = NO
|
EXT_LINKS_IN_WINDOW = NO
|
||||||
|
|
||||||
|
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
|
||||||
|
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
|
||||||
|
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
|
||||||
|
# the HTML output. These images will generally look nicer at scaled resolutions.
|
||||||
|
# Possible values are: png (the default) and svg (looks nicer but requires the
|
||||||
|
# pdf2svg or inkscape tool).
|
||||||
|
# The default value is: png.
|
||||||
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
|
HTML_FORMULA_FORMAT = png
|
||||||
|
|
||||||
# Use this tag to change the font size of LaTeX formulas included as images in
|
# Use this tag to change the font size of LaTeX formulas included as images in
|
||||||
# the HTML documentation. When you change the font size after a successful
|
# the HTML documentation. When you change the font size after a successful
|
||||||
# doxygen run you need to manually remove any form_*.png images from the HTML
|
# doxygen run you need to manually remove any form_*.png images from the HTML
|
||||||
|
@ -1595,7 +1622,7 @@ MATHJAX_FORMAT = HTML-CSS
|
||||||
# Content Delivery Network so you can quickly see the result without installing
|
# Content Delivery Network so you can quickly see the result without installing
|
||||||
# MathJax. However, it is strongly recommended to install a local copy of
|
# MathJax. However, it is strongly recommended to install a local copy of
|
||||||
# MathJax from https://www.mathjax.org before deployment.
|
# MathJax from https://www.mathjax.org before deployment.
|
||||||
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/.
|
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
|
||||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||||
|
|
||||||
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
|
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/
|
||||||
|
@ -1834,9 +1861,11 @@ LATEX_EXTRA_FILES =
|
||||||
|
|
||||||
PDF_HYPERLINKS = YES
|
PDF_HYPERLINKS = YES
|
||||||
|
|
||||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
|
||||||
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
|
# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
|
||||||
# higher quality PDF documentation.
|
# files. Set this option to YES, to get a higher quality PDF documentation.
|
||||||
|
#
|
||||||
|
# See also section LATEX_CMD_NAME for selecting the engine.
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||||
|
|
||||||
|
@ -2075,6 +2104,10 @@ DOCBOOK_PROGRAMLISTING = NO
|
||||||
|
|
||||||
GENERATE_AUTOGEN_DEF = NO
|
GENERATE_AUTOGEN_DEF = NO
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
# Configuration options related to Sqlite3 output
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
# Configuration options related to the Perl module output
|
# Configuration options related to the Perl module output
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
@ -2248,7 +2281,7 @@ EXTERNAL_PAGES = YES
|
||||||
# powerful graphs.
|
# powerful graphs.
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
|
|
||||||
CLASS_DIAGRAMS = YES
|
CLASS_DIAGRAMS = NO
|
||||||
|
|
||||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||||
# then run dia to produce the diagram and insert it in the documentation. The
|
# then run dia to produce the diagram and insert it in the documentation. The
|
||||||
|
@ -2270,7 +2303,7 @@ HIDE_UNDOC_RELATIONS = YES
|
||||||
# set to NO
|
# set to NO
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
HAVE_DOT = NO
|
HAVE_DOT = YES
|
||||||
|
|
||||||
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
|
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
|
||||||
# to run in parallel. When set to 0 doxygen will base this on the number of
|
# to run in parallel. When set to 0 doxygen will base this on the number of
|
||||||
|
@ -2387,7 +2420,7 @@ INCLUDED_BY_GRAPH = YES
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
CALL_GRAPH = NO
|
CALL_GRAPH = YES
|
||||||
|
|
||||||
# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
|
# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
|
||||||
# dependency graph for every global function or class method.
|
# dependency graph for every global function or class method.
|
||||||
|
@ -2399,7 +2432,7 @@ CALL_GRAPH = NO
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||||
|
|
||||||
CALLER_GRAPH = NO
|
CALLER_GRAPH = YES
|
||||||
|
|
||||||
# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
|
# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
|
||||||
# hierarchy of all classes instead of a textual one.
|
# hierarchy of all classes instead of a textual one.
|
||||||
|
|
16
core.ini
16
core.ini
|
@ -1,16 +0,0 @@
|
||||||
[core]
|
|
||||||
server-port = 5000
|
|
||||||
database = core.sqlite
|
|
||||||
content-dir = /usr/share/webapps/emgauwa
|
|
||||||
not-found-file = 404.html
|
|
||||||
not-found-file-mime = text/html
|
|
||||||
not-found-content = 404 - NOT FOUND
|
|
||||||
not-found-content-type = text/plain
|
|
||||||
|
|
||||||
: 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
|
|
||||||
|
|
||||||
log-level = debug
|
|
||||||
log-file = stdout
|
|
19
emgauwa-core.conf
Normal file
19
emgauwa-core.conf
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[core]
|
||||||
|
database = "emgauwa-core.sqlite"
|
||||||
|
content-dir = "/usr/share/webapps/emgauwa"
|
||||||
|
#include = "./emgauwa-core.conf.d/"
|
||||||
|
not-found-file = "404.html"
|
||||||
|
not-found-file-mime = "text/html"
|
||||||
|
not-found-content = "404 - NOT FOUND"
|
||||||
|
not-found-content-type = "text/plain"
|
||||||
|
|
||||||
|
[ports]
|
||||||
|
server = 4419
|
||||||
|
# 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"
|
|
@ -36,7 +36,6 @@ void
|
||||||
cache_invalidate_relay(int relay_id, int status_relay);
|
cache_invalidate_relay(int relay_id, int status_relay);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
cache_put_json_controller(int controller_id, char *controller_json);
|
cache_put_json_controller(int controller_id, char *controller_json);
|
||||||
|
|
||||||
|
@ -47,6 +46,16 @@ void
|
||||||
cache_invalidate_controller(int controller_id);
|
cache_invalidate_controller(int controller_id);
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
cache_put_json_macro(int macro_id, char *macro_json);
|
||||||
|
|
||||||
|
char*
|
||||||
|
cache_get_json_macro(int macro_id);
|
||||||
|
|
||||||
|
void
|
||||||
|
cache_invalidate_macro(int macro_id);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
cache_invalidate_tagged(int tag_id);
|
cache_invalidate_tagged(int tag_id);
|
||||||
|
|
12
include/cli.h
Normal file
12
include/cli.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef CORE_CLI_H
|
||||||
|
#define CORE_CLI_H
|
||||||
|
|
||||||
|
typedef struct cli
|
||||||
|
{
|
||||||
|
const char *config_file;
|
||||||
|
} cli_t;
|
||||||
|
|
||||||
|
void
|
||||||
|
cli_parse(int argc, const char **argv, cli_t *cli);
|
||||||
|
|
||||||
|
#endif /* CORE_CLI_H */
|
|
@ -4,37 +4,54 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <mongoose.h>
|
#include <mongoose.h>
|
||||||
#include <confini.h>
|
#include <toml.h>
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
RUN_TYPE_START,
|
|
||||||
RUN_TYPE_INVALID,
|
|
||||||
} run_type_t;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char *file;
|
char *include;
|
||||||
char database[256];
|
char *database;
|
||||||
char user[256];
|
char *user;
|
||||||
char group[256];
|
char *group;
|
||||||
int log_level;
|
char *content_dir;
|
||||||
FILE *log_file;
|
char *not_found_file;
|
||||||
run_type_t run_type;
|
char *not_found_file_type;
|
||||||
uint16_t server_port;
|
char *not_found_content;
|
||||||
uint16_t discovery_port;
|
char *not_found_content_type;
|
||||||
uint16_t mqtt_port;
|
|
||||||
char content_dir[1024];
|
struct
|
||||||
char not_found_file[256];
|
{
|
||||||
char not_found_file_type[256];
|
int level;
|
||||||
char not_found_content[256];
|
FILE *file;
|
||||||
char not_found_content_type[256];
|
} logging;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint16_t server;
|
||||||
|
uint16_t discovery;
|
||||||
|
uint16_t mqtt;
|
||||||
|
} ports;
|
||||||
|
|
||||||
struct mg_serve_http_opts http_server_opts;
|
struct mg_serve_http_opts http_server_opts;
|
||||||
} config_t;
|
} config_t;
|
||||||
|
|
||||||
extern config_t global_config;
|
extern config_t *global_config;
|
||||||
|
|
||||||
int
|
void
|
||||||
config_load(IniDispatch *disp, void *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 /* CORE_CONFIG_H */
|
#endif /* CORE_CONFIG_H */
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define CORE_CONTANTS_H
|
#define CORE_CONTANTS_H
|
||||||
|
|
||||||
#define SECONDS_PER_DAY 86400 // 60 * 60 * 24
|
#define SECONDS_PER_DAY 86400 // 60 * 60 * 24
|
||||||
|
#define DAYS_PER_WEEK 7
|
||||||
|
|
||||||
#define SECONDS_PER_MINUTE 60
|
#define SECONDS_PER_MINUTE 60
|
||||||
|
|
||||||
|
@ -14,13 +15,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 +22,11 @@
|
||||||
|
|
||||||
#define PIFACE_GPIO_BASE 200
|
#define PIFACE_GPIO_BASE 200
|
||||||
|
|
||||||
|
#define DEFAULT_CONFIG_PATH "emgauwa-core.conf"
|
||||||
|
#define DEFAULT_GLOBAL_CONFIG_PATH "/etc/emgauwa/core.conf"
|
||||||
|
#define DEFAULT_DATABASE_PATH "emgauwa-core.sqlite"
|
||||||
|
#define DEFAULT_DISCOVERY_PORT 4421
|
||||||
|
#define DEFAULT_MQTT_PORT 1885
|
||||||
|
#define DEFAULT_SERVER_PORT 5000
|
||||||
|
|
||||||
#endif /* CORE_CONTANTS_H */
|
#endif /* CORE_CONTANTS_H */
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
typedef int database_transaction_lock;
|
||||||
|
|
||||||
extern sqlite3 *global_database;
|
extern sqlite3 *global_database;
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -15,14 +17,14 @@ void
|
||||||
database_migrate();
|
database_migrate();
|
||||||
|
|
||||||
|
|
||||||
int
|
void
|
||||||
database_transaction_begin();
|
database_transaction_begin(database_transaction_lock *lock);
|
||||||
|
|
||||||
void
|
void
|
||||||
database_transaction_commit();
|
database_transaction_commit(const database_transaction_lock *lock);
|
||||||
|
|
||||||
void
|
void
|
||||||
database_transaction_rollback();
|
database_transaction_rollback(const database_transaction_lock *lock);
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
@ -24,7 +24,7 @@ typedef struct
|
||||||
int status_code;
|
int status_code;
|
||||||
const char *content_type;
|
const char *content_type;
|
||||||
size_t content_length;
|
size_t content_length;
|
||||||
const char *content;
|
char *content;
|
||||||
int alloced_content;
|
int alloced_content;
|
||||||
} endpoint_response_t;
|
} endpoint_response_t;
|
||||||
|
|
||||||
|
@ -53,10 +53,16 @@ endpoint_func_index(struct mg_connection *nc, struct http_message *hm, endpoint_
|
||||||
void
|
void
|
||||||
endpoint_func_not_found(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
endpoint_func_not_found(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||||
|
|
||||||
|
void
|
||||||
|
endpoint_response_msg(endpoint_response_t *response, int status_code, const char *content, int content_length);
|
||||||
|
|
||||||
void
|
void
|
||||||
endpoint_response_text(endpoint_response_t *response, int status_code, const char *content, int content_length);
|
endpoint_response_text(endpoint_response_t *response, int status_code, const char *content, int content_length);
|
||||||
|
|
||||||
void
|
void
|
||||||
endpoint_response_json(endpoint_response_t *response, int status_code, const cJSON *json_root);
|
endpoint_response_json(endpoint_response_t *response, int status_code, const cJSON *json_root);
|
||||||
|
|
||||||
|
void
|
||||||
|
endpoint_response_free_content(endpoint_response_t *response);
|
||||||
|
|
||||||
#endif /* CORE_ENDPOINT_H */
|
#endif /* CORE_ENDPOINT_H */
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <router.h>
|
#include <router.h>
|
||||||
|
|
||||||
void
|
void
|
||||||
api_v1_controllers_discover_POST(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
api_v1_controllers_discover_PUT(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||||
|
|
||||||
void
|
void
|
||||||
api_v1_controllers_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
api_v1_controllers_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||||
|
|
24
include/endpoints/api_v1_macros.h
Normal file
24
include/endpoints/api_v1_macros.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef CORE_ENDPOINTS_API_V1_MACROS_H
|
||||||
|
#define CORE_ENDPOINTS_API_V1_MACROS_H
|
||||||
|
|
||||||
|
#include <router.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_POST(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_STR_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_STR_PUT(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_STR_DELETE(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_STR_execute_PUT(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||||
|
|
||||||
|
#endif /* CORE_ENDPOINTS_API_V1_MACROS_H */
|
|
@ -2,19 +2,19 @@
|
||||||
#define CORE_HELPERS_H
|
#define CORE_HELPERS_H
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <mongoose.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <confini.h>
|
|
||||||
|
|
||||||
int
|
int
|
||||||
helper_connect_tcp_server(char* host, uint16_t port);
|
helper_connect_tcp_server(char* host, uint16_t 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);
|
||||||
|
|
||||||
int
|
int
|
||||||
helper_drop_privileges();
|
helper_drop_privileges();
|
||||||
|
|
||||||
|
char*
|
||||||
|
find_query_param(struct mg_str query_mg_str, char* search_key);
|
||||||
|
|
||||||
#endif /* CORE_HELPERS_H */
|
#endif /* CORE_HELPERS_H */
|
||||||
|
|
|
@ -8,15 +8,22 @@
|
||||||
#include <colors.h>
|
#include <colors.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
|
#define LOG_NONE INT_MAX
|
||||||
|
|
||||||
|
#ifndef __FILENAME__
|
||||||
|
#define __FILENAME__ __FILE__
|
||||||
|
#endif
|
||||||
|
|
||||||
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, ...);
|
||||||
|
|
||||||
#define LOGGER_DEBUG(...) logger_log(LOG_DEBUG , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
#define LOGGER_NONE(...) logger_log(LOG_NONE , __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
#define LOGGER_INFO(...) logger_log(LOG_INFO , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
#define LOGGER_DEBUG(...) logger_log(LOG_DEBUG , __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
#define LOGGER_NOTICE(...) logger_log(LOG_NOTICE , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
#define LOGGER_INFO(...) logger_log(LOG_INFO , __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
#define LOGGER_WARNING(...) logger_log(LOG_WARNING, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
#define LOGGER_NOTICE(...) logger_log(LOG_NOTICE , __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
#define LOGGER_ERR(...) logger_log(LOG_ERR , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
#define LOGGER_WARNING(...) logger_log(LOG_WARNING, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
#define LOGGER_CRIT(...) logger_log(LOG_CRIT , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
#define LOGGER_ERR(...) logger_log(LOG_ERR , __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
#define LOGGER_EMERG(...) logger_log(LOG_EMERG , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
#define LOGGER_CRIT(...) logger_log(LOG_CRIT , __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
|
#define LOGGER_EMERG(...) logger_log(LOG_EMERG , __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
|
|
||||||
#endif //EMGAUWA_LOGGER_H
|
#endif //EMGAUWA_LOGGER_H
|
||||||
|
|
|
@ -1,6 +1,30 @@
|
||||||
#ifndef CORE_MACROS_H
|
#ifndef CORE_MACROS_H
|
||||||
#define CORE_MACROS_H
|
#define CORE_MACROS_H
|
||||||
|
|
||||||
#define STRLEN(s) ((sizeof(s)/sizeof(s[0])) - sizeof(s[0]))
|
#include <logger.h>
|
||||||
|
|
||||||
|
#define M_STRLEN(s) ((sizeof(s)/sizeof(s[0])) - sizeof(s[0]))
|
||||||
|
|
||||||
|
#define M_RESPONSE_MSG(logger_func, response, code, content) do { logger_func("%s\n", content); endpoint_response_msg(response, code, content, M_STRLEN(content)); } while(0)
|
||||||
|
#define M_RESPONSE_TEXT(logger_func, response, code, content) do { logger_func("%s\n", content); endpoint_response_text(response, code, content, M_STRLEN(content)); } while(0)
|
||||||
|
|
||||||
|
#define M_RESPONSE_400_NO_VALID_JSON(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request is not valid json")
|
||||||
|
#define M_RESPONSE_400_NO_VALID_ID(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contain a valid id (uuid)")
|
||||||
|
#define M_RESPONSE_400_NO_NAME(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contain a name")
|
||||||
|
#define M_RESPONSE_400_NO_VALID_NAME(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contain a valid name")
|
||||||
|
#define M_RESPONSE_400_NO_IP(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contain an ip address")
|
||||||
|
#define M_RESPONSE_400_NO_VALID_IP(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contain a valid ip address")
|
||||||
|
|
||||||
|
#define M_RESPONSE_403_PROTECTED_SCHEDULE(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 403, "the target schedule is protected")
|
||||||
|
|
||||||
|
#define M_RESPONSE_404_NO_CONTROLLER_FOUND_FOR_ID(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 404, "no controller was found for the requested id")
|
||||||
|
#define M_RESPONSE_404_NO_RELAY_FOUND_FOR_ID_AND_NUMBER(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 404, "no relay was found for the requested controller id and relay number")
|
||||||
|
#define M_RESPONSE_404_NO_SCHEDULE_FOUND_FOR_ID(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 404, "no schedule was found for the requested id")
|
||||||
|
#define M_RESPONSE_404_NO_MACRO_FOUND_FOR_ID(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 404, "no schedule was found for the requested id")
|
||||||
|
#define M_RESPONSE_404_NO_TAG_FOUND(response) M_RESPONSE_MSG(LOGGER_DEBUG, response, 404, "the requested tag was not found")
|
||||||
|
|
||||||
|
#define M_RESPONSE_500_FAILED_TO_SAVE_TO_DATABASE(response) M_RESPONSE_MSG(LOGGER_ERR, response, 500, "the server failed to save to the database")
|
||||||
|
#define M_RESPONSE_500_FAILED_TO_READ_FROM_DATABASE(response) M_RESPONSE_MSG(LOGGER_ERR, response, 500, "the server failed to read from the database")
|
||||||
|
#define M_RESPONSE_500_FAILED_TO_DELETE_FROM_DATABASE(response) M_RESPONSE_MSG(LOGGER_ERR, response, 500, "the server failed to delete from the database")
|
||||||
|
|
||||||
#endif /* CORE_MACROS_H */
|
#endif /* CORE_MACROS_H */
|
||||||
|
|
|
@ -25,16 +25,16 @@ typedef struct
|
||||||
} controller_t;
|
} controller_t;
|
||||||
|
|
||||||
void
|
void
|
||||||
controller_free(controller_t* contoller);
|
controller_free(controller_t *controller);
|
||||||
|
|
||||||
int
|
int
|
||||||
controller_save(controller_t* contoller);
|
controller_save(controller_t *controller);
|
||||||
|
|
||||||
int
|
int
|
||||||
controller_remove(controller_t* contoller);
|
controller_remove(controller_t *controller);
|
||||||
|
|
||||||
cJSON*
|
cJSON*
|
||||||
controller_to_json(controller_t* contoller);
|
controller_to_json(controller_t *controller);
|
||||||
|
|
||||||
controller_t*
|
controller_t*
|
||||||
controller_get_by_id(int id);
|
controller_get_by_id(int id);
|
||||||
|
|
|
@ -10,4 +10,7 @@ junction_relay_schedule_remove_for_relay(int relay_id);
|
||||||
int
|
int
|
||||||
junction_relay_schedule_insert_weekdays(int relay_id, int *schedule_ids);
|
junction_relay_schedule_insert_weekdays(int relay_id, int *schedule_ids);
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_relay_schedule_get_relay_ids_with_schedule(int schedule_id);
|
||||||
|
|
||||||
#endif /* CORE_MODELS_JUNCTION_RELAY_SCHEDULE_H */
|
#endif /* CORE_MODELS_JUNCTION_RELAY_SCHEDULE_H */
|
||||||
|
|
45
include/models/macro.h
Normal file
45
include/models/macro.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef CORE_MACRO_H
|
||||||
|
#define CORE_MACRO_H
|
||||||
|
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
|
||||||
|
#include <cJSON.h>
|
||||||
|
|
||||||
|
#include <constants.h>
|
||||||
|
#include <models/macro_action.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
uuid_t uid;
|
||||||
|
char name[MAX_NAME_LENGTH + 1];
|
||||||
|
} macro_t;
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_save(macro_t *macro);
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_remove(macro_t *macro);
|
||||||
|
|
||||||
|
void
|
||||||
|
macro_free(macro_t *macro);
|
||||||
|
|
||||||
|
void
|
||||||
|
macro_free_list(macro_t **macro);
|
||||||
|
|
||||||
|
cJSON*
|
||||||
|
macro_to_json(macro_t *macro);
|
||||||
|
|
||||||
|
macro_t*
|
||||||
|
macro_get_by_id(int id);
|
||||||
|
|
||||||
|
macro_t*
|
||||||
|
macro_get_by_uid(uuid_t uid);
|
||||||
|
|
||||||
|
macro_t**
|
||||||
|
macro_get_all();
|
||||||
|
|
||||||
|
int*
|
||||||
|
macro_get_target_relay_ids(int macro_id);
|
||||||
|
|
||||||
|
#endif /* CORE_MACRO_H */
|
36
include/models/macro_action.h
Normal file
36
include/models/macro_action.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef CORE_MODELS_MACRO_ACTION_H
|
||||||
|
#define CORE_MODELS_MACRO_ACTION_H
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int macro_id;
|
||||||
|
int relay_id;
|
||||||
|
int schedule_id;
|
||||||
|
uint8_t weekday;
|
||||||
|
} macro_action_t;
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_action_insert(macro_action_t *macro_action);
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_action_delete_for_macro(int macro_id);
|
||||||
|
|
||||||
|
macro_action_t**
|
||||||
|
macro_action_get_for_macro(int macro_id);
|
||||||
|
|
||||||
|
macro_action_t**
|
||||||
|
macro_action_get_for_macro_and_weekday(int macro_id, int weekday);
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_action_execute(macro_action_t *macro_action);
|
||||||
|
|
||||||
|
void
|
||||||
|
macro_action_free_list(macro_action_t **macro_actions);
|
||||||
|
|
||||||
|
int*
|
||||||
|
macro_action_get_macro_ids_with_schedule(int schedule_id);
|
||||||
|
|
||||||
|
int*
|
||||||
|
macro_action_get_macro_ids_with_relay(int relay_id);
|
||||||
|
|
||||||
|
#endif /* CORE_MODELS_MACRO_ACTION_H */
|
|
@ -35,9 +35,6 @@ schedule_free_list(schedule_t **schedule);
|
||||||
cJSON*
|
cJSON*
|
||||||
schedule_to_json(schedule_t *schedule);
|
schedule_to_json(schedule_t *schedule);
|
||||||
|
|
||||||
void
|
|
||||||
schedule_free_list(schedule_t **schedules_list);
|
|
||||||
|
|
||||||
uint16_t*
|
uint16_t*
|
||||||
schedule_periods_to_blob(schedule_t *schedule);
|
schedule_periods_to_blob(schedule_t *schedule);
|
||||||
|
|
||||||
|
@ -65,4 +62,10 @@ schedule_uid_parse(const char *uid_str, uuid_t result);
|
||||||
void
|
void
|
||||||
schedule_uid_unparse(const uuid_t uid, char *result);
|
schedule_uid_unparse(const uuid_t uid, char *result);
|
||||||
|
|
||||||
|
void
|
||||||
|
schedule_get_uid_off(uuid_t target);
|
||||||
|
|
||||||
|
void
|
||||||
|
schedule_get_uid_on(uuid_t target);
|
||||||
|
|
||||||
#endif /* CORE_SCHEDULE_H */
|
#endif /* CORE_SCHEDULE_H */
|
||||||
|
|
8
shell.nix
Normal file
8
shell.nix
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
with import <nixpkgs> {};
|
||||||
|
mkShell {
|
||||||
|
nativeBuildInputs = [
|
||||||
|
cmake
|
||||||
|
sqlite
|
||||||
|
util-linux
|
||||||
|
];
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
-- a key-value table used for the json-cache
|
||||||
|
|
||||||
CREATE TABLE cache (
|
CREATE TABLE cache (
|
||||||
key STRING
|
key STRING
|
||||||
PRIMARY KEY,
|
PRIMARY KEY,
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
create table controllers
|
-- base migration
|
||||||
|
|
||||||
|
CREATE TABLE controllers
|
||||||
(
|
(
|
||||||
id INTEGER
|
id INTEGER
|
||||||
PRIMARY KEY
|
PRIMARY KEY
|
||||||
|
@ -14,7 +16,7 @@ create table controllers
|
||||||
NOT NULL
|
NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
create table relays
|
CREATE TABLE relays
|
||||||
(
|
(
|
||||||
id INTEGER
|
id INTEGER
|
||||||
PRIMARY KEY
|
PRIMARY KEY
|
||||||
|
@ -28,7 +30,7 @@ create table relays
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
create table schedules
|
CREATE TABLE schedules
|
||||||
(
|
(
|
||||||
id INTEGER
|
id INTEGER
|
||||||
PRIMARY KEY
|
PRIMARY KEY
|
||||||
|
@ -40,7 +42,7 @@ create table schedules
|
||||||
periods BLOB
|
periods BLOB
|
||||||
);
|
);
|
||||||
|
|
||||||
create table tags
|
CREATE TABLE tags
|
||||||
(
|
(
|
||||||
id INTEGER
|
id INTEGER
|
||||||
PRIMARY KEY
|
PRIMARY KEY
|
||||||
|
@ -50,7 +52,7 @@ create table tags
|
||||||
UNIQUE
|
UNIQUE
|
||||||
);
|
);
|
||||||
|
|
||||||
create table junction_tag
|
CREATE TABLE junction_tag
|
||||||
(
|
(
|
||||||
tag_id INTEGER
|
tag_id INTEGER
|
||||||
NOT NULL
|
NOT NULL
|
||||||
|
@ -64,7 +66,7 @@ create table junction_tag
|
||||||
ON DELETE CASCADE
|
ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
create table junction_relay_schedule
|
CREATE TABLE junction_relay_schedule
|
||||||
(
|
(
|
||||||
weekday SMALLINT
|
weekday SMALLINT
|
||||||
NOT NULL,
|
NOT NULL,
|
||||||
|
@ -78,4 +80,4 @@ create table junction_relay_schedule
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO schedules (uid, name, periods) VALUES (x'6f666600000000000000000000000000', 'off', x'00');
|
INSERT INTO schedules (uid, name, periods) VALUES (x'6f666600000000000000000000000000', 'off', x'00');
|
||||||
INSERT INTO schedules (uid, name, periods) VALUES (x'6f6e0000000000000000000000000000', 'on', x'010000009F05');
|
INSERT INTO schedules (uid, name, periods) VALUES (x'6f6e0000000000000000000000000000', 'on', x'010000000000');
|
||||||
|
|
28
sql/migration_1.sql
Normal file
28
sql/migration_1.sql
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
-- migration to add macros
|
||||||
|
|
||||||
|
CREATE TABLE macros
|
||||||
|
(
|
||||||
|
id INTEGER
|
||||||
|
PRIMARY KEY
|
||||||
|
AUTOINCREMENT,
|
||||||
|
uid BLOB
|
||||||
|
NOT NULL
|
||||||
|
UNIQUE,
|
||||||
|
name VARCHAR(128)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE macro_actions
|
||||||
|
(
|
||||||
|
macro_id INTEGER
|
||||||
|
NOT NULL
|
||||||
|
REFERENCES macros (id)
|
||||||
|
ON DELETE CASCADE,
|
||||||
|
relay_id INTEGER
|
||||||
|
REFERENCES relays (id)
|
||||||
|
ON DELETE CASCADE,
|
||||||
|
schedule_id INTEGER
|
||||||
|
REFERENCES schedules (id)
|
||||||
|
ON DELETE CASCADE,
|
||||||
|
weekday SMALLINT
|
||||||
|
NOT NULL
|
||||||
|
);
|
73
src/cache.c
73
src/cache.c
|
@ -1,7 +1,9 @@
|
||||||
#include <cache.h>
|
#include <cache.h>
|
||||||
#include <logger.h>
|
#include <logger.h>
|
||||||
#include <sql/cache.h>
|
#include <models/junction_relay_schedule.h>
|
||||||
#include <models/junction_tag.h>
|
#include <models/junction_tag.h>
|
||||||
|
#include <models/macro_action.h>
|
||||||
|
#include <sql/cache.h>
|
||||||
|
|
||||||
|
|
||||||
sqlite3 *cache_database;
|
sqlite3 *cache_database;
|
||||||
|
@ -23,8 +25,15 @@ cache_get_value(char *key)
|
||||||
if (s == SQLITE_ROW)
|
if (s == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
const char *found_value = (const char *)sqlite3_column_text(stmt, 0);
|
const char *found_value = (const char *)sqlite3_column_text(stmt, 0);
|
||||||
result = (char*)malloc(sizeof(char) * (strlen(found_value) + 1));
|
size_t found_value_len = sqlite3_column_bytes(stmt, 0);
|
||||||
strcpy(result, found_value);
|
|
||||||
|
if(result)
|
||||||
|
{
|
||||||
|
free(result);
|
||||||
|
}
|
||||||
|
result = (char*)malloc(sizeof(char) * (found_value_len + 1));
|
||||||
|
strncpy(result, found_value, found_value_len);
|
||||||
|
result[found_value_len] = '\0';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -32,11 +41,9 @@ cache_get_value(char *key)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
LOGGER_WARNING("failed selecting %s from cache: %s\n", key, sqlite3_errstr(s));
|
||||||
LOGGER_WARNING("failed selecting %s from cache: %s\n", key, sqlite3_errstr(s));
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,14 +141,19 @@ cache_invalidate_schedule(int schedule_id)
|
||||||
sprintf(key, "schedule_json:%d", schedule_id);
|
sprintf(key, "schedule_json:%d", schedule_id);
|
||||||
cache_invalidate(key);
|
cache_invalidate(key);
|
||||||
|
|
||||||
relay_t **relays = relay_get_with_schedule(schedule_id);
|
int *relay_ids = junction_relay_schedule_get_relay_ids_with_schedule(schedule_id);
|
||||||
|
for(int i = 0; relay_ids[i] != 0; ++i)
|
||||||
for(int i = 0; relays[i] != NULL; ++i)
|
|
||||||
{
|
{
|
||||||
cache_invalidate_relay(relays[i]->id, -1);
|
cache_invalidate_relay(relay_ids[i], -1);
|
||||||
}
|
}
|
||||||
|
free(relay_ids);
|
||||||
|
|
||||||
relay_free_list(relays);
|
int *macro_ids = macro_action_get_macro_ids_with_schedule(schedule_id);
|
||||||
|
for(int i = 0; macro_ids[i] != 0; ++i)
|
||||||
|
{
|
||||||
|
cache_invalidate_macro(macro_ids[i]);
|
||||||
|
}
|
||||||
|
free(macro_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -180,6 +192,13 @@ cache_invalidate_relay(int relay_id, int status_relay)
|
||||||
{
|
{
|
||||||
cache_invalidate_controller(controller_id);
|
cache_invalidate_controller(controller_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int *macro_ids = macro_action_get_macro_ids_with_relay(relay_id);
|
||||||
|
for(int i = 0; macro_ids[i] != 0; ++i)
|
||||||
|
{
|
||||||
|
cache_invalidate_macro(macro_ids[i]);
|
||||||
|
}
|
||||||
|
free(macro_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -208,6 +227,34 @@ cache_invalidate_controller(int controller_id)
|
||||||
cache_invalidate(key);
|
cache_invalidate(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
cache_put_json_macro(int macro_id, char *macro_json)
|
||||||
|
{
|
||||||
|
char key[32];
|
||||||
|
sprintf(key, "macro_json:%d", macro_id);
|
||||||
|
cache_insert_value(key, macro_json);
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
cache_get_json_macro(int macro_id)
|
||||||
|
{
|
||||||
|
char key[32];
|
||||||
|
sprintf(key, "macro_json:%d", macro_id);
|
||||||
|
return cache_get_value(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cache_invalidate_macro(int macro_id)
|
||||||
|
{
|
||||||
|
char key[32];
|
||||||
|
sprintf(key, "macro_json:%d", macro_id);
|
||||||
|
cache_invalidate(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
cache_invalidate_tagged(int tag_id)
|
cache_invalidate_tagged(int tag_id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,25 +5,29 @@
|
||||||
#include <argparse.h>
|
#include <argparse.h>
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <cli.h>
|
||||||
#include <logger.h>
|
#include <logger.h>
|
||||||
#include <helpers.h>
|
#include <helpers.h>
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
|
|
||||||
|
cli_t *global_cli;
|
||||||
|
|
||||||
static const char *const usage[] = {
|
static const char *const usage[] = {
|
||||||
"core [options] [[--] args]",
|
|
||||||
"core [options]",
|
"core [options]",
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
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('v', "version", &version, "print version", NULL, 0, OPT_NONEG),
|
OPT_BOOLEAN('v', "version", &version, "print version", NULL, 0, OPT_NONEG),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
|
@ -35,29 +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_CORE_VERSION);
|
printf("%s\n", EMGAUWA_CORE_VERSION);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(argc == 1)
|
|
||||||
{
|
|
||||||
config->run_type = RUN_TYPE_INVALID;
|
|
||||||
if(strcmp(argv[0], "start") == 0)
|
|
||||||
{
|
|
||||||
config->run_type = RUN_TYPE_START;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
LOGGER_CRIT("bad action '%s' given ('start')\n", argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER_CRIT("no action given ('start')\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
|
@ -90,13 +90,17 @@ command_schedule_update(schedule_t *schedule)
|
||||||
LOGGER_ERR("couldn't find controller for relay %d\n", relays[i]->id);
|
LOGGER_ERR("couldn't find controller for relay %d\n", relays[i]->id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
controller_free(controller);
|
|
||||||
|
|
||||||
LOGGER_DEBUG("sending command to controller %s\n", controller->name);
|
LOGGER_DEBUG("sending command to controller %s\n", controller->name);
|
||||||
|
|
||||||
result |= command_send(controller, payload, payload_size);
|
result |= command_send(controller, payload, payload_size);
|
||||||
|
|
||||||
|
controller_free(controller);
|
||||||
}
|
}
|
||||||
relay_free_list(relays);
|
relay_free_list(relays);
|
||||||
|
|
||||||
|
free(payload);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,8 +211,6 @@ command_controller_name_set(controller_t *controller)
|
||||||
int
|
int
|
||||||
command_send(controller_t *controller, char *payload, uint32_t payload_size)
|
command_send(controller_t *controller, char *payload, uint32_t payload_size)
|
||||||
{
|
{
|
||||||
int bytes_transferred;
|
|
||||||
|
|
||||||
int fd_controller = helper_connect_tcp_server(controller->ip, controller->port);
|
int fd_controller = helper_connect_tcp_server(controller->ip, controller->port);
|
||||||
|
|
||||||
if(fd_controller == -1)
|
if(fd_controller == -1)
|
||||||
|
@ -217,12 +219,12 @@ command_send(controller_t *controller, char *payload, uint32_t payload_size)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((bytes_transferred = send(fd_controller, &payload_size, sizeof(payload_size), 0)) <= 0)
|
if(send(fd_controller, &payload_size, sizeof(payload_size), 0) <= 0)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("error during sending size\n");
|
LOGGER_ERR("error during sending size\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if((bytes_transferred = send(fd_controller, payload, payload_size, 0)) <= 0)
|
if(send(fd_controller, payload, payload_size, 0) <= 0)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("error during sending\n");
|
LOGGER_ERR("error during sending\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
419
src/config.c
419
src/config.c
|
@ -1,151 +1,380 @@
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <logger.h>
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
#include <constants.h>
|
||||||
|
#include <logger.h>
|
||||||
|
|
||||||
config_t global_config;
|
config_t *global_config;
|
||||||
|
|
||||||
#define CONFINI_IS_KEY(SECTION, KEY) \
|
|
||||||
(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_load_section_core(config_t *config, toml_table_t* core)
|
||||||
{
|
{
|
||||||
config_t *config = (config_t*)config_void;
|
toml_datum_t config_entry;
|
||||||
|
|
||||||
if(disp->type == INI_KEY)
|
config_entry = toml_string_in(core, "database");
|
||||||
|
if(config_entry.ok)
|
||||||
{
|
{
|
||||||
if(CONFINI_IS_KEY("core", "database"))
|
config_load_string(&config->database, config_entry.u.s);
|
||||||
{
|
free(config_entry.u.s);
|
||||||
strcpy(config->database, disp->value);
|
}
|
||||||
return 0;
|
|
||||||
}
|
config_entry = toml_string_in(core, "user");
|
||||||
if(CONFINI_IS_KEY("core", "user"))
|
if(config_entry.ok)
|
||||||
{
|
{
|
||||||
strcpy(config->user, disp->value);
|
config_load_string(&config->user, config_entry.u.s);
|
||||||
return 0;
|
free(config_entry.u.s);
|
||||||
}
|
}
|
||||||
if(CONFINI_IS_KEY("core", "group"))
|
|
||||||
{
|
config_entry = toml_string_in(core, "group");
|
||||||
strcpy(config->group, disp->value);
|
if(config_entry.ok)
|
||||||
return 0;
|
{
|
||||||
}
|
config_load_string(&config->group, config_entry.u.s);
|
||||||
if(CONFINI_IS_KEY("core", "content-dir"))
|
free(config_entry.u.s);
|
||||||
{
|
}
|
||||||
strcpy(config->content_dir, disp->value);
|
|
||||||
return 0;
|
config_entry = toml_string_in(core, "content-dir");
|
||||||
}
|
if(config_entry.ok)
|
||||||
if(CONFINI_IS_KEY("core", "not-found-file"))
|
{
|
||||||
{
|
config_load_string(&config->content_dir, config_entry.u.s);
|
||||||
strcpy(config->not_found_file, disp->value);
|
free(config_entry.u.s);
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
if(CONFINI_IS_KEY("core", "not-found-file-type"))
|
config_entry = toml_string_in(core, "not-found-file");
|
||||||
{
|
if(config_entry.ok)
|
||||||
strcpy(config->not_found_file_type, disp->value);
|
{
|
||||||
return 0;
|
config_load_string(&config->not_found_file, config_entry.u.s);
|
||||||
}
|
free(config_entry.u.s);
|
||||||
if(CONFINI_IS_KEY("core", "not-found-content"))
|
}
|
||||||
{
|
|
||||||
strcpy(config->not_found_content, disp->value);
|
config_entry = toml_string_in(core, "not-found-file-type");
|
||||||
return 0;
|
if(config_entry.ok)
|
||||||
}
|
{
|
||||||
if(CONFINI_IS_KEY("core", "not-found-content-type"))
|
config_load_string(&config->not_found_file_type, config_entry.u.s);
|
||||||
{
|
free(config_entry.u.s);
|
||||||
strcpy(config->not_found_content_type, disp->value);
|
}
|
||||||
return 0;
|
|
||||||
}
|
config_entry = toml_string_in(core, "not-found-content");
|
||||||
if(CONFINI_IS_KEY("core", "log-level"))
|
if(config_entry.ok)
|
||||||
{
|
{
|
||||||
return config_load_log_level(disp, config);
|
config_load_string(&config->not_found_content, config_entry.u.s);
|
||||||
}
|
free(config_entry.u.s);
|
||||||
if(CONFINI_IS_KEY("core", "log-file"))
|
}
|
||||||
{
|
|
||||||
return config_load_log_file(disp, config);
|
config_entry = toml_string_in(core, "not-found-content-type");
|
||||||
}
|
if(config_entry.ok)
|
||||||
if(CONFINI_IS_KEY("core", "server-port"))
|
{
|
||||||
{
|
config_load_string(&config->not_found_content_type, config_entry.u.s);
|
||||||
config->server_port = atoi(disp->value);
|
free(config_entry.u.s);
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
if(CONFINI_IS_KEY("core", "discovery-port"))
|
config_entry = toml_string_in(core, "include");
|
||||||
{
|
if(config_entry.ok)
|
||||||
config->discovery_port = atoi(disp->value);
|
{
|
||||||
return 0;
|
config_load_string(&config->include, config_entry.u.s);
|
||||||
}
|
free(config_entry.u.s);
|
||||||
if(CONFINI_IS_KEY("core", "mqtt-port"))
|
}
|
||||||
{
|
}
|
||||||
config->mqtt_port = atoi(disp->value);
|
|
||||||
return 0;
|
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, "server");
|
||||||
|
if(config_entry.ok)
|
||||||
|
{
|
||||||
|
config->ports.server = config_entry.u.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
config_init()
|
||||||
|
{
|
||||||
|
global_config = calloc(1, sizeof(config_t));
|
||||||
|
|
||||||
|
config_load_string(&global_config->database, DEFAULT_DATABASE_PATH);
|
||||||
|
|
||||||
|
global_config->ports.discovery = DEFAULT_DISCOVERY_PORT;
|
||||||
|
global_config->ports.mqtt = DEFAULT_MQTT_PORT;
|
||||||
|
global_config->ports.server = DEFAULT_SERVER_PORT;
|
||||||
|
|
||||||
|
global_config->logging.level = LOG_DEBUG;
|
||||||
|
global_config->logging.file = stdout;
|
||||||
|
|
||||||
|
global_config->user = NULL;
|
||||||
|
global_config->group = NULL;
|
||||||
|
|
||||||
|
global_config->include = NULL;
|
||||||
|
|
||||||
|
config_load_string(&global_config->content_dir, ".");
|
||||||
|
config_load_string(&global_config->not_found_file, "404.html");
|
||||||
|
config_load_string(&global_config->not_found_file_type, "text/html");
|
||||||
|
config_load_string(&global_config->not_found_content, "404 - NOT FOUND");
|
||||||
|
config_load_string(&global_config->not_found_content_type, "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
config_free()
|
||||||
|
{
|
||||||
|
free(global_config->include);
|
||||||
|
free(global_config->database);
|
||||||
|
free(global_config->user);
|
||||||
|
free(global_config->group);
|
||||||
|
free(global_config->content_dir);
|
||||||
|
free(global_config->not_found_file);
|
||||||
|
free(global_config->not_found_file_type);
|
||||||
|
free(global_config->not_found_content);
|
||||||
|
free(global_config->not_found_content_type);
|
||||||
|
|
||||||
|
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* core = toml_table_in(config_toml, "core");
|
||||||
|
if(core)
|
||||||
|
{
|
||||||
|
config_load_section_core(config, core);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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);
|
||||||
|
}
|
||||||
|
|
144
src/database.c
144
src/database.c
|
@ -5,14 +5,15 @@
|
||||||
#include <database.h>
|
#include <database.h>
|
||||||
|
|
||||||
#include <sql/migration_0.h>
|
#include <sql/migration_0.h>
|
||||||
|
#include <sql/migration_1.h>
|
||||||
|
|
||||||
sqlite3 *global_database;
|
sqlite3 *global_database;
|
||||||
static int in_transaction;
|
static database_transaction_lock *transaction_lock;
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -20,10 +21,12 @@ database_init()
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOGGER_DEBUG("Opened database %s\n", global_config->database);
|
||||||
|
|
||||||
database_migrate();
|
database_migrate();
|
||||||
|
|
||||||
sqlite3_exec(global_database, "PRAGMA foreign_keys = ON", 0, 0, 0);
|
sqlite3_exec(global_database, "PRAGMA foreign_keys = ON", 0, 0, 0);
|
||||||
in_transaction = 0;
|
transaction_lock = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -32,11 +35,35 @@ database_free()
|
||||||
sqlite3_close(global_database);
|
sqlite3_close(global_database);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
database_migrate_step_simple(int level, const char* sql_migration)
|
||||||
|
{
|
||||||
|
LOGGER_INFO("migrating LEVEL %d\n", level);
|
||||||
|
char* err_msg;
|
||||||
|
|
||||||
|
sqlite3_exec(global_database, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
||||||
|
|
||||||
|
int rc = sqlite3_exec(global_database, sql_migration, NULL, NULL, &err_msg);
|
||||||
|
if(rc)
|
||||||
|
{
|
||||||
|
LOGGER_CRIT("couldn't migrate LEVEL %d (%s)\n", level, err_msg);
|
||||||
|
sqlite3_exec(global_database, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER_DEBUG("storing new user_version %d\n", level + 1);
|
||||||
|
char pragma_query[32];
|
||||||
|
sprintf(pragma_query, "PRAGMA user_version=%d;", level + 1);
|
||||||
|
sqlite3_exec(global_database, pragma_query, 0, 0, 0);
|
||||||
|
|
||||||
|
sqlite3_exec(global_database, "COMMIT TRANSACTION;", NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
database_migrate()
|
database_migrate()
|
||||||
{
|
{
|
||||||
uint16_t version_num = 0;
|
uint16_t version_num = 0;
|
||||||
int s, rc;
|
int s;
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
sqlite3_prepare_v2(global_database, "PRAGMA user_version;", -1, &stmt, NULL);
|
sqlite3_prepare_v2(global_database, "PRAGMA user_version;", -1, &stmt, NULL);
|
||||||
s = sqlite3_step(stmt);
|
s = sqlite3_step(stmt);
|
||||||
|
@ -49,61 +76,52 @@ database_migrate()
|
||||||
version_num = 0;
|
version_num = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t new_version_num = version_num;
|
|
||||||
char* err_msg;
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
switch(version_num)
|
switch(version_num)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
LOGGER_INFO("migrating LEVEL 0\n");
|
database_migrate_step_simple(0, (const char*)sql_migration_0_sql);
|
||||||
rc = sqlite3_exec(global_database, (const char *)sql_migration_0_sql, NULL, NULL, &err_msg);
|
__attribute__ ((fallthrough));
|
||||||
if(rc)
|
case 1:
|
||||||
{
|
database_migrate_step_simple(1, (const char*)sql_migration_1_sql);
|
||||||
LOGGER_CRIT("couldn't migrate LEVEL 0 (%s)\n", err_msg);
|
__attribute__ ((fallthrough));
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
new_version_num = 1;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
char pragma_query[32];
|
|
||||||
sprintf(pragma_query, "PRAGMA user_version=%d;", new_version_num);
|
|
||||||
sqlite3_exec(global_database, pragma_query, 0, 0, 0);
|
|
||||||
LOGGER_DEBUG("storing new user_version %d\n", new_version_num);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
void
|
||||||
database_transaction_begin()
|
database_transaction_begin(database_transaction_lock *lock)
|
||||||
{
|
{
|
||||||
if(!in_transaction)
|
if(transaction_lock == NULL)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("beginning transaction\n");
|
LOGGER_DEBUG("beginning transaction\n");
|
||||||
sqlite3_exec(global_database, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
sqlite3_exec(global_database, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
||||||
in_transaction = 1;
|
transaction_lock = lock;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
database_transaction_commit()
|
database_transaction_commit(const database_transaction_lock *lock)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("commiting transaction\n");
|
if(transaction_lock == lock)
|
||||||
sqlite3_exec(global_database, "COMMIT TRANSACTION;", NULL, NULL, NULL);
|
{
|
||||||
in_transaction = 0;
|
LOGGER_DEBUG("commiting transaction\n");
|
||||||
|
sqlite3_exec(global_database, "COMMIT TRANSACTION;", NULL, NULL, NULL);
|
||||||
|
transaction_lock = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
database_transaction_rollback()
|
database_transaction_rollback(const database_transaction_lock *lock)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("rolling back transaction\n");
|
if(transaction_lock == lock)
|
||||||
sqlite3_exec(global_database, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
{
|
||||||
in_transaction = 0;
|
LOGGER_DEBUG("rolling back transaction\n");
|
||||||
|
sqlite3_exec(global_database, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||||
|
transaction_lock = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -126,12 +144,10 @@ database_helper_get_id(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
LOGGER_ERR("error selecting id from database: %s\n", sqlite3_errstr(s));
|
||||||
LOGGER_ERR("error selecting id from database: %s\n", sqlite3_errstr(s));
|
sqlite3_finalize(stmt);
|
||||||
sqlite3_finalize(stmt);
|
return 0;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,12 +186,14 @@ database_helper_get_ids(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if(result)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("error selecting ids from database: %s\n", sqlite3_errstr(s));
|
free(result);
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
LOGGER_ERR("error selecting ids from database: %s\n", sqlite3_errstr(s));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,8 +216,15 @@ database_helper_get_string(sqlite3_stmt *stmt)
|
||||||
if (s == SQLITE_ROW)
|
if (s == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
const char *found_string = (const char *)sqlite3_column_text(stmt, 0);
|
const char *found_string = (const char *)sqlite3_column_text(stmt, 0);
|
||||||
result = (char*)malloc(sizeof(char) * (strlen(found_string) + 1));
|
size_t found_string_len = sqlite3_column_bytes(stmt, 0);
|
||||||
strcpy(result, found_string);
|
|
||||||
|
if(result)
|
||||||
|
{
|
||||||
|
free(result);
|
||||||
|
}
|
||||||
|
result = (char*)malloc(sizeof(char) * (found_string_len + 1));
|
||||||
|
strncpy(result, found_string, found_string_len);
|
||||||
|
result[found_string_len] = '\0';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -207,12 +232,14 @@ database_helper_get_string(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if(result)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("error selecting string from database: %s\n", sqlite3_errstr(s));
|
free(result);
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
LOGGER_ERR("error selecting string from database: %s\n", sqlite3_errstr(s));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,12 +263,13 @@ database_helper_get_strings(sqlite3_stmt *stmt)
|
||||||
if (s == SQLITE_ROW)
|
if (s == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
const char *new_string = (const char *)sqlite3_column_text(stmt, 0);
|
const char *new_string = (const char *)sqlite3_column_text(stmt, 0);
|
||||||
int new_string_len = strlen(new_string);
|
size_t new_string_len = sqlite3_column_bytes(stmt, 0);
|
||||||
row++;
|
row++;
|
||||||
|
|
||||||
result = (char**)realloc(result, sizeof(char*) * (row + 1));
|
result = (char**)realloc(result, sizeof(char*) * (row + 1));
|
||||||
result[row - 1] = malloc(sizeof(char) * (new_string_len + 1));
|
result[row - 1] = malloc(sizeof(char) * (new_string_len + 1));
|
||||||
strcpy(result[row - 1], new_string);
|
strncpy(result[row - 1], new_string, new_string_len);
|
||||||
|
result[row - 1][new_string_len] = '\0';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -249,11 +277,9 @@ database_helper_get_strings(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
LOGGER_ERR("error selecting strings from database: %s\n", sqlite3_errstr(s));
|
||||||
LOGGER_ERR("error selecting strings from database: %s\n", sqlite3_errstr(s));
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
|
@ -14,7 +14,7 @@ endpoint_func_index(struct mg_connection *nc, struct http_message *hm, endpoint_
|
||||||
|
|
||||||
response->status_code = 0;
|
response->status_code = 0;
|
||||||
|
|
||||||
mg_serve_http(nc, hm, global_config.http_server_opts);
|
mg_serve_http(nc, hm, global_config->http_server_opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -24,27 +24,61 @@ endpoint_func_not_found(struct mg_connection *nc, struct http_message *hm, endpo
|
||||||
(void)hm;
|
(void)hm;
|
||||||
(void)nc;
|
(void)nc;
|
||||||
|
|
||||||
if(access(global_config.not_found_file, R_OK) != -1)
|
if(access(global_config->not_found_file, R_OK) != -1)
|
||||||
{
|
{
|
||||||
struct mg_str mime_type = mg_mk_str(global_config.not_found_file_type);
|
struct mg_str mime_type = mg_mk_str(global_config->not_found_file_type);
|
||||||
response->status_code = 0;
|
response->status_code = 0;
|
||||||
mg_http_serve_file(nc, hm, global_config.not_found_file, mime_type, mg_mk_str(""));
|
mg_http_serve_file(nc, hm, global_config->not_found_file, mime_type, mg_mk_str(""));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("404 file not found\n");
|
LOGGER_DEBUG("404 file not found\n");
|
||||||
|
|
||||||
|
endpoint_response_free_content(response);
|
||||||
|
|
||||||
response->status_code = 404;
|
response->status_code = 404;
|
||||||
response->content_type = global_config.not_found_content_type;
|
response->content_type = global_config->not_found_content_type;
|
||||||
response->content_length = strlen(global_config.not_found_content);
|
response->content_length = strlen(global_config->not_found_content);
|
||||||
response->content = global_config.not_found_content;
|
response->content = global_config->not_found_content;
|
||||||
response->alloced_content = false;
|
response->alloced_content = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
endpoint_response_msg(endpoint_response_t *response, int status_code, const char *content, int content_length)
|
||||||
|
{
|
||||||
|
endpoint_response_free_content(response);
|
||||||
|
|
||||||
|
cJSON *json;
|
||||||
|
|
||||||
|
json = cJSON_CreateObject();
|
||||||
|
cJSON *json_msg;
|
||||||
|
|
||||||
|
if(content_length)
|
||||||
|
{
|
||||||
|
json_msg = cJSON_CreateStringReference(content);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json_msg = cJSON_CreateString(content);
|
||||||
|
}
|
||||||
|
if(json_msg == NULL)
|
||||||
|
{
|
||||||
|
endpoint_response_text(response, status_code, content, content_length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json, "msg", json_msg);
|
||||||
|
|
||||||
|
endpoint_response_json(response, status_code, json);
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
endpoint_response_text(endpoint_response_t *response, int status_code, const char *content, int content_length)
|
endpoint_response_text(endpoint_response_t *response, int status_code, const char *content, int content_length)
|
||||||
{
|
{
|
||||||
|
endpoint_response_free_content(response);
|
||||||
if(content == NULL)
|
if(content == NULL)
|
||||||
{
|
{
|
||||||
content = "";
|
content = "";
|
||||||
|
@ -53,22 +87,27 @@ endpoint_response_text(endpoint_response_t *response, int status_code, const cha
|
||||||
|
|
||||||
response->status_code = status_code;
|
response->status_code = status_code;
|
||||||
response->content_type = "text/plain";
|
response->content_type = "text/plain";
|
||||||
if(content_length >= 0)
|
if(content_length)
|
||||||
{
|
{
|
||||||
response->content_length = content_length;
|
response->content_length = content_length;
|
||||||
response->alloced_content = false;
|
response->alloced_content = false;
|
||||||
|
response->content = (char*)content;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
response->content_length = strlen(content);
|
response->content_length = strlen(content);
|
||||||
response->alloced_content = true;
|
response->alloced_content = true;
|
||||||
|
|
||||||
|
response->content = malloc(sizeof(char) * (response->content_length + 1));
|
||||||
|
strcpy(response->content, content);
|
||||||
|
response->content[response->content_length] = '\0';
|
||||||
}
|
}
|
||||||
response->content = content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
endpoint_response_json(endpoint_response_t *response, int status_code, const cJSON *json_root)
|
endpoint_response_json(endpoint_response_t *response, int status_code, const cJSON *json_root)
|
||||||
{
|
{
|
||||||
|
endpoint_response_free_content(response);
|
||||||
if(json_root != NULL)
|
if(json_root != NULL)
|
||||||
{
|
{
|
||||||
char *json_str = cJSON_Print(json_root);
|
char *json_str = cJSON_Print(json_root);
|
||||||
|
@ -84,8 +123,16 @@ endpoint_response_json(endpoint_response_t *response, int status_code, const cJS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER_ERR("failed to print schedule json\n");
|
M_RESPONSE_MSG(LOGGER_ERR, response, 500, "failed to print json");
|
||||||
|
}
|
||||||
static const char content[] = "failed to print json";
|
|
||||||
endpoint_response_text(response, status_code, content, STRLEN(content));
|
void
|
||||||
|
endpoint_response_free_content(endpoint_response_t *response)
|
||||||
|
{
|
||||||
|
if(response->alloced_content)
|
||||||
|
{
|
||||||
|
free(response->content);
|
||||||
|
response->content = NULL;
|
||||||
|
response->alloced_content = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,7 @@ api_v1_controllers_STR_GET(struct mg_connection *nc, struct http_message *hm, en
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,10 +26,7 @@ api_v1_controllers_STR_GET(struct mg_connection *nc, struct http_message *hm, en
|
||||||
|
|
||||||
if(!controller)
|
if(!controller)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_CONTROLLER_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no controller for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOGGER_DEBUG("returning controller for uid '%s'\n", args[0].value.v_str);
|
LOGGER_DEBUG("returning controller for uid '%s'\n", args[0].value.v_str);
|
||||||
|
@ -53,10 +47,7 @@ api_v1_controllers_STR_PUT(struct mg_connection *nc, struct http_message *hm, en
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,10 +55,7 @@ api_v1_controllers_STR_PUT(struct mg_connection *nc, struct http_message *hm, en
|
||||||
|
|
||||||
if(!controller)
|
if(!controller)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_CONTROLLER_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no controller for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOGGER_DEBUG("starting overwrite for controller %s\n", args[0].value.v_str);
|
LOGGER_DEBUG("starting overwrite for controller %s\n", args[0].value.v_str);
|
||||||
|
@ -76,70 +64,64 @@ api_v1_controllers_STR_PUT(struct mg_connection *nc, struct http_message *hm, en
|
||||||
|
|
||||||
if(json == NULL)
|
if(json == NULL)
|
||||||
{
|
{
|
||||||
static const char content[] = "no valid json was supplied";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
controller_free(controller);
|
controller_free(controller);
|
||||||
|
|
||||||
|
M_RESPONSE_400_NO_VALID_JSON(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||||
if(json_name)
|
if(json_name)
|
||||||
{
|
{
|
||||||
if(cJSON_IsString(json_name) && json_name->valuestring)
|
if(!cJSON_IsString(json_name) || json_name->valuestring == NULL)
|
||||||
{
|
{
|
||||||
strncpy(controller->name, json_name->valuestring, MAX_NAME_LENGTH);
|
|
||||||
controller->name[MAX_NAME_LENGTH] = '\0';
|
|
||||||
LOGGER_DEBUG("new controller name: %s\n", controller->name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static const char content[] = "the given name is no valid string";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
controller_free(controller);
|
controller_free(controller);
|
||||||
|
|
||||||
|
M_RESPONSE_400_NO_VALID_NAME(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strncpy(controller->name, json_name->valuestring, MAX_NAME_LENGTH);
|
||||||
|
controller->name[MAX_NAME_LENGTH] = '\0';
|
||||||
|
LOGGER_DEBUG("new controller name: %s\n", controller->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON *json_ip = cJSON_GetObjectItemCaseSensitive(json, "ip");
|
cJSON *json_ip = cJSON_GetObjectItemCaseSensitive(json, "ip");
|
||||||
if(json_ip)
|
if(json_ip)
|
||||||
{
|
{
|
||||||
if(cJSON_IsString(json_ip) && json_ip->valuestring)
|
if(!cJSON_IsString(json_ip) || json_ip->valuestring == NULL)
|
||||||
{
|
{
|
||||||
unsigned char buf[sizeof(struct in_addr)];
|
cJSON_Delete(json);
|
||||||
if(inet_pton(AF_INET, json_ip->valuestring, buf))
|
controller_free(controller);
|
||||||
{
|
|
||||||
strncpy(controller->ip, json_ip->valuestring, IP_LENGTH);
|
M_RESPONSE_400_NO_IP(response);
|
||||||
controller->ip[IP_LENGTH] = '\0';
|
return;
|
||||||
LOGGER_DEBUG("new controller ip: %s\n", controller->ip);
|
}
|
||||||
}
|
|
||||||
else
|
unsigned char buf[sizeof(struct in_addr)];
|
||||||
{
|
if(inet_pton(AF_INET, json_ip->valuestring, buf))
|
||||||
static const char content[] = "the given ip address is no valid IPv4 address";
|
{
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
strncpy(controller->ip, json_ip->valuestring, IP_LENGTH);
|
||||||
cJSON_Delete(json);
|
controller->ip[IP_LENGTH] = '\0';
|
||||||
controller_free(controller);
|
LOGGER_DEBUG("new controller ip: %s\n", controller->ip);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static const char content[] = "the given ip address is no valid string";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
controller_free(controller);
|
controller_free(controller);
|
||||||
|
|
||||||
|
M_RESPONSE_400_NO_VALID_IP(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(controller_save(controller))
|
if(controller_save(controller))
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to save controller\n");
|
|
||||||
controller_free(controller);
|
controller_free(controller);
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "failed to save controller to database";
|
M_RESPONSE_500_FAILED_TO_SAVE_TO_DATABASE(response);
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOGGER_DEBUG("saved controller %s\n", args[0].value.v_str);
|
LOGGER_DEBUG("saved controller %s\n", args[0].value.v_str);
|
||||||
|
@ -165,10 +147,7 @@ api_v1_controllers_STR_DELETE(struct mg_connection *nc, struct http_message *hm,
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(uuid_parse(target_uid_str, target_uid))
|
if(uuid_parse(target_uid_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,25 +155,17 @@ api_v1_controllers_STR_DELETE(struct mg_connection *nc, struct http_message *hm,
|
||||||
|
|
||||||
if(!controller)
|
if(!controller)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_CONTROLLER_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no controller for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(controller_remove(controller))
|
if(controller_remove(controller))
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to remove controller from database\n");
|
M_RESPONSE_500_FAILED_TO_DELETE_FROM_DATABASE(response);
|
||||||
|
|
||||||
static const char content[] = "failed to remove controller from database";
|
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("deleted controller %s\n", args[0].value.v_str);
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 200, "deleted controller");
|
||||||
endpoint_response_text(response, 200, "", 0);
|
|
||||||
}
|
}
|
||||||
controller_free(controller);
|
controller_free(controller);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,7 @@ api_v1_controllers_STR_relays_GET(struct mg_connection *nc, struct http_message
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,10 +23,7 @@ api_v1_controllers_STR_relays_GET(struct mg_connection *nc, struct http_message
|
||||||
|
|
||||||
if(!controller)
|
if(!controller)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_CONTROLLER_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no controller for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,7 @@ api_v1_controllers_STR_relays_INT_GET(struct mg_connection *nc, struct http_mess
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,21 +26,18 @@ api_v1_controllers_STR_relays_INT_GET(struct mg_connection *nc, struct http_mess
|
||||||
|
|
||||||
if(!controller)
|
if(!controller)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_CONTROLLER_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no controller for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
relay_t* relay = relay_get_for_controller(controller->id, args[1].value.v_int);
|
int controller_id = controller->id;
|
||||||
|
controller_free(controller);
|
||||||
|
|
||||||
|
relay_t* relay = relay_get_for_controller(controller_id, args[1].value.v_int);
|
||||||
|
|
||||||
if(!relay)
|
if(!relay)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a relay with num %d for controller '%s'\n", args[1].value.v_int, args[0].value.v_str);
|
M_RESPONSE_404_NO_RELAY_FOUND_FOR_ID_AND_NUMBER(response);
|
||||||
|
|
||||||
static const char content[] = "no relay for this controller found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +47,6 @@ api_v1_controllers_STR_relays_INT_GET(struct mg_connection *nc, struct http_mess
|
||||||
endpoint_response_json(response, 200, json);
|
endpoint_response_json(response, 200, json);
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
relay_free(relay);
|
relay_free(relay);
|
||||||
controller_free(controller);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -65,10 +58,7 @@ api_v1_controllers_STR_relays_INT_PUT(struct mg_connection *nc, struct http_mess
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,23 +66,21 @@ api_v1_controllers_STR_relays_INT_PUT(struct mg_connection *nc, struct http_mess
|
||||||
|
|
||||||
if(!controller)
|
if(!controller)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_CONTROLLER_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no controller for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(args[1].value.v_int > controller->relay_count)
|
int controller_id = controller->id;
|
||||||
|
int controller_relay_count = controller->relay_count;
|
||||||
|
controller_free(controller);
|
||||||
|
|
||||||
|
if(args[1].value.v_int > controller_relay_count)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("relay num %d is too high for %s\n", args[1].value.v_int, args[0].value.v_str);
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "relay number is too high for this controller");
|
||||||
|
|
||||||
static const char content[] = "relay number is too high for this controller";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
relay_t* relay = relay_get_for_controller(controller->id, args[1].value.v_int);
|
relay_t* relay = relay_get_for_controller(controller_id, args[1].value.v_int);
|
||||||
|
|
||||||
if(!relay)
|
if(!relay)
|
||||||
{
|
{
|
||||||
|
@ -102,7 +90,7 @@ api_v1_controllers_STR_relays_INT_PUT(struct mg_connection *nc, struct http_mess
|
||||||
relay->number = args[1].value.v_int;
|
relay->number = args[1].value.v_int;
|
||||||
snprintf(relay->name, MAX_NAME_LENGTH, "Relay %d", relay->number);
|
snprintf(relay->name, MAX_NAME_LENGTH, "Relay %d", relay->number);
|
||||||
relay->name[MAX_NAME_LENGTH] = '\0';
|
relay->name[MAX_NAME_LENGTH] = '\0';
|
||||||
relay->controller_id = controller->id;
|
relay->controller_id = controller_id;
|
||||||
|
|
||||||
uuid_t tmp_uuid;
|
uuid_t tmp_uuid;
|
||||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||||
|
@ -117,15 +105,13 @@ api_v1_controllers_STR_relays_INT_PUT(struct mg_connection *nc, struct http_mess
|
||||||
relay->active_schedule = relay->schedules[helper_get_weekday(time_struct)];
|
relay->active_schedule = relay->schedules[helper_get_weekday(time_struct)];
|
||||||
}
|
}
|
||||||
|
|
||||||
controller_free(controller);
|
|
||||||
LOGGER_DEBUG("overwriting relay %d for controller %s\n", args[1].value.v_int, args[0].value.v_str);
|
LOGGER_DEBUG("overwriting relay %d for controller %s\n", args[1].value.v_int, args[0].value.v_str);
|
||||||
|
|
||||||
cJSON *json = cJSON_ParseWithLength(hm->body.p, hm->body.len);
|
cJSON *json = cJSON_ParseWithLength(hm->body.p, hm->body.len);
|
||||||
|
|
||||||
if(json == NULL)
|
if(json == NULL)
|
||||||
{
|
{
|
||||||
static const char content[] = "no valid json was supplied";
|
M_RESPONSE_400_NO_VALID_JSON(response);
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,21 +135,17 @@ api_v1_controllers_STR_relays_INT_PUT(struct mg_connection *nc, struct http_mess
|
||||||
cJSON *json_schedule_uid = cJSON_GetObjectItemCaseSensitive(json_schedule, "id");
|
cJSON *json_schedule_uid = cJSON_GetObjectItemCaseSensitive(json_schedule, "id");
|
||||||
if(!cJSON_IsString(json_schedule_uid) || (json_schedule_uid->valuestring == NULL))
|
if(!cJSON_IsString(json_schedule_uid) || (json_schedule_uid->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("schedules[%d] is missing uid\n", schedule_position);
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "at least one schedule is missing an id";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one schedule is missing an id");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(schedule_uid_parse(json_schedule_uid->valuestring, target_uid))
|
if(schedule_uid_parse(json_schedule_uid->valuestring, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("schedules[%d] has bad uid\n", schedule_position);
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "at least one schedule has a bad id";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 404, "at least one schedule has a bad id");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,11 +172,9 @@ api_v1_controllers_STR_relays_INT_PUT(struct mg_connection *nc, struct http_mess
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(schedule_uid_parse(json_active_schedule_uid->valuestring, target_uid))
|
if(schedule_uid_parse(json_active_schedule_uid->valuestring, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("active_schedule has bad uid\n");
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "active_schedule has a bad id";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "active schedule has a bad uid");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
relay->schedules[day_of_week] = schedule_get_by_uid_or_off(target_uid);
|
relay->schedules[day_of_week] = schedule_get_by_uid_or_off(target_uid);
|
||||||
|
@ -202,21 +182,15 @@ api_v1_controllers_STR_relays_INT_PUT(struct mg_connection *nc, struct http_mess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int opened_transaction = database_transaction_begin();
|
database_transaction_lock lock;
|
||||||
|
database_transaction_begin(&lock);
|
||||||
|
|
||||||
if(relay_save(relay))
|
if(relay_save(relay))
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to save relay\n");
|
database_transaction_rollback(&lock);
|
||||||
|
|
||||||
if(opened_transaction)
|
|
||||||
{
|
|
||||||
database_transaction_rollback();
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "failed to save relay to database";
|
M_RESPONSE_500_FAILED_TO_SAVE_TO_DATABASE(response);
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,19 +209,12 @@ api_v1_controllers_STR_relays_INT_PUT(struct mg_connection *nc, struct http_mess
|
||||||
{
|
{
|
||||||
if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
|
if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("invalid tag in tags\n");
|
database_transaction_rollback(&lock);
|
||||||
|
|
||||||
if(opened_transaction)
|
|
||||||
{
|
|
||||||
database_transaction_rollback();
|
|
||||||
}
|
|
||||||
|
|
||||||
relay_free(relay);
|
relay_free(relay);
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
free(tag_ids);
|
free(tag_ids);
|
||||||
|
|
||||||
static const char content[] = "invalid tag in tags";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "invalid tag in tags");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const char *tag = json_tag->valuestring;
|
const char *tag = json_tag->valuestring;
|
||||||
|
@ -259,13 +226,12 @@ api_v1_controllers_STR_relays_INT_PUT(struct mg_connection *nc, struct http_mess
|
||||||
}
|
}
|
||||||
tag_ids[i++] = tag_id;
|
tag_ids[i++] = tag_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
junction_tag_insert_list(tag_ids, relay->id, 0, json_tags_count);
|
junction_tag_insert_list(tag_ids, relay->id, 0, json_tags_count);
|
||||||
|
free(tag_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(opened_transaction)
|
database_transaction_commit(&lock);
|
||||||
{
|
|
||||||
database_transaction_commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
json = relay_to_json(relay, 0);
|
json = relay_to_json(relay, 0);
|
||||||
|
|
|
@ -18,10 +18,7 @@ api_v1_controllers_STR_relays_INT_pulse_POST(struct mg_connection *nc, struct ht
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,21 +26,18 @@ api_v1_controllers_STR_relays_INT_pulse_POST(struct mg_connection *nc, struct ht
|
||||||
|
|
||||||
if(!controller)
|
if(!controller)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_CONTROLLER_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no controller for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
relay_t* relay = relay_get_for_controller(controller->id, args[1].value.v_int);
|
int controller_id = controller->id;
|
||||||
|
controller_free(controller);
|
||||||
|
|
||||||
|
relay_t* relay = relay_get_for_controller(controller_id, args[1].value.v_int);
|
||||||
|
|
||||||
if(!relay)
|
if(!relay)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a relay with num %d for controller '%s'\n", args[1].value.v_int, args[0].value.v_str);
|
M_RESPONSE_404_NO_RELAY_FOUND_FOR_ID_AND_NUMBER(response);
|
||||||
|
|
||||||
static const char content[] = "no relay for this controller found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +58,6 @@ api_v1_controllers_STR_relays_INT_pulse_POST(struct mg_connection *nc, struct ht
|
||||||
LOGGER_DEBUG("commanding pulse to relay %d for controller %s\n", args[1].value.v_int, args[0].value.v_str);
|
LOGGER_DEBUG("commanding pulse to relay %d for controller %s\n", args[1].value.v_int, args[0].value.v_str);
|
||||||
command_relay_pulse(relay, duration);
|
command_relay_pulse(relay, duration);
|
||||||
|
|
||||||
endpoint_response_text(response, 200, "", 0);
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 200, "sent pulse");
|
||||||
relay_free(relay);
|
relay_free(relay);
|
||||||
controller_free(controller);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ typedef enum
|
||||||
static int
|
static int
|
||||||
bind_tcp_server(const char *addr, const char *port, int max_client_backlog)
|
bind_tcp_server(const char *addr, const char *port, int max_client_backlog)
|
||||||
{
|
{
|
||||||
struct addrinfo hints, *res;
|
struct addrinfo hints;
|
||||||
|
struct addrinfo *res;
|
||||||
int fd;
|
int fd;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ send_udp_broadcast(const char *addr, uint16_t port, void *message, size_t length
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
api_v1_controllers_discover_POST(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
api_v1_controllers_discover_PUT(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||||
{
|
{
|
||||||
(void)nc;
|
(void)nc;
|
||||||
(void)hm;
|
(void)hm;
|
||||||
|
@ -114,13 +115,7 @@ api_v1_controllers_discover_POST(struct mg_connection *nc, struct http_message *
|
||||||
|
|
||||||
if(discover_server_port == -1)
|
if(discover_server_port == -1)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to get server port for discovery\n");
|
M_RESPONSE_MSG(LOGGER_ERR, response, 500, "the server failed to prepare discovery");
|
||||||
static const char content[] = "";
|
|
||||||
response->status_code = 500;
|
|
||||||
response->content_type = "text/plain";
|
|
||||||
response->content_length = STRLEN(content);;
|
|
||||||
response->content = content;
|
|
||||||
response->alloced_content = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,21 +123,16 @@ api_v1_controllers_discover_POST(struct mg_connection *nc, struct http_message *
|
||||||
payload[0] = discover_server_port;
|
payload[0] = discover_server_port;
|
||||||
|
|
||||||
LOGGER_DEBUG("sending udp broadcast\n");
|
LOGGER_DEBUG("sending udp broadcast\n");
|
||||||
if(send_udp_broadcast("255.255.255.255", global_config.discovery_port, payload, sizeof(payload)) < 0)
|
if(send_udp_broadcast("255.255.255.255", global_config->ports.discovery, payload, sizeof(payload)) < 0)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to send UDP broadcast\n");
|
M_RESPONSE_MSG(LOGGER_ERR, response, 500, "the server failed to send discovery broadcast");
|
||||||
static const char content[] = "";
|
|
||||||
response->status_code = 500;
|
|
||||||
response->content_type = "text/plain";
|
|
||||||
response->content_length = STRLEN(content);;
|
|
||||||
response->content = content;
|
|
||||||
response->alloced_content = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sockaddr_storage their_addr;
|
struct sockaddr_storage their_addr;
|
||||||
socklen_t addr_size;
|
socklen_t addr_size;
|
||||||
int client_fd, s_ret;
|
int s_ret;
|
||||||
|
int client_fd;
|
||||||
fd_set accept_fds;
|
fd_set accept_fds;
|
||||||
struct timeval timeout;
|
struct timeval timeout;
|
||||||
|
|
||||||
|
@ -165,145 +155,147 @@ api_v1_controllers_discover_POST(struct mg_connection *nc, struct http_message *
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if((client_fd = accept(discover_server_socket, (struct sockaddr *) &their_addr, &addr_size)) < 0)
|
||||||
{
|
{
|
||||||
if((client_fd = accept(discover_server_socket, (struct sockaddr *) &their_addr, &addr_size)) < 0)
|
LOGGER_ERR("error accepting client %s\n", strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t payload_length;
|
||||||
|
|
||||||
|
if(recv(client_fd, &payload_length, sizeof(payload_length), 0) <= 0)
|
||||||
|
{
|
||||||
|
LOGGER_ERR("error receiving header from client\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *answer_payload = (char*)malloc((payload_length + 1));
|
||||||
|
if(recv(client_fd, answer_payload, payload_length, 0) <= 0)
|
||||||
|
{
|
||||||
|
LOGGER_ERR("error receiving payload from client\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
socklen_t client_addr_size = sizeof(struct sockaddr_in);
|
||||||
|
if(getpeername(client_fd, (struct sockaddr *)&addr, &client_addr_size) != 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
LOGGER_ERR("error receiving payload from client\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER_DEBUG("received info for discovered controller\n");
|
||||||
|
|
||||||
|
uuid_t discovered_id;
|
||||||
|
|
||||||
|
mpack_tree_t tree;
|
||||||
|
mpack_tree_init_data(&tree, answer_payload, payload_length);
|
||||||
|
mpack_tree_parse(&tree);
|
||||||
|
mpack_node_t root = mpack_tree_root(&tree);
|
||||||
|
|
||||||
|
memcpy(discovered_id, mpack_node_data(mpack_node_map_uint(root, DISCOVERY_MAPPING_ID)), sizeof(uuid_t));
|
||||||
|
|
||||||
|
uint16_t discovered_command_port = mpack_node_u16(mpack_node_map_uint(root, DISCOVERY_MAPPING_COMMAND_PORT));
|
||||||
|
uint8_t discovered_relay_count = mpack_node_u8(mpack_node_map_uint(root, DISCOVERY_MAPPING_RELAY_COUNT));
|
||||||
|
const char *discovered_name = mpack_node_str(mpack_node_map_uint(root, DISCOVERY_MAPPING_NAME));
|
||||||
|
size_t discovered_name_len = mpack_node_strlen(mpack_node_map_uint(root, DISCOVERY_MAPPING_NAME));
|
||||||
|
|
||||||
|
if(discovered_name_len > MAX_NAME_LENGTH)
|
||||||
|
{
|
||||||
|
discovered_name_len = MAX_NAME_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found_discovered_in_list = 0;
|
||||||
|
|
||||||
|
for(int i = 0; known_controllers[i] != NULL; i++)
|
||||||
|
{
|
||||||
|
if(!found_discovered_in_list)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("error accepting client %s\n", strerror(errno));
|
if(uuid_compare(known_controllers[i]->uid, discovered_id) == 0)
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t payload_length;
|
|
||||||
|
|
||||||
if(recv(client_fd, &payload_length, sizeof(payload_length), 0) <= 0)
|
|
||||||
{
|
|
||||||
LOGGER_ERR("error receiving header from client\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *answer_payload = (char*)malloc((payload_length));
|
|
||||||
ssize_t bytes_transferred;
|
|
||||||
|
|
||||||
if((bytes_transferred = recv(client_fd, answer_payload, payload_length, 0)) <= 0)
|
|
||||||
{
|
|
||||||
LOGGER_ERR("error receiving payload from client\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
socklen_t client_addr_size = sizeof(struct sockaddr_in);
|
|
||||||
if(getpeername(client_fd, (struct sockaddr *)&addr, &client_addr_size) != 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
LOGGER_ERR("error receiving payload from client\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER_DEBUG("received info for discovered controller\n");
|
|
||||||
|
|
||||||
uuid_t discovered_id;
|
|
||||||
|
|
||||||
mpack_tree_t tree;
|
|
||||||
mpack_tree_init_data(&tree, answer_payload, payload_length);
|
|
||||||
mpack_tree_parse(&tree);
|
|
||||||
mpack_node_t root = mpack_tree_root(&tree);
|
|
||||||
|
|
||||||
memcpy(discovered_id, mpack_node_data(mpack_node_map_uint(root, DISCOVERY_MAPPING_ID)), sizeof(uuid_t));
|
|
||||||
|
|
||||||
uint16_t discovered_command_port = mpack_node_u16(mpack_node_map_uint(root, DISCOVERY_MAPPING_COMMAND_PORT));
|
|
||||||
uint8_t discovered_relay_count = mpack_node_u8(mpack_node_map_uint(root, DISCOVERY_MAPPING_RELAY_COUNT));
|
|
||||||
const char *discovered_name = mpack_node_str(mpack_node_map_uint(root, DISCOVERY_MAPPING_NAME));
|
|
||||||
size_t discovered_name_len = mpack_node_strlen(mpack_node_map_uint(root, DISCOVERY_MAPPING_NAME));
|
|
||||||
|
|
||||||
if(discovered_name_len > MAX_NAME_LENGTH)
|
|
||||||
{
|
|
||||||
discovered_name_len = MAX_NAME_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool found_discovered_in_list = 0;
|
|
||||||
|
|
||||||
for(int i = 0; known_controllers[i] != NULL; i++)
|
|
||||||
{
|
|
||||||
if(!found_discovered_in_list)
|
|
||||||
{
|
{
|
||||||
if(uuid_compare(known_controllers[i]->uid, discovered_id) == 0)
|
LOGGER_DEBUG("rediscovered a known controller at %s\n", inet_ntoa(addr.sin_addr));
|
||||||
{
|
|
||||||
LOGGER_DEBUG("rediscovered a known controller at %s\n", inet_ntoa(addr.sin_addr));
|
|
||||||
|
|
||||||
known_controllers[i]->active = 1;
|
known_controllers[i]->active = 1;
|
||||||
strncpy(known_controllers[i]->name, discovered_name, discovered_name_len);
|
strncpy(known_controllers[i]->name, discovered_name, discovered_name_len);
|
||||||
strcpy(known_controllers[i]->ip, inet_ntoa(addr.sin_addr));
|
known_controllers[i]->name[discovered_name_len] = '\0';
|
||||||
known_controllers[i]->name[discovered_name_len] = '\0';
|
|
||||||
known_controllers[i]->port = discovered_command_port;
|
|
||||||
known_controllers[i]->relay_count = discovered_relay_count;
|
|
||||||
|
|
||||||
controller_save(known_controllers[i]);
|
strncpy(known_controllers[i]->ip, inet_ntoa(addr.sin_addr), IP_LENGTH);
|
||||||
controller_free(known_controllers[i]);
|
known_controllers[i]->ip[IP_LENGTH] = '\0';
|
||||||
|
|
||||||
found_discovered_in_list = 1;
|
known_controllers[i]->port = discovered_command_port;
|
||||||
known_controllers[i] = known_controllers[i + 1];
|
known_controllers[i]->relay_count = discovered_relay_count;
|
||||||
}
|
|
||||||
}
|
controller_save(known_controllers[i]);
|
||||||
else
|
controller_free(known_controllers[i]);
|
||||||
{
|
|
||||||
|
found_discovered_in_list = 1;
|
||||||
known_controllers[i] = known_controllers[i + 1];
|
known_controllers[i] = known_controllers[i + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if(!found_discovered_in_list)
|
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("discovered a new controller at %s\n", inet_ntoa(addr.sin_addr));
|
known_controllers[i] = known_controllers[i + 1];
|
||||||
|
|
||||||
controller_t *discovered_controller = malloc(sizeof(controller_t));
|
|
||||||
discovered_controller->id = 0;
|
|
||||||
strcpy(discovered_controller->ip, inet_ntoa(addr.sin_addr));
|
|
||||||
memcpy(discovered_controller->uid, discovered_id, sizeof(uuid_t));
|
|
||||||
strncpy(discovered_controller->name, discovered_name, discovered_name_len);
|
|
||||||
discovered_controller->name[discovered_name_len] = '\0';
|
|
||||||
discovered_controller->relay_count = discovered_relay_count;
|
|
||||||
discovered_controller->port = discovered_command_port;
|
|
||||||
discovered_controller->active = 1;
|
|
||||||
|
|
||||||
controller_save(discovered_controller);
|
|
||||||
|
|
||||||
// TODO get relays during discovery and don't create empty ones
|
|
||||||
relay_t **discovered_relays = malloc(sizeof(relay_t*) * (discovered_controller->relay_count + 1));
|
|
||||||
for(int i = 0; i < discovered_controller->relay_count; ++i)
|
|
||||||
{
|
|
||||||
relay_t *new_relay = malloc(sizeof(relay_t));
|
|
||||||
new_relay->id = 0;
|
|
||||||
sprintf(new_relay->name, "Relay %d", i + 1);
|
|
||||||
new_relay->number = i;
|
|
||||||
new_relay->controller_id = discovered_controller->id;
|
|
||||||
|
|
||||||
uuid_t tmp_uuid;
|
|
||||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
|
||||||
memcpy(tmp_uuid, "off", 3);
|
|
||||||
|
|
||||||
for(int i = 0; i < 7; ++i)
|
|
||||||
{
|
|
||||||
new_relay->schedules[i] = schedule_get_by_uid(tmp_uuid);
|
|
||||||
}
|
|
||||||
time_t timestamp = time(NULL);
|
|
||||||
struct tm *time_struct = localtime(×tamp);
|
|
||||||
new_relay->active_schedule = new_relay->schedules[helper_get_weekday(time_struct)];
|
|
||||||
|
|
||||||
relay_save(new_relay);
|
|
||||||
|
|
||||||
discovered_relays[i] = new_relay;
|
|
||||||
}
|
|
||||||
discovered_relays[discovered_controller->relay_count] = NULL;
|
|
||||||
discovered_controller->relays = discovered_relays;
|
|
||||||
|
|
||||||
controller_free(discovered_controller);
|
|
||||||
}
|
}
|
||||||
mpack_tree_destroy(&tree);
|
|
||||||
free(answer_payload);
|
|
||||||
|
|
||||||
discover_answer_buf[0] = 0; // TODO add discovery return codes
|
|
||||||
send(client_fd, discover_answer_buf, sizeof(uint8_t), 0);
|
|
||||||
close(client_fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!found_discovered_in_list)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("discovered a new controller at %s\n", inet_ntoa(addr.sin_addr));
|
||||||
|
|
||||||
|
controller_t *discovered_controller = malloc(sizeof(controller_t));
|
||||||
|
discovered_controller->id = 0;
|
||||||
|
|
||||||
|
strncpy(discovered_controller->ip, inet_ntoa(addr.sin_addr), IP_LENGTH + 1);
|
||||||
|
discovered_controller->ip[IP_LENGTH] = '\0';
|
||||||
|
|
||||||
|
uuid_copy(discovered_controller->uid, discovered_id);
|
||||||
|
strncpy(discovered_controller->name, discovered_name, discovered_name_len);
|
||||||
|
discovered_controller->name[discovered_name_len] = '\0';
|
||||||
|
discovered_controller->relay_count = discovered_relay_count;
|
||||||
|
discovered_controller->port = discovered_command_port;
|
||||||
|
discovered_controller->active = 1;
|
||||||
|
|
||||||
|
controller_save(discovered_controller);
|
||||||
|
|
||||||
|
// TODO get relays during discovery and don't create empty ones
|
||||||
|
relay_t **discovered_relays = malloc(sizeof(relay_t*) * (discovered_controller->relay_count + 1));
|
||||||
|
for(int i = 0; i < discovered_controller->relay_count; ++i)
|
||||||
|
{
|
||||||
|
relay_t *new_relay = malloc(sizeof(relay_t));
|
||||||
|
new_relay->id = 0;
|
||||||
|
sprintf(new_relay->name, "Relay %d", i + 1);
|
||||||
|
new_relay->number = i;
|
||||||
|
new_relay->controller_id = discovered_controller->id;
|
||||||
|
|
||||||
|
uuid_t tmp_uuid;
|
||||||
|
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||||
|
memcpy(tmp_uuid, "off", 3);
|
||||||
|
|
||||||
|
for(int i = 0; i < 7; ++i)
|
||||||
|
{
|
||||||
|
new_relay->schedules[i] = schedule_get_by_uid(tmp_uuid);
|
||||||
|
}
|
||||||
|
time_t timestamp = time(NULL);
|
||||||
|
struct tm *time_struct = localtime(×tamp);
|
||||||
|
new_relay->active_schedule = new_relay->schedules[helper_get_weekday(time_struct)];
|
||||||
|
|
||||||
|
relay_save(new_relay);
|
||||||
|
|
||||||
|
discovered_relays[i] = new_relay;
|
||||||
|
}
|
||||||
|
discovered_relays[discovered_controller->relay_count] = NULL;
|
||||||
|
discovered_controller->relays = discovered_relays;
|
||||||
|
|
||||||
|
controller_free(discovered_controller);
|
||||||
|
}
|
||||||
|
mpack_tree_destroy(&tree);
|
||||||
|
free(answer_payload);
|
||||||
|
|
||||||
|
discover_answer_buf[0] = 0; // TODO add discovery return codes
|
||||||
|
send(client_fd, discover_answer_buf, sizeof(uint8_t), 0);
|
||||||
|
close(client_fd);
|
||||||
}
|
}
|
||||||
for(int i = 0; known_controllers[i] != NULL; i++)
|
for(int i = 0; known_controllers[i] != NULL; i++)
|
||||||
{
|
{
|
||||||
|
|
240
src/endpoints/api_v1_macros.c
Normal file
240
src/endpoints/api_v1_macros.c
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
#include <cJSON.h>
|
||||||
|
#include <macros.h>
|
||||||
|
#include <constants.h>
|
||||||
|
#include <database.h>
|
||||||
|
#include <endpoints/api_v1_macros.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <models/macro.h>
|
||||||
|
#include <models/macro_action.h>
|
||||||
|
#include <models/schedule.h>
|
||||||
|
#include <models/relay.h>
|
||||||
|
#include <models/controller.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||||
|
{
|
||||||
|
(void)args;
|
||||||
|
(void)hm;
|
||||||
|
(void)nc;
|
||||||
|
|
||||||
|
macro_t** all_macros = macro_get_all();
|
||||||
|
|
||||||
|
cJSON *json = cJSON_CreateArray();
|
||||||
|
|
||||||
|
for(int i = 0; all_macros[i] != NULL; ++i)
|
||||||
|
{
|
||||||
|
cJSON_AddItemToArray(json, macro_to_json(all_macros[i]));
|
||||||
|
free(all_macros[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint_response_json(response, 200, json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
free(all_macros);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_POST(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||||
|
{
|
||||||
|
(void)args;
|
||||||
|
(void)nc;
|
||||||
|
|
||||||
|
cJSON *json = cJSON_ParseWithLength(hm->body.p, hm->body.len);
|
||||||
|
|
||||||
|
if(json == NULL)
|
||||||
|
{
|
||||||
|
M_RESPONSE_400_NO_VALID_JSON(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||||
|
if(!cJSON_IsString(json_name) || (json_name->valuestring == NULL))
|
||||||
|
{
|
||||||
|
M_RESPONSE_400_NO_NAME(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json_actions = cJSON_GetObjectItemCaseSensitive(json, "actions");
|
||||||
|
if(!cJSON_IsArray(json_actions))
|
||||||
|
{
|
||||||
|
cJSON_Delete(json);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contains actions for the macro");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
database_transaction_lock lock;
|
||||||
|
database_transaction_begin(&lock);
|
||||||
|
|
||||||
|
macro_t *new_macro = malloc(sizeof(macro_t));
|
||||||
|
|
||||||
|
new_macro->id = 0;
|
||||||
|
uuid_generate(new_macro->uid);
|
||||||
|
|
||||||
|
strncpy(new_macro->name, json_name->valuestring, MAX_NAME_LENGTH);
|
||||||
|
new_macro->name[MAX_NAME_LENGTH] = '\0';
|
||||||
|
|
||||||
|
if(macro_save(new_macro))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_500_FAILED_TO_SAVE_TO_DATABASE(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json_action;
|
||||||
|
cJSON_ArrayForEach(json_action, json_actions)
|
||||||
|
{
|
||||||
|
cJSON *json_action_weekday = cJSON_GetObjectItemCaseSensitive(json_action, "weekday");
|
||||||
|
if(!cJSON_IsNumber(json_action_weekday) || (json_action_weekday->valueint < 0) || (json_action_weekday->valueint > 6))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains an action without weekday");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json_action_schedule = cJSON_GetObjectItemCaseSensitive(json_action, "schedule");
|
||||||
|
if(!cJSON_IsObject(json_action_schedule))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains an action without schedule");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cJSON *json_action_schedule_uid = cJSON_GetObjectItemCaseSensitive(json_action_schedule, "id");
|
||||||
|
if(!cJSON_IsString(json_action_schedule_uid) || (json_action_schedule_uid->valuestring == NULL))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains an action without schedule id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uuid_t action_schedule_uid;
|
||||||
|
if(schedule_uid_parse(json_action_schedule_uid->valuestring, action_schedule_uid))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains an action with an invalid schedule id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_t *action_schedule = schedule_get_by_uid(action_schedule_uid);
|
||||||
|
if(action_schedule == NULL)
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 404, "the schedule for at least one action was not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int action_schedule_id = action_schedule->id;
|
||||||
|
schedule_free(action_schedule);
|
||||||
|
|
||||||
|
|
||||||
|
cJSON *json_action_relay = cJSON_GetObjectItemCaseSensitive(json_action, "relay");
|
||||||
|
if(!cJSON_IsObject(json_action_relay))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains an action without relay");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cJSON *json_action_relay_number = cJSON_GetObjectItemCaseSensitive(json_action_relay, "number");
|
||||||
|
if(!cJSON_IsNumber(json_action_relay_number))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains an action without relay number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cJSON *json_action_relay_controller_uid = cJSON_GetObjectItemCaseSensitive(json_action_relay, "controller_id");
|
||||||
|
if(!cJSON_IsString(json_action_relay_controller_uid) || (json_action_relay_controller_uid->valuestring == NULL))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains an action without relay controller id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uuid_t action_controller_uid;
|
||||||
|
if(uuid_parse(json_action_relay_controller_uid->valuestring, action_controller_uid))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains an action with an invalid relay controller id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller_t *action_controller = controller_get_by_uid(action_controller_uid);
|
||||||
|
if(action_controller == NULL)
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 404, "the controller for at least one action relay was not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int controller_id = action_controller->id;
|
||||||
|
int relay_num = json_action_relay_number->valueint;
|
||||||
|
|
||||||
|
controller_free(action_controller);
|
||||||
|
|
||||||
|
relay_t *action_relay = relay_get_for_controller(controller_id, relay_num);
|
||||||
|
if(action_relay == NULL)
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 404, "the relay for at least one action was not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int action_relay_id = action_relay->id;
|
||||||
|
relay_free(action_relay);
|
||||||
|
|
||||||
|
|
||||||
|
macro_action_t *new_action = malloc(sizeof(macro_action_t));
|
||||||
|
new_action->macro_id = new_macro->id;
|
||||||
|
new_action->relay_id = action_relay_id;
|
||||||
|
new_action->schedule_id = action_schedule_id;
|
||||||
|
new_action->weekday = json_action_weekday->valueint;
|
||||||
|
|
||||||
|
macro_action_insert(new_action);
|
||||||
|
free(new_action);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
database_transaction_commit(&lock);
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
json = macro_to_json(new_macro);
|
||||||
|
|
||||||
|
endpoint_response_json(response, 201, json);
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(new_macro);
|
||||||
|
}
|
||||||
|
|
280
src/endpoints/api_v1_macros_STR.c
Normal file
280
src/endpoints/api_v1_macros_STR.c
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
#include <cJSON.h>
|
||||||
|
#include <macros.h>
|
||||||
|
#include <constants.h>
|
||||||
|
#include <endpoints/api_v1_macros.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <command.h>
|
||||||
|
#include <models/macro_action.h>
|
||||||
|
#include <models/macro.h>
|
||||||
|
#include <models/relay.h>
|
||||||
|
#include <models/tag.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_STR_GET(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||||
|
{
|
||||||
|
(void)hm;
|
||||||
|
(void)nc;
|
||||||
|
|
||||||
|
uuid_t target_uid;
|
||||||
|
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||||
|
{
|
||||||
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_t* macro = macro_get_by_uid(target_uid);
|
||||||
|
|
||||||
|
if(!macro)
|
||||||
|
{
|
||||||
|
M_RESPONSE_404_NO_MACRO_FOUND_FOR_ID(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json = macro_to_json(macro);
|
||||||
|
|
||||||
|
endpoint_response_json(response, 200, json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_STR_PUT(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||||
|
{
|
||||||
|
(void)hm;
|
||||||
|
(void)nc;
|
||||||
|
|
||||||
|
uuid_t target_uid;
|
||||||
|
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||||
|
{
|
||||||
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_t* macro = macro_get_by_uid(target_uid);
|
||||||
|
|
||||||
|
if(!macro)
|
||||||
|
{
|
||||||
|
M_RESPONSE_404_NO_MACRO_FOUND_FOR_ID(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json = cJSON_ParseWithLength(hm->body.p, hm->body.len);
|
||||||
|
|
||||||
|
if(json == NULL)
|
||||||
|
{
|
||||||
|
M_RESPONSE_400_NO_VALID_JSON(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
database_transaction_lock lock;
|
||||||
|
database_transaction_begin(&lock);
|
||||||
|
|
||||||
|
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||||
|
if(cJSON_IsString(json_name) && (json_name->valuestring != NULL))
|
||||||
|
{
|
||||||
|
strncpy(macro->name, json_name->valuestring, MAX_NAME_LENGTH);
|
||||||
|
macro->name[MAX_NAME_LENGTH] = '\0';
|
||||||
|
|
||||||
|
if(macro_save(macro))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_500_FAILED_TO_SAVE_TO_DATABASE(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_action_delete_for_macro(macro->id);
|
||||||
|
|
||||||
|
cJSON *json_actions = cJSON_GetObjectItemCaseSensitive(json, "actions");
|
||||||
|
if(cJSON_IsArray(json_actions))
|
||||||
|
{
|
||||||
|
cJSON *json_action;
|
||||||
|
cJSON_ArrayForEach(json_action, json_actions)
|
||||||
|
{
|
||||||
|
cJSON *json_action_weekday = cJSON_GetObjectItemCaseSensitive(json_action, "weekday");
|
||||||
|
if(!cJSON_IsNumber(json_action_weekday) || (json_action_weekday->valueint < 0) || (json_action_weekday->valueint > 6))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action is missing a weekday");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json_action_schedule = cJSON_GetObjectItemCaseSensitive(json_action, "schedule");
|
||||||
|
if(!cJSON_IsObject(json_action_schedule))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action is missing a schedule");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cJSON *json_action_schedule_uid = cJSON_GetObjectItemCaseSensitive(json_action_schedule, "id");
|
||||||
|
if(!cJSON_IsString(json_action_schedule_uid) || (json_action_schedule_uid->valuestring == NULL))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action is missing a schedule id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uuid_t action_schedule_uid;
|
||||||
|
if(schedule_uid_parse(json_action_schedule_uid->valuestring, action_schedule_uid))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action has a bad schedule id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_t *action_schedule = schedule_get_by_uid(action_schedule_uid);
|
||||||
|
if(action_schedule == NULL)
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action schedule was not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int action_schedule_id = action_schedule->id;
|
||||||
|
schedule_free(action_schedule);
|
||||||
|
|
||||||
|
|
||||||
|
cJSON *json_action_relay = cJSON_GetObjectItemCaseSensitive(json_action, "relay");
|
||||||
|
if(!cJSON_IsObject(json_action_relay))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action is missing a relay");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cJSON *json_action_relay_number = cJSON_GetObjectItemCaseSensitive(json_action_relay, "number");
|
||||||
|
if(!cJSON_IsNumber(json_action_relay_number))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action is missing a relay number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cJSON *json_action_relay_controller_uid = cJSON_GetObjectItemCaseSensitive(json_action_relay, "controller_id");
|
||||||
|
if(!cJSON_IsString(json_action_relay_controller_uid) || (json_action_relay_controller_uid->valuestring == NULL))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action is missing a relay controller id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uuid_t action_controller_uid;
|
||||||
|
if(uuid_parse(json_action_relay_controller_uid->valuestring, action_controller_uid))
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action has a bad relay controller id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller_t *action_controller = controller_get_by_uid(action_controller_uid);
|
||||||
|
if(action_controller == NULL)
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "at least one action relay controller was not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int controller_id = action_controller->id;
|
||||||
|
int relay_num = json_action_relay_number->valueint;
|
||||||
|
|
||||||
|
controller_free(action_controller);
|
||||||
|
|
||||||
|
relay_t *action_relay = relay_get_for_controller(controller_id, relay_num);
|
||||||
|
if(action_relay == NULL)
|
||||||
|
{
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "action relay was not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int action_relay_id = action_relay->id;
|
||||||
|
relay_free(action_relay);
|
||||||
|
|
||||||
|
|
||||||
|
macro_action_t *action = malloc(sizeof(macro_action_t));
|
||||||
|
action->macro_id = macro->id;
|
||||||
|
action->relay_id = action_relay_id;
|
||||||
|
action->schedule_id = action_schedule_id;
|
||||||
|
action->weekday = json_action_weekday->valueint;
|
||||||
|
|
||||||
|
macro_action_insert(action);
|
||||||
|
free(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
database_transaction_commit(&lock);
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
json = macro_to_json(macro);
|
||||||
|
|
||||||
|
endpoint_response_json(response, 201, json);
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
macro_free(macro);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_STR_DELETE(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||||
|
{
|
||||||
|
(void)hm;
|
||||||
|
(void)nc;
|
||||||
|
|
||||||
|
const char *target_uid_str = args[0].value.v_str;
|
||||||
|
|
||||||
|
uuid_t target_uid;
|
||||||
|
if(uuid_parse(target_uid_str, target_uid))
|
||||||
|
{
|
||||||
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_t* macro = macro_get_by_uid(target_uid);
|
||||||
|
|
||||||
|
if(!macro)
|
||||||
|
{
|
||||||
|
M_RESPONSE_404_NO_MACRO_FOUND_FOR_ID(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(macro_remove(macro))
|
||||||
|
{
|
||||||
|
M_RESPONSE_500_FAILED_TO_DELETE_FROM_DATABASE(response);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 200, "deleted macro");
|
||||||
|
}
|
||||||
|
macro_free(macro);
|
||||||
|
}
|
85
src/endpoints/api_v1_macros_STR_execute.c
Normal file
85
src/endpoints/api_v1_macros_STR_execute.c
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#include <cJSON.h>
|
||||||
|
#include <macros.h>
|
||||||
|
#include <constants.h>
|
||||||
|
#include <endpoints/api_v1_macros.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <command.h>
|
||||||
|
#include <models/macro_action.h>
|
||||||
|
#include <models/macro.h>
|
||||||
|
#include <models/relay.h>
|
||||||
|
#include <models/tag.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_macros_STR_execute_PUT(struct mg_connection *nc, struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||||
|
{
|
||||||
|
(void)nc;
|
||||||
|
|
||||||
|
uuid_t target_uid;
|
||||||
|
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||||
|
{
|
||||||
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_t* macro = macro_get_by_uid(target_uid);
|
||||||
|
|
||||||
|
if(!macro)
|
||||||
|
{
|
||||||
|
M_RESPONSE_404_NO_MACRO_FOUND_FOR_ID(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *weekday_str = find_query_param(hm->query_string, "weekday");
|
||||||
|
|
||||||
|
macro_action_t** macro_actions;
|
||||||
|
|
||||||
|
if (weekday_str != NULL) {
|
||||||
|
errno = 0;
|
||||||
|
char *end;
|
||||||
|
long weekday = strtol(weekday_str, &end, 10);
|
||||||
|
bool weekday_str_is_end = weekday_str == end;
|
||||||
|
|
||||||
|
free(weekday_str);
|
||||||
|
|
||||||
|
if (errno != 0 || weekday_str_is_end || weekday < 0 || weekday >= 7) {
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the query contains an invalid weekday");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_actions = macro_action_get_for_macro_and_weekday(macro->id, weekday);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
macro_actions = macro_action_get_for_macro(macro->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
database_transaction_lock lock;
|
||||||
|
database_transaction_begin(&lock);
|
||||||
|
|
||||||
|
for(int i = 0; macro_actions[i] != NULL; ++i)
|
||||||
|
{
|
||||||
|
macro_action_execute(macro_actions[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
database_transaction_commit(&lock);
|
||||||
|
|
||||||
|
int *target_relay_ids = macro_get_target_relay_ids(macro->id);
|
||||||
|
for(int i = 0; target_relay_ids[i] != 0; ++i)
|
||||||
|
{
|
||||||
|
relay_t *target_relay = relay_get_by_id(target_relay_ids[i]);
|
||||||
|
if(!target_relay)
|
||||||
|
{
|
||||||
|
LOGGER_ERR("failed to load target relay from database\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
command_relay_schedules_set(target_relay);
|
||||||
|
|
||||||
|
relay_free(target_relay);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(target_relay_ids);
|
||||||
|
macro_action_free_list(macro_actions);
|
||||||
|
macro_free(macro);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 200, "macro got executed");
|
||||||
|
}
|
|
@ -16,17 +16,13 @@ api_v1_relays_tag_STR_GET(struct mg_connection *nc, struct http_message *hm, end
|
||||||
int tag_id = tag_get_id(args[0].value.v_str);
|
int tag_id = tag_get_id(args[0].value.v_str);
|
||||||
if(tag_id == 0)
|
if(tag_id == 0)
|
||||||
{
|
{
|
||||||
static const char content[] = "tag was not found";
|
M_RESPONSE_404_NO_TAG_FOUND(response);
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int *relays_ids = junction_tag_get_relays_for_tag_id(tag_id);
|
int *relays_ids = junction_tag_get_relays_for_tag_id(tag_id);
|
||||||
if(relays_ids == NULL)
|
if(relays_ids == NULL)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to load relays for tag from database\n");
|
M_RESPONSE_500_FAILED_TO_READ_FROM_DATABASE(response);
|
||||||
|
|
||||||
static const char content[] = "failed to load relays for tag from database";
|
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,29 +16,25 @@ api_v1_schedules_POST(struct mg_connection *nc, struct http_message *hm, endpoin
|
||||||
|
|
||||||
if(json == NULL)
|
if(json == NULL)
|
||||||
{
|
{
|
||||||
static const char content[] = "no valid json was supplied";
|
M_RESPONSE_400_NO_VALID_JSON(response);
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||||
if(!cJSON_IsString(json_name) || (json_name->valuestring == NULL))
|
if(!cJSON_IsString(json_name) || (json_name->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("no name for schedule provided\n");
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "no name for schedule provided";
|
M_RESPONSE_400_NO_NAME(response);
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON *json_periods = cJSON_GetObjectItemCaseSensitive(json, "periods");
|
cJSON *json_periods = cJSON_GetObjectItemCaseSensitive(json, "periods");
|
||||||
if(!cJSON_IsArray(json_periods))
|
if(!cJSON_IsArray(json_periods))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("no periods for schedule provided\n");
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "no periods for schedule provided";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contain periods");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,11 +44,9 @@ api_v1_schedules_POST(struct mg_connection *nc, struct http_message *hm, endpoin
|
||||||
{
|
{
|
||||||
if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
|
if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("invalid tag in tags\n");
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "invalid tag in tags";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one invalid tag");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,22 +72,18 @@ api_v1_schedules_POST(struct mg_connection *nc, struct http_message *hm, endpoin
|
||||||
|
|
||||||
if(!cJSON_IsString(json_period_start) || (json_period_start->valuestring == NULL))
|
if(!cJSON_IsString(json_period_start) || (json_period_start->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("period is missing start\n");
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
schedule_free(new_schedule);
|
schedule_free(new_schedule);
|
||||||
|
|
||||||
static const char content[] = "one period is missing a start";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one period without a start");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!cJSON_IsString(json_period_end) || (json_period_end->valuestring == NULL))
|
if(!cJSON_IsString(json_period_end) || (json_period_end->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("period is missing end\n");
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
schedule_free(new_schedule);
|
schedule_free(new_schedule);
|
||||||
|
|
||||||
static const char content[] = "one period is missing an end";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one period without an end");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,22 +91,18 @@ api_v1_schedules_POST(struct mg_connection *nc, struct http_message *hm, endpoin
|
||||||
uint16_t end;
|
uint16_t end;
|
||||||
if(period_helper_parse_hhmm(json_period_start->valuestring, &start))
|
if(period_helper_parse_hhmm(json_period_start->valuestring, &start))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("couldn't parse start '%s'\n", json_period_start->valuestring);
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
schedule_free(new_schedule);
|
schedule_free(new_schedule);
|
||||||
|
|
||||||
static const char content[] = "the start for one period is invalid";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one period with an invalid start");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(period_helper_parse_hhmm(json_period_end->valuestring, &end))
|
if(period_helper_parse_hhmm(json_period_end->valuestring, &end))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("couldn't parse end '%s'\n", json_period_end->valuestring);
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
schedule_free(new_schedule);
|
schedule_free(new_schedule);
|
||||||
|
|
||||||
static const char content[] = "the end for one period is invalid";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one period with an invalid end");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,7 @@ api_v1_schedules_STR_GET(struct mg_connection *nc, struct http_message *hm, endp
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(schedule_uid_parse(args[0].value.v_str, target_uid))
|
if(schedule_uid_parse(args[0].value.v_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,10 +27,7 @@ api_v1_schedules_STR_GET(struct mg_connection *nc, struct http_message *hm, endp
|
||||||
|
|
||||||
if(!schedule)
|
if(!schedule)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a schedule for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_SCHEDULE_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no schedule for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,10 +47,7 @@ api_v1_schedules_STR_PUT(struct mg_connection *nc, struct http_message *hm, endp
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(schedule_uid_parse(args[0].value.v_str, target_uid))
|
if(schedule_uid_parse(args[0].value.v_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,10 +55,7 @@ api_v1_schedules_STR_PUT(struct mg_connection *nc, struct http_message *hm, endp
|
||||||
|
|
||||||
if(!schedule)
|
if(!schedule)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a schedule for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_SCHEDULE_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no schedule for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +63,7 @@ api_v1_schedules_STR_PUT(struct mg_connection *nc, struct http_message *hm, endp
|
||||||
|
|
||||||
if(json == NULL)
|
if(json == NULL)
|
||||||
{
|
{
|
||||||
static const char content[] = "no valid json was supplied";
|
M_RESPONSE_400_NO_VALID_JSON(response);
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,12 +124,10 @@ api_v1_schedules_STR_PUT(struct mg_connection *nc, struct http_message *hm, endp
|
||||||
|
|
||||||
if(schedule_save(schedule))
|
if(schedule_save(schedule))
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to save schedule\n");
|
|
||||||
free(schedule);
|
free(schedule);
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "failed to save schedule to database";
|
M_RESPONSE_500_FAILED_TO_SAVE_TO_DATABASE(response);
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,10 +175,7 @@ api_v1_schedules_STR_DELETE(struct mg_connection *nc, struct http_message *hm, e
|
||||||
uuid_t target_uid;
|
uuid_t target_uid;
|
||||||
if(schedule_uid_parse(target_uid_str, target_uid))
|
if(schedule_uid_parse(target_uid_str, target_uid))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("failed to unparse uid\n");
|
M_RESPONSE_400_NO_VALID_ID(response);
|
||||||
|
|
||||||
static const char content[] = "given id was invalid";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,33 +183,25 @@ api_v1_schedules_STR_DELETE(struct mg_connection *nc, struct http_message *hm, e
|
||||||
|
|
||||||
if(!schedule)
|
if(!schedule)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("could not find a schedule for uid '%s'\n", args[0].value.v_str);
|
M_RESPONSE_404_NO_SCHEDULE_FOUND_FOR_ID(response);
|
||||||
|
|
||||||
static const char content[] = "no schedule for id found";
|
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(schedule_is_protected(schedule))
|
if(schedule_is_protected(schedule))
|
||||||
{
|
{
|
||||||
static const char content[] = "target schedule is protected";
|
|
||||||
endpoint_response_text(response, 403, content, STRLEN(content));
|
|
||||||
|
|
||||||
schedule_free(schedule);
|
schedule_free(schedule);
|
||||||
|
|
||||||
|
M_RESPONSE_403_PROTECTED_SCHEDULE(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(schedule_remove(schedule))
|
if(schedule_remove(schedule))
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to remove schedule from database\n");
|
M_RESPONSE_500_FAILED_TO_DELETE_FROM_DATABASE(response);
|
||||||
|
|
||||||
static const char content[] = "failed to remove schedule from database";
|
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
endpoint_response_text(response, 200, "", 0);
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 200, "the target schedule got deleted");
|
||||||
}
|
}
|
||||||
schedule_free(schedule);
|
schedule_free(schedule);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,29 +20,23 @@ api_v1_schedules_list_POST(struct mg_connection *nc, struct http_message *hm, en
|
||||||
{
|
{
|
||||||
if(json == NULL)
|
if(json == NULL)
|
||||||
{
|
{
|
||||||
static const char content[] = "no valid json was supplied";
|
M_RESPONSE_400_NO_VALID_JSON(response);
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||||
if(!cJSON_IsString(json_name) || (json_name->valuestring == NULL))
|
if(!cJSON_IsString(json_name) || (json_name->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("no name for schedule provided\n");
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contain a name for at least one schedule");
|
||||||
cJSON_Delete(json_list);
|
|
||||||
|
|
||||||
static const char content[] = "no name for schedule provided";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON *json_periods = cJSON_GetObjectItemCaseSensitive(json, "periods");
|
cJSON *json_periods = cJSON_GetObjectItemCaseSensitive(json, "periods");
|
||||||
if(!cJSON_IsArray(json_periods))
|
if(!cJSON_IsArray(json_periods))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("no periods for schedule provided\n");
|
|
||||||
cJSON_Delete(json_list);
|
cJSON_Delete(json_list);
|
||||||
|
|
||||||
static const char content[] = "no periods for schedule provided";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contain periods for at least one schedule");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,11 +46,9 @@ api_v1_schedules_list_POST(struct mg_connection *nc, struct http_message *hm, en
|
||||||
{
|
{
|
||||||
if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
|
if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("invalid tag in tags\n");
|
|
||||||
cJSON_Delete(json_list);
|
cJSON_Delete(json_list);
|
||||||
|
|
||||||
static const char content[] = "invalid tag in tags";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one invalid tag");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,22 +74,18 @@ api_v1_schedules_list_POST(struct mg_connection *nc, struct http_message *hm, en
|
||||||
|
|
||||||
if(!cJSON_IsString(json_period_start) || (json_period_start->valuestring == NULL))
|
if(!cJSON_IsString(json_period_start) || (json_period_start->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("period is missing start\n");
|
|
||||||
cJSON_Delete(json_list);
|
cJSON_Delete(json_list);
|
||||||
schedule_free(new_schedule);
|
schedule_free(new_schedule);
|
||||||
|
|
||||||
static const char content[] = "one period is missing a start";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one period without a start");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(!cJSON_IsString(json_period_end) || (json_period_end->valuestring == NULL))
|
if(!cJSON_IsString(json_period_end) || (json_period_end->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("period is missing end\n");
|
|
||||||
cJSON_Delete(json_list);
|
cJSON_Delete(json_list);
|
||||||
schedule_free(new_schedule);
|
schedule_free(new_schedule);
|
||||||
|
|
||||||
static const char content[] = "one period is missing an end";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one period without an end");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,22 +93,18 @@ api_v1_schedules_list_POST(struct mg_connection *nc, struct http_message *hm, en
|
||||||
uint16_t end;
|
uint16_t end;
|
||||||
if(period_helper_parse_hhmm(json_period_start->valuestring, &start))
|
if(period_helper_parse_hhmm(json_period_start->valuestring, &start))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("couldn't parse start '%s'\n", json_period_start->valuestring);
|
|
||||||
cJSON_Delete(json_list);
|
cJSON_Delete(json_list);
|
||||||
schedule_free(new_schedule);
|
schedule_free(new_schedule);
|
||||||
|
|
||||||
static const char content[] = "the start for one period is invalid";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one period with an invalid start");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(period_helper_parse_hhmm(json_period_end->valuestring, &end))
|
if(period_helper_parse_hhmm(json_period_end->valuestring, &end))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("couldn't parse end '%s'\n", json_period_end->valuestring);
|
|
||||||
cJSON_Delete(json_list);
|
cJSON_Delete(json_list);
|
||||||
schedule_free(new_schedule);
|
schedule_free(new_schedule);
|
||||||
|
|
||||||
static const char content[] = "the end for one period is invalid";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains at least one period with an invalid end");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,17 +16,13 @@ api_v1_schedules_tag_STR_GET(struct mg_connection *nc, struct http_message *hm,
|
||||||
int tag_id = tag_get_id(args[0].value.v_str);
|
int tag_id = tag_get_id(args[0].value.v_str);
|
||||||
if(tag_id == 0)
|
if(tag_id == 0)
|
||||||
{
|
{
|
||||||
static const char content[] = "tag was not found";
|
M_RESPONSE_404_NO_TAG_FOUND(response);
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int *schedules_ids = junction_tag_get_schedules_for_tag_id(tag_id);
|
int *schedules_ids = junction_tag_get_schedules_for_tag_id(tag_id);
|
||||||
if(schedules_ids == NULL)
|
if(schedules_ids == NULL)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to load schedules for tag from database\n");
|
M_RESPONSE_500_FAILED_TO_READ_FROM_DATABASE(response);
|
||||||
|
|
||||||
static const char content[] = "failed to load schedules for tag from database";
|
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,56 +44,54 @@ api_v1_tags_POST(struct mg_connection *nc, struct http_message *hm, endpoint_arg
|
||||||
|
|
||||||
if(json == NULL)
|
if(json == NULL)
|
||||||
{
|
{
|
||||||
static const char content[] = "no valid json was supplied";
|
M_RESPONSE_400_NO_VALID_JSON(response);
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON *json_tag = cJSON_GetObjectItemCaseSensitive(json, "tag");
|
cJSON *json_tag = cJSON_GetObjectItemCaseSensitive(json, "tag");
|
||||||
if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
|
if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("no tag provided\n");
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "no tag provided";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request does not contain a tag");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(tag_get_id(json_tag->valuestring))
|
|
||||||
{
|
|
||||||
LOGGER_DEBUG("tag existed already\n");
|
|
||||||
cJSON_Delete(json);
|
|
||||||
|
|
||||||
static const char content[] = "tag existed already";
|
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strlen(json_tag->valuestring) == 0)
|
if(strlen(json_tag->valuestring) == 0)
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("tag is empty\n");
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
|
|
||||||
static const char content[] = "tag is empty";
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the request contains an empty tag");
|
||||||
endpoint_response_text(response, 400, content, STRLEN(content));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tag_get_id(json_tag->valuestring))
|
||||||
|
{
|
||||||
|
cJSON_Delete(json);
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 400, "the tag does already exist");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tag_save(0, json_tag->valuestring))
|
if(tag_save(0, json_tag->valuestring))
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("tag could not be saved\n");
|
M_RESPONSE_500_FAILED_TO_SAVE_TO_DATABASE(response);
|
||||||
|
|
||||||
static const char content[] = "tag could not be saved";
|
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOGGER_DEBUG("new tag saved\n");
|
LOGGER_DEBUG("new tag saved\n");
|
||||||
|
|
||||||
endpoint_response_text(response, 201, json_tag->valuestring, 0);
|
size_t tag_len = strlen(json_tag->valuestring);
|
||||||
|
|
||||||
|
// NOLINT(clang-analyzer-unix.Malloc): The endpoint response will be freed later.
|
||||||
|
char *tag = malloc(sizeof(char) * (tag_len + 1));
|
||||||
|
strcpy(tag, json_tag->valuestring);
|
||||||
|
tag[tag_len] = '\0';
|
||||||
|
|
||||||
|
endpoint_response_text(response, 201, tag, 0);
|
||||||
|
|
||||||
|
free(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
cJSON_Delete(json);
|
cJSON_Delete(json);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,26 +16,19 @@ api_v1_tags_STR_GET(struct mg_connection *nc, struct http_message *hm, endpoint_
|
||||||
int tag_id = tag_get_id(args[0].value.v_str);
|
int tag_id = tag_get_id(args[0].value.v_str);
|
||||||
if(tag_id == 0)
|
if(tag_id == 0)
|
||||||
{
|
{
|
||||||
static const char content[] = "tag was not found";
|
M_RESPONSE_404_NO_TAG_FOUND(response);
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int *relays_ids = junction_tag_get_relays_for_tag_id(tag_id);
|
int *relays_ids = junction_tag_get_relays_for_tag_id(tag_id);
|
||||||
if(relays_ids == NULL)
|
if(relays_ids == NULL)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to load relays for tag from database\n");
|
M_RESPONSE_500_FAILED_TO_READ_FROM_DATABASE(response);
|
||||||
|
|
||||||
static const char content[] = "failed to load relays for tag from database";
|
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int *schedules_ids = junction_tag_get_schedules_for_tag_id(tag_id);
|
int *schedules_ids = junction_tag_get_schedules_for_tag_id(tag_id);
|
||||||
if(schedules_ids == NULL)
|
if(schedules_ids == NULL)
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to load schedules for tag from database\n");
|
M_RESPONSE_500_FAILED_TO_READ_FROM_DATABASE(response);
|
||||||
|
|
||||||
static const char content[] = "failed to load schedules for tag from database";
|
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,20 +84,16 @@ api_v1_tags_STR_DELETE(struct mg_connection *nc, struct http_message *hm, endpoi
|
||||||
int tag_id = tag_get_id(args[0].value.v_str);
|
int tag_id = tag_get_id(args[0].value.v_str);
|
||||||
if(tag_id == 0)
|
if(tag_id == 0)
|
||||||
{
|
{
|
||||||
static const char content[] = "tag was not found";
|
M_RESPONSE_404_NO_TAG_FOUND(response);
|
||||||
endpoint_response_text(response, 404, content, STRLEN(content));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tag_remove(tag_id))
|
if(tag_remove(tag_id))
|
||||||
{
|
{
|
||||||
LOGGER_ERR("failed to remove tag from database\n");
|
M_RESPONSE_500_FAILED_TO_DELETE_FROM_DATABASE(response);
|
||||||
|
|
||||||
static const char content[] = "failed to remove tag from database";
|
|
||||||
endpoint_response_text(response, 500, content, STRLEN(content));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
endpoint_response_text(response, 200, "", 0);
|
M_RESPONSE_MSG(LOGGER_DEBUG, response, 200, "the tag got deleted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,16 +17,17 @@ static char*
|
||||||
add_extra_headers(char *extra_headers)
|
add_extra_headers(char *extra_headers)
|
||||||
{
|
{
|
||||||
char *result;
|
char *result;
|
||||||
size_t std_headers_len = strlen(global_config.http_server_opts.extra_headers);
|
size_t std_headers_len = strlen(global_config->http_server_opts.extra_headers);
|
||||||
if(extra_headers == NULL)
|
if(extra_headers == NULL)
|
||||||
{
|
{
|
||||||
result = malloc(sizeof(char) * (std_headers_len + 1));
|
result = malloc(sizeof(char) * (std_headers_len + 1));
|
||||||
strcpy(result, global_config.http_server_opts.extra_headers);
|
strcpy(result, global_config->http_server_opts.extra_headers);
|
||||||
|
result[std_headers_len] = '\0';
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = malloc(sizeof(char) * (std_headers_len + strlen(extra_headers) + 3));
|
result = malloc(sizeof(char) * (std_headers_len + strlen(extra_headers) + 3));
|
||||||
sprintf(result, "%s\r\n%s", global_config.http_server_opts.extra_headers, extra_headers);
|
sprintf(result, "%s\r\n%s", global_config->http_server_opts.extra_headers, extra_headers);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,11 +45,6 @@ send_response(struct mg_connection *nc, endpoint_response_t *response)
|
||||||
|
|
||||||
free(response_headers);
|
free(response_headers);
|
||||||
free(extra_headers);
|
free(extra_headers);
|
||||||
|
|
||||||
if(response->alloced_content)
|
|
||||||
{
|
|
||||||
free((char*)response->content);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,15 +78,18 @@ handle_http_request(struct mg_connection *nc, struct http_message *hm)
|
||||||
endpoint_t *endpoint = router_find_endpoint(hm->uri.p, hm->uri.len, &hm->method);
|
endpoint_t *endpoint = router_find_endpoint(hm->uri.p, hm->uri.len, &hm->method);
|
||||||
|
|
||||||
endpoint_response_t response;
|
endpoint_response_t response;
|
||||||
static const char content[] = "the server did not create a response";
|
response.content = NULL;
|
||||||
endpoint_response_text(&response, 500, content, STRLEN(content));
|
response.alloced_content = false;
|
||||||
|
|
||||||
|
M_RESPONSE_MSG(LOGGER_NONE, &response, 500, "server did not create a response");
|
||||||
|
|
||||||
if(!endpoint)
|
if(!endpoint)
|
||||||
{
|
{
|
||||||
/* Normalize path - resolve "." and ".." (in-place). */
|
/* Normalize path - resolve "." and ".." (in-place). */
|
||||||
if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) {
|
if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) {
|
||||||
mg_http_send_error(nc, 400, global_config.http_server_opts.extra_headers);
|
mg_http_send_error(nc, 400, global_config->http_server_opts.extra_headers);
|
||||||
LOGGER_DEBUG("failed to normalize uri %.*s\n", hm->uri.len, hm->uri.p);
|
LOGGER_DEBUG("failed to normalize uri %.*s\n", hm->uri.len, hm->uri.p);
|
||||||
|
endpoint_response_free_content(&response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LOGGER_DEBUG("no endpoint found - serving file\n");
|
LOGGER_DEBUG("no endpoint found - serving file\n");
|
||||||
|
@ -105,23 +104,22 @@ handle_http_request(struct mg_connection *nc, struct http_message *hm)
|
||||||
++request_file;
|
++request_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *request_file_path = malloc(sizeof(char) * (strlen(request_file) + strlen(global_config.content_dir) + 2));
|
char *request_file_path = malloc(sizeof(char) * (strlen(request_file) + strlen(global_config->content_dir) + 2));
|
||||||
sprintf(request_file_path, "%s/%s", global_config.content_dir, request_file);
|
sprintf(request_file_path, "%s/%s", global_config->content_dir, request_file);
|
||||||
int access_result = access(request_file_path, R_OK);
|
int access_result = access(request_file_path, R_OK);
|
||||||
free(request_file_path);
|
free(request_file_path);
|
||||||
free(request_file_org);
|
free(request_file_org);
|
||||||
if(access_result != -1)
|
if(access_result != -1)
|
||||||
{
|
{
|
||||||
response.status_code = 0;
|
response.status_code = 0;
|
||||||
mg_serve_http(nc, hm, global_config.http_server_opts);
|
mg_serve_http(nc, hm, global_config->http_server_opts);
|
||||||
LOGGER_DEBUG("serving %.*s\n", hm->uri.len, hm->uri.p);
|
LOGGER_DEBUG("serving %.*s\n", hm->uri.len, hm->uri.p);
|
||||||
|
endpoint_response_free_content(&response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
LOGGER_DEBUG("serving 'not found'\n");
|
||||||
LOGGER_DEBUG("serving 'not found'\n");
|
endpoint = router_get_not_found_endpoint();
|
||||||
endpoint = router_get_not_found_endpoint();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(endpoint->method == HTTP_METHOD_OPTIONS)
|
if(endpoint->method == HTTP_METHOD_OPTIONS)
|
||||||
|
@ -148,6 +146,7 @@ handle_http_request(struct mg_connection *nc, struct http_message *hm)
|
||||||
send_response(nc, &response);
|
send_response(nc, &response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endpoint_response_free_content(&response);
|
||||||
LOGGER_DEBUG("freeing endpoint args\n");
|
LOGGER_DEBUG("freeing endpoint args\n");
|
||||||
for(int i = 0; i < endpoint->args_count; ++i)
|
for(int i = 0; i < endpoint->args_count; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <models/relay.h>
|
#include <models/relay.h>
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_mqtt_publish_controller(char **topic_save, int controller_id, char *payload)
|
handle_mqtt_publish_controller(char **topic_save, int controller_id, const char *payload)
|
||||||
{
|
{
|
||||||
(void)controller_id;
|
(void)controller_id;
|
||||||
(void)payload;
|
(void)payload;
|
||||||
|
|
|
@ -13,8 +13,11 @@ helper_connect_tcp_server(char* host, uint16_t port)
|
||||||
char port_str[6];
|
char port_str[6];
|
||||||
sprintf(port_str, "%d", port);
|
sprintf(port_str, "%d", port);
|
||||||
|
|
||||||
int s, status;
|
int s;
|
||||||
struct addrinfo hints, *res;
|
int status;
|
||||||
|
struct addrinfo *res;
|
||||||
|
struct addrinfo hints;
|
||||||
|
|
||||||
memset(&hints, 0, sizeof hints);
|
memset(&hints, 0, sizeof hints);
|
||||||
hints.ai_family = AF_INET; //set IP Protocol flag (IPv4 or IPv6 - we don't care)
|
hints.ai_family = AF_INET; //set IP Protocol flag (IPv4 or IPv6 - we don't care)
|
||||||
hints.ai_socktype = SOCK_STREAM; //set socket flag
|
hints.ai_socktype = SOCK_STREAM; //set socket flag
|
||||||
|
@ -27,7 +30,7 @@ helper_connect_tcp_server(char* host, uint16_t port)
|
||||||
//res got filled out by getaddrinfo() for us
|
//res got filled out by getaddrinfo() for us
|
||||||
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //creating Socket
|
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //creating Socket
|
||||||
|
|
||||||
if ((status = connect(s, res->ai_addr, res->ai_addrlen)) != 0) {
|
if (connect(s, res->ai_addr, res->ai_addrlen) != 0) {
|
||||||
LOGGER_ERR("connect() failed\n");
|
LOGGER_ERR("connect() failed\n");
|
||||||
freeaddrinfo(res);
|
freeaddrinfo(res);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -15,21 +15,20 @@ get_uid_for_user(char *user)
|
||||||
{
|
{
|
||||||
return getuid();
|
return getuid();
|
||||||
}
|
}
|
||||||
struct passwd *pwd = calloc(1, sizeof(struct passwd));
|
struct passwd pwd;
|
||||||
|
struct passwd *result = NULL;
|
||||||
size_t buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char);
|
size_t buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char);
|
||||||
char *buffer = malloc(buffer_len);
|
char *buffer = malloc(buffer_len);
|
||||||
getpwnam_r(user, pwd, buffer, buffer_len, &pwd);
|
getpwnam_r(user, &pwd, buffer, buffer_len, &result);
|
||||||
|
|
||||||
if(pwd == NULL)
|
if(result == NULL)
|
||||||
{
|
{
|
||||||
LOGGER_CRIT("couldn't find user to drop privileges\n");
|
LOGGER_CRIT("couldn't find user to drop privileges\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uid_t result = pwd->pw_uid;
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
free(pwd);
|
return result->pw_uid;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gid_t
|
static gid_t
|
||||||
|
@ -39,28 +38,27 @@ get_gid_for_group(char *group)
|
||||||
{
|
{
|
||||||
return getgid();
|
return getgid();
|
||||||
}
|
}
|
||||||
struct group *grp = calloc(1, sizeof(struct group));
|
struct group grp;
|
||||||
|
struct group *result = NULL;
|
||||||
size_t buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char);
|
size_t buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char);
|
||||||
char *buffer = malloc(buffer_len);
|
char *buffer = malloc(buffer_len);
|
||||||
getgrnam_r(group, grp, buffer, buffer_len, &grp);
|
getgrnam_r(group, &grp, buffer, buffer_len, &result);
|
||||||
|
|
||||||
if(grp == NULL)
|
if(result == NULL)
|
||||||
{
|
{
|
||||||
LOGGER_CRIT("couldn't find group to drop privileges\n");
|
LOGGER_CRIT("couldn't find group to drop privileges\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
gid_t result = grp->gr_gid;
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
free(grp);
|
return result->gr_gid;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
|
37
src/helpers/find_query_param.c
Normal file
37
src/helpers/find_query_param.c
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <mongoose.h>
|
||||||
|
|
||||||
|
char*
|
||||||
|
find_query_param(struct mg_str query_mg_str, char* search_key)
|
||||||
|
{
|
||||||
|
if (query_mg_str.len == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert query string to null-terminated string
|
||||||
|
char* query = malloc(sizeof(char) * (query_mg_str.len + 1));
|
||||||
|
strncpy(query, query_mg_str.p, query_mg_str.len);
|
||||||
|
query[query_mg_str.len] = '\0';
|
||||||
|
|
||||||
|
char* result = NULL;
|
||||||
|
|
||||||
|
// Tokenize query string
|
||||||
|
char *token = strtok(query, "&");
|
||||||
|
while (token != NULL) {
|
||||||
|
char *key = strtok(token, "=");
|
||||||
|
char *val = strtok(NULL, "=");
|
||||||
|
if (key != NULL && val != NULL) {
|
||||||
|
if (strcmp(key, search_key) == 0) {
|
||||||
|
size_t val_len = strlen(val);
|
||||||
|
result = malloc(sizeof(char) * (val_len + 1));
|
||||||
|
strncpy(result, val, val_len + 1);
|
||||||
|
result[val_len] = '\0'; // Ensure null termination
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
token = strtok(NULL, "&");
|
||||||
|
}
|
||||||
|
free(query);
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <constants.h>
|
||||||
#include <helpers.h>
|
#include <helpers.h>
|
||||||
|
|
||||||
int
|
int
|
||||||
helper_get_weekday(const struct tm *time_struct)
|
helper_get_weekday(const struct tm *time_struct)
|
||||||
{
|
{
|
||||||
int wday_sun_sat = time_struct->tm_wday;
|
int wday_sun_sat = time_struct->tm_wday;
|
||||||
int wday_mon_sun = (wday_sun_sat + 6) % 7;
|
int wday_mon_sun = (wday_sun_sat + (DAYS_PER_WEEK - 1)) % DAYS_PER_WEEK;
|
||||||
return wday_mon_sun;
|
return wday_mon_sun;
|
||||||
}
|
}
|
||||||
|
|
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 || level == LOG_NONE )
|
||||||
{
|
{
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
75
src/main.c
75
src/main.c
|
@ -1,28 +1,28 @@
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
|
||||||
#include <mongoose.h>
|
#include <mongoose.h>
|
||||||
#include <confini.h>
|
|
||||||
|
|
||||||
#include <cache.h>
|
#include <cache.h>
|
||||||
#include <router.h>
|
#include <cli.h>
|
||||||
#include <logger.h>
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <database.h>
|
#include <database.h>
|
||||||
#include <handlers.h>
|
|
||||||
#include <enums.h>
|
#include <enums.h>
|
||||||
|
#include <handlers.h>
|
||||||
#include <helpers.h>
|
#include <helpers.h>
|
||||||
#include <status.h>
|
#include <logger.h>
|
||||||
#include <models/controller.h>
|
#include <models/controller.h>
|
||||||
|
#include <router.h>
|
||||||
|
#include <status.h>
|
||||||
|
|
||||||
static struct mg_mgr mgr;
|
static struct mg_mgr mgr;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
terminate(int signum)
|
terminate(int signum)
|
||||||
{
|
{
|
||||||
LOGGER_INFO("terminating controller (%d)\n", signum);
|
LOGGER_INFO("terminating core (%d)\n", signum);
|
||||||
|
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ terminate(int signum)
|
||||||
router_free();
|
router_free();
|
||||||
status_free();
|
status_free();
|
||||||
cache_free();
|
cache_free();
|
||||||
|
config_free();
|
||||||
|
|
||||||
closelog();
|
closelog();
|
||||||
|
|
||||||
|
@ -51,54 +52,27 @@ main(int argc, const char** argv)
|
||||||
signal(SIGABRT, terminate);
|
signal(SIGABRT, terminate);
|
||||||
signal(SIGTERM, terminate);
|
signal(SIGTERM, terminate);
|
||||||
|
|
||||||
setlogmask(LOG_UPTO(LOG_INFO));
|
openlog("emgauwa-core", 0, LOG_USER);
|
||||||
|
setlogmask(LOG_UPTO(LOG_DEBUG));
|
||||||
|
|
||||||
/******************** LOAD CONFIG ********************/
|
/******************** LOAD CONFIG ********************/
|
||||||
|
|
||||||
global_config.file = "core.ini";
|
config_init();
|
||||||
global_config.discovery_port = 4421;
|
|
||||||
global_config.mqtt_port = 1885;
|
|
||||||
global_config.server_port = 5000;
|
|
||||||
|
|
||||||
global_config.log_level = LOG_INFO;
|
cli_t cli;
|
||||||
global_config.log_file = stdout;
|
cli_parse(argc, argv, &cli);
|
||||||
|
|
||||||
strcpy(global_config.user, "");
|
config_load(global_config, cli.config_file);
|
||||||
strcpy(global_config.group, "");
|
|
||||||
|
|
||||||
strcpy(global_config.content_dir, ".");
|
if(global_config->logging.file == NULL)
|
||||||
strcpy(global_config.not_found_file, "404.html");
|
|
||||||
strcpy(global_config.not_found_file_type, "text/html");
|
|
||||||
strcpy(global_config.not_found_content, "404 - NOT FOUND");
|
|
||||||
strcpy(global_config.not_found_content_type, "text/plain");
|
|
||||||
|
|
||||||
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->include)
|
||||||
|
|
||||||
memset(&global_config.http_server_opts, 0, sizeof(global_config.http_server_opts));
|
|
||||||
global_config.http_server_opts.document_root = global_config.content_dir;
|
|
||||||
global_config.http_server_opts.enable_directory_listing = "no";
|
|
||||||
global_config.http_server_opts.extra_headers = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\nAccess-Control-Allow-Methods: *";
|
|
||||||
|
|
||||||
if(global_config.log_file == NULL)
|
|
||||||
{
|
{
|
||||||
global_config.log_file = stdout;
|
config_load_directory(global_config, global_config->include);
|
||||||
}
|
}
|
||||||
openlog("emgauwa-core", 0, LOG_USER);
|
|
||||||
|
|
||||||
|
|
||||||
/******************** SETUP CONNECTION ********************/
|
/******************** SETUP CONNECTION ********************/
|
||||||
|
@ -108,20 +82,20 @@ main(int argc, const char** argv)
|
||||||
mg_mgr_init(&mgr, NULL);
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
|
||||||
char address[100];
|
char address[100];
|
||||||
sprintf(address, "tcp://0.0.0.0:%u", global_config.server_port);
|
sprintf(address, "tcp://0.0.0.0:%u", global_config->ports.server);
|
||||||
struct mg_connection *c_http = mg_bind(&mgr, address, handler_http);
|
struct mg_connection *c_http = mg_bind(&mgr, address, handler_http);
|
||||||
if(c_http == NULL)
|
if(c_http == NULL)
|
||||||
{
|
{
|
||||||
LOGGER_CRIT("failed to bind http server to port %u\n", global_config.server_port);
|
LOGGER_CRIT("failed to bind http server to port %u\n", global_config->ports.server);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
mg_set_protocol_http_websocket(c_http);
|
mg_set_protocol_http_websocket(c_http);
|
||||||
|
|
||||||
sprintf(address, "tcp://0.0.0.0:%u", global_config.mqtt_port);
|
sprintf(address, "tcp://0.0.0.0:%u", global_config->ports.mqtt);
|
||||||
struct mg_connection *c_mqtt = mg_bind(&mgr, address, handler_mqtt);
|
struct mg_connection *c_mqtt = mg_bind(&mgr, address, handler_mqtt);
|
||||||
if(c_mqtt == NULL)
|
if(c_mqtt == NULL)
|
||||||
{
|
{
|
||||||
LOGGER_CRIT("failed to bind mqtt server to port %u\n", global_config.mqtt_port);
|
LOGGER_CRIT("failed to bind mqtt server to port %u\n", global_config->ports.mqtt);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
mg_mqtt_broker_init(&brk, NULL);
|
mg_mqtt_broker_init(&brk, NULL);
|
||||||
|
@ -130,6 +104,11 @@ main(int argc, const char** argv)
|
||||||
|
|
||||||
helper_drop_privileges();
|
helper_drop_privileges();
|
||||||
|
|
||||||
|
memset(&global_config->http_server_opts, 0, sizeof(global_config->http_server_opts));
|
||||||
|
global_config->http_server_opts.document_root = global_config->content_dir;
|
||||||
|
global_config->http_server_opts.enable_directory_listing = "no";
|
||||||
|
global_config->http_server_opts.extra_headers = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\nAccess-Control-Allow-Methods: *";
|
||||||
|
|
||||||
|
|
||||||
/******************** INIT COMPONENTS ********************/
|
/******************** INIT COMPONENTS ********************/
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@ static controller_t*
|
||||||
controller_db_select_mapper(sqlite3_stmt *stmt)
|
controller_db_select_mapper(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
controller_t *new_controller = malloc(sizeof(controller_t));
|
controller_t *new_controller = malloc(sizeof(controller_t));
|
||||||
|
new_controller->id = 0;
|
||||||
|
|
||||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||||
{
|
{
|
||||||
const char *name = sqlite3_column_name(stmt, i);
|
const char *name = sqlite3_column_name(stmt, i);
|
||||||
|
@ -50,14 +52,16 @@ controller_db_select_mapper(sqlite3_stmt *stmt)
|
||||||
new_controller->id = sqlite3_column_int(stmt, i);
|
new_controller->id = sqlite3_column_int(stmt, i);
|
||||||
break;
|
break;
|
||||||
case 'p': // ip
|
case 'p': // ip
|
||||||
strncpy(new_controller->ip, (const char*)sqlite3_column_text(stmt, i), 16);
|
strncpy(new_controller->ip, (const char*)sqlite3_column_text(stmt, i), IP_LENGTH);
|
||||||
|
new_controller->ip[IP_LENGTH] = '\0';
|
||||||
break;
|
break;
|
||||||
default: // ignore columns not implemented
|
default: // ignore columns not implemented
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'n': // name
|
case 'n': // name
|
||||||
strncpy(new_controller->name, (const char*)sqlite3_column_text(stmt, i), 127);
|
strncpy(new_controller->name, (const char*)sqlite3_column_text(stmt, i), MAX_NAME_LENGTH);
|
||||||
|
new_controller->name[MAX_NAME_LENGTH] = '\0';
|
||||||
break;
|
break;
|
||||||
case 'p': // port
|
case 'p': // port
|
||||||
new_controller->port = sqlite3_column_int(stmt, i);
|
new_controller->port = sqlite3_column_int(stmt, i);
|
||||||
|
@ -102,11 +106,9 @@ controller_db_select(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
LOGGER_ERR("error selecting controllers from database: %s\n", sqlite3_errstr(s));
|
||||||
LOGGER_ERR("error selecting controllers from database: %s\n", sqlite3_errstr(s));
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
@ -117,7 +119,8 @@ controller_db_select(sqlite3_stmt *stmt)
|
||||||
int
|
int
|
||||||
controller_save(controller_t *controller)
|
controller_save(controller_t *controller)
|
||||||
{
|
{
|
||||||
int opened_transaction = database_transaction_begin();
|
database_transaction_lock lock;
|
||||||
|
database_transaction_begin(&lock);
|
||||||
|
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
if(controller->id)
|
if(controller->id)
|
||||||
|
@ -142,10 +145,7 @@ controller_save(controller_t *controller)
|
||||||
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(opened_transaction)
|
database_transaction_rollback(&lock);
|
||||||
{
|
|
||||||
database_transaction_rollback();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -154,10 +154,7 @@ controller_save(controller_t *controller)
|
||||||
controller->id = sqlite3_last_insert_rowid(global_database);
|
controller->id = sqlite3_last_insert_rowid(global_database);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(opened_transaction)
|
database_transaction_commit(&lock);
|
||||||
{
|
|
||||||
database_transaction_commit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cache_invalidate_controller(controller->id);
|
cache_invalidate_controller(controller->id);
|
||||||
|
|
|
@ -40,14 +40,16 @@ junction_relay_schedule_insert_weekdays(int relay_id, int *schedule_ids)
|
||||||
static const char query_base[] = "INSERT INTO junction_relay_schedule (weekday, schedule_id, relay_id) VALUES";
|
static const char query_base[] = "INSERT INTO junction_relay_schedule (weekday, schedule_id, relay_id) VALUES";
|
||||||
static const char query_extender[] = " (?, ?, ?)";
|
static const char query_extender[] = " (?, ?, ?)";
|
||||||
|
|
||||||
size_t query_len = STRLEN(query_base) + (7 * (STRLEN(query_extender) + 1)) + 1;
|
size_t query_len = M_STRLEN(query_base) + (7 * (M_STRLEN(query_extender) + 1)) + 1;
|
||||||
char *query = malloc(sizeof(char) * query_len + 1);
|
char *query = malloc(sizeof(char) * query_len + 1);
|
||||||
strncpy(query, query_base, query_len);
|
strncpy(query, query_base, query_len);
|
||||||
query_len -= STRLEN(query_base);
|
query[query_len] = '\0';
|
||||||
|
|
||||||
|
query_len -= M_STRLEN(query_base);
|
||||||
for(int i = 0; i < 7; ++i)
|
for(int i = 0; i < 7; ++i)
|
||||||
{
|
{
|
||||||
strncat(query, query_extender, query_len);
|
strncat(query, query_extender, query_len);
|
||||||
query_len -= STRLEN(query_extender);
|
query_len -= M_STRLEN(query_extender);
|
||||||
char *query_divider = (i < 7 - 1) ? "," : ";";
|
char *query_divider = (i < 7 - 1) ? "," : ";";
|
||||||
strncat(query, query_divider, query_len);
|
strncat(query, query_divider, query_len);
|
||||||
query_len -= 1;
|
query_len -= 1;
|
||||||
|
@ -62,16 +64,18 @@ junction_relay_schedule_insert_weekdays(int relay_id, int *schedule_ids)
|
||||||
sqlite3_bind_int(stmt, i * 3 + 3, relay_id);
|
sqlite3_bind_int(stmt, i * 3 + 3, relay_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(query);
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
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 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
return true;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -87,3 +91,14 @@ junction_relay_schedule_remove_for_relay(int relay_id)
|
||||||
|
|
||||||
return rc == SQLITE_DONE;
|
return rc == SQLITE_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_relay_schedule_get_relay_ids_with_schedule(int schedule_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT DISTINCT relay_id FROM junction_relay_schedule WHERE schedule_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||||
|
|
||||||
|
return database_helper_get_ids(stmt);
|
||||||
|
}
|
||||||
|
|
|
@ -67,14 +67,16 @@ junction_tag_insert_list(int *tag_ids, int relay_id, int schedule_id, int count)
|
||||||
static const char query_base[] = "INSERT INTO junction_tag(tag_id, schedule_id, relay_id) VALUES";
|
static const char query_base[] = "INSERT INTO junction_tag(tag_id, schedule_id, relay_id) VALUES";
|
||||||
static const char query_extender[] = " (?, ?, ?)";
|
static const char query_extender[] = " (?, ?, ?)";
|
||||||
|
|
||||||
size_t query_len = STRLEN(query_base) + (count * (STRLEN(query_extender) + 1)) + 1;
|
size_t query_len = M_STRLEN(query_base) + (count * (M_STRLEN(query_extender) + 1)) + 1;
|
||||||
char *query = malloc(sizeof(char) * query_len + 1);
|
char *query = malloc(sizeof(char) * (query_len + 1));
|
||||||
strncpy(query, query_base, query_len);
|
strncpy(query, query_base, query_len);
|
||||||
query_len -= STRLEN(query_base);
|
query[query_len] = '\0';
|
||||||
|
|
||||||
|
query_len -= M_STRLEN(query_base);
|
||||||
for(int i = 0; i < count; ++i)
|
for(int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
strncat(query, query_extender, query_len);
|
strncat(query, query_extender, query_len);
|
||||||
query_len -= STRLEN(query_extender);
|
query_len -= M_STRLEN(query_extender);
|
||||||
char *query_divider = (i < count - 1) ? "," : ";";
|
char *query_divider = (i < count - 1) ? "," : ";";
|
||||||
strncat(query, query_divider, query_len);
|
strncat(query, query_divider, query_len);
|
||||||
query_len -= 1;
|
query_len -= 1;
|
||||||
|
@ -105,6 +107,8 @@ junction_tag_insert_list(int *tag_ids, int relay_id, int schedule_id, int count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(query);
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
if (rc != SQLITE_DONE)
|
if (rc != SQLITE_DONE)
|
||||||
{
|
{
|
||||||
|
|
328
src/models/macro.c
Normal file
328
src/models/macro.c
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
#include <cache.h>
|
||||||
|
#include <cJSON.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <database.h>
|
||||||
|
#include <models/macro.h>
|
||||||
|
#include <models/macro_action.h>
|
||||||
|
#include <models/schedule.h>
|
||||||
|
#include <models/tag.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
db_update_insert(macro_t *macro, sqlite3_stmt *stmt)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("saving macro '%s' into database (id: %d)\n", macro->name, macro->id);
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
sqlite3_bind_int(stmt, 1, macro->id);
|
||||||
|
sqlite3_bind_blob(stmt, 2, macro->uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||||
|
sqlite3_bind_text(stmt, 3, macro->name, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return rc != SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static macro_t*
|
||||||
|
macro_db_select_mapper(sqlite3_stmt *stmt)
|
||||||
|
{
|
||||||
|
macro_t *new_macro = malloc(sizeof(macro_t));
|
||||||
|
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||||
|
{
|
||||||
|
const char *name = sqlite3_column_name(stmt, i);
|
||||||
|
switch(name[0])
|
||||||
|
{
|
||||||
|
case 'i': // id
|
||||||
|
new_macro->id = sqlite3_column_int(stmt, i);
|
||||||
|
break;
|
||||||
|
case 'n': // name
|
||||||
|
strncpy(new_macro->name, (const char*)sqlite3_column_text(stmt, i), MAX_NAME_LENGTH);
|
||||||
|
new_macro->name[MAX_NAME_LENGTH] = '\0';
|
||||||
|
break;
|
||||||
|
case 'u': // uid
|
||||||
|
uuid_copy(new_macro->uid, (const unsigned char*)sqlite3_column_blob(stmt, i));
|
||||||
|
break;
|
||||||
|
default: // ignore columns not implemented
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_macro;
|
||||||
|
}
|
||||||
|
|
||||||
|
static macro_t**
|
||||||
|
macro_db_select(sqlite3_stmt *stmt)
|
||||||
|
{
|
||||||
|
macro_t **all_macros = malloc(sizeof(macro_t*));
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = sqlite3_step(stmt);
|
||||||
|
if (s == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
macro_t *new_macro = macro_db_select_mapper(stmt);
|
||||||
|
row++;
|
||||||
|
|
||||||
|
all_macros = (macro_t**)realloc(all_macros, sizeof(macro_t*) * (row + 1));
|
||||||
|
all_macros[row - 1] = new_macro;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(s == SQLITE_DONE)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER_ERR("error selecting macros from database: %s\n", sqlite3_errstr(s));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
all_macros[row] = NULL;
|
||||||
|
return all_macros;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_save(macro_t *macro)
|
||||||
|
{
|
||||||
|
database_transaction_lock lock;
|
||||||
|
database_transaction_begin(&lock);
|
||||||
|
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
if(macro->id)
|
||||||
|
{
|
||||||
|
sqlite3_prepare_v2(global_database, "UPDATE macros SET uid = ?2, name = ?3 WHERE id=?1;", -1, &stmt, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sqlite3_prepare_v2(global_database, "INSERT INTO macros(uid, name) values (?2, ?3);", -1, &stmt, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = db_update_insert(macro, stmt);
|
||||||
|
|
||||||
|
if(result)
|
||||||
|
{
|
||||||
|
if(macro->id)
|
||||||
|
{
|
||||||
|
LOGGER_ERR("error inserting data: %s\n", sqlite3_errmsg(global_database));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
||||||
|
}
|
||||||
|
|
||||||
|
database_transaction_rollback(&lock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(!macro->id)
|
||||||
|
{
|
||||||
|
macro->id = sqlite3_last_insert_rowid(global_database);
|
||||||
|
}
|
||||||
|
|
||||||
|
database_transaction_commit(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_invalidate_macro(macro->id);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_remove(macro_t *macro)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
if(macro->id == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "DELETE FROM macros WHERE id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, macro->id);
|
||||||
|
|
||||||
|
int rc = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
cache_invalidate_macro(macro->id);
|
||||||
|
|
||||||
|
return rc != SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
macro_free(macro_t *macro)
|
||||||
|
{
|
||||||
|
free(macro);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
macro_free_list(macro_t **macros)
|
||||||
|
{
|
||||||
|
for(int i = 0; macros[i] != NULL; ++i)
|
||||||
|
{
|
||||||
|
macro_free(macros[i]);
|
||||||
|
}
|
||||||
|
free(macros);
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON*
|
||||||
|
macro_to_json(macro_t *macro)
|
||||||
|
{
|
||||||
|
cJSON *json;
|
||||||
|
|
||||||
|
char *cached = cache_get_json_macro(macro->id);
|
||||||
|
if(cached)
|
||||||
|
{
|
||||||
|
json = cJSON_CreateRaw(cached);
|
||||||
|
free(cached);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
char uuid_str[UUID_STR_LEN];
|
||||||
|
uuid_unparse(macro->uid, uuid_str);
|
||||||
|
|
||||||
|
LOGGER_DEBUG("JSONifying macro %s\n", uuid_str);
|
||||||
|
|
||||||
|
json = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON *json_name = cJSON_CreateString(macro->name);
|
||||||
|
if(json_name == NULL)
|
||||||
|
{
|
||||||
|
cJSON_Delete(json);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json, "name", json_name);
|
||||||
|
|
||||||
|
cJSON *json_id = cJSON_CreateString(uuid_str);
|
||||||
|
if(json_name == NULL)
|
||||||
|
{
|
||||||
|
cJSON_Delete(json);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json, "id", json_id);
|
||||||
|
|
||||||
|
cJSON *json_actions = cJSON_CreateArray();
|
||||||
|
macro_action_t **macro_actions = macro_action_get_for_macro(macro->id);
|
||||||
|
for(int i = 0; macro_actions[i] != NULL; ++i)
|
||||||
|
{
|
||||||
|
cJSON *json_action = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON *json_action_weekday = cJSON_CreateNumber(macro_actions[i]->weekday);
|
||||||
|
if(json_action_weekday == NULL)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("failed to create weekday from number %d\n", macro_actions[i]->weekday);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
relay_t *relay = relay_get_by_id(macro_actions[i]->relay_id);
|
||||||
|
if(!relay)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("failed to get relay\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
schedule_t *schedule = schedule_get_by_id(macro_actions[i]->schedule_id);
|
||||||
|
if(!schedule)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("failed to get schedule\n");
|
||||||
|
relay_free(relay);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_AddItemToObject(json_action, "weekday", json_action_weekday);
|
||||||
|
cJSON_AddItemToObject(json_action, "relay", relay_to_json(relay, 0));
|
||||||
|
cJSON_AddItemToObject(json_action, "schedule", schedule_to_json(schedule));
|
||||||
|
|
||||||
|
cJSON_AddItemToArray(json_actions, json_action);
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json, "actions", json_actions);
|
||||||
|
|
||||||
|
macro_action_free_list(macro_actions);
|
||||||
|
|
||||||
|
char *json_str = cJSON_Print(json);
|
||||||
|
cache_put_json_macro(macro->id, json_str);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
|
||||||
|
json = cJSON_CreateRaw(json_str);
|
||||||
|
free(json_str);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_t*
|
||||||
|
macro_get_by_id(int id)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("getting macro [id=%d] from database\n", id);
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT * FROM macros WHERE id = ?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, id);
|
||||||
|
|
||||||
|
macro_t **sql_result = macro_db_select(stmt);
|
||||||
|
|
||||||
|
macro_t *result = sql_result[0];
|
||||||
|
free(sql_result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_t*
|
||||||
|
macro_get_by_uid(uuid_t uid)
|
||||||
|
{
|
||||||
|
char uuid_str[UUID_STR_LEN];
|
||||||
|
uuid_unparse(uid, uuid_str);
|
||||||
|
LOGGER_DEBUG("getting macro [uid=%s] from database\n", uuid_str);
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT * FROM macros WHERE uid = ?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_blob(stmt, 1, uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||||
|
|
||||||
|
macro_t **sql_result = macro_db_select(stmt);
|
||||||
|
|
||||||
|
macro_t *result = sql_result[0];
|
||||||
|
free(sql_result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_t**
|
||||||
|
macro_get_relay_weekdays(int relay_id)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("getting macros [relay_id=%d] from database\n", relay_id);
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT macros.* FROM macros INNER JOIN junction_relay_macro ON macros.id == junction_relay_macro.macro_id WHERE junction_relay_macro.relay_id = ?1 ORDER BY junction_relay_macro.weekday ASC", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, relay_id);
|
||||||
|
|
||||||
|
return macro_db_select(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_t**
|
||||||
|
macro_get_all()
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("getting all macros from database\n");
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT * FROM macros;", -1, &stmt, NULL);
|
||||||
|
|
||||||
|
return macro_db_select(stmt);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int*
|
||||||
|
macro_get_target_relay_ids(int macro_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT DISTINCT relay_id FROM macro_actions WHERE macro_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, macro_id);
|
||||||
|
|
||||||
|
return database_helper_get_ids(stmt);
|
||||||
|
}
|
189
src/models/macro_action.c
Normal file
189
src/models/macro_action.c
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <models/macro_action.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <cache.h>
|
||||||
|
#include <database.h>
|
||||||
|
|
||||||
|
static macro_action_t*
|
||||||
|
macro_action_db_select_mapper(sqlite3_stmt *stmt)
|
||||||
|
{
|
||||||
|
macro_action_t *new_macro_action = malloc(sizeof(macro_action_t));
|
||||||
|
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||||
|
{
|
||||||
|
const char *name = sqlite3_column_name(stmt, i);
|
||||||
|
switch(name[0])
|
||||||
|
{
|
||||||
|
case 'm': // macro_id
|
||||||
|
new_macro_action->macro_id = sqlite3_column_int(stmt, i);
|
||||||
|
break;
|
||||||
|
case 'r': // relay_id
|
||||||
|
new_macro_action->relay_id = sqlite3_column_int(stmt, i);
|
||||||
|
break;
|
||||||
|
case 's': // schedule_id
|
||||||
|
new_macro_action->schedule_id = sqlite3_column_int(stmt, i);
|
||||||
|
break;
|
||||||
|
case 'w': // weekday
|
||||||
|
new_macro_action->weekday = (uint8_t)sqlite3_column_int(stmt, i);
|
||||||
|
break;
|
||||||
|
default: // ignore columns not implemented
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_macro_action;
|
||||||
|
}
|
||||||
|
|
||||||
|
static macro_action_t**
|
||||||
|
macro_action_db_select(sqlite3_stmt *stmt)
|
||||||
|
{
|
||||||
|
macro_action_t **all_macro_actions = malloc(sizeof(macro_action_t*));
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = sqlite3_step(stmt);
|
||||||
|
if (s == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
macro_action_t *new_macro_action = macro_action_db_select_mapper(stmt);
|
||||||
|
row++;
|
||||||
|
|
||||||
|
all_macro_actions = (macro_action_t**)realloc(all_macro_actions, sizeof(macro_action_t*) * (row + 1));
|
||||||
|
all_macro_actions[row - 1] = new_macro_action;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(s == SQLITE_DONE)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGER_ERR("error selecting macro_actions from database: %s\n", sqlite3_errstr(s));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
all_macro_actions[row] = NULL;
|
||||||
|
return all_macro_actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_action_insert(macro_action_t *macro_action)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "INSERT INTO macro_actions(macro_id, relay_id, schedule_id, weekday) values (?1, ?2, ?3, ?4);", -1, &stmt, NULL);
|
||||||
|
|
||||||
|
sqlite3_bind_int(stmt, 1, macro_action->macro_id);
|
||||||
|
sqlite3_bind_int(stmt, 2, macro_action->relay_id);
|
||||||
|
sqlite3_bind_int(stmt, 3, macro_action->schedule_id);
|
||||||
|
sqlite3_bind_int(stmt, 4, macro_action->weekday);
|
||||||
|
|
||||||
|
int rc = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
cache_invalidate_macro(macro_action->macro_id);
|
||||||
|
|
||||||
|
return rc != SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_action_delete_for_macro(int macro_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "DELETE FROM macro_actions WHERE macro_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, macro_id);
|
||||||
|
|
||||||
|
int rc = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
cache_invalidate_macro(macro_id);
|
||||||
|
|
||||||
|
return rc != SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_action_t**
|
||||||
|
macro_action_get_for_macro(int macro_id)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("getting macro_actions [macro_id=%d] from database\n", macro_id);
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT * FROM macro_actions WHERE macro_id=?", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, macro_id);
|
||||||
|
|
||||||
|
return macro_action_db_select(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_action_t**
|
||||||
|
macro_action_get_for_macro_and_weekday(int macro_id, int weekday)
|
||||||
|
{
|
||||||
|
LOGGER_DEBUG("getting macro_actions [macro_id=%d weekday=%d] from database\n", macro_id, weekday);
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT * FROM macro_actions WHERE macro_id=? AND weekday=?", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, macro_id);
|
||||||
|
sqlite3_bind_int(stmt, 2, weekday);
|
||||||
|
|
||||||
|
return macro_action_db_select(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
macro_action_execute(macro_action_t *macro_action)
|
||||||
|
{
|
||||||
|
schedule_t *schedule = schedule_get_by_id(macro_action->schedule_id);
|
||||||
|
if(!schedule)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
relay_t *relay = relay_get_by_id(macro_action->relay_id);
|
||||||
|
if(!relay)
|
||||||
|
{
|
||||||
|
free(schedule);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_free(relay->schedules[macro_action->weekday]);
|
||||||
|
relay->schedules[macro_action->weekday] = schedule;
|
||||||
|
|
||||||
|
return relay_save(relay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
macro_action_free_list(macro_action_t **macro_actions)
|
||||||
|
{
|
||||||
|
for(int i = 0; macro_actions[i] != NULL; ++i)
|
||||||
|
{
|
||||||
|
free(macro_actions[i]);
|
||||||
|
}
|
||||||
|
free(macro_actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
int*
|
||||||
|
macro_action_get_macro_ids_with_schedule(int schedule_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT macro_id FROM macro_actions WHERE schedule_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||||
|
|
||||||
|
return database_helper_get_ids(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int*
|
||||||
|
macro_action_get_macro_ids_with_relay(int relay_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT macro_id FROM macro_actions WHERE relay_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, relay_id);
|
||||||
|
|
||||||
|
return database_helper_get_ids(stmt);
|
||||||
|
}
|
|
@ -27,9 +27,8 @@ period_helper_parse_hhmm(const char *hhmm_str, uint16_t *hhmm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t tmp_h, tmp_m;
|
uint16_t tmp_h = (uint16_t)strtol(&hhmm_str[0], NULL, 10);
|
||||||
tmp_h = (uint16_t)strtol(&hhmm_str[0], NULL, 10);
|
uint16_t tmp_m = (uint16_t)strtol(&hhmm_str[3], NULL, 10);
|
||||||
tmp_m = (uint16_t)strtol(&hhmm_str[3], NULL, 10);
|
|
||||||
|
|
||||||
if(tmp_h > 24 || tmp_m >= 60)
|
if(tmp_h > 24 || tmp_m >= 60)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
|
#include <sqlite3.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sqlite3.h>
|
|
||||||
|
|
||||||
#include <cache.h>
|
|
||||||
#include <cJSON.h>
|
#include <cJSON.h>
|
||||||
#include <logger.h>
|
#include <cache.h>
|
||||||
|
#include <constants.h>
|
||||||
#include <database.h>
|
#include <database.h>
|
||||||
#include <status.h>
|
#include <logger.h>
|
||||||
#include <models/relay.h>
|
|
||||||
#include <models/controller.h>
|
#include <models/controller.h>
|
||||||
#include <models/schedule.h>
|
|
||||||
#include <models/junction_tag.h>
|
|
||||||
#include <models/junction_relay_schedule.h>
|
#include <models/junction_relay_schedule.h>
|
||||||
|
#include <models/junction_tag.h>
|
||||||
|
#include <models/relay.h>
|
||||||
|
#include <models/schedule.h>
|
||||||
#include <models/tag.h>
|
#include <models/tag.h>
|
||||||
|
#include <status.h>
|
||||||
|
|
||||||
static int
|
static int
|
||||||
db_update_insert(relay_t *relay, sqlite3_stmt *stmt)
|
db_update_insert(relay_t *relay, sqlite3_stmt *stmt)
|
||||||
|
@ -35,6 +36,7 @@ static relay_t*
|
||||||
relay_db_select_mapper(sqlite3_stmt *stmt)
|
relay_db_select_mapper(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
relay_t *new_relay = malloc(sizeof(relay_t));
|
relay_t *new_relay = malloc(sizeof(relay_t));
|
||||||
|
new_relay->id = 0;
|
||||||
new_relay->is_on = 0;
|
new_relay->is_on = 0;
|
||||||
|
|
||||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||||
|
@ -52,7 +54,8 @@ relay_db_select_mapper(sqlite3_stmt *stmt)
|
||||||
switch(name[1])
|
switch(name[1])
|
||||||
{
|
{
|
||||||
case 'a': // name
|
case 'a': // name
|
||||||
strncpy(new_relay->name, (const char*)sqlite3_column_text(stmt, i), 127);
|
strncpy(new_relay->name, (const char*)sqlite3_column_text(stmt, i), MAX_NAME_LENGTH);
|
||||||
|
new_relay->name[MAX_NAME_LENGTH] = '\0';
|
||||||
break;
|
break;
|
||||||
case 'u': // number
|
case 'u': // number
|
||||||
new_relay->number = sqlite3_column_int(stmt, i);
|
new_relay->number = sqlite3_column_int(stmt, i);
|
||||||
|
@ -66,7 +69,7 @@ relay_db_select_mapper(sqlite3_stmt *stmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(new_relay->schedules, 0, sizeof(schedule_t*) * 7);
|
memset(new_relay->schedules, 0, sizeof(schedule_t*) * DAYS_PER_WEEK);
|
||||||
relay_reload_schedules(new_relay);
|
relay_reload_schedules(new_relay);
|
||||||
|
|
||||||
relay_reload_active_schedule(new_relay);
|
relay_reload_active_schedule(new_relay);
|
||||||
|
@ -100,11 +103,9 @@ relay_db_select(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
LOGGER_ERR("error selecting relays from database: %s\n", sqlite3_errstr(s));
|
||||||
LOGGER_ERR("error selecting relays from database: %s\n", sqlite3_errstr(s));
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
@ -115,7 +116,8 @@ relay_db_select(sqlite3_stmt *stmt)
|
||||||
int
|
int
|
||||||
relay_save(relay_t *relay)
|
relay_save(relay_t *relay)
|
||||||
{
|
{
|
||||||
int opened_transaction = database_transaction_begin();
|
database_transaction_lock lock;
|
||||||
|
database_transaction_begin(&lock);
|
||||||
|
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
if(relay->id)
|
if(relay->id)
|
||||||
|
@ -140,10 +142,7 @@ relay_save(relay_t *relay)
|
||||||
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(opened_transaction)
|
database_transaction_rollback(&lock);
|
||||||
{
|
|
||||||
database_transaction_rollback();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -156,17 +155,14 @@ relay_save(relay_t *relay)
|
||||||
junction_relay_schedule_remove_for_relay(relay->id);
|
junction_relay_schedule_remove_for_relay(relay->id);
|
||||||
|
|
||||||
LOGGER_DEBUG("rebuilding relay_schedule junction\n");
|
LOGGER_DEBUG("rebuilding relay_schedule junction\n");
|
||||||
int schedule_ids[7];
|
int schedule_ids[DAYS_PER_WEEK];
|
||||||
for(int i = 0; i < 7; ++i)
|
for(int i = 0; i < DAYS_PER_WEEK; ++i)
|
||||||
{
|
{
|
||||||
schedule_ids[i] = relay->schedules[i]->id;
|
schedule_ids[i] = relay->schedules[i]->id;
|
||||||
}
|
}
|
||||||
junction_relay_schedule_insert_weekdays(relay->id, schedule_ids);
|
junction_relay_schedule_insert_weekdays(relay->id, schedule_ids);
|
||||||
|
|
||||||
if(opened_transaction)
|
database_transaction_commit(&lock);
|
||||||
{
|
|
||||||
database_transaction_commit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cache_invalidate_relay(relay->id, -1);
|
cache_invalidate_relay(relay->id, -1);
|
||||||
|
@ -188,17 +184,16 @@ relay_reload_schedules(relay_t *relay)
|
||||||
{
|
{
|
||||||
schedule_t **schedules = schedule_get_relay_weekdays(relay->id);
|
schedule_t **schedules = schedule_get_relay_weekdays(relay->id);
|
||||||
|
|
||||||
uuid_t off_id;
|
uuid_t off_uid;
|
||||||
memset(off_id, 0, sizeof(uuid_t));
|
schedule_get_uid_off(off_uid);
|
||||||
memcpy(off_id, "off", 3);
|
|
||||||
|
|
||||||
int fill_with_off = 0;
|
int fill_with_off = 0;
|
||||||
for(int i = 0; i < 7; ++i)
|
for(int i = 0; i < DAYS_PER_WEEK; ++i)
|
||||||
{
|
{
|
||||||
if(schedules[i] == NULL || fill_with_off)
|
if(schedules[i] == NULL || fill_with_off)
|
||||||
{
|
{
|
||||||
LOGGER_WARNING("got only %d/7 schedules for relay_id %d\n", i, relay->id);
|
LOGGER_WARNING("got only %d/7 schedules for relay_id %d\n", i, relay->id);
|
||||||
relay->schedules[i] = schedule_get_by_uid(off_id);
|
relay->schedules[i] = schedule_get_by_uid(off_uid);
|
||||||
|
|
||||||
fill_with_off = 1;
|
fill_with_off = 1;
|
||||||
}
|
}
|
||||||
|
@ -218,7 +213,7 @@ relay_reload_schedules(relay_t *relay)
|
||||||
void
|
void
|
||||||
relay_free(relay_t *relay)
|
relay_free(relay_t *relay)
|
||||||
{
|
{
|
||||||
for(int i = 0; i < 7; ++i)
|
for(int i = 0; i < DAYS_PER_WEEK; ++i)
|
||||||
{
|
{
|
||||||
schedule_free(relay->schedules[i]);
|
schedule_free(relay->schedules[i]);
|
||||||
}
|
}
|
||||||
|
@ -311,7 +306,7 @@ relay_to_json(relay_t *relay, int status_relay)
|
||||||
cJSON_AddItemToObject(json, "active_schedule", json_active_schedule);
|
cJSON_AddItemToObject(json, "active_schedule", json_active_schedule);
|
||||||
|
|
||||||
cJSON *json_schedules = cJSON_CreateArray();
|
cJSON *json_schedules = cJSON_CreateArray();
|
||||||
for(int i = 0; i < 7; ++i)
|
for(int i = 0; i < DAYS_PER_WEEK; ++i)
|
||||||
{
|
{
|
||||||
cJSON *json_schedule = schedule_to_json(relay->schedules[i]);
|
cJSON *json_schedule = schedule_to_json(relay->schedules[i]);
|
||||||
cJSON_AddItemToArray(json_schedules, json_schedule);
|
cJSON_AddItemToArray(json_schedules, json_schedule);
|
||||||
|
|
|
@ -37,6 +37,8 @@ schedule_db_select_mapper(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
const uint16_t *periods_blob;
|
const uint16_t *periods_blob;
|
||||||
schedule_t *new_schedule = malloc(sizeof(schedule_t));
|
schedule_t *new_schedule = malloc(sizeof(schedule_t));
|
||||||
|
new_schedule->id = 0;
|
||||||
|
|
||||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||||
{
|
{
|
||||||
const char *name = sqlite3_column_name(stmt, i);
|
const char *name = sqlite3_column_name(stmt, i);
|
||||||
|
@ -46,8 +48,8 @@ schedule_db_select_mapper(sqlite3_stmt *stmt)
|
||||||
new_schedule->id = sqlite3_column_int(stmt, i);
|
new_schedule->id = sqlite3_column_int(stmt, i);
|
||||||
break;
|
break;
|
||||||
case 'n': // name
|
case 'n': // name
|
||||||
strncpy(new_schedule->name, (const char*)sqlite3_column_text(stmt, i), 127);
|
strncpy(new_schedule->name, (const char*)sqlite3_column_text(stmt, i), MAX_NAME_LENGTH);
|
||||||
new_schedule->name[127] = '\0';
|
new_schedule->name[MAX_NAME_LENGTH] = '\0';
|
||||||
break;
|
break;
|
||||||
case 'p': // periods
|
case 'p': // periods
|
||||||
periods_blob = sqlite3_column_blob(stmt, i);
|
periods_blob = sqlite3_column_blob(stmt, i);
|
||||||
|
@ -97,11 +99,9 @@ schedule_db_select(sqlite3_stmt *stmt)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
LOGGER_ERR("error selecting schedules from database: %s\n", sqlite3_errstr(s));
|
||||||
LOGGER_ERR("error selecting schedules from database: %s\n", sqlite3_errstr(s));
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
|
@ -112,7 +112,8 @@ schedule_db_select(sqlite3_stmt *stmt)
|
||||||
int
|
int
|
||||||
schedule_save(schedule_t *schedule)
|
schedule_save(schedule_t *schedule)
|
||||||
{
|
{
|
||||||
int opened_transaction = database_transaction_begin();
|
database_transaction_lock lock;
|
||||||
|
database_transaction_begin(&lock);
|
||||||
|
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
if(schedule->id)
|
if(schedule->id)
|
||||||
|
@ -137,10 +138,7 @@ schedule_save(schedule_t *schedule)
|
||||||
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(opened_transaction)
|
database_transaction_rollback(&lock);
|
||||||
{
|
|
||||||
database_transaction_rollback();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -149,10 +147,7 @@ schedule_save(schedule_t *schedule)
|
||||||
schedule->id = sqlite3_last_insert_rowid(global_database);
|
schedule->id = sqlite3_last_insert_rowid(global_database);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(opened_transaction)
|
database_transaction_commit(&lock);
|
||||||
{
|
|
||||||
database_transaction_commit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cache_invalidate_schedule(schedule->id);
|
cache_invalidate_schedule(schedule->id);
|
||||||
|
@ -182,18 +177,16 @@ schedule_remove(schedule_t *schedule)
|
||||||
int
|
int
|
||||||
schedule_is_protected(schedule_t *schedule)
|
schedule_is_protected(schedule_t *schedule)
|
||||||
{
|
{
|
||||||
uuid_t tmp_uuid;
|
uuid_t tmp_uid;
|
||||||
|
|
||||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
schedule_get_uid_off(tmp_uid);
|
||||||
memcpy(tmp_uuid, "off", 3);
|
if(uuid_compare(schedule->uid, tmp_uid) == 0)
|
||||||
if(uuid_compare(schedule->uid, tmp_uuid) == 0)
|
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
schedule_get_uid_on(tmp_uid);
|
||||||
memcpy(tmp_uuid, "on", 2);
|
if(uuid_compare(schedule->uid, tmp_uid) == 0)
|
||||||
if(uuid_compare(schedule->uid, tmp_uuid) == 0)
|
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -285,9 +278,10 @@ schedule_to_json(schedule_t *schedule)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
char start_str[8], end_str[8];
|
char start_str[8];
|
||||||
|
char end_str[8];
|
||||||
period_t *period = &schedule->periods[i];
|
period_t *period = &schedule->periods[i];
|
||||||
sprintf(start_str, "%02d:%02d", period->start / 60, period->start % 60);
|
sprintf(start_str, "%02d:%02d", period->start / 60 , period->start % 60);
|
||||||
sprintf(end_str, "%02d:%02d", period->end / 60, period->end % 60);
|
sprintf(end_str, "%02d:%02d", period->end / 60, period->end % 60);
|
||||||
|
|
||||||
cJSON *json_period_start = cJSON_CreateString(start_str);
|
cJSON *json_period_start = cJSON_CreateString(start_str);
|
||||||
|
@ -364,11 +358,10 @@ schedule_get_by_id_or_off(int id)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid_t tmp_uuid;
|
uuid_t tmp_uid;
|
||||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
schedule_get_uid_off(tmp_uid);
|
||||||
memcpy(tmp_uuid, "off", 3);
|
|
||||||
|
|
||||||
return schedule_get_by_uid(tmp_uuid);
|
return schedule_get_by_uid(tmp_uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule_t*
|
schedule_t*
|
||||||
|
@ -409,11 +402,10 @@ schedule_get_by_uid_or_off(uuid_t uid)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid_t tmp_uuid;
|
uuid_t tmp_uid;
|
||||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
schedule_get_uid_off(tmp_uid);
|
||||||
memcpy(tmp_uuid, "off", 3);
|
|
||||||
|
|
||||||
return schedule_get_by_uid(tmp_uuid);
|
return schedule_get_by_uid(tmp_uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule_t*
|
schedule_t*
|
||||||
|
@ -464,14 +456,12 @@ schedule_uid_parse(const char *uid_str, uuid_t result)
|
||||||
{
|
{
|
||||||
if(strcmp("off", uid_str) == 0)
|
if(strcmp("off", uid_str) == 0)
|
||||||
{
|
{
|
||||||
memset(result, 0, sizeof(uuid_t));
|
schedule_get_uid_off(result);
|
||||||
memcpy(result, "off", 3);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if(strcmp("on", uid_str) == 0)
|
if(strcmp("on", uid_str) == 0)
|
||||||
{
|
{
|
||||||
memset(result, 0, sizeof(uuid_t));
|
schedule_get_uid_on(result);
|
||||||
memcpy(result, "on", 2);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,23 +475,37 @@ schedule_uid_parse(const char *uid_str, uuid_t result)
|
||||||
void
|
void
|
||||||
schedule_uid_unparse(const uuid_t uid, char *result)
|
schedule_uid_unparse(const uuid_t uid, char *result)
|
||||||
{
|
{
|
||||||
uuid_t tmp_uuid;
|
uuid_t tmp_uid;
|
||||||
|
|
||||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
schedule_get_uid_off(tmp_uid);
|
||||||
memcpy(tmp_uuid, "off", 3);
|
if(uuid_compare(uid, tmp_uid) == 0)
|
||||||
if(uuid_compare(uid, tmp_uuid) == 0)
|
|
||||||
{
|
{
|
||||||
strcpy(result, "off");
|
strncpy(result, "off", 4);
|
||||||
|
result[3] = '\0';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
schedule_get_uid_on(tmp_uid);
|
||||||
memcpy(tmp_uuid, "on", 2);
|
if(uuid_compare(uid, tmp_uid) == 0)
|
||||||
if(uuid_compare(uid, tmp_uuid) == 0)
|
|
||||||
{
|
{
|
||||||
strcpy(result, "on");
|
strncpy(result, "on", 3);
|
||||||
|
result[2] = '\0';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid_unparse(uid, result);
|
uuid_unparse(uid, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
schedule_get_uid_off(uuid_t target)
|
||||||
|
{
|
||||||
|
uuid_clear(target);
|
||||||
|
strncpy((char*)target, "off", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
schedule_get_uid_on(uuid_t target)
|
||||||
|
{
|
||||||
|
uuid_clear(target);
|
||||||
|
strncpy((char*)target, "on", 3);
|
||||||
|
}
|
||||||
|
|
17
src/router.c
17
src/router.c
|
@ -10,6 +10,7 @@
|
||||||
#include <endpoints/api_v1_controllers.h>
|
#include <endpoints/api_v1_controllers.h>
|
||||||
#include <endpoints/api_v1_relays.h>
|
#include <endpoints/api_v1_relays.h>
|
||||||
#include <endpoints/api_v1_tags.h>
|
#include <endpoints/api_v1_tags.h>
|
||||||
|
#include <endpoints/api_v1_macros.h>
|
||||||
#include <endpoints/api_v1_ws.h>
|
#include <endpoints/api_v1_ws.h>
|
||||||
|
|
||||||
static endpoint_t endpoints[ROUTER_ENDPOINTS_MAX_COUNT];
|
static endpoint_t endpoints[ROUTER_ENDPOINTS_MAX_COUNT];
|
||||||
|
@ -71,7 +72,7 @@ router_init()
|
||||||
router_register_endpoint("/api/v1/schedules/{str}", HTTP_METHOD_DELETE, api_v1_schedules_STR_DELETE);
|
router_register_endpoint("/api/v1/schedules/{str}", HTTP_METHOD_DELETE, api_v1_schedules_STR_DELETE);
|
||||||
router_register_endpoint("/api/v1/schedules/tag/{str}", HTTP_METHOD_GET, api_v1_schedules_tag_STR_GET);
|
router_register_endpoint("/api/v1/schedules/tag/{str}", HTTP_METHOD_GET, api_v1_schedules_tag_STR_GET);
|
||||||
|
|
||||||
router_register_endpoint("/api/v1/controllers/discover/", HTTP_METHOD_POST, api_v1_controllers_discover_POST);
|
router_register_endpoint("/api/v1/controllers/discover/", HTTP_METHOD_PUT, api_v1_controllers_discover_PUT);
|
||||||
router_register_endpoint("/api/v1/controllers/", HTTP_METHOD_GET, api_v1_controllers_GET);
|
router_register_endpoint("/api/v1/controllers/", HTTP_METHOD_GET, api_v1_controllers_GET);
|
||||||
router_register_endpoint("/api/v1/controllers/{str}", HTTP_METHOD_GET, api_v1_controllers_STR_GET);
|
router_register_endpoint("/api/v1/controllers/{str}", HTTP_METHOD_GET, api_v1_controllers_STR_GET);
|
||||||
router_register_endpoint("/api/v1/controllers/{str}", HTTP_METHOD_PUT, api_v1_controllers_STR_PUT);
|
router_register_endpoint("/api/v1/controllers/{str}", HTTP_METHOD_PUT, api_v1_controllers_STR_PUT);
|
||||||
|
@ -90,6 +91,13 @@ router_init()
|
||||||
router_register_endpoint("/api/v1/tags/{str}", HTTP_METHOD_GET, api_v1_tags_STR_GET);
|
router_register_endpoint("/api/v1/tags/{str}", HTTP_METHOD_GET, api_v1_tags_STR_GET);
|
||||||
router_register_endpoint("/api/v1/tags/{str}", HTTP_METHOD_DELETE, api_v1_tags_STR_DELETE);
|
router_register_endpoint("/api/v1/tags/{str}", HTTP_METHOD_DELETE, api_v1_tags_STR_DELETE);
|
||||||
|
|
||||||
|
router_register_endpoint("/api/v1/macros/", HTTP_METHOD_GET, api_v1_macros_GET);
|
||||||
|
router_register_endpoint("/api/v1/macros/", HTTP_METHOD_POST, api_v1_macros_POST);
|
||||||
|
router_register_endpoint("/api/v1/macros/{str}", HTTP_METHOD_GET, api_v1_macros_STR_GET);
|
||||||
|
router_register_endpoint("/api/v1/macros/{str}", HTTP_METHOD_PUT, api_v1_macros_STR_PUT);
|
||||||
|
router_register_endpoint("/api/v1/macros/{str}", HTTP_METHOD_DELETE, api_v1_macros_STR_DELETE);
|
||||||
|
router_register_endpoint("/api/v1/macros/{str}/execute", HTTP_METHOD_PUT, api_v1_macros_STR_execute_PUT);
|
||||||
|
|
||||||
router_register_endpoint("/api/v1/ws/relays", HTTP_METHOD_WEBSOCKET, api_v1_ws_relays);
|
router_register_endpoint("/api/v1/ws/relays", HTTP_METHOD_WEBSOCKET, api_v1_ws_relays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +154,7 @@ router_register_endpoint(const char *route, int method, endpoint_func_f func)
|
||||||
// +1 for NULL terminator
|
// +1 for NULL terminator
|
||||||
endpoint->route = malloc(sizeof(char*) * (route_parts_count + 1));
|
endpoint->route = malloc(sizeof(char*) * (route_parts_count + 1));
|
||||||
endpoint->route_keeper = malloc(sizeof(char) * (route_len + 1));
|
endpoint->route_keeper = malloc(sizeof(char) * (route_len + 1));
|
||||||
strncpy(endpoint->route_keeper, route, route_len);
|
strncpy(endpoint->route_keeper, route, route_len + 1);
|
||||||
endpoint->route_keeper[route_len] = '\0';
|
endpoint->route_keeper[route_len] = '\0';
|
||||||
|
|
||||||
int route_part = 0;
|
int route_part = 0;
|
||||||
|
@ -305,7 +313,6 @@ router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method_
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
endpoints[i].possible_route = 0;
|
endpoints[i].possible_route = 0;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
uri_token = strtok_r(NULL, delimiter, &uri_token_save);
|
uri_token = strtok_r(NULL, delimiter, &uri_token_save);
|
||||||
++route_part;
|
++route_part;
|
||||||
|
@ -333,8 +340,10 @@ router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method_
|
||||||
{
|
{
|
||||||
if(best_endpoint->args[i].type == ENDPOINT_ARG_TYPE_STR)
|
if(best_endpoint->args[i].type == ENDPOINT_ARG_TYPE_STR)
|
||||||
{
|
{
|
||||||
char *arg_value_str = malloc(sizeof(char) * (strlen(best_endpoint->args[i].value.v_str) + 1));
|
size_t arg_value_str_len = strlen(best_endpoint->args[i].value.v_str);
|
||||||
|
char *arg_value_str = malloc(sizeof(char) * (arg_value_str_len + 1));
|
||||||
strcpy(arg_value_str, best_endpoint->args[i].value.v_str);
|
strcpy(arg_value_str, best_endpoint->args[i].value.v_str);
|
||||||
|
arg_value_str[arg_value_str_len] = '\0';
|
||||||
best_endpoint->args[i].value.v_str = arg_value_str;
|
best_endpoint->args[i].value.v_str = arg_value_str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,65 +0,0 @@
|
||||||
[controller]
|
|
||||||
name = new emgauwa device
|
|
||||||
|
|
||||||
: 4422 for testing; 4421 for dev-env; 4420 for testing-env; 4419 for prod-env
|
|
||||||
discovery-port = 4422
|
|
||||||
: 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
|
|
||||||
mqtt-port = 1886
|
|
||||||
mqtt-host = localhost
|
|
||||||
|
|
||||||
relay-count = 10
|
|
||||||
database = controller.sqlite
|
|
||||||
log-level = debug
|
|
||||||
log-file = stdout
|
|
||||||
|
|
||||||
[relay-0]
|
|
||||||
driver = piface
|
|
||||||
pin = 0
|
|
||||||
inverted = 0
|
|
||||||
|
|
||||||
[relay-1]
|
|
||||||
driver = piface
|
|
||||||
pin = 1
|
|
||||||
inverted = 0
|
|
||||||
|
|
||||||
[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
|
|
|
@ -1,16 +0,0 @@
|
||||||
[core]
|
|
||||||
server-port = 5000
|
|
||||||
database = core.sqlite
|
|
||||||
content-dir = /usr/share/webapps/emgauwa
|
|
||||||
not-found-file = 404.html
|
|
||||||
not-found-file-mime = text/html
|
|
||||||
not-found-content = 404 - NOT FOUND
|
|
||||||
not-found-content-type = text/plain
|
|
||||||
|
|
||||||
: 4422 for testing; 4421 for dev-env; 4420 for testing-env; 4419 for prod-env
|
|
||||||
discovery-port = 4422
|
|
||||||
: 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
|
|
||||||
mqtt-port = 1886
|
|
||||||
|
|
||||||
log-level = debug
|
|
||||||
log-file = stdout
|
|
66
tests/emgauwa-controller-testing.conf
Normal file
66
tests/emgauwa-controller-testing.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 = 4422
|
||||||
|
# 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
|
||||||
|
mqtt = 1886
|
||||||
|
|
||||||
|
[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
|
17
tests/emgauwa-core-testing.conf
Normal file
17
tests/emgauwa-core-testing.conf
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[core]
|
||||||
|
database = "emgauwa-core.sqlite"
|
||||||
|
include = "../emgauwa-core-testing.conf.d"
|
||||||
|
content-dir = "."
|
||||||
|
not-found-file = "404.html"
|
||||||
|
not-found-file-mime = "text/html"
|
||||||
|
|
||||||
|
[ports]
|
||||||
|
server = 5000
|
||||||
|
# 4422 for testing; 4421 for dev-env; 4420 for testing-env; 4419 for prod-env
|
||||||
|
discovery = 4422
|
||||||
|
# 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
|
||||||
|
mqtt = 1886
|
||||||
|
|
||||||
|
[logging]
|
||||||
|
level = "debug"
|
||||||
|
file = "stdout"
|
3
tests/emgauwa-core-testing.conf.d/conf_d_working.conf
Normal file
3
tests/emgauwa-core-testing.conf.d/conf_d_working.conf
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[core]
|
||||||
|
not-found-content = '{"msg": "conf.d working"}'
|
||||||
|
not-found-content-type = "application/json"
|
|
@ -1,14 +1,9 @@
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
source_dir=$PWD
|
source_dir=$PWD
|
||||||
working_dir=$PWD/testing_latest
|
working_dir=$PWD/testing
|
||||||
working_bak=$PWD/testing_bak
|
|
||||||
|
|
||||||
alias valgrind_emgauwa="valgrind $2 --log-file=$working_dir/valgrind.log"
|
alias valgrind_emgauwa="valgrind -s $2 --log-file=$working_dir/valgrind.log"
|
||||||
|
|
||||||
|
|
||||||
rm -rf $working_bak
|
|
||||||
[ -d $working_dir ] && mv $working_dir $working_bak
|
|
||||||
|
|
||||||
mkdir -p $working_dir
|
mkdir -p $working_dir
|
||||||
cd $working_dir
|
cd $working_dir
|
||||||
|
@ -17,15 +12,18 @@ target_branch=$(git rev-parse --abbrev-ref HEAD)
|
||||||
|
|
||||||
if [ -z "$EMGAUWA_CONTROLLER_EXE" ]
|
if [ -z "$EMGAUWA_CONTROLLER_EXE" ]
|
||||||
then
|
then
|
||||||
git clone --quiet ssh://git@git.serguzim.me:3022/emgauwa/controller.git controller || exit
|
mkdir -p controller
|
||||||
cd ./controller
|
cd controller
|
||||||
|
|
||||||
|
echo "Trying to pull or clone controller"
|
||||||
|
git clone --quiet ssh://git@git.serguzim.me:3022/emgauwa/controller.git . >/dev/null 2>&1 || git pull >/dev/null || exit
|
||||||
|
|
||||||
git checkout dev >/dev/null 2>&1
|
git checkout dev >/dev/null 2>&1
|
||||||
git checkout $target_branch >/dev/null 2>&1
|
git checkout $target_branch >/dev/null 2>&1
|
||||||
git checkout $2 >/dev/null 2>&1
|
git checkout $2 >/dev/null 2>&1
|
||||||
|
|
||||||
echo "Building controller on branch $(git rev-parse --abbrev-ref HEAD)"
|
echo "Building controller on branch $(git rev-parse --abbrev-ref HEAD)"
|
||||||
mkdir build
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
cmake -DWIRING_PI_DEBUG=on .. >/dev/null
|
cmake -DWIRING_PI_DEBUG=on .. >/dev/null
|
||||||
|
@ -35,20 +33,16 @@ fi
|
||||||
|
|
||||||
echo "Emgauwa controller: $($EMGAUWA_CONTROLLER_EXE --version)"
|
echo "Emgauwa controller: $($EMGAUWA_CONTROLLER_EXE --version)"
|
||||||
|
|
||||||
$EMGAUWA_CONTROLLER_EXE start -c $source_dir/controller.testing.ini >$working_dir/controller.log 2>&1 &
|
$EMGAUWA_CONTROLLER_EXE -c $source_dir/emgauwa-controller-testing.conf >$working_dir/controller.log 2>&1 &
|
||||||
controller_id=$!
|
controller_id=$!
|
||||||
|
|
||||||
cd $working_dir
|
cd $working_dir
|
||||||
|
|
||||||
|
touch $working_dir/index.html
|
||||||
|
|
||||||
cp $1 $working_dir/core
|
cp $1 $working_dir/core
|
||||||
cp $source_dir/core.testing.ini $working_dir/core.ini
|
|
||||||
|
|
||||||
echo "=== invalids start (must exit) ===" >$working_dir/core.log
|
valgrind_emgauwa $working_dir/core -c $source_dir/emgauwa-core-testing.conf >>$working_dir/core.log 2>&1 &
|
||||||
$working_dir/core >>$working_dir/core.log 2>&1
|
|
||||||
$working_dir/core INVALID_ACTION >>$working_dir/core.log 2>&1
|
|
||||||
|
|
||||||
echo "=== valid start ===" >>$working_dir/core.log
|
|
||||||
valgrind_emgauwa $working_dir/core start >>$working_dir/core.log 2>&1 &
|
|
||||||
core_id=$!
|
core_id=$!
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,4 +62,12 @@ test_result=$?
|
||||||
kill $core_id
|
kill $core_id
|
||||||
kill $controller_id
|
kill $controller_id
|
||||||
|
|
||||||
|
timestamp=$(date -Iseconds)
|
||||||
|
for backup_file in core.log controller.log valgrind.log emgauwa-core.sqlite; do
|
||||||
|
mv $backup_file $timestamp.$backup_file
|
||||||
|
ln -sf $timestamp.$backup_file latest.$backup_file
|
||||||
|
done
|
||||||
|
|
||||||
|
cat latest.valgrind.log
|
||||||
|
|
||||||
exit $test_result
|
exit $test_result
|
||||||
|
|
25
tests/tavern_tests/0.1.test_basics.tavern.yaml
Normal file
25
tests/tavern_tests/0.1.test_basics.tavern.yaml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
test_name: "[test_basics] Test basic calls"
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- name: "[test_basics] get index"
|
||||||
|
request:
|
||||||
|
url: "http://localhost:5000/"
|
||||||
|
method: GET
|
||||||
|
response:
|
||||||
|
status_code: 200
|
||||||
|
|
||||||
|
- name: "[test_basics] get 404"
|
||||||
|
request:
|
||||||
|
url: "http://localhost:5000/invalid_url_for_testing_do_not_use"
|
||||||
|
method: GET
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
||||||
|
- name: "[test_basics] verify conf.d by 404"
|
||||||
|
request:
|
||||||
|
url: "http://localhost:5000/invalid_url_for_testing_do_not_use"
|
||||||
|
method: GET
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
json:
|
||||||
|
msg: "conf.d working"
|
|
@ -3,7 +3,7 @@ test_name: Test basic controller functions
|
||||||
stages:
|
stages:
|
||||||
- name: "[controllers_basic] discover controllers"
|
- name: "[controllers_basic] discover controllers"
|
||||||
request:
|
request:
|
||||||
method: POST
|
method: PUT
|
||||||
url: "http://localhost:5000/api/v1/controllers/discover/"
|
url: "http://localhost:5000/api/v1/controllers/discover/"
|
||||||
response:
|
response:
|
||||||
status_code: 200
|
status_code: 200
|
||||||
|
@ -86,7 +86,7 @@ stages:
|
||||||
|
|
||||||
- name: "[controllers_basic] discover controllers again"
|
- name: "[controllers_basic] discover controllers again"
|
||||||
request:
|
request:
|
||||||
method: POST
|
method: PUT
|
||||||
url: "http://localhost:5000/api/v1/controllers/discover/"
|
url: "http://localhost:5000/api/v1/controllers/discover/"
|
||||||
response:
|
response:
|
||||||
status_code: 200
|
status_code: 200
|
||||||
|
|
|
@ -26,7 +26,7 @@ stages:
|
||||||
extra_kwargs:
|
extra_kwargs:
|
||||||
relay_count: !int "{returned_relay_count:d}"
|
relay_count: !int "{returned_relay_count:d}"
|
||||||
|
|
||||||
- name: "[controller_relays_basic] get controller relays, check length"
|
- name: "[controller_relays_basic] get controller relay"
|
||||||
request:
|
request:
|
||||||
method: GET
|
method: GET
|
||||||
url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays/5"
|
url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays/5"
|
||||||
|
@ -40,3 +40,69 @@ stages:
|
||||||
function: validate_relay:check_number
|
function: validate_relay:check_number
|
||||||
extra_kwargs:
|
extra_kwargs:
|
||||||
number: 5
|
number: 5
|
||||||
|
|
||||||
|
- name: "[controller_relays_basic] get controller relay with invalid uid"
|
||||||
|
request:
|
||||||
|
method: GET
|
||||||
|
url: "http://localhost:5000/api/v1/controllers/INVALID-UUID/relays/5"
|
||||||
|
response:
|
||||||
|
status_code: 400
|
||||||
|
|
||||||
|
- name: "[controller_relays_basic] get controller relay with unavailable uid"
|
||||||
|
request:
|
||||||
|
method: GET
|
||||||
|
url: "http://localhost:5000/api/v1/controllers/00000000-0000-0000-0000-000000000000/relays/5"
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
||||||
|
- name: "[controller_relays_basic] get controller relay with invalid number"
|
||||||
|
request:
|
||||||
|
method: GET
|
||||||
|
url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays/not_a_number"
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
||||||
|
- name: "[controller_relays_basic] get controller relay with unavailable number"
|
||||||
|
request:
|
||||||
|
method: GET
|
||||||
|
url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays/9001"
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- name: "[controller_relays_basic] pulse relay"
|
||||||
|
request:
|
||||||
|
method: POST
|
||||||
|
url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays/6/pulse"
|
||||||
|
response:
|
||||||
|
status_code: 200
|
||||||
|
|
||||||
|
- name: "[controller_relays_basic] pulse relay with invalid uid"
|
||||||
|
request:
|
||||||
|
method: POST
|
||||||
|
url: "http://localhost:5000/api/v1/controllers/INVALID-UUID/relays/6/pulse"
|
||||||
|
response:
|
||||||
|
status_code: 400
|
||||||
|
|
||||||
|
- name: "[controller_relays_basic] pulse relay with unavailable uid"
|
||||||
|
request:
|
||||||
|
method: POST
|
||||||
|
url: "http://localhost:5000/api/v1/controllers/00000000-0000-0000-0000-000000000000/relays/6/pulse"
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
||||||
|
- name: "[controller_relays_basic] pulse relay with invalid number"
|
||||||
|
request:
|
||||||
|
method: POST
|
||||||
|
url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays/not_a_number/pulse"
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
||||||
|
- name: "[controller_relays_basic] pulse relay with unavailable number"
|
||||||
|
request:
|
||||||
|
method: POST
|
||||||
|
url: "http://localhost:5000/api/v1/controllers/{returned_id}/relays/9001/pulse"
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
|
@ -98,6 +98,13 @@ stages:
|
||||||
controller_id: "{returned_id}"
|
controller_id: "{returned_id}"
|
||||||
tag: "{returned_tag}"
|
tag: "{returned_tag}"
|
||||||
|
|
||||||
|
- name: "[tags] get returned tag with relays and schedules"
|
||||||
|
request:
|
||||||
|
method: GET
|
||||||
|
url: "http://localhost:5000/api/v1/tags/{returned_tag}"
|
||||||
|
response:
|
||||||
|
status_code: 200
|
||||||
|
|
||||||
- name: "[tags] get tags"
|
- name: "[tags] get tags"
|
||||||
request:
|
request:
|
||||||
method: GET
|
method: GET
|
||||||
|
@ -106,3 +113,56 @@ stages:
|
||||||
status_code: 200
|
status_code: 200
|
||||||
verify_response_with:
|
verify_response_with:
|
||||||
function: validate_tag:multiple
|
function: validate_tag:multiple
|
||||||
|
|
||||||
|
- name: "[tags] get unavailable tag"
|
||||||
|
request:
|
||||||
|
method: GET
|
||||||
|
url: "http://localhost:5000/api/v1/tags/invalid_unavailable_tag"
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
||||||
|
- name: "[tags] post tag"
|
||||||
|
request:
|
||||||
|
method: POST
|
||||||
|
url: "http://localhost:5000/api/v1/tags/"
|
||||||
|
json:
|
||||||
|
tag: "unused_tag_1"
|
||||||
|
response:
|
||||||
|
status_code: 201
|
||||||
|
|
||||||
|
- name: "[tags] get posted tag"
|
||||||
|
request:
|
||||||
|
method: GET
|
||||||
|
url: "http://localhost:5000/api/v1/tags/unused_tag_1"
|
||||||
|
response:
|
||||||
|
status_code: 200
|
||||||
|
|
||||||
|
- name: "[tags] delete posted tag"
|
||||||
|
request:
|
||||||
|
method: DELETE
|
||||||
|
url: "http://localhost:5000/api/v1/tags/unused_tag_1"
|
||||||
|
response:
|
||||||
|
status_code: 200
|
||||||
|
|
||||||
|
- name: "[tags] get deleted tag"
|
||||||
|
request:
|
||||||
|
method: GET
|
||||||
|
url: "http://localhost:5000/api/v1/tags/unused_tag_1"
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
||||||
|
- name: "[tags] delete deleted tag again"
|
||||||
|
request:
|
||||||
|
method: DELETE
|
||||||
|
url: "http://localhost:5000/api/v1/tags/unused_tag_1"
|
||||||
|
response:
|
||||||
|
status_code: 404
|
||||||
|
|
||||||
|
- name: "[tags] get tags again"
|
||||||
|
request:
|
||||||
|
method: GET
|
||||||
|
url: "http://localhost:5000/api/v1/tags/"
|
||||||
|
response:
|
||||||
|
status_code: 200
|
||||||
|
verify_response_with:
|
||||||
|
function: validate_tag:multiple
|
||||||
|
|
|
@ -11,24 +11,9 @@ def multiple(response):
|
||||||
for tag in response.json():
|
for tag in response.json():
|
||||||
_verify_single(tag)
|
_verify_single(tag)
|
||||||
|
|
||||||
#def find(response, name=None, number=None, controller_id=None, tag=None):
|
def find(response, tag):
|
||||||
# print(response.json())
|
print(response.json())
|
||||||
# for tag in response.json():
|
for response_tag in response.json():
|
||||||
# if number != None and number != tag.get("number"):
|
if response_tag == tag:
|
||||||
# continue
|
return
|
||||||
#
|
assert False, "tag not found in list"
|
||||||
# if name != None and name != tag.get("name"):
|
|
||||||
# continue
|
|
||||||
#
|
|
||||||
# if controller_id != None and controller_id != tag.get("controller_id"):
|
|
||||||
# continue
|
|
||||||
#
|
|
||||||
# if tag != None:
|
|
||||||
# found_in_response = False
|
|
||||||
# for response_tag in tag.get("tags"):
|
|
||||||
# if response_tag == tag:
|
|
||||||
# found_in_response = True
|
|
||||||
# if not found_in_response:
|
|
||||||
# continue
|
|
||||||
# return
|
|
||||||
# assert False, "tag not found in list"
|
|
||||||
|
|
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.h
vendored
2
vendor/mongoose.h
vendored
|
@ -1867,7 +1867,7 @@ typedef struct {
|
||||||
|
|
||||||
void cs_md5_init(cs_md5_ctx *c);
|
void cs_md5_init(cs_md5_ctx *c);
|
||||||
void cs_md5_update(cs_md5_ctx *c, const unsigned char *data, size_t len);
|
void cs_md5_update(cs_md5_ctx *c, const unsigned char *data, size_t len);
|
||||||
void cs_md5_final(unsigned char *md, cs_md5_ctx *c);
|
void cs_md5_final(unsigned char digest[16], cs_md5_ctx *c);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
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