Add config files and service file
This commit is contained in:
parent
338d9360a6
commit
7ad8ea25a6
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
bin/
|
bin/
|
||||||
*.core
|
*.core
|
||||||
rpi4b-temp-humidity
|
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
.cache/
|
.cache/
|
||||||
|
config.conf
|
||||||
|
44
Makefile
44
Makefile
@ -11,10 +11,14 @@ SRCS=src/main.c src/util.c src/lcd.c src/ths.c src/button.c src/ui/screen.c\
|
|||||||
src/ui/exportscreen.c
|
src/ui/exportscreen.c
|
||||||
PROG=rpi4b-temp-humidity
|
PROG=rpi4b-temp-humidity
|
||||||
|
|
||||||
|
all: config.conf bin/${PROG}
|
||||||
|
|
||||||
OBJS=${SRCS:C/^src/bin/:C/.c$/.o/}
|
OBJS=${SRCS:C/^src/bin/:C/.c$/.o/}
|
||||||
bin/${PROG}: ${OBJS}
|
bin/${PROG}: ${OBJS}
|
||||||
${LD} ${LDFLAGS} -o ${@} ${OBJS}
|
${LD} ${LDFLAGS} -o ${@} ${OBJS}
|
||||||
|
|
||||||
|
bin/main.o bin/config.o: config.mk
|
||||||
|
|
||||||
bin/main.o bin/util.o bin/lcd.o bin/ths.o bin/button.o: src/util.h
|
bin/main.o bin/util.o bin/lcd.o bin/ths.o bin/button.o: src/util.h
|
||||||
bin/ui/screen.o bin/ui/statsby.o bin/ui/datesel.o: src/util.h
|
bin/ui/screen.o bin/ui/statsby.o bin/ui/datesel.o: src/util.h
|
||||||
bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/util.h
|
bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/util.h
|
||||||
@ -30,7 +34,6 @@ bin/ui/statrange.o: src/ui/screen.h
|
|||||||
bin/main.o bin/ui/datesel.o bin/ui/statsby.o: src/ui/datesel.h
|
bin/main.o bin/ui/datesel.o bin/ui/statsby.o: src/ui/datesel.h
|
||||||
bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/ui/datesel.h
|
bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/ui/datesel.h
|
||||||
|
|
||||||
|
|
||||||
bin/main.o bin/ui/timesel.o bin/ui/datapoints.o: src/ui/timesel.h
|
bin/main.o bin/ui/timesel.o bin/ui/datapoints.o: src/ui/timesel.h
|
||||||
bin/ui/statrange.o: src/ui/timesel.h
|
bin/ui/statrange.o: src/ui/timesel.h
|
||||||
|
|
||||||
@ -49,23 +52,34 @@ bin/main.o bin/drive.o bin/ui/exportscreen.o: src/drive.h
|
|||||||
.error Invalid temperature unit "${DEFAULT_TEMP_UNIT}"
|
.error Invalid temperature unit "${DEFAULT_TEMP_UNIT}"
|
||||||
.endif
|
.endif
|
||||||
|
|
||||||
|
DEFINES:=\
|
||||||
|
-DDEFAULT_CONFIG_PATH="${DEFAULT_CONFIG_PATH}"\
|
||||||
|
-DDEFAULT_GPIO_DEVICE="${DEFAULT_GPIO_DEVICE}"\
|
||||||
|
-DDEFAULT_TEMP_KEY="${DEFAULT_TEMP_KEY}"\
|
||||||
|
-DDEFAULT_HUMID_KEY="${DEFAULT_HUMID_KEY}"\
|
||||||
|
-DDEFAULT_FAIL_KEY="${DEFAULT_FAIL_KEY}"\
|
||||||
|
-DDEFAULT_FAIL_LIMIT="${DEFAULT_FAIL_LIMIT}"\
|
||||||
|
-DDEFAULT_DATABASE_LOCATION="${DEFAULT_DATABASE_LOCATION}"\
|
||||||
|
-DDEFAULT_REFRESH_TIME="${DEFAULT_REFRESH_TIME}"\
|
||||||
|
-DDEFAULT_TEMP_UNIT="${DEFAULT_TEMP_UNIT}"\
|
||||||
|
-DDEFAULT_EXPORT_FILE_NAME="${DEFAULT_EXPORT_FILE_NAME}"\
|
||||||
|
|
||||||
${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
||||||
@mkdir -p ${.TARGET:H}
|
@mkdir -p ${.TARGET:H}
|
||||||
${CC} ${CFLAGS} -c\
|
${CC} ${CFLAGS} -c ${DEFINES} -o ${@} ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
||||||
-DDEFAULT_CONFIG_PATH="\"${DEFAULT_CONFIG_PATH}\""\
|
|
||||||
-DDEFAULT_GPIO_DEVICE="\"${DEFAULT_GPIO_DEVICE}\""\
|
config.conf: config.conf.m4
|
||||||
-DDEFAULT_TEMP_KEY="\"${DEFAULT_TEMP_KEY}\""\
|
m4 ${DEFINES} config.conf.m4 >config.conf
|
||||||
-DDEFAULT_HUMID_KEY="\"${DEFAULT_HUMID_KEY}\""\
|
|
||||||
-DDEFAULT_FAIL_KEY="\"${DEFAULT_FAIL_KEY}\""\
|
install: all
|
||||||
-DDEFAULT_FAIL_LIMIT="${DEFAULT_FAIL_LIMIT}"\
|
mkdir -p "${PREFIX}/bin"
|
||||||
-DDEFAULT_DATABASE_LOCATION="\"${DEFAULT_DATABASE_LOCATION}\""\
|
install -o root -g wheel "bin/${PROG}" "${PREFIX}/bin"
|
||||||
-DDEFAULT_REFRESH_TIME="${DEFAULT_REFRESH_TIME}"\
|
mkdir -p "${PREFIX}/etc/rpi4b-temp-humidity"
|
||||||
-DDEFAULT_TEMP_UNIT="'${DEFAULT_TEMP_UNIT}'"\
|
install -b -o root -g wheel -m 0644 config.conf "${PREFIX}/etc/rpi4b-temp-humidity"
|
||||||
-DDEFAULT_EXPORT_FILE_NAME="\"${DEFAULT_EXPORT_FILE_NAME}\""\
|
install -o root -g wheel -m 0555 rpi4b-temp-humidity "${PREFIX}/etc/rc.d"
|
||||||
-o ${@} ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf bin/
|
rm -rf config.conf bin/
|
||||||
|
|
||||||
.SUFFIXES: .c .o
|
.SUFFIXES: .c .o
|
||||||
.PHONY: clean
|
.PHONY: all install clean
|
||||||
|
49
config.conf.m4
Normal file
49
config.conf.m4
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
dnl The following line enables macro expansion in # comments
|
||||||
|
changecom(`//')
|
||||||
|
# These options do not have default value and MUST be set before running
|
||||||
|
|
||||||
|
# LCD pins (the ones below match up with the diagram in README.md)
|
||||||
|
# Register select
|
||||||
|
rs_pin = 4
|
||||||
|
# Read-write
|
||||||
|
rw_pin = 17
|
||||||
|
# Enable
|
||||||
|
en_pin = 27
|
||||||
|
# Optional backlight pin, set to -1 to disable
|
||||||
|
bl_pin = 26
|
||||||
|
# d0-d7
|
||||||
|
data_pins = 22 10 9 11 5 6 13 19
|
||||||
|
|
||||||
|
# Button pins
|
||||||
|
sel_pin = 14
|
||||||
|
back_pin = 15
|
||||||
|
up_pin = 18
|
||||||
|
down_pin = 23
|
||||||
|
|
||||||
|
# Options below this comment have compile-time defaults (which are show below)
|
||||||
|
|
||||||
|
# GPIO device file
|
||||||
|
#gpio_file = DEFAULT_GPIO_DEVICE
|
||||||
|
|
||||||
|
# sysctl(8) keys for the temperature sensor
|
||||||
|
#temp_key = DEFAULT_TEMP_KEY
|
||||||
|
#humid_key = DEFAULT_HUMID_KEY
|
||||||
|
# Set to empty to disable
|
||||||
|
#fail_key = DEFAULT_FAIL_KEY
|
||||||
|
|
||||||
|
# Number of temperature sensor fails before exit
|
||||||
|
#fail_limit = DEFAULT_FAIL_LIMIT
|
||||||
|
|
||||||
|
# Database location
|
||||||
|
#database_location = DEFAULT_DATABASE_LOCATION
|
||||||
|
|
||||||
|
# Time between data points, note that this is probably limited by the kernel's
|
||||||
|
# gpioths driver (5 seconds)
|
||||||
|
#refresh_time = DEFAULT_REFRESH_TIME
|
||||||
|
|
||||||
|
# Temperature unit F for Fahrenheit, C for Celsius
|
||||||
|
#temp_unit = DEFAULT_TEMP_UNIT
|
||||||
|
|
||||||
|
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
||||||
|
# .csv will be appended for CSV exports
|
||||||
|
#export_file_name = DEFAULT_EXPORT_FILE_NAME
|
25
config.mk
25
config.mk
@ -2,14 +2,35 @@
|
|||||||
SQLITE3_LDFLAGS=-L/usr/local/lib -lsqlite3
|
SQLITE3_LDFLAGS=-L/usr/local/lib -lsqlite3
|
||||||
SQLITE3_CFLAGS=-I/usr/local/include
|
SQLITE3_CFLAGS=-I/usr/local/include
|
||||||
|
|
||||||
|
# Install prefix
|
||||||
|
PREFIX=/usr/local
|
||||||
|
|
||||||
# Default option values, empty means NULL (for strings)
|
# Default option values, empty means NULL (for strings)
|
||||||
DEFAULT_CONFIG_PATH=config.conf
|
# Config file path
|
||||||
|
DEFAULT_CONFIG_PATH=/usr/local/etc/rpi4b-temp-humidity/config.conf
|
||||||
|
|
||||||
|
# GPIO device
|
||||||
DEFAULT_GPIO_DEVICE=/dev/gpioc0
|
DEFAULT_GPIO_DEVICE=/dev/gpioc0
|
||||||
|
|
||||||
|
# sysctl(8) keys for the temperature sensor
|
||||||
DEFAULT_TEMP_KEY=dev.gpioths.0.temperature
|
DEFAULT_TEMP_KEY=dev.gpioths.0.temperature
|
||||||
DEFAULT_HUMID_KEY=dev.gpioths.0.humidity
|
DEFAULT_HUMID_KEY=dev.gpioths.0.humidity
|
||||||
|
# Set this to empty to disable checking failures
|
||||||
DEFAULT_FAIL_KEY=dev.gpioths.0.fails
|
DEFAULT_FAIL_KEY=dev.gpioths.0.fails
|
||||||
|
|
||||||
|
# Max failures of the temperature sensor before exit
|
||||||
DEFAULT_FAIL_LIMIT=5
|
DEFAULT_FAIL_LIMIT=5
|
||||||
DEFAULT_DATABASE_LOCATION=/var/db/rpi4-temp-humidity/db.sqlite
|
|
||||||
|
# Database location
|
||||||
|
DEFAULT_DATABASE_LOCATION=/var/db/rpi4b-temp-humidity/db.sqlite
|
||||||
|
|
||||||
|
# Time between data points, note that this is probably limited by the kernel's
|
||||||
|
# gpioths driver (5 seconds)
|
||||||
DEFAULT_REFRESH_TIME=5000
|
DEFAULT_REFRESH_TIME=5000
|
||||||
|
|
||||||
|
# F for Fahrenheit, C for Celsius
|
||||||
DEFAULT_TEMP_UNIT=F
|
DEFAULT_TEMP_UNIT=F
|
||||||
|
|
||||||
|
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
||||||
|
# .csv will be appended for CSV exports
|
||||||
DEFAULT_EXPORT_FILE_NAME=env_data
|
DEFAULT_EXPORT_FILE_NAME=env_data
|
||||||
|
44
rpi4b-temp-humidity
Normal file
44
rpi4b-temp-humidity
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. /etc/rc.subr
|
||||||
|
|
||||||
|
name=rpi4b_temp_humidity
|
||||||
|
rcvar=rpi4b_temp_humidity_enable
|
||||||
|
|
||||||
|
command="/usr/sbin/daemon"
|
||||||
|
procname="/usr/local/bin/rpi4b-temp-humidity"
|
||||||
|
|
||||||
|
start_precmd="${name}_prestart"
|
||||||
|
|
||||||
|
load_rc_config ${name}
|
||||||
|
: ${rpi4b_temp_humidity_enable:=NO}
|
||||||
|
: ${rpi4b_temp_humidity_config_file:=""}
|
||||||
|
: ${rpi4b_temp_humidity_strict_config:=NO}
|
||||||
|
: ${rpi4b_temp_humidity_verbose:=YES}
|
||||||
|
: ${rpi4b_temp_humidity_log_file:="/var/log/rpi4b-temp-humidity.log"}
|
||||||
|
|
||||||
|
rpi4b_temp_humidity_prestart()
|
||||||
|
{
|
||||||
|
if checkyesno rpi4b_temp_humidity_strict_config; then
|
||||||
|
rc_flags="-s ${rc_flags}"
|
||||||
|
fi
|
||||||
|
if ! [ -z "${rpi4b_temp_humidity_config_file}" ]; then
|
||||||
|
rc_flags="-f \"${rpi4b_temp_humidity_config_file}\" ${rc_flags}"
|
||||||
|
fi
|
||||||
|
if checkyesno rpi4b_temp_humidity_verbose; then
|
||||||
|
rc_flags="-v ${rc_flags}"
|
||||||
|
fi
|
||||||
|
if ! [ -z "${rpi4b_temp_humidity_log_file}" ]; then
|
||||||
|
# simple log rotation
|
||||||
|
mv "${rpi4b_temp_humidity_log_file}" "${rpi4b_temp_humidity_log_file}.old"
|
||||||
|
rpi4b_temp_humidity_daemon_flags="-o \"${rpi4b_temp_humidity_log_file}\""
|
||||||
|
if ! mkdir -p "$(dirname "${rpi4b_temp_humidity_log_file}")"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
rpi4b_temp_humidity_daemon_flags="-f"
|
||||||
|
fi
|
||||||
|
rc_flags="${rpi4b_temp_humidity_daemon_flags} ${procname} ${rc_flags}"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_rc_command "$1"
|
14
src/config.c
14
src/config.c
@ -221,7 +221,7 @@ void parse_config_file(const char *path) {
|
|||||||
.directive = "gpio_file",
|
.directive = "gpio_file",
|
||||||
.type = FIGPAR_TYPE_STR,
|
.type = FIGPAR_TYPE_STR,
|
||||||
.action = parse_str_callback,
|
.action = parse_str_callback,
|
||||||
.value = {.str = strdup_default_opt(DEFAULT_GPIO_DEVICE)},
|
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_GPIO_DEVICE))},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.directive = "rs_pin",
|
.directive = "rs_pin",
|
||||||
@ -247,19 +247,19 @@ void parse_config_file(const char *path) {
|
|||||||
.directive = "temp_key",
|
.directive = "temp_key",
|
||||||
.type = FIGPAR_TYPE_STR,
|
.type = FIGPAR_TYPE_STR,
|
||||||
.action = parse_str_callback,
|
.action = parse_str_callback,
|
||||||
.value = {.str = strdup_default_opt(DEFAULT_TEMP_KEY)},
|
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_TEMP_KEY))},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.directive = "humid_key",
|
.directive = "humid_key",
|
||||||
.type = FIGPAR_TYPE_STR,
|
.type = FIGPAR_TYPE_STR,
|
||||||
.action = parse_str_callback,
|
.action = parse_str_callback,
|
||||||
.value = {.str = strdup_default_opt(DEFAULT_HUMID_KEY)},
|
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_HUMID_KEY))},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.directive = "fail_key",
|
.directive = "fail_key",
|
||||||
.type = FIGPAR_TYPE_STR,
|
.type = FIGPAR_TYPE_STR,
|
||||||
.action = parse_str_callback,
|
.action = parse_str_callback,
|
||||||
.value = {.str = strdup_default_opt(DEFAULT_FAIL_KEY)},
|
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_FAIL_KEY))},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.directive = "fail_limit",
|
.directive = "fail_limit",
|
||||||
@ -271,7 +271,7 @@ void parse_config_file(const char *path) {
|
|||||||
.directive = "database_location",
|
.directive = "database_location",
|
||||||
.type = FIGPAR_TYPE_STR,
|
.type = FIGPAR_TYPE_STR,
|
||||||
.action = parse_str_callback,
|
.action = parse_str_callback,
|
||||||
.value = {.str = strdup_default_opt(DEFAULT_DATABASE_LOCATION)},
|
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_DATABASE_LOCATION))},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.directive = "refresh_time",
|
.directive = "refresh_time",
|
||||||
@ -303,7 +303,7 @@ void parse_config_file(const char *path) {
|
|||||||
.directive = "temp_unit",
|
.directive = "temp_unit",
|
||||||
.type = FIGPAR_TYPE_INT,
|
.type = FIGPAR_TYPE_INT,
|
||||||
.action = parse_temp_unit_callback,
|
.action = parse_temp_unit_callback,
|
||||||
.value = {.num = DEFAULT_TEMP_UNIT},
|
.value = {.num = STRINGIFY(DEFAULT_TEMP_UNIT)[0]},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.directive = "bl_pin",
|
.directive = "bl_pin",
|
||||||
@ -315,7 +315,7 @@ void parse_config_file(const char *path) {
|
|||||||
.directive = "export_file_name",
|
.directive = "export_file_name",
|
||||||
.type = FIGPAR_TYPE_STR,
|
.type = FIGPAR_TYPE_STR,
|
||||||
.action = parse_str_callback,
|
.action = parse_str_callback,
|
||||||
.value = {.str = strdup_default_opt(DEFAULT_EXPORT_FILE_NAME)},
|
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_EXPORT_FILE_NAME))},
|
||||||
},
|
},
|
||||||
{ .directive = NULL },
|
{ .directive = NULL },
|
||||||
};
|
};
|
||||||
|
53
src/main.c
53
src/main.c
@ -35,6 +35,7 @@
|
|||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Print help message to standard output.
|
* Print help message to standard output.
|
||||||
@ -49,9 +50,13 @@ void parse_arguments(int argc, char *const *argv);
|
|||||||
*/
|
*/
|
||||||
void setup_signals(void);
|
void setup_signals(void);
|
||||||
/*
|
/*
|
||||||
* Open an sqlite3 database connection.
|
* Open the sqlite3 database connection.
|
||||||
*/
|
*/
|
||||||
void open_database(void);
|
void open_database(void);
|
||||||
|
/*
|
||||||
|
* Create the env_data table in DATABASE
|
||||||
|
*/
|
||||||
|
void create_db_table(void);
|
||||||
/*
|
/*
|
||||||
* Read the temp. and humid., store it in the configured database, and output it
|
* Read the temp. and humid., store it in the configured database, and output it
|
||||||
* to the given uint32_t pointers.
|
* to the given uint32_t pointers.
|
||||||
@ -93,6 +98,7 @@ int main(int argc, char *const *argv) {
|
|||||||
GLOBAL_OPTS.data_pins[7], GLOBAL_OPTS.bl_pin);
|
GLOBAL_OPTS.data_pins[7], GLOBAL_OPTS.bl_pin);
|
||||||
setup_signals();
|
setup_signals();
|
||||||
open_database();
|
open_database();
|
||||||
|
create_db_table();
|
||||||
initialize_util_queries(DATABASE);
|
initialize_util_queries(DATABASE);
|
||||||
pthread_t bg_update;
|
pthread_t bg_update;
|
||||||
start_update_thread(&bg_update);
|
start_update_thread(&bg_update);
|
||||||
@ -167,7 +173,7 @@ void parse_arguments(int argc, char *const *argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!GLOBAL_OPTS.config_path) {
|
if (!GLOBAL_OPTS.config_path) {
|
||||||
GLOBAL_OPTS.config_path = strdup_checked(DEFAULT_CONFIG_PATH);
|
GLOBAL_OPTS.config_path = strdup_checked(STRINGIFY(DEFAULT_CONFIG_PATH));
|
||||||
LOG_VERBOSE("Config file path set: \"%s\"\n", GLOBAL_OPTS.config_path);
|
LOG_VERBOSE("Config file path set: \"%s\"\n", GLOBAL_OPTS.config_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,18 +196,43 @@ void setup_signals() {
|
|||||||
SIGNAL_SETUP_CHECKED(SIGUSR1, SIG_IGN); // used to kill export operations
|
SIGNAL_SETUP_CHECKED(SIGUSR1, SIG_IGN); // used to kill export operations
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *CREATE_DB_TABLE_QUERY =
|
||||||
|
"CREATE TABLE IF NOT EXISTS env_data("
|
||||||
|
"time INTEGER PRIMARY KEY,"
|
||||||
|
"temp INTEGER,"
|
||||||
|
"humid INTEGER"
|
||||||
|
");";
|
||||||
|
void create_db_table() {
|
||||||
|
char *errmsg;
|
||||||
|
int status = sqlite3_exec(DATABASE, CREATE_DB_TABLE_QUERY, NULL, NULL,
|
||||||
|
&errmsg);
|
||||||
|
if (status != SQLITE_OK) {
|
||||||
|
errx(1, "could not create table. sqlite3 error follows:\n%s",
|
||||||
|
errmsg ? errmsg : "No message generated");
|
||||||
|
}
|
||||||
|
LOG_VERBOSE("Ensured env_data table existance\n");
|
||||||
|
}
|
||||||
|
|
||||||
void open_database() {
|
void open_database() {
|
||||||
int status = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
|
int status = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
|
||||||
if (status != SQLITE_OK) {
|
if (status != SQLITE_OK) {
|
||||||
errx(1, "failed to enable multi-thread mode for sqlite: %s",
|
errx(1, "failed to enable multi-thread mode for sqlite: %s",
|
||||||
sqlite3_errstr(status));
|
sqlite3_errstr(status));
|
||||||
}
|
}
|
||||||
|
char *to_free = strdup_checked(GLOBAL_OPTS.database_location);
|
||||||
|
char *db_dir = dirname(to_free);
|
||||||
|
if (!mkdirs(db_dir, 0755)) {
|
||||||
|
errx(1, "failed to create database directory: %s", db_dir);
|
||||||
|
}
|
||||||
|
free(to_free);
|
||||||
status = sqlite3_open(GLOBAL_OPTS.database_location, &DATABASE);
|
status = sqlite3_open(GLOBAL_OPTS.database_location, &DATABASE);
|
||||||
if (status != SQLITE_OK) {
|
if (status != SQLITE_OK) {
|
||||||
sqlite3_close(DATABASE);
|
sqlite3_close(DATABASE);
|
||||||
errx(1, "failed to open database: %s",
|
errx(1, "failed to open database: %s",
|
||||||
sqlite3_errstr(status));
|
sqlite3_errstr(status));
|
||||||
}
|
}
|
||||||
|
LOG_VERBOSE("Successfully opened database at \"%s\"\n",
|
||||||
|
GLOBAL_OPTS.database_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_stats(THS *ths, sqlite3_stmt *insert_statement) {
|
void update_stats(THS *ths, sqlite3_stmt *insert_statement) {
|
||||||
@ -229,28 +260,10 @@ void update_stats(THS *ths, sqlite3_stmt *insert_statement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *CREATE_DB_TABLE_QUERY =
|
|
||||||
"CREATE TABLE IF NOT EXISTS env_data("
|
|
||||||
"time INTEGER PRIMARY KEY,"
|
|
||||||
"temp INTEGER,"
|
|
||||||
"humid INTEGER"
|
|
||||||
");";
|
|
||||||
static void create_db_table() {
|
|
||||||
char *errmsg;
|
|
||||||
int status = sqlite3_exec(DATABASE, CREATE_DB_TABLE_QUERY, NULL, NULL,
|
|
||||||
&errmsg);
|
|
||||||
if (status != SQLITE_OK) {
|
|
||||||
errx(1, "could not create table. sqlite3 error follows:\n%s",
|
|
||||||
errmsg ? errmsg : "No message generated");
|
|
||||||
}
|
|
||||||
LOG_VERBOSE("Ensured env_data table existance\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *UPDATE_STATS_QUERY =
|
static const char *UPDATE_STATS_QUERY =
|
||||||
"INSERT INTO env_data (time, temp, humid) "
|
"INSERT INTO env_data (time, temp, humid) "
|
||||||
"VALUES(?, ?, ?);";
|
"VALUES(?, ?, ?);";
|
||||||
static void *update_thread_action(void *_ignored) {
|
static void *update_thread_action(void *_ignored) {
|
||||||
create_db_table();
|
|
||||||
sqlite3_stmt *insert_statement = NULL;
|
sqlite3_stmt *insert_statement = NULL;
|
||||||
int status = sqlite3_prepare_v2(DATABASE, UPDATE_STATS_QUERY, -1,
|
int status = sqlite3_prepare_v2(DATABASE, UPDATE_STATS_QUERY, -1,
|
||||||
&insert_statement, NULL);
|
&insert_statement, NULL);
|
||||||
|
39
src/util.c
39
src/util.c
@ -18,6 +18,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
const char *PERIOD_LABELS[] = {
|
const char *PERIOD_LABELS[] = {
|
||||||
"HOUR",
|
"HOUR",
|
||||||
@ -164,7 +165,7 @@ static const char *AVG_FOR_PERIOD_QUERY_STR =
|
|||||||
"min(humid) AS minhumid\n"
|
"min(humid) AS minhumid\n"
|
||||||
"FROM env_data\n"
|
"FROM env_data\n"
|
||||||
"WHERE time >= stime\n"
|
"WHERE time >= stime\n"
|
||||||
"AND time <= etime);\n";
|
"AND time <= etime);";
|
||||||
static sqlite3_stmt *AVG_FOR_PERIOD_QUERY;
|
static sqlite3_stmt *AVG_FOR_PERIOD_QUERY;
|
||||||
static const char *DATA_POINT_QUERY_STR =
|
static const char *DATA_POINT_QUERY_STR =
|
||||||
"SELECT max(time) IS NULL, max(time), temp, humid FROM env_data WHERE time < ?1\n"
|
"SELECT max(time) IS NULL, max(time), temp, humid FROM env_data WHERE time < ?1\n"
|
||||||
@ -528,3 +529,39 @@ bool export_database_as_csv(sqlite3 *db, const char *dest) {
|
|||||||
pthread_cleanup_pop(true); // close file
|
pthread_cleanup_pop(true); // close file
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ensure_dir_exists(const char *path, mode_t mode) {
|
||||||
|
struct stat statbuf;
|
||||||
|
errno = 0;
|
||||||
|
if (mkdir(path, mode) < 0) {
|
||||||
|
if (errno != EEXIST) {
|
||||||
|
warn("mkdir failed: %s", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (stat(path, &statbuf) < 0) {
|
||||||
|
warn("mkdir and stat failed: %s", path);
|
||||||
|
return false;
|
||||||
|
} else if (!S_ISDIR(statbuf.st_mode)) {
|
||||||
|
warn("not a directory: %s", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mkdirs(const char *path, mode_t mode) {
|
||||||
|
LOG_VERBOSE("Creating directory: \"%s\"\n", path)
|
||||||
|
char *copy = strdup_checked(path);
|
||||||
|
char *work_str = copy, *token;
|
||||||
|
while ((token = strsep(&work_str, "/"))) {
|
||||||
|
if (token != copy) {
|
||||||
|
token[-1] = '/';
|
||||||
|
}
|
||||||
|
if (*copy && *token && !ensure_dir_exists(copy, mode)) {
|
||||||
|
free(copy);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(copy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
#include <libgpio.h>
|
#include <libgpio.h>
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
#define _STRINGIFY_LIT(s) (#s)
|
||||||
|
#define STRINGIFY(v) _STRINGIFY_LIT(v)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
TEMP_UNIT_F = 'F',
|
TEMP_UNIT_F = 'F',
|
||||||
TEMP_UNIT_C = 'C'
|
TEMP_UNIT_C = 'C'
|
||||||
@ -239,4 +242,10 @@ bool export_database(sqlite3 *db, const char *dest);
|
|||||||
*/
|
*/
|
||||||
bool export_database_as_csv(sqlite3 *db, const char *dest);
|
bool export_database_as_csv(sqlite3 *db, const char *dest);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create all the missing directories along path.
|
||||||
|
* Return: true on success, false otherwise
|
||||||
|
*/
|
||||||
|
bool mkdirs(const char *path, mode_t mode);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user