Add config files and service file
This commit is contained in:
14
src/config.c
14
src/config.c
@ -221,7 +221,7 @@ void parse_config_file(const char *path) {
|
||||
.directive = "gpio_file",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_GPIO_DEVICE)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_GPIO_DEVICE))},
|
||||
},
|
||||
{
|
||||
.directive = "rs_pin",
|
||||
@ -247,19 +247,19 @@ void parse_config_file(const char *path) {
|
||||
.directive = "temp_key",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_TEMP_KEY)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_TEMP_KEY))},
|
||||
},
|
||||
{
|
||||
.directive = "humid_key",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_HUMID_KEY)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_HUMID_KEY))},
|
||||
},
|
||||
{
|
||||
.directive = "fail_key",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_FAIL_KEY)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_FAIL_KEY))},
|
||||
},
|
||||
{
|
||||
.directive = "fail_limit",
|
||||
@ -271,7 +271,7 @@ void parse_config_file(const char *path) {
|
||||
.directive = "database_location",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_DATABASE_LOCATION)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_DATABASE_LOCATION))},
|
||||
},
|
||||
{
|
||||
.directive = "refresh_time",
|
||||
@ -303,7 +303,7 @@ void parse_config_file(const char *path) {
|
||||
.directive = "temp_unit",
|
||||
.type = FIGPAR_TYPE_INT,
|
||||
.action = parse_temp_unit_callback,
|
||||
.value = {.num = DEFAULT_TEMP_UNIT},
|
||||
.value = {.num = STRINGIFY(DEFAULT_TEMP_UNIT)[0]},
|
||||
},
|
||||
{
|
||||
.directive = "bl_pin",
|
||||
@ -315,7 +315,7 @@ void parse_config_file(const char *path) {
|
||||
.directive = "export_file_name",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.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 },
|
||||
};
|
||||
|
53
src/main.c
53
src/main.c
@ -35,6 +35,7 @@
|
||||
#include <stdatomic.h>
|
||||
#include <signal.h>
|
||||
#include <sqlite3.h>
|
||||
#include <libgen.h>
|
||||
|
||||
/*
|
||||
* Print help message to standard output.
|
||||
@ -49,9 +50,13 @@ void parse_arguments(int argc, char *const *argv);
|
||||
*/
|
||||
void setup_signals(void);
|
||||
/*
|
||||
* Open an sqlite3 database connection.
|
||||
* Open the sqlite3 database connection.
|
||||
*/
|
||||
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
|
||||
* 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);
|
||||
setup_signals();
|
||||
open_database();
|
||||
create_db_table();
|
||||
initialize_util_queries(DATABASE);
|
||||
pthread_t bg_update;
|
||||
start_update_thread(&bg_update);
|
||||
@ -167,7 +173,7 @@ void parse_arguments(int argc, char *const *argv) {
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -190,18 +196,43 @@ void setup_signals() {
|
||||
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() {
|
||||
int status = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
|
||||
if (status != SQLITE_OK) {
|
||||
errx(1, "failed to enable multi-thread mode for sqlite: %s",
|
||||
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);
|
||||
if (status != SQLITE_OK) {
|
||||
sqlite3_close(DATABASE);
|
||||
errx(1, "failed to open database: %s",
|
||||
sqlite3_errstr(status));
|
||||
}
|
||||
LOG_VERBOSE("Successfully opened database at \"%s\"\n",
|
||||
GLOBAL_OPTS.database_location);
|
||||
}
|
||||
|
||||
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 =
|
||||
"INSERT INTO env_data (time, temp, humid) "
|
||||
"VALUES(?, ?, ?);";
|
||||
static void *update_thread_action(void *_ignored) {
|
||||
create_db_table();
|
||||
sqlite3_stmt *insert_statement = NULL;
|
||||
int status = sqlite3_prepare_v2(DATABASE, UPDATE_STATS_QUERY, -1,
|
||||
&insert_statement, NULL);
|
||||
|
39
src/util.c
39
src/util.c
@ -18,6 +18,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
const char *PERIOD_LABELS[] = {
|
||||
"HOUR",
|
||||
@ -164,7 +165,7 @@ static const char *AVG_FOR_PERIOD_QUERY_STR =
|
||||
"min(humid) AS minhumid\n"
|
||||
"FROM env_data\n"
|
||||
"WHERE time >= stime\n"
|
||||
"AND time <= etime);\n";
|
||||
"AND time <= etime);";
|
||||
static sqlite3_stmt *AVG_FOR_PERIOD_QUERY;
|
||||
static const char *DATA_POINT_QUERY_STR =
|
||||
"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
|
||||
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 <sqlite3.h>
|
||||
|
||||
#define _STRINGIFY_LIT(s) (#s)
|
||||
#define STRINGIFY(v) _STRINGIFY_LIT(v)
|
||||
|
||||
typedef enum {
|
||||
TEMP_UNIT_F = 'F',
|
||||
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);
|
||||
|
||||
/*
|
||||
* Create all the missing directories along path.
|
||||
* Return: true on success, false otherwise
|
||||
*/
|
||||
bool mkdirs(const char *path, mode_t mode);
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user