add remove mount capabilities to cli and ui #62

This commit is contained in:
2025-09-30 11:12:48 -05:00
parent f3719ea361
commit 4e6beed219
12 changed files with 73 additions and 55 deletions

View File

@@ -42,7 +42,7 @@ public:
~lock_data(); ~lock_data();
private: private:
std::string data_directory; std::string data_directory_;
std::string mutex_id_; std::string mutex_id_;
private: private:
@@ -50,12 +50,12 @@ private:
int lock_status_{EWOULDBLOCK}; int lock_status_{EWOULDBLOCK};
private: private:
[[nodiscard]] auto get_state_directory() -> std::string;
[[nodiscard]] auto get_lock_data_file() const -> std::string; [[nodiscard]] auto get_lock_data_file() const -> std::string;
[[nodiscard]] auto get_lock_file() const -> std::string; [[nodiscard]] auto get_lock_file() const -> std::string;
[[nodiscard]] auto get_state_directory() const -> std::string;
private: private:
[[nodiscard]] static auto wait_for_lock(int handle, [[nodiscard]] static auto wait_for_lock(int handle,
std::uint8_t retry_count = 30U) std::uint8_t retry_count = 30U)

View File

@@ -30,13 +30,13 @@ struct rpc_host_info {
std::string user; std::string user;
}; };
enum class rpc_response_type { enum class rpc_response_type : std::uint8_t {
success, success,
config_value_not_found, config_value_not_found,
http_error, http_error,
}; };
struct rpc_response { struct rpc_response final {
rpc_response_type response_type; rpc_response_type response_type;
json data; json data;
}; };

View File

@@ -40,7 +40,8 @@ lock_data::lock_data(std::string data_directory, provider_type prov,
std::string_view unique_id) std::string_view unique_id)
: data_directory_(std::move(data_directory)), : data_directory_(std::move(data_directory)),
mutex_id_(create_lock_id(prov, unique_id)) { mutex_id_(create_lock_id(prov, unique_id)) {
handle_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); handle_ =
::open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
} }
lock_data::~lock_data() { release(); } lock_data::~lock_data() { release(); }
@@ -72,7 +73,7 @@ auto lock_data::get_lock_file() const -> std::string {
} }
auto lock_data::get_mount_state(json &mount_state) -> bool { auto lock_data::get_mount_state(json &mount_state) -> bool {
auto handle = open(get_lock_data_file().c_str(), O_RDWR, S_IWUSR | S_IRUSR); auto handle = ::open(get_lock_data_file().c_str(), O_RDWR, S_IWUSR | S_IRUSR);
if (handle == -1) { if (handle == -1) {
mount_state = { mount_state = {
{"Active", false}, {"Active", false},
@@ -93,14 +94,14 @@ auto lock_data::get_mount_state(json &mount_state) -> bool {
{"PID", -1}, {"PID", -1},
}; };
} }
flock(handle, LOCK_UN); ::flock(handle, LOCK_UN);
} }
close(handle); ::close(handle);
return ret; return ret;
} }
auto lock_data::get_state_directory() -> std::string { auto lock_data::get_state_directory() const -> std::string {
return utils::path::absolute(data_directory_ + "/state"); return utils::path::absolute(data_directory_ + "/state");
} }
@@ -130,7 +131,7 @@ void lock_data::release() {
flock(handle_, LOCK_UN); flock(handle_, LOCK_UN);
} }
close(handle_); ::close(handle_);
handle_ = -1; handle_ = -1;
} }
@@ -139,7 +140,7 @@ auto lock_data::set_mount_state(bool active, std::string_view mount_location,
REPERTORY_USES_FUNCTION_NAME(); REPERTORY_USES_FUNCTION_NAME();
auto handle = auto handle =
open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); ::open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
if (handle == -1) { if (handle == -1) {
return false; return false;
} }
@@ -172,10 +173,10 @@ auto lock_data::set_mount_state(bool active, std::string_view mount_location,
ret = true; ret = true;
} }
flock(handle, LOCK_UN); ::flock(handle, LOCK_UN);
} }
close(handle); ::close(handle);
return ret; return ret;
} }

View File

@@ -22,6 +22,8 @@
#ifndef REPERTORY_INCLUDE_CLI_ACTIONS_HPP_ #ifndef REPERTORY_INCLUDE_CLI_ACTIONS_HPP_
#define REPERTORY_INCLUDE_CLI_ACTIONS_HPP_ #define REPERTORY_INCLUDE_CLI_ACTIONS_HPP_
#include "cli/common.hpp"
#include "cli/check_version.hpp" #include "cli/check_version.hpp"
#include "cli/display_config.hpp" #include "cli/display_config.hpp"
#include "cli/drive_information.hpp" #include "cli/drive_information.hpp"

View File

@@ -49,15 +49,16 @@ check_version(const std::string &data_directory, provider_type prov,
auto client_version = utils::get_version_number(project_get_version()); auto client_version = utils::get_version_number(project_get_version());
auto res = client.check_version(client_version, min_version); auto res = client.check_version(client_version, min_version);
return cli::handle_error( return cli::handle_error(
res == api_error::success || res == api_error::incompatible_version res == api_error::success ? exit_code::success
? res : res == api_error::incompatible_version
? exit_code::incompatible_version
: exit_code::communication_error, : exit_code::communication_error,
fmt::format("required|{}|actual|{}", min_version, client_version)); fmt::format("required|{}|actual|{}", min_version, client_version));
} }
if (prov != provider_type::sia) { if (prov != provider_type::sia) {
return cli::handle_error( return cli::handle_error(
api_error::success, exit_code::success,
fmt::format("version not required|provider|", fmt::format("version not required|provider|",
app_config::get_provider_display_name(prov))); app_config::get_provider_display_name(prov)));
} }
@@ -69,7 +70,7 @@ check_version(const std::string &data_directory, provider_type prov,
std::string required_version; std::string required_version;
std::string returned_version; std::string returned_version;
if (provider.check_version(required_version, returned_version)) { if (provider.check_version(required_version, returned_version)) {
return cli::handle_error(api_error::success, return cli::handle_error(exit_code::success,
fmt::format("required|{}|actual|{}", fmt::format("required|{}|actual|{}",
required_version, returned_version)); required_version, returned_version));
} }

View File

@@ -60,7 +60,7 @@ using remote_instance = repertory::remote_fuse::i_remote_instance;
namespace repertory::cli { namespace repertory::cli {
[[nodiscard]] inline auto handle_error(exit_code code, std::string_view msg) [[nodiscard]] inline auto handle_error(exit_code code, std::string_view msg)
-> exit_code { -> exit_code {
fmt::println("{}", code); fmt::println("{}", static_cast<std::int32_t>(code));
fmt::println("{}", msg); fmt::println("{}", msg);
return code; return code;
} }
@@ -68,7 +68,7 @@ namespace repertory::cli {
[[nodiscard]] inline auto handle_error(exit_code code, [[nodiscard]] inline auto handle_error(exit_code code,
rpc_response_type response_type, rpc_response_type response_type,
std::string_view msg) -> exit_code { std::string_view msg) -> exit_code {
fmt::println("{}", response_type); fmt::println("{}", static_cast<std::uint8_t>(response_type));
fmt::println("{}", msg); fmt::println("{}", msg);
return code; return code;
} }

View File

@@ -41,7 +41,6 @@ namespace repertory::cli::actions {
if (lock_res == lock_result::success) { if (lock_res == lock_result::success) {
app_config config(prov, data_directory); app_config config(prov, data_directory);
const auto cfg = config.get_json(); const auto cfg = config.get_json();
return cli::handle_error(exit_code::success, cfg.dump(2)); return cli::handle_error(exit_code::success, cfg.dump(2));
} }

View File

@@ -63,8 +63,8 @@ remove(std::vector<const char *> /* args */, const std::string &data_directory,
return cli::handle_error( return cli::handle_error(
exit_code::remove_failed, exit_code::remove_failed,
fmt::format("failed to remove provider|type|{}|id|{}|directory|", fmt::format("failed to remove provider|type|{}|id|{}|directory|",
app_config::get_provider_name(prov), unique_id), app_config::get_provider_name(prov), unique_id,
data_directory); data_directory));
} }
} // namespace repertory::cli::actions } // namespace repertory::cli::actions

View File

@@ -31,7 +31,6 @@ unmount(std::vector<const char *> /* args */, const std::string &data_directory,
std::string user, std::string password) -> exit_code { std::string user, std::string password) -> exit_code {
constexpr const std::uint8_t retry_count{30U}; constexpr const std::uint8_t retry_count{30U};
auto ret{exit_code::success};
auto port{app_config::default_api_port(prov)}; auto port{app_config::default_api_port(prov)};
utils::cli::get_api_authentication_data(user, password, port, prov, utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory); data_directory);

View File

@@ -29,7 +29,6 @@
#include "utils/cli_utils.hpp" #include "utils/cli_utils.hpp"
#include "utils/error_utils.hpp" #include "utils/error_utils.hpp"
#include "utils/file.hpp" #include "utils/file.hpp"
#include "utils/path.hpp"
namespace repertory::ui { namespace repertory::ui {
[[nodiscard]] auto ui_main(const std::vector<const char *> &args) -> int { [[nodiscard]] auto ui_main(const std::vector<const char *> &args) -> int {

View File

@@ -539,6 +539,8 @@ void ui_server::handle_get_mount_location(const httplib::Request &req,
void ui_server::handle_get_mount_status(const httplib::Request &req, void ui_server::handle_get_mount_status(const httplib::Request &req,
httplib::Response &res) const { httplib::Response &res) const {
REPERTORY_USES_FUNCTION_NAME();
auto name = req.get_param_value("name"); auto name = req.get_param_value("name");
auto prov = provider_type_from_string(req.get_param_value("type")); auto prov = provider_type_from_string(req.get_param_value("type"));
@@ -548,6 +550,14 @@ void ui_server::handle_get_mount_status(const httplib::Request &req,
} }
auto lines = launch_process(prov, name, {"-status"}); auto lines = launch_process(prov, name, {"-status"});
if (lines.at(0U) != "0") {
throw utils::error::create_exception(function_name, {
"command failed",
lines.at(0U),
});
}
lines.erase(lines.begin());
auto result = nlohmann::json::parse(utils::string::join(lines, '\n')); auto result = nlohmann::json::parse(utils::string::join(lines, '\n'));
if (result.at("Location").get<std::string>().empty()) { if (result.at("Location").get<std::string>().empty()) {

View File

@@ -23,86 +23,93 @@
#include "platform/platform.hpp" #include "platform/platform.hpp"
namespace {
[[nodiscard]] auto get_lock_test_dir() -> std::string {
return repertory::utils::path::combine(repertory::test::get_test_output_dir(),
{"lock"});
}
} // namespace
namespace repertory { namespace repertory {
TEST(lock_data_test, lock_and_unlock) { TEST(lock_data_test, lock_and_unlock) {
{ {
lock_data l(provider_type::sia, "1"); lock_data lock(get_lock_test_dir(), provider_type::sia, "1");
EXPECT_EQ(lock_result::success, l.grab_lock()); EXPECT_EQ(lock_result::success, lock.grab_lock());
std::thread([]() { std::thread([]() {
lock_data l2(provider_type::sia, "1"); lock_data lock2(get_lock_test_dir(), provider_type::sia, "1");
EXPECT_EQ(lock_result::locked, l2.grab_lock(10)); EXPECT_EQ(lock_result::locked, lock2.grab_lock(10));
}).join(); }).join();
} }
std::thread([]() { std::thread([]() {
lock_data l(provider_type::sia, "1"); lock_data lock(get_lock_test_dir(), provider_type::sia, "1");
EXPECT_EQ(lock_result::success, l.grab_lock(10)); EXPECT_EQ(lock_result::success, lock.grab_lock(10));
}).join(); }).join();
#if defined(_WIN32) #if defined(_WIN32)
lock_data l2(provider_type::remote, "1"); lock_data lock2(get_lock_test_dir(), provider_type::remote, "1");
EXPECT_EQ(lock_result::success, l2.grab_lock()); EXPECT_EQ(lock_result::success, lock2.grab_lock());
lock_data l3(provider_type::remote, "2"); lock_data lock3(get_lock_test_dir(), provider_type::remote, "2");
EXPECT_EQ(lock_result::success, l3.grab_lock()); EXPECT_EQ(lock_result::success, lock3.grab_lock());
#endif #endif
} }
#if defined(_WIN32) #if defined(_WIN32)
TEST(lock_data_test, set_and_unset_mount_state) { TEST(lock_data_test, set_and_unset_mount_state) {
lock_data l(provider_type::sia, "1"); lock_data lock(get_lock_test_dir(), provider_type::sia, "1");
EXPECT_TRUE(l.set_mount_state(true, "C:", 99)); EXPECT_TRUE(lock.set_mount_state(true, "C:", 99));
lock_data l2(provider_type::remote, "1"); lock_data lock2(get_lock_test_dir(), provider_type::remote, "1");
EXPECT_TRUE(l2.set_mount_state(true, "D:", 97)); EXPECT_TRUE(lock2.set_mount_state(true, "D:", 97));
lock_data l3(provider_type::remote, "2"); lock_data lock3(get_lock_test_dir(), provider_type::remote, "2");
EXPECT_TRUE(l3.set_mount_state(true, "E:", 96)); EXPECT_TRUE(lock3.set_mount_state(true, "E:", 96));
json mount_state; json mount_state;
EXPECT_TRUE(l.get_mount_state(mount_state)); EXPECT_TRUE(lock.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"C:","PID":99})", EXPECT_STREQ(R"({"Active":true,"Location":"C:","PID":99})",
mount_state.dump().c_str()); mount_state.dump().c_str());
EXPECT_TRUE(l2.get_mount_state(mount_state)); EXPECT_TRUE(lock2.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"D:","PID":97})", EXPECT_STREQ(R"({"Active":true,"Location":"D:","PID":97})",
mount_state.dump().c_str()); mount_state.dump().c_str());
EXPECT_TRUE(l3.get_mount_state(mount_state)); EXPECT_TRUE(lock3.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"E:","PID":96})", EXPECT_STREQ(R"({"Active":true,"Location":"E:","PID":96})",
mount_state.dump().c_str()); mount_state.dump().c_str());
EXPECT_TRUE(l.set_mount_state(false, "C:", 99)); EXPECT_TRUE(lock.set_mount_state(false, "C:", 99));
EXPECT_TRUE(l2.set_mount_state(false, "D:", 98)); EXPECT_TRUE(lock2.set_mount_state(false, "D:", 98));
EXPECT_TRUE(l3.set_mount_state(false, "E:", 97)); EXPECT_TRUE(lock3.set_mount_state(false, "E:", 97));
EXPECT_TRUE(l.get_mount_state(mount_state)); EXPECT_TRUE(lock.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})", EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str()); mount_state.dump().c_str());
EXPECT_TRUE(l2.get_mount_state(mount_state)); EXPECT_TRUE(lock2.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})", EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str()); mount_state.dump().c_str());
EXPECT_TRUE(l3.get_mount_state(mount_state)); EXPECT_TRUE(lock3.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})", EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str()); mount_state.dump().c_str());
} }
#else #else
TEST(lock_data_test, set_and_unset_mount_state) { TEST(lock_data_test, set_and_unset_mount_state) {
lock_data l(provider_type::sia, "1"); lock_data lock(get_lock_test_dir(), provider_type::sia, "1");
EXPECT_TRUE(l.set_mount_state(true, "/mnt/1", 99)); EXPECT_TRUE(lock.set_mount_state(true, "/mnt/1", 99));
json mount_state; json mount_state;
EXPECT_TRUE(l.get_mount_state(mount_state)); EXPECT_TRUE(lock.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":true,"Location":"/mnt/1","PID":99})", EXPECT_STREQ(R"({"Active":true,"Location":"/mnt/1","PID":99})",
mount_state.dump().c_str()); mount_state.dump().c_str());
EXPECT_TRUE(l.set_mount_state(false, "/mnt/1", 99)); EXPECT_TRUE(lock.set_mount_state(false, "/mnt/1", 99));
EXPECT_TRUE(l.get_mount_state(mount_state)); EXPECT_TRUE(lock.get_mount_state(mount_state));
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})", EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
mount_state.dump().c_str()); mount_state.dump().c_str());
} }