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