/* dblayer.c - Database layer for ck -----------------------------------*- C -*- * * This file is part of ck, the config keeper * * ----------------------------------------------------------------------------- * * Copyright (C) 2018 Anastasis Grammenos * GPLv3 (see LICENCE for the full notice) * * -------------------------------------------------------------------------- */ #include #include "dblayer.h" #include "dbhelper.h" #include "ckutil.h" #include "ckerrlog.h" ERRLOG(ckdb); const char * const DB_FILE_NAME = "/ckdb"; /* figure out the database name */ void make_db_name(char *ret, const char *confPath) { char db_path[STR_L]; strcpy(db_path, confPath); strcat(db_path, DB_FILE_NAME); strcpy(ret, db_path); } /* Check if the db file exists*/ int db_exists(const UserOpt *opt) { char db_path[STR_L]; make_db_name(db_path, opt->confDir); return util_is_file_rw(db_path); } /* check if db has the correct tables */ int check_initialized_DB(sqlite3 *db) { char sql[STR_M]; dbh_form_query_select_all_tables(sql); sqlite3_stmt *stmt; sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL); int program_table_ok, config_table_ok, rel_table_ok = 0; while (sqlite3_step(stmt) != SQLITE_DONE) { const unsigned char *tmpbuf = sqlite3_column_text(stmt, 1); if (strcmp((char *)tmpbuf, TBL_PROGRAM) == 0) { program_table_ok = 1; } if (strcmp((char *)tmpbuf, TBL_CONFIG) == 0) { config_table_ok = 1; } if (strcmp((char *)tmpbuf, TBL_REL) == 0) { rel_table_ok = 1; } } if (!program_table_ok || !config_table_ok || !rel_table_ok ) { return 1; } sqlite3_finalize(stmt); return 0; } DB empty_DB(SqlError err) { return (DB){ .ptr = NULL, .error = err }; } DB new_DB(sqlite3 *db) { return (DB){ .ptr = db, .error = SQL_NO_ERR }; } void close_DB(DB *db) { sqlite3_close(db->ptr); } DB init_make_DB(const UserOpt *opt) { sqlite3 *db; char db_path[STR_L]; int rc; make_db_name(db_path, opt->confDir); rc = sqlite3_open(db_path, &db); if (rc != SQLITE_OK) { return empty_DB(SQL_ERR_NO_DB_FILE); } return new_DB(db); } DB open_DB(const UserOpt *opt) { sqlite3 *db; int rc; char db_path[STR_L]; make_db_name(db_path, opt->confDir); rc = sqlite3_open(db_path, &db); if (rc) { return empty_DB(SQL_ERR_NO_DB_FILE); } if (check_initialized_DB(db)) { return empty_DB(SQL_ERR_NO_TABLES); } return new_DB(db); } void init_make_tables(DB *db) { char sql[STR_L]; dbh_form_query_make_tables(sql); int rc = sqlite3_exec(db->ptr, sql, 0, 0, 0); if (rc != SQLITE_OK ) { PRINT_ERR("Could not create empty db."); db->error = SQL_ERR_SQLITE; return; } } int get_next_valid_id_from_table(DB *db, const char* tableName) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dbh_form_query_select_id_from(sql, tableName); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -1; } sqlite3_bind_text(stmt, 1, tableName, strlen(tableName), 0); int id = 0; while (sqlite3_step(stmt) == SQLITE_ROW) { int a = sqlite3_column_int(stmt, 0); if (a != id) { break; } id++; } sqlite3_finalize(stmt); return id; } int insert_to_program_table(DB *db, const char *name) { sqlite3_stmt *stmt; int rc; char sql[STR_L]; dbh_form_query_insert_program(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { PRINT_ERR("while preparing insert to program sql."); db->error = SQL_ERR_SQLITE; return -1; } int id = get_next_valid_id_from_table(db, TBL_PROGRAM); if (id == -1) { db->error = SQL_ERR_SQLITE; return -1; } sqlite3_bind_int(stmt, 1, id); sqlite3_bind_text(stmt, 2, name, strlen(name), 0); if (sqlite3_step(stmt) != SQLITE_DONE) { PRINT_ERR("while excecuting insert to program sql."); db->error = SQL_ERR_SQLITE; return -1; } sqlite3_finalize(stmt); return id; } int insert_to_config_table(DB *db, const char *path, const int secret, const int prime) { sqlite3_stmt *stmt; int rc; char sql[STR_L]; dbh_form_query_insert_config(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { PRINT_ERR("Error while preparing insert to config sql."); db->error = SQL_ERR_SQLITE; return -1; } int id = get_next_valid_id_from_table(db, TBL_CONFIG); if (id == -1) { db->error = SQL_ERR_SQLITE; return -1; } sqlite3_bind_int(stmt, 1, id); sqlite3_bind_text(stmt, 2, path, strlen(path), 0); sqlite3_bind_int(stmt, 3, secret); sqlite3_bind_int(stmt, 4, prime); if (sqlite3_step(stmt) != SQLITE_DONE) { PRINT_ERR("Error while excecuting insert to config sql."); db->error = SQL_ERR_SQLITE; return-1; } sqlite3_finalize(stmt); return id; } int insert_to_rel_table(DB *db, const int pid, const int cid) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dhb_form_query_insert_relationship(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { db->error = SQL_ERR_SQLITE; PRINT_ERR("while preparing insert to rel sql."); return -1; } sqlite3_bind_int(stmt, 1, pid); sqlite3_bind_int(stmt, 2, cid); if (sqlite3_step(stmt) != SQLITE_DONE) { db->error = SQL_ERR_SQLITE; PRINT_ERR("while excecuting insert to rel sql."); return-1; } sqlite3_finalize(stmt); return 1; } /* Returns -2 or error, -1 if program doesn't exist * else the program ID */ int get_program_id(DB *db, const char* name) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dhb_form_query_find_program(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { PRINT_ERR("Error while preparing get_program_id sql."); return -2; } sqlite3_bind_text(stmt, 1, name, strlen(name), 0); int id = -1; while (sqlite3_step(stmt) == SQLITE_ROW) { id = sqlite3_column_int(stmt, 0); break; } sqlite3_finalize(stmt); return id; } int program_exists(DB *db, const char *pName) { if (get_program_id(db, pName) == -1) { return 0; } return 1; } int get_config_id(DB *db, const char* path) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dhb_form_query_find_config(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { PRINT_ERR("while preparing get_config_id sql."); return -2; } sqlite3_bind_text(stmt, 1, path, strlen(path), 0); int id = -1; while (sqlite3_step(stmt) == SQLITE_ROW) { id = sqlite3_column_int(stmt, 0); break; } sqlite3_finalize(stmt); return id; } int add_insert_relationship(DB *db, const int pid, const int cid) { // do checks return insert_to_rel_table(db, pid, cid); } /* Returns the path of the found config via *ret */ int program_has_primary_config(DB *db, const int pid, char *ret, int *sec) { sqlite3_stmt *stmt; int rc; char sql[STR_L]; char condition[STR_S] = TBL_PROGRAM; strcat(condition, "."); strcat(condition, COL_PROGRAM_ID); char selection[STR_M] = COL_CONFIG_PRIMARY; strcat(selection, ", "); strcat(selection, COL_CONFIG_PATH); strcat(selection, ", "); strcat(selection, COL_CONFIG_SECRET); dbh_form_query_select_from_joined_eq(sql, selection, condition); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { PRINT_ERR("whinnle preparing program_has_primary_exists sql."); return -2; } sqlite3_bind_int(stmt, 1, pid); while (sqlite3_step(stmt) == SQLITE_ROW) { if (sqlite3_column_int(stmt, 0) == 1) { if (ret) { strcpy(ret,(char *)sqlite3_column_text(stmt, 1)); } if (sec) { *sec = sqlite3_column_int(stmt, 2); } sqlite3_finalize(stmt); return 1; } } sqlite3_finalize(stmt); return 0; } int add_get_or_insert_config_to_db(DB *db, const int pid, const char *path, const int secret, const int prime) { int cid = get_config_id(db, path); if (cid == -2) { db->error = SQL_ERR_SQLITE; return -1; } /* If config doesnt exist insert it and return it's cid */ if (cid == -1) { if (program_has_primary_config(db, pid, NULL, NULL) && prime) { db->error = SQL_ERR_PRIMARY_REDEFINITION; return -1; } return insert_to_config_table(db, path, secret, prime); } /* If it exist it means the user has inserted the same path twice */ db->error = SQL_CONFIG_PATH_EXISTS; return -1; } int add_get_or_insert_program_to_db(DB *db, const char *name) { int pid = get_program_id(db, name); if (pid == -2) { db->error = SQL_ERR_SQLITE; return -1; } if (pid == -1) { return insert_to_program_table(db, name); } return pid; } int add_transaction_try(DB *db, const AddOpt * const opt) { __BEGIN_TRANSACTION__ int pid = add_get_or_insert_program_to_db(db, opt->progName); if (db->error == SQL_ERR_SQLITE) { PRINT_ERR("Could not insert program to db.\n"); return 1; } int cid = add_get_or_insert_config_to_db(db, pid, opt->confPath, opt->secret, opt->prime); if (db->error == SQL_ERR_SQLITE) { PRINT_ERR("Could not insert config to db.\n"); return 1; } else if (db->error == SQL_CONFIG_PATH_EXISTS) { PRINT_ERR("This config already exists in the database.\n"); return 1; } else if (db->error == SQL_ERR_PRIMARY_REDEFINITION) { PRINT_ERR("This program already has a primary config.\n"); return 1; } add_insert_relationship(db, pid, cid); if (db->error == SQL_ERR_SQLITE) { PRINT_ERR("rel update failed\n"); return 1; } __END_TRANSACTION__ return 0; } int edit_get_prime_config_from_program(DB *db, char *pName, char *ret, int *secret) { int pid = get_program_id(db, pName); /* error */ if (pid == -2) { return -1; } /* program exists */ if (pid > -1) { char path[STR_M]; if (program_has_primary_config(db, pid, path, secret) == 1) { if (!str_is_empty(path)) { if (ret) { char confName[STR_M]; str_make_ck_config_name(confName, path, pName); strcpy(ret, confName); } return 0; } } } /* No prime config found */ return -1; } int edit_get_config(DB *db, const char *pName, char *ret, const char *cName, int *sec) { int pid = get_program_id(db, pName); /* error */ if (pid == -2) { return -1; } /* program exists */ if (pid > -1) { sqlite3_stmt *stmt; int rc; char selection[STR_M] = COL_CONFIG_PATH; strcat(selection, ", "); strcat(selection, COL_CONFIG_SECRET); char condition[STR_M] = TBL_PROGRAM; strcat(condition, "."); strcat(condition, COL_PROGRAM_ID); char sql[STR_L]; dbh_form_query_select_from_joined_eq(sql, selection, condition); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); sqlite3_bind_int(stmt, 1, pid); if (rc != SQLITE_OK) { return -2; } int flag = -1; while (sqlite3_step(stmt) == SQLITE_ROW) { char confName[STR_M]; if (cName) { char *tmp = strdup((char *)sqlite3_column_text(stmt, 0)); if (strcmp(cName, basename(tmp)) == 0) { flag = 0; str_make_ck_config_name(confName, (char *)sqlite3_column_text(stmt, 0), pName); strcpy(ret, confName); if (sec) { *sec = sqlite3_column_int(stmt, 1); } } free(tmp); } else { str_make_ck_config_name(confName, (char *)sqlite3_column_text(stmt, 0), pName); strcpy(ret, confName); flag = 0; if (sec) { *sec = sqlite3_column_int(stmt, 1); } break; } } sqlite3_finalize(stmt); return flag; } return -1; } int get_program_paths(DB *db, cklist *ckl, const char* pName, int bname, int attr) { int pid = get_program_id(db, pName); /* error */ if (pid == -2) { return -1; } /* program exists */ if (pid > -1) { sqlite3_stmt *stmt; int rc; char selection[STR_M] = COL_CONFIG_PATH; strcat(selection, ","); strcat(selection, COL_CONFIG_SECRET); strcat(selection, ","); strcat(selection, COL_CONFIG_PRIMARY); char condition[STR_M] = TBL_PROGRAM; strcat(condition, "."); strcat(condition, COL_PROGRAM_ID); char sql[STR_L]; dbh_form_query_select_from_joined_eq(sql, selection, condition); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); sqlite3_bind_int(stmt, 1, pid); if (rc != SQLITE_OK) { return -2; } while (sqlite3_step(stmt) == SQLITE_ROW) { char *tmp = strdup((char *)sqlite3_column_text(stmt, 0)); char entry[STR_M] = ""; if (bname) { strcat(entry, basename(tmp)); } else { strcat(entry, tmp); } if (attr) { /* secret */ if (sqlite3_column_int(stmt, 1)) { strcat(entry, " [s]"); } /* primary */ if (sqlite3_column_int(stmt, 2)) { strcat(entry, " [p]"); } } list_add(ckl, entry); free(tmp); } sqlite3_finalize(stmt); return 0; } return -1; } int list_get_paths(DB *db, cklist *ckl, int attr) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dbh_form_query_select_paths_with_attributes(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -2; } while (sqlite3_step(stmt) == SQLITE_ROW) { char path[STR_L] = ""; strcat(path, (char *)sqlite3_column_text(stmt, 0)); if (attr) { /* secret */ if (sqlite3_column_int(stmt, 1)) { strcat(path, " [s]"); } /* primary */ if (sqlite3_column_int(stmt, 2)) { strcat(path, " [p]"); } } list_add(ckl, path); } sqlite3_finalize(stmt); return 1; } int list_get_programs(DB *db, cklist *ckl) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dbh_form_query_select_programs(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -2; } while (sqlite3_step(stmt) == SQLITE_ROW) { list_add(ckl, (char *)sqlite3_column_text(stmt, 0)); } sqlite3_finalize(stmt); return 1; } int list_get_path_program_tree(DB *db, cklist *ckl, int attr) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dbh_form_query_select_programs(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -2; } while (sqlite3_step(stmt) == SQLITE_ROW) { char programName[STR_M] = ""; strcat(programName, (char *)sqlite3_column_text(stmt, 0)); strcat(programName, ":"); list_add(ckl, programName); sqlite3_stmt *stmt2; int rc2; char sql2[STR_L]; char selection[STR_M] = COL_CONFIG_PATH; strcat(selection, ","); strcat(selection, COL_CONFIG_SECRET); strcat(selection, ","); strcat(selection, COL_CONFIG_PRIMARY); char condition[STR_M] = TBL_PROGRAM; strcat(condition, "."); strcat(condition, COL_PROGRAM_NAME); dbh_form_query_select_from_joined_eq(sql2, selection, condition); rc2 = sqlite3_prepare_v2(db->ptr, sql2, -1, &stmt2, 0); if (rc2 != SQLITE_OK) { return -2; } sqlite3_bind_text(stmt2, 1, (char *)sqlite3_column_text(stmt, 0), -1, 0); while (sqlite3_step(stmt2) == SQLITE_ROW) { char treePath[STR_L] = "|- "; strcat(treePath, (char *)sqlite3_column_text(stmt2, 0)); if (attr) { /* secret */ if (sqlite3_column_int(stmt2, 1)) { strcat(treePath, " [s]"); } /* primary */ if (sqlite3_column_int(stmt2, 2)) { strcat(treePath, " [p]"); } } list_add(ckl, treePath); } sqlite3_finalize(stmt2); } sqlite3_finalize(stmt); return 1; } int delete_prog(DB *db, int pid) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dbh_form_query_delete_x_from_y(sql, COL_PROGRAM_ID, TBL_PROGRAM); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -1; } sqlite3_bind_int(stmt, 1, pid); sqlite3_step(stmt); if (rc != SQLITE_OK) { return -1; } sqlite3_finalize(stmt); return 0; } int delete_conf(DB *db, int cid) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dbh_form_query_delete_x_from_y(sql, COL_CONFIG_ID, TBL_CONFIG); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -1; } sqlite3_bind_int(stmt, 1, cid); sqlite3_step(stmt); if (rc != SQLITE_OK) { return -1; } sqlite3_finalize(stmt); return 0; } int get_pid_from_cid(DB *db, int cid) { int pid = -1; sqlite3_stmt *stmt; int rc; char sql[STR_M]; dbh_form_query_get_pid_from_cid(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -1; } sqlite3_bind_int(stmt, 1, cid); while (sqlite3_step(stmt) == SQLITE_ROW) { pid = sqlite3_column_int(stmt, 0); } if (rc != SQLITE_OK) { return -1; } sqlite3_finalize(stmt); return pid; } int get_program_relations(DB *db, int pid) { int count = -1; sqlite3_stmt *stmt; int rc; char sql[STR_M]; dbh_form_query_count_program_relations(sql); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -1; } sqlite3_bind_int(stmt, 1, pid); while (sqlite3_step(stmt) == SQLITE_ROW) { count = sqlite3_column_int(stmt, 0); } if (rc != SQLITE_OK) { return -1; } sqlite3_finalize(stmt); return count; } int get_config_number(DB *db, char* pName) { int pid = get_program_id(db, pName); /* program exists */ if (pid > -1) { return get_program_relations(db, pid); } return -1; } /* Removes the relationship of `cid` with the corresponding program. * Returns the program's pid on succes, negative integer otherwise. */ int remove_conf_rel(DB *db, int cid) { sqlite3_stmt *stmt; int rc; int pid = get_pid_from_cid(db, cid); char sql[STR_M]; dbh_form_query_delete_x_from_y(sql, COL_REL_CONFIG_ID, TBL_REL); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -1; } sqlite3_bind_int(stmt, 1, cid); sqlite3_step(stmt); if (rc != SQLITE_OK) { return -1; } sqlite3_finalize(stmt); return pid; } int remove_all_configs(DB *db, int pid) { sqlite3_stmt *stmt; int rc; char sql[STR_M]; dbh_form_query_select_from_joined_eq(sql, COL_REL_CONFIG_ID, COL_REL_PROGRAM_ID); rc = sqlite3_prepare_v2(db->ptr, sql, -1, &stmt, 0); if (rc != SQLITE_OK) { return -2; } sqlite3_bind_int(stmt, 1, pid); while (sqlite3_step(stmt) == SQLITE_ROW) { delete_conf(db, sqlite3_column_int(stmt, 0)); remove_conf_rel(db, sqlite3_column_int(stmt, 0)); } sqlite3_finalize(stmt); return 0; } int del_transaction_try(DB *db, char *arg, int conf) { __BEGIN_TRANSACTION__ int pid = -1; if (conf) { // del conf int cid = get_config_id(db, arg); if (cid < 0) { ERR("Config %s doesn't exist in the database.", arg); return -1; } if (delete_conf(db, cid)) { ERR("Could not delete config from db."); return -1; } // handle relations pid = remove_conf_rel(db, cid); HELP("Deleted config: %s", arg); if (get_program_relations(db, pid) > 0) { goto end; } } else { pid = get_program_id(db, arg); } if (pid < 0) { ERR("Program not found in the db."); return -1; } /* If we are deleting a program we should delete everything that * refferences it (configs and relationships) */ if (!conf) { remove_all_configs(db, pid); } if(delete_prog(db, pid)) { ERR("Could not delete program from db."); return -1; } HELP("Deleted program %s", conf ? "" : arg); end: __END_TRANSACTION__ return 0; }