diff --git a/configs/example.ini b/configs/example.ini new file mode 100644 index 0000000..445e130 --- /dev/null +++ b/configs/example.ini @@ -0,0 +1,32 @@ +[general] +log_filepath= + +[postgres] +enabled= + +origin_host= +origin_user= +origin_password= +origin_port= +origin_database= + +target_host= +target_user= +target_password= +target_port= +target_database= + +backup_type= +email= + +[smtp] +username= +password= +smtp_host= +smtp_port= +auth_mode= +enabled= + +from= +to= +cc= diff --git a/configs/ci-config.ini b/configs/tests/ci-config.ini similarity index 100% rename from configs/ci-config.ini rename to configs/tests/ci-config.ini diff --git a/configs/tests/multiple_db_config.ini b/configs/tests/multiple_db_config.ini new file mode 100644 index 0000000..6310650 --- /dev/null +++ b/configs/tests/multiple_db_config.ini @@ -0,0 +1,47 @@ +[postgres] +enabled=true + +origin_host=localhost +origin_user=postgres +origin_password=password +origin_port=5432 +origin_database=postgres_origin + +target_host=localhost +target_user=postgres +target_password=password +target_port=5432 +target_database=postgres_origin + +backup_type=full +email=false + +[postgres] +enabled=true + +origin_host=localhost +origin_user=postgres +origin_password=password +origin_port=5433 +origin_database=postgres_origin + +target_host=localhost +target_user=postgres +target_password=password +target_port=5433 +target_database=postgres_origin + +backup_type=full +email=false + +[smtp] +username = test@posteo.net +password = 123 +smtp_host = posteo.de +smtp_port = 587 +auth_mode = tls +enabled=true + +from = charmitro@posteo.net +to = charmitro@pm.me, trsbisb@gmail.com +cc = charmitro@gmail.com, charmitro@terrasync.net diff --git a/configs/test.ini b/configs/tests/single_db_config.ini similarity index 85% rename from configs/test.ini rename to configs/tests/single_db_config.ini index 68df4de..ff046e1 100644 --- a/configs/test.ini +++ b/configs/tests/single_db_config.ini @@ -5,16 +5,16 @@ origin_host=localhost origin_user=postgres origin_password=password origin_port=5432 -origin_database=postgres +origin_database=postgres_origin target_host=localhost target_user=postgres target_password=password target_port=5432 -target_database=postgres +target_database=postgres_origin backup_type=full -email=true +email=false [smtp] username = test@posteo.net diff --git a/include/config.h b/include/config.h index ead50e1..43d9ea7 100644 --- a/include/config.h +++ b/include/config.h @@ -14,7 +14,7 @@ typedef enum backup_type { FULL, } backup_type; -typedef struct postgres_t { +typedef struct postgres_node_t { bool enabled; d_str_t host; @@ -25,7 +25,9 @@ typedef struct postgres_t { backup_type backup_type; bool email; -} postgres_t; + + struct postgres_node_t *next; +} postgres_node_t; typedef enum auth_mode_t { SSL = 1, @@ -53,7 +55,7 @@ typedef struct general_t { } general_t; typedef struct config_t { - postgres_t *postgres_config; + postgres_node_t *postgres_config; smtp_t *smtp_config; general_t *general_config; } config_t; diff --git a/include/db/db.h b/include/db/db.h index 0418722..20265ea 100644 --- a/include/db/db.h +++ b/include/db/db.h @@ -2,6 +2,7 @@ #define DB_H #include +#include #include "config.h" @@ -10,6 +11,7 @@ typedef int (*init_db_func)(void); extern init_db_func init_functions[MAX_AVAILABLE_DBS]; extern int num_init_functions; +extern pthread_mutex_t mutex; #define ADD_FUNC(init_func) \ void __attribute__((constructor)) init_func##_register(void) \ @@ -28,13 +30,13 @@ extern int num_init_functions; /* struct db_t * Holds the information every database will need. * - * * postgres_t *pg_conf - inherited from the config + * * postgres_node_t *pg_conf - inherited from the config * * void *host_conn - host connection, should be * allocated based on the target (e.g. PGconn *) * * void *target_conn - target connection (same logic as host_conn) */ struct db_t { - postgres_t *pg_conf; + void *db_conf; void *origin_conn; void *target_conn; @@ -73,6 +75,14 @@ struct db_operations { int (*replicate)(struct db_t *); }; +/* + * execute_db_operations + * + * Returns: + * 0 Success + * -2 Error creating thread + * -ENOMEM Error allocating memory + */ int execute_db_operations(void); #endif diff --git a/include/db/postgres.h b/include/db/postgres.h index 1fc7f6f..fd9dab4 100644 --- a/include/db/postgres.h +++ b/include/db/postgres.h @@ -17,6 +17,7 @@ * * Returns: * 0 on success + * -1 Maximum number of available databases reached * -ENOMEM Error allocating memory * */ @@ -34,4 +35,8 @@ int connect_pg(struct db_t *); void close_pg(struct db_t *pg_db_t); int replicate(struct db_t *pg_db_t); +bool is_enabled(void *); +const char *get_origin(void *); +void *get_next(void *); + #endif diff --git a/include/util.h b/include/util.h index f5c97dc..d964be6 100644 --- a/include/util.h +++ b/include/util.h @@ -5,6 +5,7 @@ #include #include "log.h" +#include "db/db.h" #ifdef _POSIX_C_SOURCE #include @@ -90,4 +91,18 @@ static inline int cnc_strdup(char **string, char *string_to_dup) */ void construct_filepath(char *path, char *filename); +/* + * Append `.sql` to a string, mainly to create dump files for SQL-like databases + */ +void construct_sql_dump_file(char *backup_filename, const char *database_name); + +/* + * This function is responsible for iterating through a linked list and for + * each node, allocating the correct db structs and populating the `available_dbs` array. + */ +int construct_db(void *db_config, bool (*is_enabled)(void *), + void *(*get_next)(void *), const char *(*get_origin)(void *), + int (*connect_func)(struct db_t *), + void (*close_func)(struct db_t *), + int (*replicate_func)(struct db_t *), size_t size_of_node); #endif diff --git a/scripts/postgres-down.sh b/scripts/postgres-down.sh index 586e7e7..0c9189f 100755 --- a/scripts/postgres-down.sh +++ b/scripts/postgres-down.sh @@ -1,9 +1,17 @@ #! /bin/bash -if docker ps --format "{{.Names}}" | grep -q "cnc-postgres"; then - docker stop cnc-postgres - docker rm cnc-postgres - echo "PostgreSQL Docker container was successfully removed." +if docker ps --format "{{.Names}}" | grep -q "cnc-postgres-origin"; then + docker stop cnc-postgres-origin + docker rm cnc-postgres-origin + echo "Postgres-origin docker container was successfully removed." else - echo "PostgreSQL Docker container is not currently running." + echo "Postgre-origin docker container is not currently running." +fi + +if docker ps --format "{{.Names}}" | grep -q "cnc-postgres-target"; then + docker stop cnc-postgres-target + docker rm cnc-postgres-target + echo "Postgres-target docker container was successfully removed." +else + echo "Postgres-target docker container is not currently running." fi diff --git a/scripts/postgres-up.sh b/scripts/postgres-up.sh index bb01bd4..f73f209 100755 --- a/scripts/postgres-up.sh +++ b/scripts/postgres-up.sh @@ -9,13 +9,26 @@ To install docker visit (https://docs.docker.com/engine/install/)." exit 1 fi -docker_args="--name cnc-postgres -d \ - -e POSTGRES_PASSWORD=password \ - -p 5432:5432 postgres:15" +docker_args_origin="--name cnc-postgres-origin -d \ + -e POSTGRES_PASSWORD=password \ + -e POSTGRES_DB=postgres_origin \ + -p 5432:5432 postgres:14" -if docker run $docker_args; then - echo "Postgres container was successfully set up." +if docker run $docker_args_origin; then + echo "Postgres-origin container was successfully set up." else - echo "Failed to start the Postgres container." + echo "Failed to start the Postgres-origin container." + exit 1 +fi + +docker_args_target="--name cnc-postgres-target -d \ + -e POSTGRES_PASSWORD=password \ + -e POSTGRES_DB=postgres_target \ + -p 5433:5432 postgres:14" + +if docker run $docker_args_target; then + echo "Postgres-target container was successfully set up." +else + echo "Failed to start the Postgres-target container." exit 1 fi diff --git a/src/cnc.c b/src/cnc.c index e788e23..adc7011 100644 --- a/src/cnc.c +++ b/src/cnc.c @@ -42,6 +42,9 @@ int process_args(int argc, char **argv) int c; char *config_file = NULL; + // This is needed to access the localtime in a thread-safe manner + tzset(); + optind = 0; while ((c = getopt_long(argc, argv, "f:vhV", options, NULL)) != -1) { switch (c) { diff --git a/src/config.c b/src/config.c index b6d2c1f..27ae491 100644 --- a/src/config.c +++ b/src/config.c @@ -9,6 +9,7 @@ config_t *ini_config; char *log_filepath; +bool new_node = false; #define CHECK_SECTION(s) strcmp(section, s) == 0 #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 @@ -47,6 +48,14 @@ int handler(void *user, const char *section, const char *name, } if (CHECK_SECTION("postgres")) { + // If true, create a new node, and change the head of our linked list. + if (new_node) { + postgres_node_t *temp_node = + CNC_MALLOC(sizeof(postgres_node_t)); + temp_node->next = ini_config->postgres_config; + ini_config->postgres_config = temp_node; + new_node = false; + } if (MATCH("postgres", "enabled")) { if (strcmp("true", value) == 0) { ini_config->postgres_config->enabled = true; @@ -108,6 +117,12 @@ int handler(void *user, const char *section, const char *name, "Accepted `enabled` values are \"true\" or \"false\".\n"); return 0; } + /* + * Since we reached the end of the `postgres` section, we need to change + * the flag so we can create a new node the next time we enter to + * this section. + */ + new_node = true; } else { return 0; } @@ -164,7 +179,8 @@ int initialize_config(const char *config_file) int ret = 0; ini_config = CNC_MALLOC(sizeof(config_t)); - ini_config->postgres_config = CNC_MALLOC(sizeof(postgres_t)); + ini_config->postgres_config = CNC_MALLOC(sizeof(postgres_node_t)); + ini_config->postgres_config->next = NULL; ini_config->smtp_config = CNC_MALLOC(sizeof(smtp_t)); ini_config->general_config = CNC_MALLOC(sizeof(general_t)); ini_config->general_config->log_filepath = NULL; @@ -183,28 +199,38 @@ int initialize_config(const char *config_file) return ret; } +/* + * A helper function to free each node that we allocated. We iterate through the list + * and free all the fields that we have parsed from the `.ini` file. + */ +void config_free_linked_list(postgres_node_t *head) +{ + postgres_node_t *temp; + while (head != NULL) { + temp = head; + head = head->next; + free((void *)temp->host.origin); + free((void *)temp->user.origin); + free((void *)temp->password.origin); + free((void *)temp->port.origin); + free((void *)temp->database.origin); + free((void *)temp->host.target); + free((void *)temp->user.target); + free((void *)temp->password.target); + free((void *)temp->port.target); + free((void *)temp->database.target); + free(temp); + } +} + void free_config(void) { - free((void *)ini_config->postgres_config->host.origin); - free((void *)ini_config->postgres_config->user.origin); - free((void *)ini_config->postgres_config->password.origin); - free((void *)ini_config->postgres_config->port.origin); - free((void *)ini_config->postgres_config->database.origin); - - free((void *)ini_config->postgres_config->host.target); - free((void *)ini_config->postgres_config->user.target); - free((void *)ini_config->postgres_config->password.target); - free((void *)ini_config->postgres_config->port.target); - free((void *)ini_config->postgres_config->database.target); + config_free_linked_list(ini_config->postgres_config); free((void *)ini_config->smtp_config->username); free((void *)ini_config->smtp_config->password); free((void *)ini_config->smtp_config->smtp_port); free((void *)ini_config->smtp_config->smtp_host); - - if (ini_config->general_config->log_filepath != NULL) { - free((void *)ini_config->general_config->log_filepath); - } free((void *)ini_config->smtp_config->from); for (int i = 0; i < ini_config->smtp_config->to_len; i++) { @@ -214,8 +240,9 @@ void free_config(void) free(ini_config->smtp_config->cc[i]); } + free((void *)ini_config->general_config->log_filepath); + free(ini_config->smtp_config); - free(ini_config->postgres_config); free(ini_config->general_config); free(ini_config); free(log_filepath); diff --git a/src/db.c b/src/db.c index 131819f..a4f868e 100644 --- a/src/db.c +++ b/src/db.c @@ -2,6 +2,7 @@ #include "db/postgres.h" #include "log.h" +#include #include #include #include @@ -16,6 +17,7 @@ int num_init_functions = 0; */ struct db_operations **available_dbs; size_t db_ops_counter = 0; +pthread_mutex_t mutex; void *db_operation_thread(void *arg) { @@ -37,26 +39,26 @@ void *db_operation_thread(void *arg) int execute_db_operations(void) { int ret = 0; - pthread_t threads[db_ops_counter]; + pthread_t threads[MAX_AVAILABLE_DBS]; init_db_func init_function; + if (pthread_mutex_init(&mutex, NULL) != 0) { + printf("Mutex initialization failed\n"); + return 1; + } + available_dbs = (struct db_operations **)calloc( MAX_AVAILABLE_DBS, sizeof(struct db_operations **)); section_foreach_entry(init_function) { - if (db_ops_counter < MAX_AVAILABLE_DBS) { - int ret = init_function(); - if (ret == -ENOMEM) { - pr_error("Error allocating memory\n"); - free(available_dbs); - return ret; - } - db_ops_counter++; - } else { - pr_info("Max available database number was reached.\n" - "Executing replication for %ld database systems.\n", - db_ops_counter); + int ret = init_function(); + if (ret == -ENOMEM) { + pr_error("Error allocating memory\n"); + free(available_dbs); + pthread_mutex_destroy(&mutex); + return ret; + } else if (ret == -1) { break; } } @@ -66,7 +68,10 @@ int execute_db_operations(void) (void *)available_dbs[i]); if (ret != 0) { pr_error("Error creating thread: %s\n", strerror(ret)); - return ret; + pthread_mutex_destroy(&mutex); + free(available_dbs); + + return -2; } } @@ -76,11 +81,17 @@ int execute_db_operations(void) if (ret != 0) { pr_error("Error joining thread: %s\n", strerror(ret)); + pthread_mutex_destroy(&mutex); + free(available_dbs[i]); + return ret; } + free(available_dbs[i]); } } + pthread_mutex_destroy(&mutex); + free(available_dbs); return ret; } diff --git a/src/log.c b/src/log.c index 88cf666..bd6fc6a 100644 --- a/src/log.c +++ b/src/log.c @@ -20,7 +20,8 @@ extern char *log_filepath; void construct_log_filename(char *log_filename, const char *log_name) { time_t t = time(NULL); - struct tm tm = *localtime(&t); + struct tm tm; + localtime_r(&t, &tm); snprintf(log_filename, PATH_MAX, "%scnc_%s_%02d%02d%02d%02d%02d%02d.log", log_filepath, diff --git a/src/postgres.c b/src/postgres.c index 0edac5e..8fcc4aa 100644 --- a/src/postgres.c +++ b/src/postgres.c @@ -23,31 +23,29 @@ extern config_t *ini_config; extern struct db_operations **available_dbs; extern size_t db_ops_counter; -static struct db_operations pg_db_ops = { - .connect = connect_pg, - .close = close_pg, - .replicate = replicate, -}; - -int construct_pg(void) +bool pg_is_enabled(void *postgres_node) { - if (!ini_config->postgres_config->enabled) { - pr_info("Database: `%s` is disabled, skipping.\n", - ini_config->postgres_config->database.origin); - return -1; // not enabled, skip. - } + return ((postgres_node_t *)postgres_node)->enabled; +} - struct db_t *pg_db_t = CNC_MALLOC(sizeof(struct db_t)); +const char *pg_get_origin(void *postgres_node) +{ + return ((postgres_node_t *)postgres_node)->database.origin; +} - pg_db_t->pg_conf = CNC_MALLOC(sizeof(postgres_t)); - pg_db_t->log_filename = CNC_MALLOC(sizeof(char) * (PATH_MAX + 1)); - memcpy(pg_db_t->pg_conf, ini_config->postgres_config, - sizeof(postgres_t)); +void *pg_get_next(void *postgres_node) +{ + return ((postgres_node_t *)postgres_node)->next; +} - pg_db_ops.db = pg_db_t; - available_dbs[db_ops_counter] = &pg_db_ops; +int construct_pg(void) +{ + postgres_node_t *temp_node = ini_config->postgres_config; + int ret = construct_db(temp_node, pg_is_enabled, pg_get_next, + pg_get_origin, connect_pg, close_pg, replicate, + sizeof(*ini_config->postgres_config)); - return 0; + return ret; } int connect_pg(struct db_t *pg_db_t) @@ -60,8 +58,10 @@ int connect_pg(struct db_t *pg_db_t) pg_db_t->origin_conn = NULL; pg_db_t->target_conn = NULL; - construct_log_filename(pg_db_t->log_filename, - pg_db_t->pg_conf->database.origin); + construct_log_filename( + pg_db_t->log_filename, + ((postgres_node_t *)pg_db_t->db_conf)->database.origin); + pg_db_t->log_file = fopen(pg_db_t->log_filename, "a"); if (pg_db_t->log_file == NULL) { pr_error("Error opening %s\n", pg_db_t->log_filename); @@ -69,25 +69,31 @@ int connect_pg(struct db_t *pg_db_t) } pr_info_fd(pg_db_t->log_file, "Summary of Postgres Database: `%s`\n\n", - pg_db_t->pg_conf->database.origin); + ((postgres_node_t *)pg_db_t->db_conf)->database.origin); // Initialize the fields that are to be used to connect to postgres. const char *keywords[] = { "host", "user", "password", "port", "dbname", NULL }; // Construct the array that will hold the values of the fields. - const char *origin_values[] = { pg_db_t->pg_conf->host.origin, - pg_db_t->pg_conf->user.origin, - pg_db_t->pg_conf->password.origin, - pg_db_t->pg_conf->port.origin, - pg_db_t->pg_conf->database.origin }; - const char *target_values[] = { pg_db_t->pg_conf->host.target, - pg_db_t->pg_conf->user.target, - pg_db_t->pg_conf->password.target, - pg_db_t->pg_conf->port.target, - pg_db_t->pg_conf->database.target }; + const char *origin_values[] = { + ((postgres_node_t *)pg_db_t->db_conf)->host.origin, + ((postgres_node_t *)pg_db_t->db_conf)->user.origin, + ((postgres_node_t *)pg_db_t->db_conf)->password.origin, + ((postgres_node_t *)pg_db_t->db_conf)->port.origin, + ((postgres_node_t *)pg_db_t->db_conf)->database.origin + }; + const char *target_values[] = { + ((postgres_node_t *)pg_db_t->db_conf)->host.target, + ((postgres_node_t *)pg_db_t->db_conf)->user.target, + ((postgres_node_t *)pg_db_t->db_conf)->password.target, + ((postgres_node_t *)pg_db_t->db_conf)->port.target, + ((postgres_node_t *)pg_db_t->db_conf)->database.target + }; // Connect to origin-database. + pthread_mutex_lock(&mutex); pg_db_t->origin_conn = PQconnectdbParams(keywords, origin_values, 0); + pthread_mutex_unlock(&mutex); if (PQstatus(pg_db_t->origin_conn) != CONNECTION_OK) { pr_debug_fd(pg_db_t->log_file, "Postgres origin-database connection: Failed!\n"); @@ -97,10 +103,13 @@ int connect_pg(struct db_t *pg_db_t) ret = -1; return ret; } + pr_debug("Origin-database connection: Success!\n"); // Connect to target-database. + pthread_mutex_lock(&mutex); pg_db_t->target_conn = PQconnectdbParams(keywords, target_values, 0); + pthread_mutex_unlock(&mutex); if (PQstatus(pg_db_t->target_conn) != CONNECTION_OK) { pr_debug_fd(pg_db_t->log_file, "Target-database connection: Failed!\n"); @@ -120,7 +129,8 @@ void close_pg(struct db_t *pg_db_t) PQfinish(pg_db_t->origin_conn); PQfinish(pg_db_t->target_conn); - if (ini_config->smtp_config->enabled && pg_db_t->pg_conf->email) { + if (ini_config->smtp_config->enabled && + ((postgres_node_t *)pg_db_t->db_conf)->email) { EmailInfo email_info = { .from = ini_config->smtp_config->from, .to = (const char *const *)ini_config->smtp_config->to, @@ -144,7 +154,7 @@ void close_pg(struct db_t *pg_db_t) fclose(pg_db_t->log_file); free(pg_db_t->log_filename); - free(pg_db_t->pg_conf); + free(((postgres_node_t *)pg_db_t->db_conf)); free(pg_db_t); } @@ -198,38 +208,46 @@ int replicate(struct db_t *pg_db_t) char *pg_pass = NULL; char *pg_command_path = NULL; char backup_path[PATH_MAX + 1] = { 0 }; + char backup_filename[NAME_MAX] = { 0 }; char *pg_bin, prefixed_command_path[PATH_MAX + 6] = "PATH="; char command_path[PATH_MAX + 1] = "/usr/bin/"; int command_path_size = strlen(command_path) + 1; - construct_filepath(backup_path, PG_DUMP_FILE); + construct_sql_dump_file( + backup_filename, + ((postgres_node_t *)pg_db_t->db_conf)->database.origin); + construct_filepath(backup_path, backup_filename); char *const dump_args[] = { PG_DUMP_COMMAND, "-U", - (char *const)pg_db_t->pg_conf->user.origin, + (char *const)((postgres_node_t *)pg_db_t->db_conf)->user.origin, "-h", - (char *const)pg_db_t->pg_conf->host.origin, + (char *const)((postgres_node_t *)pg_db_t->db_conf)->host.origin, "-p", - (char *const)pg_db_t->pg_conf->port.origin, + (char *const)((postgres_node_t *)pg_db_t->db_conf)->port.origin, "-l", - (char *const)pg_db_t->pg_conf->database.origin, + (char *const)((postgres_node_t *)pg_db_t->db_conf) + ->database.origin, "-f", backup_path, "-v", - pg_db_t->pg_conf->backup_type == SCHEMA ? "-s" : NULL, + ((postgres_node_t *)pg_db_t->db_conf)->backup_type == SCHEMA ? + "-s" : + NULL, NULL }; char *const restore_args[] = { PSQL_COMMAND, "-h", - (char *const)pg_db_t->pg_conf->host.target, + (char *const)((postgres_node_t *)pg_db_t->db_conf)->host.target, "-U", - (char *const)pg_db_t->pg_conf->user.target, + (char *const)((postgres_node_t *)pg_db_t->db_conf)->user.target, "-p", - (char *const)pg_db_t->pg_conf->port.target, - (char *const)pg_db_t->pg_conf->database.target, + (char *const)((postgres_node_t *)pg_db_t->db_conf)->port.target, + (char *const)((postgres_node_t *)pg_db_t->db_conf) + ->database.target, "-f", backup_path, NULL @@ -247,16 +265,16 @@ int replicate(struct db_t *pg_db_t) strncat(prefixed_command_path, command_path, command_path_size + 1); setup_command(&pg_command_path, &pg_pass, PG_DUMP_COMMAND, - pg_db_t->pg_conf->password.origin, command_path, - command_path_size, strlen(PG_DUMP_COMMAND)); + ((postgres_node_t *)pg_db_t->db_conf)->password.origin, + command_path, command_path_size, strlen(PG_DUMP_COMMAND)); ret = exec_command(pg_command_path, dump_args, pg_pass, prefixed_command_path, pg_db_t->log_file); if (ret != 0) goto cleanup; setup_command(&pg_command_path, &pg_pass, PSQL_COMMAND, - pg_db_t->pg_conf->password.target, command_path, - command_path_size, strlen(PSQL_COMMAND)); + ((postgres_node_t *)pg_db_t->db_conf)->password.target, + command_path, command_path_size, strlen(PSQL_COMMAND)); ret = exec_command(pg_command_path, restore_args, pg_pass, prefixed_command_path, pg_db_t->log_file); if (ret != 0) @@ -264,7 +282,8 @@ int replicate(struct db_t *pg_db_t) pr_info_fd(pg_db_t->log_file, "\nReplication of Postgres Database: `%s` was successful.\n", - pg_db_t->pg_conf->database.origin); + ((postgres_node_t *)pg_db_t->db_conf)->database.origin); + cleanup: free(pg_command_path); free(pg_pass); diff --git a/src/util.c b/src/util.c index 544c6a1..d516f0e 100644 --- a/src/util.c +++ b/src/util.c @@ -1,3 +1,4 @@ +#include "db/db.h" #include #include #include @@ -7,6 +8,9 @@ #include "util.h" #include "log.h" +extern int db_ops_counter; +extern struct db_operations **available_dbs; + void format_buffer(char *buffer) { int i = 0, num_of_new_lines = 0; @@ -84,7 +88,7 @@ int execve_binary(char *command_path, char *const command_args[], int read_buffer_pipe(int *pipefd, FILE *fp) { - char buffer[4096] = { 0 }; + char buffer[4096]; int nbytes = read(pipefd[READ_END], buffer, sizeof(buffer)); if (nbytes < 0) { @@ -92,10 +96,10 @@ int read_buffer_pipe(int *pipefd, FILE *fp) } while (nbytes > 0) { + buffer[nbytes] = '\0'; // Null-terminate the buffer fprintf(fp, "%s", buffer); - memset(buffer, 0, sizeof(buffer)); - nbytes = read(pipefd[READ_END], buffer, sizeof(buffer)); + nbytes = read(pipefd[READ_END], buffer, sizeof(buffer)); if (nbytes < 0) { return -1; } @@ -171,3 +175,56 @@ void construct_filepath(char *path, char *filename) snprintf(path, PATH_MAX - 1, "%s", home_path); strncat(path, filename, PATH_MAX - strlen(path) - 1); } + +void construct_sql_dump_file(char *backup_filename, const char *database_name) +{ + snprintf(backup_filename, NAME_MAX - 1, "/%s.sql", database_name); +} + +int construct_db(void *db_config, bool (*is_enabled)(void *), + void *(*get_next)(void *), const char *(*get_origin)(void *), + int (*connect_func)(struct db_t *), + void (*close_func)(struct db_t *), + int (*replicate_func)(struct db_t *), size_t size_of_node) +{ + struct db_operations *db_ops; + + /* + * Iterate through the linked list, and for each node check if the + * database is enabled. If it is, allocate memory for the various + * structs and populate the `available_dbs` array. + */ + while (db_config != NULL) { + if (db_ops_counter < MAX_AVAILABLE_DBS) { + if (!is_enabled(db_config)) { + pr_info("Database: `%s` is disabled, skipping.\n", + get_origin(db_config)); + db_config = get_next(db_config); + continue; + } + + struct db_t *db_t = CNC_MALLOC(sizeof(struct db_t)); + db_t->db_conf = CNC_MALLOC(size_of_node); + db_t->log_filename = + CNC_MALLOC(sizeof(char) * (PATH_MAX + 1)); + db_ops = CNC_MALLOC(sizeof(struct db_operations)); + + db_ops->connect = connect_func; + db_ops->close = close_func; + db_ops->replicate = replicate_func; + + memcpy(db_t->db_conf, db_config, size_of_node); + db_ops->db = db_t; + available_dbs[db_ops_counter] = db_ops; + + db_config = get_next(db_config); + db_ops_counter++; + } else { + pr_warn("Max available database number was reached.\n"); + + return -1; + } + } + + return 0; +} diff --git a/tests/cnc_test.c b/tests/cnc_test.c index e9bab90..05ce42d 100644 --- a/tests/cnc_test.c +++ b/tests/cnc_test.c @@ -50,7 +50,8 @@ void test_missing_config(void) void test_backup_full(void) { ini_config = (config_t *)malloc(sizeof(config_t)); - ini_config->postgres_config = (postgres_t *)malloc(sizeof(postgres_t)); + ini_config->postgres_config = + (postgres_node_t *)malloc(sizeof(postgres_node_t)); ini_config->smtp_config = (smtp_t *)malloc(sizeof(smtp_t)); ini_parse_string("[postgres]\nbackup_type=full", handler, ini_config); CU_ASSERT_EQUAL(ini_config->postgres_config->backup_type, FULL); @@ -59,7 +60,8 @@ void test_backup_full(void) void test_backup_schema_only(void) { ini_config = (config_t *)malloc(sizeof(config_t)); - ini_config->postgres_config = (postgres_t *)malloc(sizeof(postgres_t)); + ini_config->postgres_config = + (postgres_node_t *)malloc(sizeof(postgres_node_t)); ini_config->smtp_config = (smtp_t *)malloc(sizeof(smtp_t)); ini_parse_string("[postgres]\nbackup_type=schema", handler, ini_config); CU_ASSERT_EQUAL(ini_config->postgres_config->backup_type, SCHEMA); @@ -69,7 +71,8 @@ void test_invalid_backup_type(void) { int result; ini_config = (config_t *)malloc(sizeof(config_t)); - ini_config->postgres_config = (postgres_t *)malloc(sizeof(postgres_t)); + ini_config->postgres_config = + (postgres_node_t *)malloc(sizeof(postgres_node_t)); ini_config->smtp_config = (smtp_t *)malloc(sizeof(smtp_t)); result = ini_parse_string("[postgres]\nbackup_type=wrong_type", handler, ini_config); @@ -80,7 +83,8 @@ void test_empty_backup_type(void) { int result; ini_config = (config_t *)malloc(sizeof(config_t)); - ini_config->postgres_config = (postgres_t *)malloc(sizeof(postgres_t)); + ini_config->postgres_config = + (postgres_node_t *)malloc(sizeof(postgres_node_t)); ini_config->smtp_config = (smtp_t *)malloc(sizeof(smtp_t)); result = ini_parse_string("[postgres]\nbackup_type=", handler, ini_config); @@ -109,7 +113,8 @@ void test_parse_log_filepath(void) { int result; ini_config = (config_t *)malloc(sizeof(config_t)); - ini_config->postgres_config = (postgres_t *)malloc(sizeof(postgres_t)); + ini_config->postgres_config = + (postgres_node_t *)malloc(sizeof(postgres_node_t)); ini_config->smtp_config = (smtp_t *)malloc(sizeof(smtp_t)); ini_config->general_config = (general_t *)malloc(sizeof(general_t)); result = ini_parse_string("[general]\nlog_filepath=/var/log/cnc/", @@ -122,7 +127,8 @@ void test_construct_log_filepath_wrong_path(void) int result; char *log_filepath = NULL; ini_config = (config_t *)malloc(sizeof(config_t)); - ini_config->postgres_config = (postgres_t *)malloc(sizeof(postgres_t)); + ini_config->postgres_config = + (postgres_node_t *)malloc(sizeof(postgres_node_t)); ini_config->smtp_config = (smtp_t *)malloc(sizeof(smtp_t)); ini_config->general_config = (general_t *)malloc(sizeof(general_t)); ini_parse_string("[general]\nlog_filepath=var/log/cnc/", handler,