From b51633563b93e07b6e0696531fb731c4638da3c8 Mon Sep 17 00:00:00 2001 From: Chalith Desaman Date: Thu, 8 Jul 2021 17:18:01 +0530 Subject: [PATCH] Applying cpu, memory and disk quotas. (#22) CPU and memory quotas using cgroups. Disk quotas using userquota. Limiting max allowed hp instances. --- examples/message-board/message-board.js | 6 +- installer/sashimono-install.sh | 25 +++++++- installer/sashimono-uninstall.sh | 16 ++++- installer/user-install.sh | 32 +++++++++- installer/user-uninstall.sh | 7 +++ src/comm/comm_session.cpp | 12 ++-- src/conf.cpp | 20 +++---- src/conf.hpp | 8 +-- src/hp_manager.cpp | 77 ++++++++++++++++--------- src/hp_manager.hpp | 10 ++-- src/hpfs_manager.cpp | 6 +- src/sqlite.cpp | 29 +++++++++- src/sqlite.hpp | 2 + src/util/util.cpp | 4 +- 14 files changed, 188 insertions(+), 66 deletions(-) diff --git a/examples/message-board/message-board.js b/examples/message-board/message-board.js index 4549cde..73e6457 100644 --- a/examples/message-board/message-board.js +++ b/examples/message-board/message-board.js @@ -21,7 +21,11 @@ const wss = new WebSocket.Server({ server }); wss.on('connection', (ws) => { ws.on('message', (msg) => { - console.log('Received: ', JSON.parse(Buffer.from(msg).toString())); + try { + console.log('Received: ', JSON.parse(Buffer.from(msg).toString())); + } catch (error) { + console.error("Error occured in json parsing." + error); + } }); }); diff --git a/installer/sashimono-install.sh b/installer/sashimono-install.sh index f041a10..31853cc 100755 --- a/installer/sashimono-install.sh +++ b/installer/sashimono-install.sh @@ -5,9 +5,14 @@ sashimono_bin=/usr/bin/sashimono-agent docker_bin=/usr/bin/sashimono-agent/dockerbin sashimono_data=/etc/sashimono +group="sashimonousers" +cgroupsuffix="-cg" echo "Installing Sashimono..." +# Check cgroup rule config exists. +[ ! -f /etc/cgred.conf ] && echo "Cgroup is not configured. Make sure you've installed and configured cgroup-tools." && exit 1 + # Create bin dirs first so it automatically checks for privileged access. mkdir -p $sashimono_bin [ "$?" == "1" ] && echo "Could not create '$sashimono_bin'. Make sure you are running as sudo." && exit 1 @@ -17,8 +22,7 @@ mkdir -p $sashimono_data [ "$?" == "1" ] && echo "Could not create '$sashimono_data'. Make sure you are running as sudo." && exit 1 # Install curl if not exists (required to download installation artifacts). -if ! command -v curl &> /dev/null -then +if ! command -v curl &>/dev/null; then apt-get install -y curl fi @@ -39,7 +43,17 @@ fi # Download docker packages into a tmp dir and extract into docker bin. echo "Installing rootless docker packages into $docker_bin" + +installer_dir=$(pwd) tmp=$(mktemp -d) +function rollback() { + echo "Rolling back sashimono installation." + $installer_dir/sashimono-uninstall.sh + [ -d $tmp ] && rm -r $tmp + echo "Rolled back the installation." + exit 1 +} + cd $tmp curl https://download.docker.com/linux/static/stable/$(uname -m)/docker-20.10.7.tgz --output docker.tgz curl https://download.docker.com/linux/static/stable/$(uname -m)/docker-rootless-extras-20.10.7.tgz --output rootless.tgz @@ -53,5 +67,10 @@ rm -r $tmp # Check whether installation dir is still empty. [ -z "$(ls -A $docker_bin 2>/dev/null)" ] && echo "Installation failed." && exit 1 -echo "Done." +# Setting up cgroup rules. +! groupadd $group && echo "Group creation failed." && rollback +! echo "@$group cpu,memory %u$cgroupsuffix" >> /etc/cgrules.conf && echo "Cgroup rule creation failed." && rollback + +echo "Sashimono installed successfully." +echo "Please restart your cgroup rule generator service or reboot your server for changes to apply." exit 0 diff --git a/installer/sashimono-uninstall.sh b/installer/sashimono-uninstall.sh index 3be866f..de238fd 100755 --- a/installer/sashimono-uninstall.sh +++ b/installer/sashimono-uninstall.sh @@ -3,6 +3,15 @@ sashimono_bin=/usr/bin/sashimono-agent sashimono_data=/etc/sashimono +group="sashimonousers" +cgroupsuffix="-cg" + +[ ! -d $sashimono_bin ] && echo "$sashimono_bin does not exist. Aborting uninstall." && exit 1 + +echo "Are you sure you want to uninstall Sashimono?" +echo "Type 'yes' to confirm uninstall:" +read yes +[ "$yes" != "yes" ] && echo "Uninstall cancelled." && exit 0 # Uninstall all contract instance users prefix="sashi" @@ -44,5 +53,10 @@ rm -r $sashimono_bin echo "Deleting data folder..." rm -r $sashimono_data -echo "Done." +echo "Deleting cgroup rules..." +groupdel $group +sed -i -r "/^@$group\s+cpu,memory\s+%u$cgroupsuffix/d" /etc/cgrules.conf + +echo "Sashimono uninstalled successfully." +echo "Please restart your cgroup rule generator service or reboot your server for changes to apply." exit 0 \ No newline at end of file diff --git a/installer/user-install.sh b/installer/user-install.sh index 69441ca..410307b 100755 --- a/installer/user-install.sh +++ b/installer/user-install.sh @@ -2,14 +2,28 @@ # Sashimono contract instance user installation script. # This is intended to be called by Sashimono agent. +# Check for user cpu and memory quotas. +cpu=$1 +memory=$2 +disk=$3 +if [ -z "$cpu" ] || [ -z "$memory" ] || [ -z "$disk" ]; then + echo "Expected: user-install.sh " + echo "INVALID_PARAMS,INST_ERR" && exit 1 +fi + prefix="sashi" suffix=$(date +%s%N) # Epoch nanoseconds user="$prefix$suffix" +group="sashimonousers" +cgroupsuffix="-cg" user_dir=/home/$user docker_bin=/usr/bin/sashimono-agent/dockerbin # Check if users already exists. -[ `id -u $user 2>/dev/null || echo -1` -ge 0 ] && echo "HAS_USER,INST_ERR" && exit 1 +[ $(id -u $user 2>/dev/null || echo -1) -ge 0 ] && echo "HAS_USER,INST_ERR" && exit 1 + +# Check cgroup mounts exists. +([ ! -d /sys/fs/cgroup/cpu ] || [ ! -d /sys/fs/cgroup/memory ]) && echo "CGROUP_ERR,INST_ERR" && exit 1 function rollback() { echo "Rolling back user installation. $1" @@ -21,6 +35,7 @@ function rollback() { # Setup user and dockerd service. useradd --shell /usr/sbin/nologin -m $user usermod --lock $user +usermod -a -G $group $user loginctl enable-linger $user # Enable lingering to support rootless dockerd service installation. chmod o-rwx $user_dir echo "Created '$user' user." @@ -29,6 +44,18 @@ user_id=$(id -u $user) user_runtime_dir="/run/user/$user_id" dockerd_socket="unix://$user_runtime_dir/docker.sock" +# Setup user cgroup. +! (cgcreate -g cpu:$user$cgroupsuffix && + echo "$cpu" > /sys/fs/cgroup/cpu/$user$cgroupsuffix/cpu.cfs_quota_us) && echo rollback "CGROUP_CPU_CREAT" +! (cgcreate -g memory:$user$cgroupsuffix && + echo "${memory}K" > /sys/fs/cgroup/memory/$user$cgroupsuffix/memory.limit_in_bytes && + echo "${memory}K" > /sys/fs/cgroup/memory/$user$cgroupsuffix/memory.memsw.limit_in_bytes) && echo rollback "CGROUP_MEM_CREAT" + +# Adding disk quota to the new user. +sudo setquota -u -F vfsv0 "$user" "$disk" "$disk" 0 0 / + +echo "Configured the resources" + # Setup env variables for the user. echo " export XDG_RUNTIME_DIR=$user_runtime_dir @@ -38,8 +65,7 @@ echo "Updated user .bashrc." # Wait until user systemd is functioning. user_systemd="" -for (( i=0; i<30; i++ )) -do +for ((i = 0; i < 30; i++)); do sleep 0.1 user_systemd=$(sudo -u $user XDG_RUNTIME_DIR=$user_runtime_dir systemctl --user is-system-running 2>/dev/null) [ "$user_systemd" == "running" ] && break diff --git a/installer/user-uninstall.sh b/installer/user-uninstall.sh index 3bfbf63..db88f1d 100755 --- a/installer/user-uninstall.sh +++ b/installer/user-uninstall.sh @@ -6,6 +6,8 @@ user=$1 # Check whether this is a valid sashimono username. prefix="sashi" [ ${#user} -lt 24 ] || [ ${#user} -gt 32 ] || [[ ! "$user" =~ ^$prefix[0-9]+$ ]] && echo "ARGS,UNINST_ERR" && exit 1 +group="sashimonousers" +cgroupsuffix="-cg" user_dir=/home/$user user_id=$(id -u $user) @@ -55,6 +57,11 @@ if [ "$procs" != "0" ]; then fi +echo "Removing cgroups" +# Delete config values. +cgdelete -g cpu:$user$cgroupsuffix +cgdelete -g memory:$user$cgroupsuffix + echo "Deleting user '$user'" userdel $user rm -r /home/$user diff --git a/src/comm/comm_session.cpp b/src/comm/comm_session.cpp index 1d8325b..c98e6b1 100644 --- a/src/comm/comm_session.cpp +++ b/src/comm/comm_session.cpp @@ -2,12 +2,12 @@ #include "../util/util.hpp" #include "../hp_manager.hpp" -#define __HANDLE_RESPONSE(id, type, content, ret) \ - { \ - std::string res; \ - msg_parser.build_response(res, type, id, content, type == msg::MSGTYPE_CREATE_RES); \ - send(res); \ - return ret; \ +#define __HANDLE_RESPONSE(id, type, content, ret) \ + { \ + std::string res; \ + msg_parser.build_response(res, type, id, content, type == msg::MSGTYPE_CREATE_RES && ret == 0); \ + send(res); \ + return ret; \ } namespace comm diff --git a/src/conf.cpp b/src/conf.cpp index b3fd313..a9c6f90 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -65,9 +65,9 @@ namespace conf cfg.server.ip_port = {}; cfg.system.max_instance_count = 10; - cfg.system.max_mem_bytes = cfg.system.max_instance_count * 50 * 1024 * 1024; // 50MB per instance, Minimum allowed by single docker image is 6MB - cfg.system.max_cpu_micro_seconds = cfg.system.max_instance_count * 1000000; // CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000); - cfg.system.max_storage_bytes = cfg.system.max_instance_count * 100 * 1024 * 1024; // 100MB per instance. + cfg.system.max_mem_kbytes = cfg.system.max_instance_count * 50 * 1024; // 50MB per instance, Minimum allowed by single docker image is 6MB + cfg.system.max_cpu_us = cfg.system.max_instance_count * 1000000; // CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000); + cfg.system.max_storage_kbytes = cfg.system.max_instance_count * 200 * 1024; // 200MB per instance. cfg.log.max_file_count = 50; cfg.log.max_mbytes_per_file = 10; @@ -212,7 +212,7 @@ namespace conf std::cerr << "Configured hp host_address is empty.\n"; return -1; } - + cfg.hp.init_peer_port = hp["init_peer_port"].as(); if (cfg.hp.init_peer_port <= 1024) { @@ -271,9 +271,9 @@ namespace conf { const jsoncons::ojson &system = d["system"]; - cfg.system.max_mem_bytes = system["max_mem_bytes"].as(); - cfg.system.max_cpu_micro_seconds = system["max_cpu_micro_seconds"].as(); - cfg.system.max_storage_bytes = system["max_storage_bytes"].as(); + cfg.system.max_mem_kbytes = system["max_mem_kbytes"].as(); + cfg.system.max_cpu_us = system["max_cpu_us"].as(); + cfg.system.max_storage_kbytes = system["max_storage_kbytes"].as(); cfg.system.max_instance_count = system["max_instance_count"].as(); } catch (const std::exception &e) @@ -345,9 +345,9 @@ namespace conf { jsoncons::ojson system_config; - system_config.insert_or_assign("max_mem_bytes", cfg.system.max_mem_bytes); - system_config.insert_or_assign("max_cpu_micro_seconds", cfg.system.max_cpu_micro_seconds); - system_config.insert_or_assign("max_storage_bytes", cfg.system.max_storage_bytes); + system_config.insert_or_assign("max_mem_kbytes", cfg.system.max_mem_kbytes); + system_config.insert_or_assign("max_cpu_us", cfg.system.max_cpu_us); + system_config.insert_or_assign("max_storage_kbytes", cfg.system.max_storage_kbytes); system_config.insert_or_assign("max_instance_count", cfg.system.max_instance_count); d.insert_or_assign("system", system_config); diff --git a/src/conf.hpp b/src/conf.hpp index d280e4f..daed9ef 100644 --- a/src/conf.hpp +++ b/src/conf.hpp @@ -63,10 +63,10 @@ namespace conf struct system_config { - size_t max_cpu_micro_seconds = 0; // Max CPU time the agent process can consume. - size_t max_mem_bytes = 0; // Max memory the agent process can allocate. - size_t max_storage_bytes = 0; // Max physical storage the agent process can allocate. - size_t max_instance_count = 0; // Max number of instances that can be created. + size_t max_cpu_us = 0; // Max CPU time the agent process can consume. + size_t max_mem_kbytes = 0; // Max memory the agent process can allocate in KB. + size_t max_storage_kbytes = 0; // Max physical storage the agent process can allocate in KB. + size_t max_instance_count = 0; // Max number of instances that can be created. }; struct sa_config diff --git a/src/hp_manager.cpp b/src/hp_manager.cpp index f21dc19..e9d5d50 100644 --- a/src/hp_manager.cpp +++ b/src/hp_manager.cpp @@ -57,9 +57,9 @@ namespace hp // hp_monitor_thread = std::thread(hp_monitor_loop); // Calculate the resources per instance. - instance_resources.cpu_micro_seconds = conf::cfg.system.max_cpu_micro_seconds / conf::cfg.system.max_instance_count; - instance_resources.mem_bytes = conf::cfg.system.max_mem_bytes / conf::cfg.system.max_instance_count; - instance_resources.storage_bytes = conf::cfg.system.max_storage_bytes / conf::cfg.system.max_instance_count; + instance_resources.cpu_us = conf::cfg.system.max_cpu_us / conf::cfg.system.max_instance_count; + instance_resources.mem_kbytes = conf::cfg.system.max_mem_kbytes / conf::cfg.system.max_instance_count; + instance_resources.storage_kbytes = conf::cfg.system.max_storage_kbytes / conf::cfg.system.max_instance_count; return 0; } @@ -136,7 +136,20 @@ namespace hp */ int create_new_instance(instance_info &info, std::string_view owner_pubkey, const std::string &contract_id) { - LOG_INFO << "Resources for instance - CPU: " << instance_resources.cpu_micro_seconds << " MicroS, RAM: " << instance_resources.mem_bytes << " Bytes, Storage: " << instance_resources.storage_bytes << " Bytes."; + // If the max alloved instance count is already allocated. We won't allow more. + const int allocated_count = sqlite::get_allocated_instance_count(db); + if (allocated_count == -1) + { + LOG_ERROR << "Error getting allocated instance count from db."; + return -1; + } + else if (allocated_count >= conf::cfg.system.max_instance_count) + { + LOG_ERROR << "Max instance count is reached."; + return -1; + } + + LOG_INFO << "Resources for instance - CPU: " << instance_resources.cpu_us << " MicroS, RAM: " << instance_resources.mem_kbytes << " KB, Storage: " << instance_resources.storage_kbytes << " KB."; // First check whether contract_id is valid uuid. if (!crypto::verify_uuid(contract_id)) @@ -179,7 +192,7 @@ namespace hp int user_id; std::string username; - if (install_user(user_id, username) == -1) + if (install_user(user_id, username, instance_resources.cpu_us, instance_resources.mem_kbytes, instance_resources.storage_kbytes) == -1) return -1; const std::string contract_dir = util::get_user_contract_dir(username, container_name); @@ -187,7 +200,7 @@ namespace hp if (create_contract(username, owner_pubkey, contract_id, contract_dir, instance_ports, info) == -1 || create_container(username, container_name, contract_dir, instance_ports, info) == -1) { - LOG_ERROR << errno << ": Error creating hp instance for " << owner_pubkey; + LOG_ERROR << "Error creating hp instance for " << owner_pubkey; // Remove user if instance creation failed. uninstall_user(username); return -1; @@ -195,7 +208,7 @@ namespace hp if (sqlite::insert_hp_instance_row(db, info) == -1) { - LOG_ERROR << errno << ": Error creating hp instance for " << owner_pubkey; + LOG_ERROR << "Error creating hp instance for " << owner_pubkey; // Remove container and uninstall user if database update failed. docker_remove(username, container_name); uninstall_user(username); @@ -732,13 +745,20 @@ namespace hp * Executes the given bash file and populates final comma seperated output into a vector. * @param file_name Name of the bash script. * @param output_params Final output of the bash script. - * @param input_param Input parameter to the bash script (Optional). + * @param input_params Input parameters to the bash script (Optional). */ - int execute_bash_file(std::string_view file_name, std::vector &output_params, std::string_view input_param) + int execute_bash_file(std::string_view file_name, std::vector &output_params, const std::vector &input_params) { - const int len = 23 + (file_name.length() * 2) + input_param.length(); + std::string params = ""; + for (auto itr = input_params.begin(); itr != input_params.end(); itr++) + { + params.append(*itr); + if (std::next(itr) != input_params.end()) + params.append(" ");; + } + const int len = 23 + (file_name.length() * 2) + params.length(); char command[len]; - sprintf(command, RUN_SH, file_name.data(), file_name.data(), input_param.empty() ? "\0" : input_param.data()); + sprintf(command, RUN_SH, file_name.data(), file_name.data(), params.empty() ? "\0" : params.data()); FILE *fpipe = popen(command, "r"); if (fpipe == NULL) @@ -772,33 +792,37 @@ namespace hp * Create new user and install dependencies and populate id and username. * @param user_id Uid of the created user to be populated. * @param username Username of the created user to be populated. + * @param max_cpu_us CPU quota allowed for this user. + * @param max_mem_kbytes Memory quota allowed for this user. + * @param storage_kbytes Disk quota allowed for this user. */ - int install_user(int &user_id, std::string &username) + int install_user(int &user_id, std::string &username, const size_t max_cpu_us, const size_t max_mem_kbytes, const size_t storage_kbytes) { - std::vector params; - if (execute_bash_file(conf::ctx.user_install_sh, params) == -1) + const std::vector input_params = {std::to_string(max_cpu_us), std::to_string(max_mem_kbytes), std::to_string(storage_kbytes)}; + std::vector output_params; + if (execute_bash_file(conf::ctx.user_install_sh, output_params, input_params) == -1) return -1; - if (strncmp(params.at(params.size() - 1).data(), "INST_SUC", 8) == 0) // If success. + if (strncmp(output_params.at(output_params.size() - 1).data(), "INST_SUC", 8) == 0) // If success. { - if (util::stoi(params.at(0), user_id) == -1) + if (util::stoi(output_params.at(0), user_id) == -1) { LOG_ERROR << "Create user error: Invalid user id."; return -1; } - username = params.at(1); + username = output_params.at(1); LOG_DEBUG << "Created new user : " << username << ", uid : " << user_id; return 0; } - else if (strncmp(params.at(params.size() - 1).data(), "INST_ERR", 8) == 0) // If error. + else if (strncmp(output_params.at(output_params.size() - 1).data(), "INST_ERR", 8) == 0) // If error. { - const std::string error = params.at(0); + const std::string error = output_params.at(0); LOG_ERROR << "User creation error : " << error; return -1; } else { - const std::string error = params.at(0); + const std::string error = output_params.at(0); LOG_ERROR << "Unknown user creation error : " << error; return -1; } @@ -810,25 +834,26 @@ namespace hp */ int uninstall_user(std::string_view username) { - std::vector params; - if (execute_bash_file(conf::ctx.user_uninstall_sh, params, username) == -1) + const std::vector input_params = {username}; + std::vector output_params; + if (execute_bash_file(conf::ctx.user_uninstall_sh, output_params, input_params) == -1) return -1; // const std::string contract_dir = util::get_user_contract_dir(info.username, container_name); - if (strncmp(params.at(params.size() - 1).data(), "UNINST_SUC", 8) == 0) // If success. + if (strncmp(output_params.at(output_params.size() - 1).data(), "UNINST_SUC", 8) == 0) // If success. { LOG_DEBUG << "Deleted the user : " << username; return 0; } - if (strncmp(params.at(params.size() - 1).data(), "UNINST_ERR", 8) == 0) // If error. + if (strncmp(output_params.at(output_params.size() - 1).data(), "UNINST_ERR", 8) == 0) // If error. { - const std::string error = params.at(0); + const std::string error = output_params.at(0); LOG_ERROR << "User removing error : " << error; return -1; } else { - const std::string error = params.at(0); + const std::string error = output_params.at(0); LOG_ERROR << "Unknown user removing error : " << error; return -1; } diff --git a/src/hp_manager.hpp b/src/hp_manager.hpp index a965d3c..297d732 100644 --- a/src/hp_manager.hpp +++ b/src/hp_manager.hpp @@ -47,9 +47,9 @@ namespace hp struct resources { - size_t cpu_micro_seconds = 0; // CPU time an instance can consume. - size_t mem_bytes = 0; // Memory an instance can allocate. - size_t storage_bytes = 0; // Physical storage an instance can allocate. + size_t cpu_us = 0; // CPU time an instance can consume. + size_t mem_kbytes = 0; // Memory an instance can allocate. + size_t storage_kbytes = 0; // Physical storage an instance can allocate. }; int init(); @@ -85,9 +85,9 @@ namespace hp int write_json_values(jsoncons::ojson &d, const msg::initiate_msg &config_msg); - int execute_bash_file(std::string_view file_name, std::vector &output_params, std::string_view input_param = {}); + int execute_bash_file(std::string_view file_name, std::vector &output_params, const std::vector &input_params = {}); - int install_user(int &user_id, std::string &username); + int install_user(int &user_id, std::string &username, const size_t max_cpu_us, const size_t max_mem_kbytes, const size_t storage_kbytes); int uninstall_user(std::string_view username); } // namespace hp diff --git a/src/hpfs_manager.cpp b/src/hpfs_manager.cpp index e1e5ab1..adf08cd 100644 --- a/src/hpfs_manager.cpp +++ b/src/hpfs_manager.cpp @@ -176,11 +176,7 @@ namespace hpfs util::kill_process(pid, true); } - if (pclose(fpipe) != 0) - { - LOG_ERROR << errno << ": Error getting pids of hpfs for user: " << username; - return -1; - } + pclose(fpipe); return 0; } diff --git a/src/sqlite.cpp b/src/sqlite.cpp index d248594..96645fe 100644 --- a/src/sqlite.cpp +++ b/src/sqlite.cpp @@ -37,6 +37,8 @@ namespace sqlite constexpr const char *IS_CONTAINER_EXISTS = "SELECT username, status, peer_port, user_port FROM instances WHERE name = ?"; + constexpr const char *GET_ALOCATED_INSTANCE_COUNT = "SELECT COUNT(name) FROM instances WHERE status != ?"; + constexpr const char *GET_RUNNING_INSTANCE_NAMES = "SELECT name FROM instances WHERE status = ?"; constexpr const char *GET_RUNNING_INSTANCE_USER_AND_NAME_LIST = "SELECT username,name FROM instances WHERE status = ?"; @@ -330,7 +332,7 @@ namespace sqlite return 0; } - LOG_ERROR << "Error inserting hp instance record. " << sqlite3_errmsg(db); + LOG_ERROR << errno << ": Error inserting hp instance record. " << sqlite3_errmsg(db); return -1; } @@ -516,4 +518,29 @@ namespace sqlite // Finalize and distroys the statement. sqlite3_finalize(stmt); } + + /** + * Get count of running instances + * @param db Database connection. + * @return Count on success -1 on error. + */ + int get_allocated_instance_count(sqlite3 *db) + { + sqlite3_stmt *stmt; + std::string_view destroyed_status(hp::CONTAINER_STATES[hp::STATES::DESTROYED]); + + if (sqlite3_prepare_v2(db, GET_ALOCATED_INSTANCE_COUNT, -1, &stmt, 0) == SQLITE_OK && stmt != NULL && + sqlite3_bind_text(stmt, 1, destroyed_status.data(), destroyed_status.length(), SQLITE_STATIC) == SQLITE_OK && + sqlite3_step(stmt) == SQLITE_ROW) + { + const uint64_t count = sqlite3_column_int64(stmt, 0); + // Finalize and distroys the statement. + sqlite3_finalize(stmt); + return count; + } + + // Finalize and distroys the statement. + sqlite3_finalize(stmt); + return -1; + } } diff --git a/src/sqlite.hpp b/src/sqlite.hpp index 6eee204..83c31b5 100644 --- a/src/sqlite.hpp +++ b/src/sqlite.hpp @@ -78,5 +78,7 @@ namespace sqlite void get_running_instance_names(sqlite3 *db, std::vector &running_instance_names); void get_running_instance_user_and_name_list(sqlite3 *db, std::vector> &running_instances); + + int get_allocated_instance_count(sqlite3 *db); } #endif diff --git a/src/util/util.cpp b/src/util/util.cpp index 9a629e6..2e4d2dd 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -3,6 +3,8 @@ namespace util { + constexpr mode_t DIR_PERMS = 0755; + const std::string to_hex(const std::string_view bin) { // Allocate the target string. @@ -83,7 +85,7 @@ namespace util free(path2); // Create this dir. - if (!error_thrown && mkdir(path.data(), S_IRWXU | S_IRWXG | S_IROTH) == -1) + if (!error_thrown && mkdir(path.data(), DIR_PERMS) == -1) { std::cerr << errno << ": Error in recursive dir creation. " << path << std::endl; error_thrown = true;