/* add.c - the add action ----------------------------------------------*- C -*- * * This file is part of ck, the config keeper * * ----------------------------------------------------------------------------- * * Copyright (C) 2019 Anastasis Grammenos * GPLv3 (see LICENCE for the full notice) * * -------------------------------------------------------------------------- */ #include #include "dblayer.h" #include "ckerrlog.h" ERRLOG(add); static 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, (int)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; } static 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) { 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, (int)strlen(name), 0); if (sqlite3_step(stmt) != SQLITE_DONE) { ERR("while excecuting insert to program sql."); db->error = SQL_ERR_SQLITE; return -1; } sqlite3_finalize(stmt); return id; } static 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) { 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, (int)strlen(path), 0); sqlite3_bind_int(stmt, 3, secret); sqlite3_bind_int(stmt, 4, prime); if (sqlite3_step(stmt) != SQLITE_DONE) { ERR("Error while excecuting insert to config sql."); db->error = SQL_ERR_SQLITE; return-1; } sqlite3_finalize(stmt); return id; } static int add_get_or_insert_config_to_db(DB *db, const int pid, const char *path, const int secret, const int prime, const char *home) { char tpath[STR_L] = ""; if (!swap_home_with_tilde(tpath, path, home)) { strcpy(tpath, path); } int cid = get_config_id(db, tpath); 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, tpath, secret, prime); } /* If it exist it means the user has inserted the same path twice */ db->error = SQL_CONFIG_PATH_EXISTS; return -1; } static 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; } static int add_basename_exists(DB *db, const char *pName, const char *path) { cklist *baseNames = list_make_new(); get_program_paths(db, baseNames, pName, 1 /*basename */, 0, NULL); char *tmp = strdup(path); int rc = list_exists(baseNames, basename(tmp)); free(tmp); list_free(baseNames); return rc; } static int add_insert_relationship(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; 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; ERR("while excecuting insert to rel sql."); return-1; } sqlite3_finalize(stmt); return 1; } /* Returns 1 in error, 0 otherwise */ static int add_transaction_try(DB *db, const AddOpt * const opt, const char *home) { __BEGIN_TRANSACTION__ int pid = add_get_or_insert_program_to_db(db, opt->progName); if (db->error == SQL_ERR_SQLITE) { ERR("Could not insert program to db."); return 1; } if (add_basename_exists(db, opt->progName, opt->confPath)) { ERR("Cannot have two configs with the same basename, for the same program."); return 1; } int cid = add_get_or_insert_config_to_db(db, pid, opt->confPath, opt->secret, opt->prime, home); if (db->error == SQL_ERR_SQLITE) { ERR("Could not insert config to db."); return 1; } else if (db->error == SQL_CONFIG_PATH_EXISTS) { ERR("This config already exists in the database."); return 1; } else if (db->error == SQL_ERR_PRIMARY_REDEFINITION) { ERR("This program already has a primary config."); return 1; } add_insert_relationship(db, pid, cid); if (db->error == SQL_ERR_SQLITE) { ERR("rel update failed\n"); return 1; } __END_TRANSACTION__ return 0; } static int link_config(const AddOpt *opt, const char* newPath) { hLOG("Linking %s -> %s", newPath, opt->confPath); if (util_symlink_file(newPath, opt->confPath) != 0) { ERR("Could not link file."); return -1; } return 0; } static int move_config(const AddOpt *opt, char *progDir, char *ret) { char newPath[STR_L] = ""; char *tmp = strdup(opt->confPath); str_join_dirname_with_basename(newPath, progDir, basename(tmp)); free(tmp); if (util_file_exists(newPath, NULL)) { ERR("File already exists"); return -1; } strcpy(ret, newPath); hLOG("Moving %s -> %s", opt->confPath, newPath); if (util_move_file(opt->confPath, newPath) != 0) { ERR("Could not move file."); return -1; } return 0; } static AddOpt add_make_options(cklist *args, DB *db) { list_rewind(args); /* since we are here, the first two arguments must exist */ AddOpt addOpt = { .progName = list_get(args), .secret = 0, .prime = 0, .err = ADD_NO_ERR }; list_next(args); strcpy(addOpt.confPath, list_get(args)); if (!util_is_file_rw(addOpt.confPath)) { addOpt.err = ADD_ERR_WRONG_CONFIG; return addOpt; } if (util_is_file_link(addOpt.confPath)) { addOpt.err = ADD_ERR_LINK_CONFIG; return addOpt; } realpath(list_get(args), addOpt.confPath); while (list_next(args)) { if (strcmp(list_get(args), "-s") == 0 && addOpt.secret == 0) { if (!secret_enabled(db)) { addOpt.err = ADD_ERR_NO_SECRET; return addOpt; } addOpt.secret = 1; } else if (strcmp(list_get(args), "-p") == 0 && addOpt.prime == 0) { addOpt.prime = 1; } else { addOpt.err = ADD_ERR_WRONG_FLAGS; return addOpt; } } list_rewind(args); return addOpt; } static void add_print_opts(AddOpt *opt) { printf("Program:\t%s\nConfig:\t\t%s\n", opt->progName, opt->confPath); if (opt->prime && opt->secret) { printf("Options:\tsecret, primary\n"); } else if (opt->prime) { printf("Options:\tprimary\n"); } else if (opt->secret) { printf("Options:\tsecret\n"); } } static void get_or_make_program_dir(const AddOpt *opt, const Conf *conf, char *ret) { char tmp[STR_L] = ""; str_join_dirname_with_basename(tmp, opt->secret ? conf->scrt_dir : conf->vc_dir, opt->progName); if (!util_file_exists(tmp, NULL)) { util_mkdir(tmp); } strcpy(ret, tmp); } static int add_make_link(const AddOpt *opt, const Conf *conf) { char progDir[STR_L] = ""; get_or_make_program_dir(opt, conf, progDir); char newPath[STR_L] = ""; if (move_config(opt, progDir, newPath)) { return -1; } if (link_config(opt, newPath)) { return -1; } return 0; } int run_ADD(UserOpt * opt, Conf *conf) { DB db; if (open_DB(&db, opt)) { return -1; } AddOpt addOpt = add_make_options(opt->args, &db); switch (addOpt.err) { case ADD_NO_ERR: break; case ADD_ERR_NO_SECRET: ERR("Secret is not enabled for this ck instance."); goto error; case ADD_ERR_LINK_CONFIG: ERR("%s is a link.", addOpt.confPath); goto error; case ADD_ERR_WRONG_CONFIG: ERR("%s doesn't exist.", addOpt.confPath); goto error; case ADD_ERR_WRONG_FLAGS: ERR("Flags are: -s for secret and -p for primary."); goto error; } add_print_opts(&addOpt); /* Try adding the new config to the DB */ if (add_transaction_try(&db, &addOpt, conf->home_dir)) { goto error; } if (add_make_link(&addOpt, conf)) { error: close_DB(&db); sERR("Could not complete add transaction."); return -1; } close_DB(&db); hLOG("ckdb updated succesfully."); return 0; } void print_ADD_help() { HELP("ck add PROGRAM_NAME CONFIG_PATH [-p] [-s]"); }