From 71326e34b2bc0a456ea1cb1e673da00a5ad84436 Mon Sep 17 00:00:00 2001 From: Max <51083570+DRdrProfessor@users.noreply.github.com> Date: Sat, 6 Jan 2024 23:33:14 +0100 Subject: [PATCH] Implemented simple config system (loading/writing to file in JSON format) --- src/net/configuration.c | 200 +++++++++++++++++++++++++++++++++----- src/net/include/config.h | 25 ++++- src/net/include/storage.h | 2 +- src/net/netex.c | 2 +- src/net/storage.c | 4 +- src/net/str/str_utils.c | 4 +- src/server/server.c | 22 ++--- 7 files changed, 215 insertions(+), 44 deletions(-) diff --git a/src/net/configuration.c b/src/net/configuration.c index 29c8d12..2de2324 100644 --- a/src/net/configuration.c +++ b/src/net/configuration.c @@ -1,25 +1,160 @@ +#include +#include #include -#include +#include +#include #include "config.h" #include "netex.h" #include "strutil.h" -#define SEPARATOR_CHAR '.' +#define DELIM_CHAR '.' - -int config_set(CONFIGURATION* config, const char* key, const char* value) +const char* config_to_formatted_json_string(const CONFIGURATION* config) { if (config == NULL) + return NULL; + return json_object_to_json_string_ext(config->json, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED); +} +CONFIGURATION* config_create(void) +{ + CONFIGURATION* config_obj = malloc(sizeof(CONFIGURATION)); + config_obj->json = json_object_new_object(); + return config_obj; +} +int config_clear(const CONFIGURATION* config) +{ + if (config == NULL) + return -1; + if (config->json != NULL) + json_object_put(config->json); + return 0; +} +CONFIGURATION* config_load_from_path(const char* path) +{ + if (path == NULL) + return NULL; + struct stat con_file_stat; + if (lstat(path, &con_file_stat) == -1) { - WARN("Parameter 'config' is NULL!"); + const int error = errno; + WARN("Failed to get stat from file '%s'. Error code: %i", path, error); + return NULL; + } + const long int file_size = con_file_stat.st_size; + if (file_size <= 0) + { + //TODO: Empty file? + return NULL; + } + FILE* config_file = fopen(path, "r"); + if (config_file == NULL) + { + WARN("Failed to load file '%s'", path); + return NULL; + } + void* config_mem = malloc(file_size); + if (config_mem == NULL) + { + const int error = errno; + WARN("Failed to allocate %ld bytes, error: %i", file_size, error); + return NULL; + } + const size_t total_read = fread(config_mem, file_size, file_size, config_file); + fclose(config_file); + if (total_read != 1) + { + free(config_mem); + WARN("Failed to load read file!"); + return NULL; + } + json_object* tokenized = json_tokener_parse(config_mem); + free(config_mem); + if (tokenized == NULL) + { + WARN("Failed to tokenize JSON!"); + return NULL; + } + CONFIGURATION* config_obj = malloc(sizeof(CONFIGURATION)); + if (config_obj == NULL) + { + WARN("Failed to allocate memory!"); + return NULL; + } + config_obj->json = tokenized; + return config_obj; +} + +int config_save_to_path(const CONFIGURATION* config, const char* path) +{ + if (config == NULL || path == NULL) + return -1; + FILE* config_file = fopen(path, "w"); + if (config_file == NULL) + { + const int error = errno; + WARN("Failed to open file: '%s' Error code: %i", path, error); + return error; + } + const char* json_str = json_object_to_json_string_ext(config->json, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED); + if (json_str == NULL) + { + WARN("Failed to convert JSON obj to string."); return -1; } - SPLIT_STR* splitted_key = splstr(key, SEPARATOR_CHAR); + const size_t json_str_len = strlen(json_str); + const int write_result = fwrite(json_str, 1, json_str_len, config_file); + fclose(config_file); + if (write_result <= 0) + { + const int wr_error = errno; + WARN("Error occured while writing file '%s' to disk, error code: %i", path, wr_error); + return wr_error; + } + return 0; +} +int config_remove(const CONFIGURATION* config, const char* key) +{ + if (key == NULL) + return -1; + json_object* to_remove = config_get_json(config, key); + if (json_object_put(to_remove) != 1) + { + WARN("Failed to remove item from JSON"); + return -1; + } + return 0; +} +int config_set_string(const CONFIGURATION* config, const char* key, const char* str_value) +{ + if (str_value == NULL) + return -1; + json_object* str_json_value = json_object_new_string(str_value); + return config_set_json(config, key, str_json_value); +} +int config_set_int(const CONFIGURATION* config, const char* key, const size_t int_value) +{ + json_object* int_value_json = json_object_new_int64(int_value); + return config_set_json(config, key, int_value_json); +} +int config_set_uint(const CONFIGURATION* config, const char* key, const uint64_t int_value) +{ + json_object* int_value_json = json_object_new_uint64(int_value); + return config_set_json(config, key, int_value_json); +} +int config_set_double(const CONFIGURATION* config, const char* key, const double double_value) +{ + json_object* double_value_json = json_object_new_double(double_value); + return config_set_json(config, key, double_value_json); +} + +int config_set_json(const CONFIGURATION* config, const char* key, json_object* json_value_object) +{ + if ((config == NULL || json_value_object == NULL) || config->json == NULL) + return -1; + SPLIT_STR* splitted_key = splstr(key, DELIM_CHAR); if (splitted_key == NULL || splitted_key->count == 0) return -1; - if (config->json == NULL) - config->json = json_object_new_object(); json_object* temp_obj = NULL; for (int i = 0; i != splitted_key->count; i++) { @@ -35,9 +170,8 @@ int config_set(CONFIGURATION* config, const char* key, const char* value) if (i == splitted_key->count - 1) // Last key (set value) { - json_object* value_object = json_object_new_string(value); - if (json_object_object_add(current_obj, splitted_key->delimited[i], value_object) != 0) - ERROR("Failed to set value '%s'", value); + if (json_object_object_add(current_obj, splitted_key->delimited[i], json_value_object) != 0) + ERROR("Failed to set value to JSON!"); break; } @@ -53,15 +187,44 @@ int config_set(CONFIGURATION* config, const char* key, const char* value) return 0; } -char* config_get(const CONFIGURATION* config, const char* key) +char* config_get_string(const CONFIGURATION* config, const char* key) +{ + json_object* json_string_obj = config_get_json(config, key); + if (json_string_obj == NULL) + return NULL; + // Duplicate the string, if the json object is free'd (using json_object_put()) the returned string is still there and can be used further in the application. + return strdup(json_object_get_string(json_string_obj)); +} +int64_t config_get_int(const CONFIGURATION* config, const char* key) +{ + const json_object* json_int_obj = config_get_json(config, key); + if (json_int_obj == NULL) + return 0; + return json_object_get_int64(json_int_obj); +} +uint64_t config_get_uint(const CONFIGURATION* config, const char* key) +{ + const json_object* json_int_obj = config_get_json(config, key); + if (json_int_obj == NULL) + return 0; + return json_object_get_uint64(json_int_obj); +} +double config_get_double(const CONFIGURATION* config, const char* key) +{ + const json_object* json_double_obj = config_get_json(config, key); + if (json_double_obj == NULL) + return 0; + return json_object_get_double(json_double_obj); +} + +json_object* config_get_json(const CONFIGURATION* config, const char* key) { if (config == NULL || config->json == NULL) return NULL; - SPLIT_STR* splitted = splstr(key, SEPARATOR_CHAR); + SPLIT_STR* splitted = splstr(key, DELIM_CHAR); if (splitted == NULL || splitted->count == 0) return NULL; json_object* tmp_json = config->json; - char* found_value = NULL; for (int i = 0; i != splitted->count; i++) { json_object* current_json_object = json_object_new_object(); @@ -73,13 +236,6 @@ char* config_get(const CONFIGURATION* config, const char* key) return NULL; } } - if (tmp_json != NULL) - found_value = strdup(json_object_get_string(tmp_json)); strsplfree(splitted); - return found_value; -} - -char* config_get_default(const char* key, const char* default_value) -{ - return ""; + return tmp_json; } \ No newline at end of file diff --git a/src/net/include/config.h b/src/net/include/config.h index 70aaa88..458f66a 100644 --- a/src/net/include/config.h +++ b/src/net/include/config.h @@ -5,10 +5,29 @@ typedef struct config_container { json_object* json; - int has_allocations : 1; } CONFIGURATION; -int config_set(CONFIGURATION* config, const char* key, const char* value); -char* config_get(const CONFIGURATION* config, const char* key); +const char* config_to_formatted_json_string(const CONFIGURATION* config); +CONFIGURATION* config_create(void); +int config_clear(const CONFIGURATION* config); + +CONFIGURATION* config_load_from_path(const char* path); +int config_save_to_path(const CONFIGURATION* config, const char* path); +int config_remove(const CONFIGURATION* config, const char* key); + +int config_set_json(const CONFIGURATION* config, const char* key, json_object* json_value_object); + +int config_set_string(const CONFIGURATION* config, const char* key, const char* str_value); +int config_set_int(const CONFIGURATION* config, const char* key, size_t int_value); +int config_set_uint(const CONFIGURATION* config, const char* key, uint64_t int_value); +int config_set_double(const CONFIGURATION* config, const char* key, double double_value); + +json_object* config_get_json(const CONFIGURATION* config, const char* key); + +// The returning value need to be free'd with the 'free()' function! +char* config_get_string(const CONFIGURATION* config, const char* key); +int64_t config_get_int(const CONFIGURATION* config, const char* key); +uint64_t config_get_uint(const CONFIGURATION* config, const char* key); +double config_get_double(const CONFIGURATION* config, const char* key); #endif //CONFIG_H diff --git a/src/net/include/storage.h b/src/net/include/storage.h index 0e0e05f..412cb17 100644 --- a/src/net/include/storage.h +++ b/src/net/include/storage.h @@ -14,6 +14,6 @@ struct netex_configuration }; int save_config(void); -int load_config(void); +int load_config_storage(void); #endif //STORAGE_H diff --git a/src/net/netex.c b/src/net/netex.c index f43570e..adb2ab5 100644 --- a/src/net/netex.c +++ b/src/net/netex.c @@ -8,7 +8,7 @@ void netex_init(void) { // Config - const int config_loaded = load_config(); + const int config_loaded = load_config_storage(); if (config_loaded != 0) { ERROR("Failed to load config! Shutting down..."); diff --git a/src/net/storage.c b/src/net/storage.c index 09f38c3..ddceadd 100644 --- a/src/net/storage.c +++ b/src/net/storage.c @@ -41,7 +41,7 @@ int save_config(void) } return 0; } -int load_config(void) +int load_config_storage(void) { struct stat config_stat; if (lstat(CONFIG_FILE_NAME, &config_stat) == -1) @@ -92,7 +92,7 @@ struct netex_configuration* get_configuration() { if (loaded_configuration == NULL) { - const int load_result = load_config(); + const int load_result = load_config_storage(); if (load_result != 0) WARN("Failed to get configuration!"); } diff --git a/src/net/str/str_utils.c b/src/net/str/str_utils.c index bdd8849..f23dcad 100644 --- a/src/net/str/str_utils.c +++ b/src/net/str/str_utils.c @@ -20,6 +20,7 @@ SPLIT_STR* splstr(const char* str, const char delimiter) { const int error = errno; ERROR("Failed to allocate memory, error code: %i", error); + free(dup); return NULL; } if (delim_occ != 0) @@ -31,6 +32,7 @@ SPLIT_STR* splstr(const char* str, const char delimiter) const int error = errno; ERROR("Allocation failed, error code: %i", error); free(splitted); + free(dup); return NULL; } } @@ -50,7 +52,7 @@ SPLIT_STR* splstr(const char* str, const char delimiter) void strsplfree(SPLIT_STR* str) { - free(str->delimited); + free(*str->delimited); // Free the allocated array and not the ptr pointing to the array! free(str); } diff --git a/src/server/server.c b/src/server/server.c index 1c970f8..c8991a3 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -1,25 +1,19 @@ #include + #include "netex.h" #include "config.h" +#define CONFIG_PATH "srv_config.json" + int srv_setup(void); int main(int argc, char *argv[]) { - CONFIGURATION configuration; - config_set(&configuration, "Server.Connection.Port", "6920"); - config_set(&configuration, "Server.Connection.IP", "localhost"); - config_set(&configuration, "Global.Project.Name", "NetEx"); - config_set(&configuration, "Global.Project.Lang", "C"); - config_set(&configuration, "Global.Project.OS", "Linux"); - config_set(&configuration, "Server.Connection.db", "SQLite"); - PRINT_LINE("JSON:\n%s", json_object_to_json_string_ext(configuration.json, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY)); - - char* proj_name = config_get(&configuration,"Global.Project.Name"); - PRINT_LINE("Project name: %s", proj_name); - - /*config_set("Server.Test", "Test value for server config"); - config_set("Server.port", "6920");*/ + const CONFIGURATION* srv_config = config_load_from_path(CONFIG_PATH); + int remove_result = config_remove(srv_config, "Connection.CertPath"); + config_set_string(srv_config, "Connection.CertPath", "cert.pem"); + config_save_to_path(srv_config, CONFIG_PATH); + config_clear(srv_config); netex_init(); PRINT_LINE("Hello, server!");