diff --git a/CMakeLists.txt b/CMakeLists.txt index e9c9eae..c19e6e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,7 @@ add_custom_target(installer COMMAND mkdir -p ./build/sashimono-installer COMMAND bash -c "cp -r ./build/{sagent,hpfs,hpws,user-install.sh,user-uninstall.sh,contract_template} ./build/sashimono-installer/" COMMAND bash -c "cp -r ./installer/{docker-install.sh,sashimono-install.sh,sashimono-uninstall.sh} ./build/sashimono-installer/" + COMMAND bash -c "cp -r ./dependencies/libblake3.so ./build/sashimono-installer/" COMMAND tar cfz ./build/sashimono-installer.tar.gz --directory=./build/ sashimono-installer COMMAND rm -r ./build/sashimono-installer ) diff --git a/dependencies/user-install.sh b/dependencies/user-install.sh index e560f14..bb678ce 100755 --- a/dependencies/user-install.sh +++ b/dependencies/user-install.sh @@ -6,6 +6,7 @@ cpu=$1 memory=$2 disk=$3 +contract_dir=$4 if [ -z "$cpu" ] || [ -z "$memory" ] || [ -z "$disk" ]; then echo "Expected: user-install.sh " echo "INVALID_PARAMS,INST_ERR" && exit 1 @@ -17,40 +18,40 @@ user="$prefix$suffix" group="sashimonousers" cgroupsuffix="-cg" user_dir=/home/$user -script_dir=$(dirname $(realpath $0)) +script_dir=$(dirname "$(realpath "$0")") docker_bin=$script_dir/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 +{ [ ! -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" - $script_dir/user-uninstall.sh $user + "$script_dir"/user-uninstall.sh "$user" echo "Rolled back the installation." echo "$1,INST_ERR" && exit 1 } # 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 +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." -user_id=$(id -u $user) +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" +! (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. setquota -u -F vfsv0 "$user" "$disk" "$disk" 0 0 / @@ -61,24 +62,49 @@ echo "Configured the resources" echo " export XDG_RUNTIME_DIR=$user_runtime_dir export PATH=$docker_bin:\$PATH -export DOCKER_HOST=$dockerd_socket" >>$user_dir/.bashrc +export DOCKER_HOST=$dockerd_socket" >>"$user_dir"/.bashrc echo "Updated user .bashrc." # Wait until user systemd is functioning. user_systemd="" 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=$(sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user is-system-running 2>/dev/null) [ "$user_systemd" == "running" ] && break done [ "$user_systemd" != "running" ] && rollback "NO_SYSTEMD" echo "Installing rootless dockerd for user." -sudo -H -u $user PATH=$docker_bin:$PATH XDG_RUNTIME_DIR=$user_runtime_dir $docker_bin/dockerd-rootless-setuptool.sh install +sudo -H -u "$user" PATH="$docker_bin":"$PATH" XDG_RUNTIME_DIR="$user_runtime_dir" "$docker_bin"/dockerd-rootless-setuptool.sh install -svcstat=$(sudo -u $user XDG_RUNTIME_DIR=$user_runtime_dir systemctl --user is-active docker.service) +svcstat=$(sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user is-active docker.service) [ "$svcstat" != "active" ] && rollback "NO_DOCKERSVC" echo "Installed rootless dockerd." + +echo "Adding hpfs services for the instance." +echo "[Unit] + Description=Running and monitoring contract fs. + StartLimitIntervalSec=0 + [Service] + Type=simple + ExecStart=$script_dir/hpfs fs $user_dir/$contract_dir/contract_fs $user_dir/$contract_dir/contract_fs/mnt merge=true ugid= trace=err + Restart=on-failure + RestartSec=5 + [Install] + WantedBy=multi-user.target" >"$user_dir"/.config/systemd/user/contract_fs.service + +echo "[Unit] + Description=Running and monitoring ledger fs. + StartLimitIntervalSec=0 + [Service] + Type=simple + ExecStart=$script_dir/hpfs fs $user_dir/$contract_dir/ledger_fs $user_dir/$contract_dir/ledger_fs/mnt merge=true ugid= trace=err + Restart=on-failure + RestartSec=5 + [Install] + WantedBy=multi-user.target" >"$user_dir"/.config/systemd/user/ledger_fs.service + +sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user daemon-reload echo "$user_id,$user,$dockerd_socket,INST_SUC" exit 0 diff --git a/dependencies/user-uninstall.sh b/dependencies/user-uninstall.sh index 005f668..0fcdfca 100755 --- a/dependencies/user-uninstall.sh +++ b/dependencies/user-uninstall.sh @@ -9,13 +9,13 @@ prefix="sashi" cgroupsuffix="-cg" user_dir=/home/$user -user_id=$(id -u $user) +user_id=$(id -u "$user") user_runtime_dir="/run/user/$user_id" -script_dir=$(dirname $(realpath $0)) +script_dir=$(dirname "$(realpath "$0")") docker_bin=$script_dir/dockerbin # Check if users exists. -if [[ `id -u $user 2>/dev/null || echo -1` -ge 0 ]]; then +if [[ $(id -u "$user" 2>/dev/null || echo -1) -ge 0 ]]; then : else echo "NO_USER,UNINST_ERR" @@ -24,11 +24,19 @@ fi echo "Uninstalling user '$user'." +echo "Stopping and cleaning hpfs systemd services." +contract_fs_service="contract_fs" +ledger_fs_service="ledger_fs" +sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user stop "$contract_fs_service" +sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user stop "$ledger_fs_service" +sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user disable "$contract_fs_service" +sudo -u "$user" XDG_RUNTIME_DIR="$user_runtime_dir" systemctl --user disable "$ledger_fs_service" + # Uninstall rootless dockerd. echo "Uninstalling rootless dockerd." -sudo -H -u $user PATH=$docker_bin:$PATH XDG_RUNTIME_DIR=$user_runtime_dir $docker_bin/dockerd-rootless-setuptool.sh uninstall +sudo -H -u "$user" PATH="$docker_bin":"$PATH" XDG_RUNTIME_DIR="$user_runtime_dir" "$docker_bin"/dockerd-rootless-setuptool.sh uninstall echo "Removing rootless docker data." -sudo -H -u $user PATH=$docker_bin:$PATH XDG_RUNTIME_DIR=$user_runtime_dir $docker_bin/rootlesskit rm -rf $user_dir/.local/share/docker +sudo -H -u "$user" PATH="$docker_bin":"$PATH" XDG_RUNTIME_DIR="$user_runtime_dir" "$docker_bin"/rootlesskit rm -rf "$user_dir"/.local/share/docker # Gracefully terminate user processes. echo "Terminating user processes." @@ -40,7 +48,7 @@ fsmounts=$(cat /proc/mounts | cut -d ' ' -f 2 | grep "/home/$user") readarray -t mntarr <<<"$fsmounts" for mnt in "${mntarr[@]}" do - [ -z "$mnt" ] || umount $mnt + [ -z "$mnt" ] || umount "$mnt" done # Force kill user processes. @@ -52,21 +60,21 @@ if [ "$procs" != "0" ]; then procs=$(ps -U root 2>/dev/null | wc -l) if [ "$procs" != "0" ]; then echo "Force killing user processes." - pkill -SIGKILL -u $user + pkill -SIGKILL -u "$user" fi fi echo "Removing cgroups" # Delete config values. -cgdelete -g cpu:$user$cgroupsuffix -cgdelete -g memory:$user$cgroupsuffix +cgdelete -g cpu:"$user"$cgroupsuffix +cgdelete -g memory:"$user"$cgroupsuffix echo "Deleting user '$user'" -userdel $user -rm -r /home/$user +userdel "$user" +rm -r /home/"${user:?}" -[ -d /home/$user ] && echo "NOT_CLEAN,UNINST_ERR" && exit 1 +[ -d /home/"$user" ] && echo "NOT_CLEAN,UNINST_ERR" && exit 1 echo "UNINST_SUC" exit 0 diff --git a/installer/sashimono-install.sh b/installer/sashimono-install.sh index 042307b..9b62ce9 100755 --- a/installer/sashimono-install.sh +++ b/installer/sashimono-install.sh @@ -8,9 +8,9 @@ sashimono_data=/etc/sashimono sashimono_service="sashimono-agent" group="sashimonousers" cgroupsuffix="-cg" -script_dir=$(dirname $(realpath $0)) +script_dir=$(dirname "$(realpath "$0")") -[ -d $sashimono_bin ] && [ ! -z "$(ls -A $sashimono_bin)" ] && \ +[ -d $sashimono_bin ] && [ -n "$(ls -A $sashimono_bin)" ] && echo "Aborting installation. Previous Sashimono installation detected at $sashimono_bin" && exit 1 # Check cgroup rule config exists. @@ -36,20 +36,30 @@ if ! command -v openssl &>/dev/null; then apt-get install -y openssl fi +# Blake3 +if [ ! -f /usr/local/lib/libblake3.so ]; then + cp "$script_dir"/libblake3.so /usr/local/lib/ +fi + +# Libfuse +apt-get install -y fuse3 + +# Update linker library cache. +sudo ldconfig + function rollback() { echo "Rolling back sashimono installation." - $script_dir/sashimono-uninstall.sh - [ -d $tmp ] && rm -r $tmp + "$script_dir"/sashimono-uninstall.sh echo "Rolled back the installation." exit 1 } # Install Sashimono agent binaries into sashimono bin dir. -cp $script_dir/{sagent,hpfs,hpws,user-install.sh,user-uninstall.sh} $sashimono_bin +cp "$script_dir"/{sagent,hpfs,hpws,user-install.sh,user-uninstall.sh} $sashimono_bin chmod -R +x $sashimono_bin # Download and install rootless dockerd. -$script_dir/docker-install.sh $docker_bin +"$script_dir"/docker-install.sh $docker_bin # Check whether docker installation dir is still empty. [ -z "$(ls -A $docker_bin 2>/dev/null)" ] && echo "Rootless Docker installation failed." && rollback @@ -59,7 +69,7 @@ $script_dir/docker-install.sh $docker_bin ! echo "@$group cpu,memory %u$cgroupsuffix" >>/etc/cgrules.conf && echo "Cgroup rule creation failed." && rollback # Setup Sashimono data dir. -cp -r $script_dir/contract_template $sashimono_data +cp -r "$script_dir"/contract_template $sashimono_data $sashimono_bin/sagent new $sashimono_data # Install Sashimono Agent systemd service. diff --git a/src/conf.cpp b/src/conf.cpp index 7fa1de2..7659b4b 100644 --- a/src/conf.cpp +++ b/src/conf.cpp @@ -63,7 +63,7 @@ namespace conf cfg.server.ip_port = {"127.0.0.1", 5000}; cfg.system.max_instance_count = 5; - cfg.system.max_mem_kbytes = 512000; // Total 512MB RAM + cfg.system.max_mem_kbytes = 1024000; // Total 1GB RAM cfg.system.max_cpu_us = 5000000; // CPU cfs period cannot be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000) per instance. cfg.system.max_storage_kbytes = 2048000; // Total 2GB diff --git a/src/hp_manager.cpp b/src/hp_manager.cpp index dc38b1e..194ca08 100644 --- a/src/hp_manager.cpp +++ b/src/hp_manager.cpp @@ -35,7 +35,6 @@ namespace hp constexpr const char *COPY_DIR = "cp -r %s %s"; constexpr const char *MOVE_DIR = "mv %s %s"; constexpr const char *CHOWN_DIR = "chown -R %s:%s %s"; - constexpr const char *RUN_SH = "bash %s %s"; /** * Initialize hp related environment. @@ -192,7 +191,7 @@ namespace hp int user_id; std::string username; - if (install_user(user_id, username, instance_resources.cpu_us, instance_resources.mem_kbytes, instance_resources.storage_kbytes) == -1) + if (install_user(user_id, username, instance_resources.cpu_us, instance_resources.mem_kbytes, instance_resources.storage_kbytes, container_name) == -1) return -1; const std::string contract_dir = util::get_user_contract_dir(username, container_name); @@ -262,7 +261,9 @@ namespace hp write_json_values(d, config_msg) == -1 || read_json_values(d, hpfs_log_level, is_full_history) == -1 || util::write_json_file(config_fd, d) == -1 || - hpfs::start_fs_processes(info.username, contract_dir, hpfs_log_level, is_full_history) == -1) + // [TODO] Will add a environment file to systemd so these settings can be updated from there. + // Settings are hardcoded for now. Will do this change in next PBI. + hpfs::start_hpfs_systemd(info.username) == -1) { LOG_ERROR << "Error when setting up container. name: " << container_name; close(config_fd); @@ -274,7 +275,7 @@ namespace hp { LOG_ERROR << "Error when starting container. name: " << container_name; // Stop started hpfs processes if starting instance failed. - hpfs::stop_fs_processes(info.username); + hpfs::stop_hpfs_systemd(info.username); return -1; } @@ -283,7 +284,7 @@ namespace hp LOG_ERROR << "Error when starting container. name: " << container_name; // Stop started docker and hpfs processes if database update fails. docker_stop(info.username, container_name); - hpfs::stop_fs_processes(info.username); + hpfs::stop_hpfs_systemd(info.username); return -1; } @@ -338,7 +339,7 @@ namespace hp if (docker_stop(info.username, container_name) == -1 || sqlite::update_status_in_container(db, container_name, CONTAINER_STATES[STATES::STOPPED]) == -1 || - hpfs::stop_fs_processes(info.username) == -1) + hpfs::stop_hpfs_systemd(info.username) == -1) { LOG_ERROR << "Error when stopping container. name: " << container_name; return -1; @@ -367,35 +368,10 @@ namespace hp return -1; } - // Read the config file into json document object. - const std::string contract_dir = util::get_user_contract_dir(info.username, container_name); - std::string config_file_path(contract_dir); - config_file_path.append("/cfg/hp.cfg"); - const int config_fd = open(config_file_path.data(), O_RDONLY); - if (config_fd == -1) - { - LOG_ERROR << errno << ": Error opening hp config file " << config_file_path; - return -1; - } - - jsoncons::ojson d; - std::string hpfs_log_level; - bool is_full_history; - if (util::read_json_file(config_fd, d) == -1 || - read_json_values(d, hpfs_log_level, is_full_history) == -1 || - hpfs::start_fs_processes(info.username, contract_dir, hpfs_log_level, is_full_history) == -1) - { - LOG_ERROR << "Error when setting up container. name: " << container_name; - close(config_fd); - return -1; - } - close(config_fd); - - if (docker_start(info.username, container_name) == -1) + if (hpfs::start_hpfs_systemd(info.username) == -1 || + docker_start(info.username, container_name) == -1) { LOG_ERROR << "Error when starting container. name: " << container_name; - // Stop started hpfs processes if starting instance failed. - hpfs::stop_fs_processes(info.username); return -1; } @@ -404,7 +380,7 @@ namespace hp LOG_ERROR << "Error when starting container. name: " << container_name; // Stop started docker and hpfs processes if database update fails. docker_stop(info.username, container_name); - hpfs::stop_fs_processes(info.username); + hpfs::stop_hpfs_systemd(info.username); return -1; } @@ -469,8 +445,7 @@ namespace hp } if (docker_remove(info.username, container_name) == -1 || - sqlite::update_status_in_container(db, container_name, CONTAINER_STATES[STATES::DESTROYED]) == -1 || - hpfs::stop_fs_processes(info.username) == -1) + sqlite::update_status_in_container(db, container_name, CONTAINER_STATES[STATES::DESTROYED]) == -1) { LOG_ERROR << errno << ": Error destroying container " << container_name; return -1; @@ -752,53 +727,6 @@ namespace hp return 0; } - /** - * 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_params Input parameters to the bash script (Optional). - */ - int execute_bash_file(std::string_view file_name, std::vector &output_params, const std::vector &input_params) - { - 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(), params.empty() ? "\0" : params.data()); - - FILE *fpipe = popen(command, "r"); - if (fpipe == NULL) - { - LOG_ERROR << "Error on popen for command " << std::string(command); - return -1; - } - - char buffer[200]; - std::string output; - - // Only take the last cout string It contains the output of the execution. - while (fgets(buffer, sizeof(buffer), fpipe) != NULL) - { - output = buffer; - // Replace ending new line character at the end of the log line. - if (!output.empty()) - { - if (output.back() == '\n') - output.pop_back(); - LOG_DEBUG << output; - } - } - - pclose(fpipe); - util::split_string(output_params, output, ","); - return 0; - } - /** * Create new user and install dependencies and populate id and username. * @param user_id Uid of the created user to be populated. @@ -807,11 +735,11 @@ namespace hp * @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, const size_t max_cpu_us, const size_t max_mem_kbytes, const size_t storage_kbytes) + 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, const std::string container_name) { - const std::vector input_params = {std::to_string(max_cpu_us), std::to_string(max_mem_kbytes), std::to_string(storage_kbytes)}; + const std::vector input_params = {std::to_string(max_cpu_us), std::to_string(max_mem_kbytes), std::to_string(storage_kbytes), container_name}; std::vector output_params; - if (execute_bash_file(conf::ctx.user_install_sh, output_params, input_params) == -1) + if (util::execute_bash_file(conf::ctx.user_install_sh, output_params, input_params) == -1) return -1; if (strncmp(output_params.at(output_params.size() - 1).data(), "INST_SUC", 8) == 0) // If success. @@ -847,7 +775,7 @@ namespace hp { const std::vector input_params = {username}; std::vector output_params; - if (execute_bash_file(conf::ctx.user_uninstall_sh, output_params, input_params) == -1) + if (util::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); diff --git a/src/hp_manager.hpp b/src/hp_manager.hpp index 297d732..7b8769b 100644 --- a/src/hp_manager.hpp +++ b/src/hp_manager.hpp @@ -85,9 +85,7 @@ 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, const std::vector &input_params = {}); - - 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 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, const std::string container_name); int uninstall_user(std::string_view username); } // namespace hp diff --git a/src/hpfs_manager.cpp b/src/hpfs_manager.cpp index 5ab1da3..6c94b47 100644 --- a/src/hpfs_manager.cpp +++ b/src/hpfs_manager.cpp @@ -4,141 +4,25 @@ namespace hpfs { - constexpr const char *PGREP_HPFS = "pgrep -f hpfs -u %s"; - /** - * Starts the hpfs process for the instance. + * Start hpfs systemd services of the instance. * @param username Username of the instance user. - * @param fs_dir File system directory - * @param mount_dir Mount directory. - * @param log_level Log level for the hpfs. - * @param merge Whether changes are needed to be merged. - * @return -1 on error, pid of the spawned hpfs process if success. - */ - int start_hpfs_process(std::string_view username, std::string_view fs_dir, std::string_view mount_dir, std::string_view log_level, const bool merge) - { - util::user_info user; - if (util::get_system_user_info(username, user) == -1) - return -1; - - const pid_t pid = fork(); - - // This check for hpfs FUSE interface is commented out since hpfs fuse fs is running under the instance user. - // So the mount won't be accessible by the root, so stat sys call cannot be used. - // if (pid > 0) - // { - // const ino_t ROOT_INO = 1; - // const uint16_t HPFS_PROCESS_INIT_TIMEOUT = 2000; - // const uint16_t HPFS_INIT_CHECK_INTERVAL = 20; - - // // Sashimono process. - - // LOG_DEBUG << "Starting hpfs process at " << mount_dir << "."; - - // // Wait until hpfs is initialized properly. - // const uint16_t max_retries = HPFS_PROCESS_INIT_TIMEOUT / HPFS_INIT_CHECK_INTERVAL; - // bool hpfs_initialized = false; - // uint16_t retry_count = 0; - // do - // { - // util::sleep(HPFS_INIT_CHECK_INTERVAL); - - // // Check if hpfs process is still running. - // // Sending signal 0 to test whether process exist. - // if (util::kill_process(pid, false, 0) == -1) - // { - // LOG_ERROR << "hpfs process " << pid << " has stopped."; - // break; - // } - - // // We check for the specific inode no. of the mounted root dir. That means hpfs FUSE interface is up. - // struct stat st; - // if (stat(mount_dir.data(), &st) == -1) - // { - // LOG_ERROR << errno << ": Error in checking hpfs status at mount " << mount_dir << "."; - // break; - // } - - // hpfs_initialized = (st.st_ino == ROOT_INO); - // // Keep retrying until root inode no. matches or timeout occurs. - - // } while (!hpfs_initialized && ++retry_count <= max_retries); - - // // Kill the process if hpfs couldn't be initialized properly. - // if (!hpfs_initialized) - // { - // LOG_ERROR << "Couldn't initialize hpfs process at mount " << mount_dir << "."; - // util::kill_process(pid, true); - // return -1; - // } - - // LOG_DEBUG << "hpfs process started. pid:" << pid; - // } - - if (pid == 0) - { - // hpfs process. - util::fork_detach(); - - // Detach hpfs terminal outputs from the sagent terminal, These will be printed in the trace log of particular hpfs mount. - int fd = open("/dev/null", O_WRONLY); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - close(fd); - - const std::string ugid_arg = "ugid="; - const std::string trace_arg = "trace=" + std::string(log_level); - - char *execv_args[] = { - conf::ctx.hpfs_exe_path.data(), - (char *)"fs", - (char *)fs_dir.data(), - (char *)mount_dir.data(), - (char *)(merge ? "merge=true" : "merge=false"), - (char *)ugid_arg.data(), - (char *)trace_arg.data(), - NULL}; - - setgid(user.group_id); - setuid(user.user_id); - const int ret = execv(execv_args[0], execv_args); - std::cerr << errno << ": hpfs process start failed at mount " << mount_dir << ".\n"; - exit(1); - } - else if (pid < 0) - { - LOG_ERROR << errno << ": fork() failed when starting hpfs process at mount " << mount_dir << "."; - return -1; - } - - LOG_DEBUG << "hpfs process started. mount:" << mount_dir << ",pid : " << pid; - return pid; - } - - /** - * Creates hpfs processes for the instance. - * @param username Username of the instance user. - * @param contract_dir Contract directory. - * @param log_level Log level for hpfs. - * @param is_full_history Whether hpfs instances are for full history node. * @return -1 on error and 0 on success. * */ - int start_fs_processes(std::string_view username, const std::string &contract_dir, std::string_view log_level, const bool is_full_history) + int start_hpfs_systemd(const std::string &username) { - std::string fs_path = contract_dir + "/contract_fs"; - std::string mnt_path = fs_path + "/mnt"; - if (start_hpfs_process(username, fs_path, mnt_path, log_level, !is_full_history) <= 0) - { - LOG_ERROR << errno << " : Error occured while starting contract_fs processes - " << contract_dir; - return -1; - } + const std::string contract_fs_start = "sudo -u " + username + " XDG_RUNTIME_DIR=/run/user/$(id -u " + username + ") systemctl --user start contract_fs"; + const std::string contract_fs_enable = "sudo -u " + username + " XDG_RUNTIME_DIR=/run/user/$(id -u " + username + ") systemctl --user enable contract_fs"; + const std::string ledger_fs_start = "sudo -u " + username + " XDG_RUNTIME_DIR=/run/user/$(id -u " + username + ") systemctl --user start ledger_fs"; + const std::string ledger_fs_enable = "sudo -u " + username + " XDG_RUNTIME_DIR=/run/user/$(id -u " + username + ") systemctl --user enable ledger_fs"; - fs_path = contract_dir + "/ledger_fs"; - mnt_path = fs_path + "/mnt"; - if (start_hpfs_process(username, fs_path, mnt_path, log_level, true) <= 0) + if (system(contract_fs_start.c_str()) == -1 || + system(ledger_fs_start.c_str()) == -1 || + system(contract_fs_enable.c_str()) == -1 || + system(ledger_fs_enable.c_str()) == -1) { - LOG_ERROR << errno << " : Error occured while starting ledger_fs processes - " << contract_dir; + LOG_ERROR << "Error stopping and disabling hpfs systemd services for user: " << username; return -1; } @@ -146,40 +30,27 @@ namespace hpfs } /** - * Stop hpfs processes of the instance. + * Stop hpfs systemd services of the instance. * @param username Username of the instance user. * @return -1 on error and 0 on success. * */ - int stop_fs_processes(std::string_view username) + int stop_hpfs_systemd(const std::string &username) { - const int len = 19 + username.length(); - char command[len]; - sprintf(command, PGREP_HPFS, username.data()); + const std::string contract_fs_stop = "sudo -u " + username + " XDG_RUNTIME_DIR=/run/user/$(id -u " + username + ") systemctl --user stop contract_fs"; + const std::string contract_fs_disable = "sudo -u " + username + " XDG_RUNTIME_DIR=/run/user/$(id -u " + username + ") systemctl --user disable contract_fs"; + const std::string ledger_fs_stop = "sudo -u " + username + " XDG_RUNTIME_DIR=/run/user/$(id -u " + username + ") systemctl --user stop ledger_fs"; + const std::string ledger_fs_disable = "sudo -u " + username + " XDG_RUNTIME_DIR=/run/user/$(id -u " + username + ") systemctl --user disable ledger_fs"; - FILE *fpipe = popen(command, "r"); - if (fpipe == NULL) + if (system(contract_fs_stop.c_str()) == -1 || + system(ledger_fs_stop.c_str()) == -1 || + system(contract_fs_disable.c_str()) == -1 || + system(ledger_fs_disable.c_str()) == -1) { - LOG_ERROR << "Error on popen for command: " << std::string(command); + LOG_ERROR << "Error stopping and disabling hpfs systemd services for user: " << username; return -1; } - char buffer[50]; - int pid; - - while (fgets(buffer, sizeof(buffer), fpipe) != NULL) - { - if(util::stoi(buffer, pid) == -1) - { - LOG_ERROR << "Error converting " << buffer << " to a valid pid."; - pclose(fpipe); - return -1; - } - util::kill_process(pid, true); - } - - pclose(fpipe); - return 0; } diff --git a/src/hpfs_manager.hpp b/src/hpfs_manager.hpp index fc457da..910534d 100644 --- a/src/hpfs_manager.hpp +++ b/src/hpfs_manager.hpp @@ -5,8 +5,7 @@ namespace hpfs { - int start_hpfs_process(std::string_view username, std::string_view fs_dir, std::string_view mount_dir, std::string_view log_level, const bool merge); - int start_fs_processes(std::string_view username, const std::string &contract_dir, std::string_view log_level, const bool is_full_history); - int stop_fs_processes(std::string_view username); + int start_hpfs_systemd(const std::string &username); + int stop_hpfs_systemd(const std::string &username); } // namespace hpfs #endif \ No newline at end of file diff --git a/src/util/util.cpp b/src/util/util.cpp index 2e4d2dd..fa46401 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -4,6 +4,7 @@ namespace util { constexpr mode_t DIR_PERMS = 0755; + constexpr const char *RUN_SH = "chmod +x %s && sudo bash %s %s"; // Enable execute permission before running in case bash script does not have the permission. const std::string to_hex(const std::string_view bin) { @@ -391,4 +392,51 @@ namespace util return 0; } + /** + * 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_params Input parameters to the bash script (Optional). + */ + int execute_bash_file(std::string_view file_name, std::vector &output_params, const std::vector &input_params) + { + 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(), params.empty() ? "\0" : params.data()); + + FILE *fpipe = popen(command, "r"); + if (fpipe == NULL) + { + LOG_ERROR << "Error on popen for command " << std::string(command); + return -1; + } + + char buffer[200]; + std::string output; + + // Only take the last cout string It contains the output of the execution. + while (fgets(buffer, sizeof(buffer), fpipe) != NULL) + { + output = buffer; + // Replace ending new line character at the end of the log line. + if (!output.empty()) + { + if (output.back() == '\n') + output.pop_back(); + LOG_DEBUG << output; + } + } + + pclose(fpipe); + util::split_string(output_params, output, ","); + return 0; + } + } // namespace util diff --git a/src/util/util.hpp b/src/util/util.hpp index 40da1c5..d4d1cfd 100644 --- a/src/util/util.hpp +++ b/src/util/util.hpp @@ -58,6 +58,8 @@ namespace util int read_json_file(const int fd, jsoncons::ojson &d); + int execute_bash_file(std::string_view file_name, std::vector &output_params, const std::vector &input_params = {}); + } // namespace util #endif