diff --git a/include/cli.hpp b/include/cli.hpp index a5c89cf..d017716 100644 --- a/include/cli.hpp +++ b/include/cli.hpp @@ -1,5 +1,7 @@ #pragma once +#include enum class LAUNCH_MODE { HELP, INSTALL, UNINSTALL, LIST, RUN }; LAUNCH_MODE launch_mode(const char* str); +bool home_directory(std::string& str); diff --git a/include/dl.hpp b/include/dl.hpp index b89f8dd..3935c52 100644 --- a/include/dl.hpp +++ b/include/dl.hpp @@ -1,4 +1,5 @@ #pragma once +#include class DL { void* m_handle; @@ -8,7 +9,7 @@ public: DL& operator=(const DL& other) = delete; DL& operator=(DL&& other) noexcept; - DL(const char* path); + DL(const std::filesystem::path& dl); ~DL(); void* resolve(const char* symbol) const; diff --git a/include/install.hpp b/include/install.hpp new file mode 100644 index 0000000..3b610e6 --- /dev/null +++ b/include/install.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include + +class InstallManager { + static constexpr const char* INSTALL_PATH = ".rewire/wirekits"; // Relative to home directory + + bool m_valid; + std::filesystem::path m_home_directory; + std::filesystem::path m_install_directory; +public: + InstallManager(const InstallManager& other) = delete; + InstallManager(InstallManager&& other) noexcept; + InstallManager& operator=(const InstallManager& other) = delete; + InstallManager& operator=(InstallManager&& other) noexcept; + + InstallManager(); + ~InstallManager(); + + operator bool() const; + + bool install(const char* from, const char* to); + bool uninstall(const char* name); + bool get(const char* name, std::filesystem::path& path) const; + std::list installs() const; +}; diff --git a/src/cli.cpp b/src/cli.cpp index 3cbf8f5..5516c62 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -1,6 +1,7 @@ #include "cli.hpp" #include +#include LAUNCH_MODE launch_mode(const char* str) { if (std::strcmp(str, "help") == 0) @@ -14,3 +15,12 @@ LAUNCH_MODE launch_mode(const char* str) { else return LAUNCH_MODE::RUN; } + +bool home_directory(std::string& str) { + const char* hd_cstr = std::getenv("HOME"); + if (!hd_cstr) + return false; + + str = std::string{ hd_cstr }; + return true; +} diff --git a/src/dl.cpp b/src/dl.cpp index 9afb54e..de092c7 100644 --- a/src/dl.cpp +++ b/src/dl.cpp @@ -17,17 +17,8 @@ DL& DL::operator=(DL&& other) noexcept { return *this; } -DL::DL(const char* path) : m_handle{ nullptr } { - if (!path) - return; - - m_handle = dlopen(path, RTLD_NOW); - - // Try opening in cwd if library is not known system-wide - if (!m_handle) { - std::string cwd_path = std::string{ "./" } + std::string{ path }; - m_handle = dlopen(cwd_path.c_str(), RTLD_NOW); - } +DL::DL(const std::filesystem::path& dl) : m_handle{ nullptr } { + m_handle = dlopen(dl.c_str(), RTLD_NOW); } DL::~DL() { diff --git a/src/install.cpp b/src/install.cpp new file mode 100644 index 0000000..b910b8c --- /dev/null +++ b/src/install.cpp @@ -0,0 +1,108 @@ +#include "install.hpp" +#include "cli.hpp" + +InstallManager::InstallManager(InstallManager&& other) noexcept { + m_valid = other.m_valid; + other.m_valid = false; + m_home_directory = std::move(other.m_home_directory); + m_install_directory = std::move(other.m_install_directory); +} + +InstallManager& InstallManager::operator=(InstallManager&& other) noexcept { + if (this == &other) + return *this; + + m_valid = other.m_valid; + other.m_valid = false; + m_home_directory = std::move(other.m_home_directory); + m_install_directory = std::move(other.m_install_directory); + return *this; +} + +InstallManager::InstallManager() : m_valid{ false } { + std::string hdstr; + if (!home_directory(hdstr)) + return; + + m_home_directory = std::filesystem::path{ hdstr }; + m_install_directory = m_home_directory / std::filesystem::path{ INSTALL_PATH }; + + if (!std::filesystem::exists(m_install_directory) || !std::filesystem::is_directory(m_install_directory)) { + if (!std::filesystem::create_directories(m_install_directory)) + return; + } + + m_valid = true; +} + +InstallManager::~InstallManager() {} + +InstallManager::operator bool() const { + return m_valid; +} + +bool InstallManager::install(const char* from, const char* to) { + if (!m_valid || !from) + return false; + + std::filesystem::path src { from }; + std::filesystem::path dst; + + if (to) { + dst = std::filesystem::path{ to }; + if (dst != dst.filename()) + return false; // Wirekit name may not include a path + } else { + std::string src_name { src.filename() }; + if (src_name.ends_with(".so")) + dst = std::filesystem::path{ src_name.substr(0, src_name.length() - 3) }; + else + dst = std::filesystem::path{ src_name }; + } + + std::filesystem::path install_dst = m_install_directory / dst; + + if (!std::filesystem::exists(src) || !std::filesystem::is_regular_file(src)) + return false; + + return std::filesystem::copy_file(src, install_dst, std::filesystem::copy_options::overwrite_existing); +} + +bool InstallManager::uninstall(const char* name) { + if (!m_valid || !name) + return false; + + std::filesystem::path target { name }; + if (target != target.filename()) + return false; // Wirekit names never include paths (all are within/relative to installation directory) + + std::filesystem::path install_target = m_install_directory / target; + + if (!std::filesystem::exists(install_target) || !std::filesystem::is_regular_file(install_target)) + return false; + + return std::filesystem::remove(install_target); +} + +bool InstallManager::get(const char* name, std::filesystem::path& path) const { + if (!m_valid || !name) + return false; + + std::filesystem::path target { name }; + if (target != target.filename()) + return false; + + path = m_install_directory / target; + return std::filesystem::exists(path) && std::filesystem::is_regular_file(path); +} + +std::list InstallManager::installs() const { + if (!m_valid) + return {}; + + std::list res; + for (const auto& entry : std::filesystem::directory_iterator{ m_install_directory }) + res.emplace_back(entry.path().filename()); + + return res; +} diff --git a/src/main.cpp b/src/main.cpp index 8ef241e..473b7e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,9 @@ #include +#include #include "cli.hpp" #include "dl.hpp" +#include "install.hpp" int launch_help(int argc, char** argv) { std::cout << "Use rewire via one of the following commands:" << std::endl; @@ -20,6 +22,7 @@ int launch_help(int argc, char** argv) { std::cout << " \"rewire \"" << std::endl; std::cout << " -> The wirekit to use for execution" << std::endl; + std::cout << " -> Can either be installed or in current working directory" << std::endl; std::cout << " -> (Optional) A command to be executed" << std::endl; std::cout << " -> Acts as rewired shell if no command is given" << std::endl; @@ -27,18 +30,106 @@ int launch_help(int argc, char** argv) { } int launch_install(int argc, char** argv) { + if (argc < 3 || argc > 4) { + std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl; + return 1; + } + + InstallManager im; + if (!im) { + std::cout << "Unable to manage installations!" << std::endl; + return 1; + } + + if (!im.install(argv[2], (argc == 4 ? argv[3] : nullptr))) { + std::cout << "Failed to install wirekit!" << std::endl; + return 1; + } + + std::cout << "Successfully installed wirekit!" << std::endl; return 0; } int launch_uninstall(int argc, char** argv) { + if (argc != 3) { + std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl; + return 1; + } + + InstallManager im; + if (!im) { + std::cout << "Unable to manage installations!" << std::endl; + return 1; + } + + if (!im.uninstall(argv[2])) { + std::cout << "Failed to uninstall wirekit!" << std::endl; + return 1; + } + + std::cout << "Successfully uninstalled wirekit!" << std::endl; return 0; } int launch_list(int argc, char** argv) { + if (argc != 2) { + std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl; + return 1; + } + + InstallManager im; + if (!im) { + std::cout << "Unable to access installations!" << std::endl; + return 1; + } + + auto wirekits = im.installs(); + if (wirekits.empty()) { + std::cout << "Currently, no wirekits are installed!" << std::endl; + } else { + std::cout << "Currently, the following wirekits are installed:" << std::endl; + for (const std::string& kit : wirekits) + std::cout << " \"" << kit << "\"" << std::endl; + } + return 0; } int launch_run(int argc, char** argv) { + if (argc < 2) { + std::cout << "Invalid usage, try \"rewire help\" for more information!" << std::endl; + return 1; + } + + InstallManager im; + if (!im) { + std::cout << "Unable to access installations!" << std::endl; + return 1; + } + + std::filesystem::path wirekit_path; + if (!im.get(argv[1], wirekit_path)) { + std::cout << "No wirekit named \"" << argv[1] << "\" is installed!" << std::endl; + return 1; + } + + DL wirekit { wirekit_path }; + if (!wirekit) { + std::cout << "Unable to load wirekit!" << std::endl; + return 1; + } + + /* + TODO + environment.hpp/cpp + class Environment -> kit handles, hooks, trace contexts + class CommandScope -> hooks and trace contexts can only be added while scope holds environment, all cleared on scope exit + class TraceContext -> execution data on individual tracee threads + class TraceContextCollection -> management of all trace contexts and selection of active context + */ + + // TODO -> Execute single command (argc > 2) or shell + return 0; }