[add remove mount capabilities to cli and ui #62] [refactor cli messages and error handling]

This commit is contained in:
2025-09-30 10:36:23 -05:00
parent 6ceb687e78
commit f3719ea361
28 changed files with 451 additions and 332 deletions

View File

@@ -31,6 +31,7 @@
* Fixed intermittent client hang on remote mount server disconnect
* Implemented POSIX-compliant `unlink()` with FUSE `hard_remove`
* Open handles remain valid after `unlink()`
* Refactored CLI messages and error handling to use common methods
## v2.0.7-release

View File

@@ -30,7 +30,8 @@ class i_provider;
class lock_data final {
public:
lock_data(provider_type prov, std::string_view unique_id);
lock_data(std::string data_directory, provider_type prov,
std::string_view unique_id);
lock_data(const lock_data &) = delete;
lock_data(lock_data &&) = delete;
@@ -41,6 +42,7 @@ public:
~lock_data();
private:
std::string data_directory;
std::string mutex_id_;
private:
@@ -48,7 +50,7 @@ private:
int lock_status_{EWOULDBLOCK};
private:
[[nodiscard]] static auto get_state_directory() -> std::string;
[[nodiscard]] auto get_state_directory() -> std::string;
[[nodiscard]] auto get_lock_data_file() const -> std::string;

View File

@@ -30,7 +30,8 @@ class i_provider;
class lock_data final {
public:
explicit lock_data(provider_type prov, std::string unique_id);
explicit lock_data(std::string data_directory, provider_type prov,
std::string unique_id);
lock_data(const lock_data &) = delete;
lock_data(lock_data &&) = delete;
@@ -40,6 +41,7 @@ public:
auto operator=(lock_data &&) -> lock_data & = delete;
private:
std::string dir_id_;
std::string mutex_id_;
HANDLE mutex_handle_{INVALID_HANDLE_VALUE};
DWORD mutex_state_{WAIT_FAILED};

View File

@@ -207,7 +207,8 @@ enum class exit_code : std::int32_t {
exception = -20,
provider_offline = -21,
ui_failed = -22,
remove_failed = -23
remove_failed = -23,
mount_not_found = -24,
};
enum http_error_codes : std::int32_t {

View File

@@ -49,6 +49,7 @@ inline const option pin_file_option = {"-pf", "--pin_file"};
inline const option pinned_status_option = {"-ps", "--pinned_status"};
inline const option password_option = {"-pw", "--password"};
inline const option remote_mount_option = {"-rm", "--remote_mount"};
inline const option remove_option = {"-rp", "--remove"};
inline const option set_option = {"-set", "--set"};
inline const option status_option = {"-status", "--status"};
inline const option test_option = {"-test", "--test"};
@@ -80,6 +81,7 @@ inline const std::vector<option> option_list = {
pin_file_option,
pinned_status_option,
remote_mount_option,
remove_option,
set_option,
status_option,
test_option,

View File

@@ -36,8 +36,10 @@
#include "utils/unix.hpp"
namespace repertory {
lock_data::lock_data(provider_type prov, std::string_view unique_id)
: mutex_id_(create_lock_id(prov, unique_id)) {
lock_data::lock_data(std::string data_directory, provider_type prov,
std::string_view unique_id)
: data_directory_(std::move(data_directory)),
mutex_id_(create_lock_id(prov, unique_id)) {
handle_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
}
@@ -99,13 +101,7 @@ auto lock_data::get_mount_state(json &mount_state) -> bool {
}
auto lock_data::get_state_directory() -> std::string {
#if defined(__APPLE__)
return utils::path::absolute("~/Library/Application Support/" +
std::string{REPERTORY_DATA_NAME} + "/state");
#else // !defined(__APPLE__)
return utils::path::absolute("~/.local/" + std::string{REPERTORY_DATA_NAME} +
"/state");
#endif // defined(__APPLE__)
return utils::path::absolute(data_directory_ + "/state");
}
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {

View File

@@ -26,13 +26,27 @@
#include "events/event_system.hpp"
#include "events/types/filesystem_item_added.hpp"
#include "providers/i_provider.hpp"
#include "utils/collection.hpp"
#include "utils/config.hpp"
#include "utils/error_utils.hpp"
#include "utils/hash.hpp"
#include "utils/string.hpp"
namespace {
[[nodiscard]] auto create_lock_key(std::string_view dir_id,
std::string_view mutex_id) -> std::string {
return fmt::format(R"(SOFTWARE\{}\Lock\{}\{})",
repertory::REPERTORY_DATA_NAME, dir_id, mutex_id)
}
} // namespace
namespace repertory {
lock_data::lock_data(provider_type prov, std::string unique_id)
: mutex_id_(create_lock_id(prov, unique_id)),
lock_data::lock_data(std::string_view data_directory, provider_type prov,
std::string unique_id)
: dir_id_(
utils::collection::to_hex_string(utils::hash::create_hash_blake2b_64(
utils::string::to_lower(data_directory)))),
mutex_id_(create_lock_id(prov, unique_id)),
mutex_handle_(::CreateMutex(nullptr, FALSE,
create_lock_id(prov, unique_id).c_str())) {}
@@ -43,10 +57,8 @@ auto lock_data::get_current_mount_state(json &mount_state) -> bool {
HKEY key{};
if (::RegOpenKeyEx(HKEY_CURRENT_USER,
fmt::format(R"(SOFTWARE\{}\Mounts\{})",
REPERTORY_DATA_NAME, mutex_id_)
.c_str(),
0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) {
create_lock_key(dir_id_, mutex_id_).c_str(), 0,
KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) {
return true;
}
@@ -150,10 +162,8 @@ auto lock_data::set_mount_state(bool active, std::string_view mount_location,
HKEY key{};
if (::RegCreateKeyExA(HKEY_CURRENT_USER,
fmt::format(R"(SOFTWARE\{}\Mounts\{})",
REPERTORY_DATA_NAME, mutex_id_)
.c_str(),
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key,
create_lock_key(dir_id_, mutex_id_).c_str(), 0, nullptr,
0, KEY_ALL_ACCESS, nullptr, &key,
nullptr) != ERROR_SUCCESS) {
return false;
}
@@ -162,11 +172,10 @@ auto lock_data::set_mount_state(bool active, std::string_view mount_location,
if (mount_location.empty() && not active) {
::RegCloseKey(key);
if (::RegCreateKeyExA(
HKEY_CURRENT_USER,
fmt::format(R"(SOFTWARE\{}\Mounts)", REPERTORY_DATA_NAME).c_str(),
0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key,
nullptr) != ERROR_SUCCESS) {
if (::RegCreateKeyExA(HKEY_CURRENT_USER,
create_lock_key(dir_id_, mutex_id_).c_str(), 0,
nullptr, 0, KEY_ALL_ACCESS, nullptr, &key,
nullptr) != ERROR_SUCCESS) {
return false;
}

View File

@@ -35,6 +35,7 @@
#include "cli/open_files.hpp"
#include "cli/pin_file.hpp"
#include "cli/pinned_status.hpp"
#include "cli/remove.hpp"
#include "cli/set.hpp"
#include "cli/status.hpp"
#include "cli/test.hpp"
@@ -55,26 +56,66 @@ struct option_hasher {
inline const std::unordered_map<utils::cli::option, action, option_hasher>
option_actions = {
{utils::cli::options::display_config_option,
cli::actions::display_config},
{utils::cli::options::drive_information_option,
cli::actions::drive_information},
{utils::cli::options::get_directory_items_option,
cli::actions::get_directory_items},
{utils::cli::options::get_item_info_option,
cli::actions::get_item_info},
{utils::cli::options::get_option, cli::actions::get},
{utils::cli::options::get_pinned_files_option,
cli::actions::get_pinned_files},
{utils::cli::options::open_files_option, cli::actions::open_files},
{utils::cli::options::pin_file_option, cli::actions::pin_file},
{utils::cli::options::pinned_status_option,
cli::actions::pinned_status},
{utils::cli::options::set_option, cli::actions::set},
{utils::cli::options::status_option, cli::actions::status},
{utils::cli::options::test_option, cli::actions::test},
{utils::cli::options::unmount_option, cli::actions::unmount},
{utils::cli::options::unpin_file_option, cli::actions::unpin_file},
{
utils::cli::options::display_config_option,
cli::actions::display_config,
},
{
utils::cli::options::drive_information_option,
cli::actions::drive_information,
},
{
utils::cli::options::get_directory_items_option,
cli::actions::get_directory_items,
},
{
utils::cli::options::get_item_info_option,
cli::actions::get_item_info,
},
{
utils::cli::options::get_option,
cli::actions::get,
},
{
utils::cli::options::get_pinned_files_option,
cli::actions::get_pinned_files,
},
{
utils::cli::options::open_files_option,
cli::actions::open_files,
},
{
utils::cli::options::pin_file_option,
cli::actions::pin_file,
},
{
utils::cli::options::pinned_status_option,
cli::actions::pinned_status,
},
{
utils::cli::options::remove_option,
cli::actions::remove,
},
{
utils::cli::options::set_option,
cli::actions::set,
},
{
utils::cli::options::status_option,
cli::actions::status,
},
{
utils::cli::options::test_option,
cli::actions::test,
},
{
utils::cli::options::unmount_option,
cli::actions::unmount,
},
{
utils::cli::options::unpin_file_option,
cli::actions::unpin_file,
},
};
[[nodiscard]] inline auto

View File

@@ -32,6 +32,11 @@ namespace repertory::cli::actions {
check_version(const std::string &data_directory, provider_type prov,
const std::string &remote_host, std::uint16_t remote_port)
-> exit_code {
auto ret = cli::check_data_directory(data_directory, prov);
if (ret != exit_code::success) {
return ret;
}
if (prov == provider_type::remote) {
app_config config(prov, data_directory);
auto remote_cfg = config.get_remote_config();
@@ -43,25 +48,18 @@ check_version(const std::string &data_directory, provider_type prov,
std::uint32_t min_version{};
auto client_version = utils::get_version_number(project_get_version());
auto res = client.check_version(client_version, min_version);
if (res == api_error::success) {
fmt::println("0\nSuccess:\n\tRequired: {}\n\tActual: {}", min_version,
client_version);
} else {
fmt::println("1\nFailed:\n\tRequired: {}\n\tActual: {}", min_version,
client_version);
}
return res == api_error::success ? exit_code::success
: res == api_error::incompatible_version
? exit_code::incompatible_version
: exit_code::communication_error;
return cli::handle_error(
res == api_error::success || res == api_error::incompatible_version
? res
: exit_code::communication_error,
fmt::format("required|{}|actual|{}", min_version, client_version));
}
if (prov != provider_type::sia) {
fmt::println(
"0\nSuccess:\n\tNo specific version is required for {} providers",
app_config::get_provider_display_name(prov));
return exit_code::success;
return cli::handle_error(
api_error::success,
fmt::format("version not required|provider|",
app_config::get_provider_display_name(prov)));
}
app_config config(prov, data_directory);
@@ -71,14 +69,14 @@ check_version(const std::string &data_directory, provider_type prov,
std::string required_version;
std::string returned_version;
if (provider.check_version(required_version, returned_version)) {
fmt::println("0\nSuccess:\n\tRequired: {}\n\tActual: {}", required_version,
returned_version);
return exit_code::success;
return cli::handle_error(api_error::success,
fmt::format("required|{}|actual|{}",
required_version, returned_version));
}
fmt::println("1\nFailed:\n\tRequired: {}\n\tActual: {}", required_version,
returned_version);
return exit_code::incompatible_version;
return cli::handle_error(
exit_code::incompatible_version,
fmt::format("required|{}|actual|{}", required_version, returned_version));
}
} // namespace repertory::cli::actions

View File

@@ -33,6 +33,7 @@
#include "types/rpc.hpp"
#include "utils/cli_utils.hpp"
#include "utils/file.hpp"
#include "utils/path.hpp"
#include "utils/string.hpp"
#include "version.hpp"
@@ -56,4 +57,34 @@ using remote_drive = repertory::remote_fuse::remote_fuse_drive;
using remote_instance = repertory::remote_fuse::i_remote_instance;
#endif // defined(_WIN32)
namespace repertory::cli {
[[nodiscard]] inline auto handle_error(exit_code code, std::string_view msg)
-> exit_code {
fmt::println("{}", code);
fmt::println("{}", msg);
return code;
}
[[nodiscard]] inline auto handle_error(exit_code code,
rpc_response_type response_type,
std::string_view msg) -> exit_code {
fmt::println("{}", response_type);
fmt::println("{}", msg);
return code;
}
[[nodiscard]] inline auto check_data_directory(std::string_view data_directory,
provider_type prov)
-> exit_code {
auto full_directory = data_directory.empty()
? app_config::default_data_directory(prov)
: utils::path::absolute(data_directory);
if (not utils::file::directory{full_directory}.exists()) {
return handle_error(exit_code::mount_not_found, "failed: mount not found");
}
return exit_code::success;
}
} // namespace repertory::cli
#endif // REPERTORY_INCLUDE_CLI_COMMON_HPP_

View File

@@ -31,27 +31,36 @@ namespace repertory::cli::actions {
const std::string &unique_id,
std::string user, std::string password)
-> exit_code {
lock_data lock(prov, unique_id);
const auto res = lock.grab_lock(1U);
if (res == lock_result::success) {
auto ret = cli::check_data_directory(data_directory, prov);
if (ret != exit_code::success) {
return ret;
}
lock_data lock(data_directory, prov, unique_id);
auto lock_res = lock.grab_lock(1U);
if (lock_res == lock_result::success) {
app_config config(prov, data_directory);
const auto cfg = config.get_json();
std::cout << 0 << std::endl;
std::cout << cfg.dump(2) << std::endl;
} else if (res == lock_result::locked) {
return cli::handle_error(exit_code::success, cfg.dump(2));
}
if (lock_res == lock_result::locked) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.get_config();
std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl;
return cli::handle_error(exit_code::success, response.response_type,
response.data.dump(2));
}
return exit_code::success;
return cli::handle_error(exit_code::lock_failed, "failed to get mount lock");
}
} // namespace repertory::cli::actions

View File

@@ -30,28 +30,27 @@ drive_information(std::vector<const char *> /* args */,
const std::string &data_directory, provider_type prov,
const std::string &unique_id, std::string user,
std::string password) -> exit_code {
auto ret = exit_code::success;
lock_data lock(prov, unique_id);
const auto res = lock.grab_lock(1U);
if (res == lock_result::locked) {
lock_data lock(data_directory, prov, unique_id);
auto lock_res = lock.grab_lock(1U);
if (lock_res == lock_result::locked) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.get_drive_information();
std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl;
} else {
std::cerr << app_config::get_provider_display_name(prov)
<< " is not mounted." << std::endl;
ret = exit_code::not_mounted;
return cli::handle_error(exit_code::success, response.response_type,
response.data.dump(2));
}
return ret;
return cli::handle_error(
exit_code::not_mounted,
fmt::format("{} is not mounted",
app_config::get_provider_display_name(prov)));
}
} // namespace repertory::cli::actions

View File

@@ -27,39 +27,49 @@
namespace repertory::cli::actions {
[[nodiscard]] inline auto get(std::vector<const char *> args,
const std::string &data_directory,
provider_type prov,
const std::string &unique_id, std::string user,
std::string password) -> exit_code {
std::string data;
auto ret = utils::cli::parse_string_option(
args, repertory::utils::cli::options::get_option, data);
if (ret == exit_code::success) {
lock_data lock(prov, unique_id);
const auto res = lock.grab_lock(1);
if (res == lock_result::success) {
app_config config(prov, data_directory);
const auto value = config.get_value_by_name(data);
std::cout << (value.empty()
? static_cast<int>(
rpc_response_type::config_value_not_found)
: 0)
<< std::endl;
std::cout << json({{"value", value}}).dump(2) << std::endl;
} else if (res == lock_result::locked) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.get_config_value_by_name(data);
std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl;
}
provider_type prov, const std::string &unique_id,
std::string user, std::string password)
-> exit_code {
auto ret = cli::check_data_directory(data_directory, prov);
if (ret != exit_code::success) {
return ret;
}
return ret;
std::string data;
ret = utils::cli::parse_string_option(
args, repertory::utils::cli::options::get_option, data);
if (ret != exit_code::success) {
return cli::handle_error(exit_code::invalid_syntax, "missing option name");
}
lock_data lock(data_directory, prov, unique_id);
auto lock_res = lock.grab_lock(1);
if (lock_res == lock_result::success) {
app_config config(prov, data_directory);
const auto value = config.get_value_by_name(data);
return cli::handle_error(exit_code::success,
value.empty()
? rpc_response_type::config_value_not_found
: rpc_response_type::success,
json({{"value", value}}).dump(2));
}
if (lock_res == lock_result::locked) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.get_config_value_by_name(data);
return cli::handle_error(exit_code::success, response.response_type,
response.data.dump(2));
}
return cli::handle_error(exit_code::lock_failed, "failed to get mount lock");
}
} // namespace repertory::cli::actions

View File

@@ -33,24 +33,22 @@ get_directory_items(std::vector<const char *> args,
std::string data;
auto ret = utils::cli::parse_string_option(
args, repertory::utils::cli::options::get_directory_items_option, data);
if (ret == exit_code::success) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.get_directory_items(data);
if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl;
} else {
std::cerr << response.data.dump(2) << std::endl;
ret = exit_code::export_failed;
}
if (ret != exit_code::success) {
return cli::handle_error(exit_code::invalid_syntax, "missing api path");
}
return ret;
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.get_directory_items(data);
return cli::handle_error(exit_code::success, response.response_type,
response.data.dump(2));
}
} // namespace repertory::cli::actions

View File

@@ -32,24 +32,22 @@ get_item_info(std::vector<const char *> args, const std::string &data_directory,
std::string data;
auto ret = utils::cli::parse_string_option(
args, repertory::utils::cli::options::get_item_info_option, data);
if (ret == exit_code::success) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.get_item_info(data);
if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl;
} else {
std::cerr << response.data.dump(2) << std::endl;
ret = exit_code::export_failed;
}
if (ret != exit_code::success) {
return cli::handle_error(exit_code::invalid_syntax, "missing api path");
}
return ret;
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.get_item_info(data);
return cli::handle_error(exit_code::success, response.response_type,
response.data.dump(2));
}
} // namespace repertory::cli::actions

View File

@@ -31,23 +31,18 @@ namespace repertory::cli::actions {
const std::string & /* unique_id */,
std::string user,
std::string password) -> exit_code {
auto ret = exit_code::success;
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.get_pinned_files();
if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl;
} else {
std::cerr << response.data.dump(2) << std::endl;
ret = exit_code::export_failed;
}
return ret;
return cli::handle_error(exit_code::success, response.response_type,
response.data.dump(2));
}
} // namespace repertory::cli::actions

View File

@@ -39,7 +39,7 @@ template <typename drive> inline void help(std::vector<const char *> args) {
"information"
<< std::endl;
std::cout << " -na,--name [name] Unique configuration "
"name [Required for Encrypt, S3 and Sia]"
"name [Required for Encrypt, S3 and Sia providers]"
<< std::endl;
std::cout << " -s3,--s3 Enables S3 mode"
<< std::endl;
@@ -67,6 +67,9 @@ template <typename drive> inline void help(std::vector<const char *> args) {
<< std::endl;
std::cout << " -rm,--remote_mount [host/ip:port] Enables remote mount mode"
<< std::endl;
std::cout << " -rp,--remove Remove existing provider "
"configuration"
<< std::endl;
std::cout << " -pf,--pin_file [API path] Pin a file to cache to "
"prevent eviction"
<< std::endl;

View File

@@ -36,7 +36,16 @@ mount(std::vector<const char *> args, std::string data_directory,
orig_args.emplace_back(arg);
}
lock_data global_lock(provider_type::unknown, "global");
auto has_gc_option =
utils::cli::has_option(args, utils::cli::options::generate_config_option);
if (has_gc_option) {
auto res = cli::check_data_directory(data_directory, prov);
if (res != exit_code::success) {
return res;
}
}
lock_data global_lock(data_directory, provider_type::unknown, "global");
{
auto lock_result = global_lock.grab_lock(100U);
if (lock_result != lock_result::success) {
@@ -45,7 +54,7 @@ mount(std::vector<const char *> args, std::string data_directory,
}
}
lock_data lock(prov, unique_id);
lock_data lock(data_directory, prov, unique_id);
auto lock_result = lock.grab_lock();
if (lock_result == lock_result::locked) {
std::cerr << app_config::get_provider_display_name(prov)
@@ -58,8 +67,7 @@ mount(std::vector<const char *> args, std::string data_directory,
return exit_code::lock_failed;
}
if (utils::cli::has_option(args,
utils::cli::options::generate_config_option)) {
if (has_gc_option) {
app_config config(prov, data_directory);
if (prov == provider_type::remote) {
auto remote_config = config.get_remote_config();
@@ -106,7 +114,7 @@ mount(std::vector<const char *> args, std::string data_directory,
global_lock.release();
mount_result = utils::run_process_elevated(args);
lock_data prov_lock(prov, unique_id);
lock_data prov_lock(data_directory, prov, unique_id);
if (prov_lock.grab_lock() == lock_result::success) {
if (not prov_lock.set_mount_state(false, "", -1)) {
std::cerr << "failed to set mount state" << std::endl;

View File

@@ -30,27 +30,27 @@ open_files(std::vector<const char *> /* args */,
const std::string &data_directory, provider_type prov,
const std::string &unique_id, std::string user, std::string password)
-> exit_code {
auto ret = exit_code::success;
lock_data lock(prov, unique_id);
const auto res = lock.grab_lock(1U);
if (res == lock_result::locked) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.get_open_files();
std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl;
} else {
std::cerr << app_config::get_provider_display_name(prov)
<< " is not mounted." << std::endl;
ret = exit_code::not_mounted;
lock_data lock(data_directory, prov, unique_id);
auto lock_res = lock.grab_lock(1U);
if (lock_res != lock_result::locked) {
return cli::handle_error(
exit_code::not_mounted,
fmt::format("{} is not mounted",
app_config::get_provider_display_name(prov)));
}
return ret;
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.get_open_files();
return cli::handle_error(exit_code::success, response.response_type,
response.data.dump(2));
}
} // namespace repertory::cli::actions

View File

@@ -32,24 +32,22 @@ pin_file(std::vector<const char *> args, const std::string &data_directory,
std::string data;
auto ret = utils::cli::parse_string_option(
args, repertory::utils::cli::options::pin_file_option, data);
if (ret == exit_code::success) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.pin_file(data);
if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl;
} else {
std::cerr << response.data.dump(2) << std::endl;
ret = exit_code::pin_failed;
}
if (ret != exit_code::success) {
return cli::handle_error(exit_code::invalid_syntax, "missing api path");
}
return ret;
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.pin_file(data);
return cli::handle_error(exit_code::success, response.response_type,
response.data.dump(2));
}
} // namespace repertory::cli::actions

View File

@@ -32,24 +32,22 @@ pinned_status(std::vector<const char *> args, const std::string &data_directory,
std::string data;
auto ret = utils::cli::parse_string_option(
args, repertory::utils::cli::options::pinned_status_option, data);
if (ret == exit_code::success) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.pinned_status(data);
if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl;
} else {
std::cerr << response.data.dump(2) << std::endl;
ret = exit_code::export_failed;
}
if (ret != exit_code::success) {
return cli::handle_error(exit_code::invalid_syntax, "missing api path");
}
return ret;
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.pinned_status(data);
return cli::handle_error(exit_code::success, response.response_type,
response.data.dump(2));
}
} // namespace repertory::cli::actions

View File

@@ -31,12 +31,16 @@ namespace repertory::cli::actions {
remove(std::vector<const char *> /* args */, const std::string &data_directory,
provider_type prov, const std::string &unique_id, std::string /* user */,
std::string /* password */) -> exit_code {
lock_data lock(prov, unique_id);
auto ret = cli::check_data_directory(data_directory, prov);
if (ret != exit_code::success) {
return ret;
}
lock_data lock(data_directory, prov, unique_id);
auto res = lock.grab_lock(1);
if (res != lock_result::success) {
std::cout << static_cast<std::int32_t>(exit_code::lock_failed) << std::endl;
std::cerr << "FATAL: Unable to get provider lock" << std::endl;
return exit_code::lock_failed;
return cli::handle_error(exit_code::lock_failed,
"failed to get mount lock");
}
auto trash_path = utils::path::combine(
@@ -50,15 +54,17 @@ remove(std::vector<const char *> /* args */, const std::string &data_directory,
utils::generate_secure_random<data_buffer>(4U))),
});
if (utils::file::directory{data_directory}.move_to(trash_path)) {
fmt::println("0");
fmt::println("successfully removed provider|type|{}|id|{}",
app_config::get_provider_name(prov), unique_id);
return exit_code::success;
return cli::handle_error(
exit_code::success,
fmt::format("successfully removed provider|type|{}|id|{}",
app_config::get_provider_name(prov), unique_id));
}
std::cout << static_cast<std::int32_t>(exit_code::remove_failed) << std::endl;
std::cerr << "FATAL: Failed to remove|" << data_directory << std::endl;
return exit_code::remove_failed;
return cli::handle_error(
exit_code::remove_failed,
fmt::format("failed to remove provider|type|{}|id|{}|directory|",
app_config::get_provider_name(prov), unique_id),
data_directory);
}
} // namespace repertory::cli::actions

View File

@@ -30,48 +30,55 @@ namespace repertory::cli::actions {
provider_type prov, const std::string &unique_id,
std::string user, std::string password)
-> exit_code {
auto ret = exit_code::success;
auto ret = cli::check_data_directory(data_directory, prov);
if (ret != exit_code::success) {
return ret;
}
auto data = utils::cli::parse_option(args, "-set", 2U);
if (data.empty()) {
data = utils::cli::parse_option(args, "--set", 2U);
if (data.empty()) {
ret = exit_code::invalid_syntax;
std::cerr << "Invalid syntax for '-set'" << std::endl;
}
}
if (ret == exit_code::success) {
lock_data lock(prov, unique_id);
const auto res = lock.grab_lock(1U);
if (res == lock_result::success) {
app_config config(prov, data_directory);
const auto value = config.set_value_by_name(data[0U], data[1U]);
const auto notFound = value.empty() && not data[1U].empty();
ret = notFound ? exit_code::set_option_not_found : exit_code::success;
std::cout << (notFound ? static_cast<int>(
rpc_response_type::config_value_not_found)
: 0)
<< std::endl;
std::cout << json({{"value", value}}).dump(2) << std::endl;
} else if (res == lock_result::locked) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.set_config_value_by_name(data[0U], data[1U]);
std::cout << static_cast<int>(response.response_type) << std::endl;
std::cout << response.data.dump(2) << std::endl;
ret = response.response_type == rpc_response_type::config_value_not_found
? exit_code::set_option_not_found
: response.response_type == rpc_response_type::success
? exit_code::success
: exit_code::communication_error;
}
}
return ret;
if (data.empty()) {
return cli::handle_error(exit_code::invalid_syntax,
"missing option name or option data");
}
lock_data lock(data_directory, prov, unique_id);
auto lock_res = lock.grab_lock(1U);
if (lock_res == lock_result::success) {
app_config config(prov, data_directory);
auto value = config.set_value_by_name(data[0U], data[1U]);
auto notFound = value.empty() && not data[1U].empty();
return cli::handle_error(
notFound ? exit_code::set_option_not_found : exit_code::success,
notFound ? rpc_response_type::config_value_not_found
: rpc_response_type::success,
json({{"value", value}}).dump(2));
}
if (lock_res == lock_result::locked) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.set_config_value_by_name(data[0U], data[1U]);
return cli::handle_error(
response.response_type == rpc_response_type::config_value_not_found
? exit_code::set_option_not_found
: response.response_type == rpc_response_type::success
? exit_code::success
: exit_code::communication_error,
response.response_type, response.data.dump(2));
}
return cli::handle_error(exit_code::lock_failed, "failed to get mount lock");
}
} // namespace repertory::cli::actions

View File

@@ -25,24 +25,23 @@
#include "cli/common.hpp"
namespace repertory::cli::actions {
[[nodiscard]] inline auto status(std::vector<const char *> /* args */,
const std::string & /*data_directory*/,
provider_type prov,
const std::string &unique_id,
std::string /* user */,
std::string /* password */) -> exit_code {
auto ret = exit_code::success;
lock_data lock(prov, unique_id);
[[nodiscard]] inline auto
status(std::vector<const char *> /* args */, const std::string &data_directory,
provider_type prov, const std::string &unique_id, std::string /* user */,
std::string /* password */) -> exit_code {
auto ret = cli::check_data_directory(data_directory, prov);
if (ret != exit_code::success) {
return ret;
}
lock_data lock(data_directory, prov, unique_id);
[[maybe_unused]] auto status = lock.grab_lock(10U);
json mount_state;
if (lock.get_mount_state(mount_state)) {
std::cout << mount_state.dump(2) << std::endl;
} else {
std::cout << "{}" << std::endl;
ret = exit_code::failed_to_get_mount_state;
return cli::handle_error(exit_code::success, mount_state.dump(2));
}
return ret;
return cli::handle_error(exit_code::failed_to_get_mount_state, "{}");
}
} // namespace repertory::cli::actions

View File

@@ -29,17 +29,21 @@ namespace repertory::cli::actions {
test(std::vector<const char *> /* args */, const std::string &data_directory,
provider_type prov, const std::string & /*unique_id*/,
std::string /*user*/, std::string /*password*/) -> exit_code {
app_config config(prov, data_directory);
auto ret = cli::check_data_directory(data_directory, prov);
if (ret != exit_code::success) {
return ret;
}
app_config config(prov, data_directory);
auto is_online{
(prov == provider_type::remote)
? remote_client(config).check() == 0
: create_provider(prov, config)->is_online(),
};
fmt::println("{}\nProvider is {}!", utils::string::from_bool(not is_online),
is_online ? "online" : "offline");
return is_online ? exit_code::success : exit_code::provider_offline;
return cli::handle_error(
is_online ? exit_code::success : exit_code::provider_offline,
fmt::format("provider is {}", is_online ? "online" : "offline"));
}
} // namespace repertory::cli::actions

View File

@@ -35,10 +35,12 @@ unmount(std::vector<const char *> /* args */, const std::string &data_directory,
auto port{app_config::default_api_port(prov)};
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.unmount();
if (response.response_type == rpc_response_type::success) {
auto orig_response{response};
@@ -48,28 +50,29 @@ unmount(std::vector<const char *> /* args */, const std::string &data_directory,
response.response_type == rpc_response_type::success;
++retry) {
std::this_thread::sleep_for(1s);
response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.unmount();
std::cout << "." << std::flush;
}
if (response.response_type == rpc_response_type::success) {
std::cerr << " failed! mount still active" << std::endl;
ret = exit_code::mount_active;
} else {
std::cout << " done!" << std::endl;
std::cout << static_cast<int>(orig_response.response_type) << std::endl;
std::cout << orig_response.data.dump(2) << std::endl;
std::cout << "failed!" << std::endl;
return cli::handle_error(exit_code::mount_active,
"mount is still active");
}
} else {
std::cerr << response.data.dump(2) << std::endl;
ret = exit_code::communication_error;
std::cout << " done!" << std::endl;
return cli::handle_error(exit_code::success, orig_response.response_type,
orig_response.data.dump(2));
}
return ret;
return cli::handle_error(exit_code::communication_error,
response.response_type, response.data.dump(2));
}
} // namespace repertory::cli::actions

View File

@@ -32,24 +32,24 @@ unpin_file(std::vector<const char *> args, const std::string &data_directory,
std::string data;
auto ret = utils::cli::parse_string_option(
args, repertory::utils::cli::options::unpin_file_option, data);
if (ret == exit_code::success) {
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({.host = "localhost",
.password = password,
.port = port,
.user = user})
.unpin_file(data);
if (response.response_type == rpc_response_type::success) {
std::cout << response.data.dump(2) << std::endl;
} else {
std::cerr << response.data.dump(2) << std::endl;
ret = exit_code::unpin_failed;
}
if (ret != exit_code::success) {
return cli::handle_error(exit_code::invalid_syntax, "missing api path");
}
return ret;
auto port = app_config::default_api_port(prov);
utils::cli::get_api_authentication_data(user, password, port, prov,
data_directory);
auto response = client({
.host = "localhost",
.password = password,
.port = port,
.user = user,
})
.unpin_file(data);
return cli::handle_error(response.response_type == rpc_response_type::success
? exit_code::success
: exit_code::unpin_failed,
response.data.dump(2));
}
} // namespace repertory::cli::actions

View File

@@ -938,7 +938,8 @@ void ui_server::start() {
nonce_thread_ =
std::make_unique<std::thread>([this]() { removed_expired_nonces(); });
lock_data ui_lock(provider_type::unknown, "ui");
lock_data ui_lock(app_config::get_root_data_directory(),
provider_type::unknown, "ui");
auto res = ui_lock.grab_lock(1U);
if (res != lock_result::success) {
notify_and_unlock(nonce_lock);