Implemented simple config system (loading/writing to file in JSON format)

This commit is contained in:
Max 2024-01-06 23:33:14 +01:00
parent 334df8f9f0
commit 71326e34b2
7 changed files with 215 additions and 44 deletions

View File

@ -1,25 +1,160 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <json-c/json_object.h>
#include <json-c/json_tokener.h>
#include <sys/stat.h>
#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;
}

View File

@ -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

View File

@ -14,6 +14,6 @@ struct netex_configuration
};
int save_config(void);
int load_config(void);
int load_config_storage(void);
#endif //STORAGE_H

View File

@ -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...");

View File

@ -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!");
}

View File

@ -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);
}

View File

@ -1,25 +1,19 @@
#include <stdio.h>
#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!");