Debugging
Some checks failed
Dropshell Test / Build_and_Test (push) Failing after 2m10s

This commit is contained in:
Your Name 2025-05-25 19:17:51 +12:00
parent 1502d6e3d2
commit 8f06fc31ae
17 changed files with 220 additions and 188 deletions

View File

@ -84,7 +84,7 @@ namespace dropshell
remote_command_script_file, remote_command_script_file,
remotefile(server, user).service_env(service)}, user)) remotefile(server, user).service_env(service)}, user))
{ {
error << "Error: Required service directories not found on remote server" << std::endl; error << "Required service directories not found on remote server" << std::endl;
info << "Is the service installed?" << std::endl; info << "Is the service installed?" << std::endl;
return false; return false;
} }
@ -103,7 +103,7 @@ namespace dropshell
std::string local_backups_dir = localpath::backups(); std::string local_backups_dir = localpath::backups();
if (local_backups_dir.empty()) if (local_backups_dir.empty())
{ {
error << "Error: Local backups directory not found" << std::endl; error << "Local backups directory not found" << std::endl;
info << "Run 'dropshell edit' to configure DropShell" << std::endl; info << "Run 'dropshell edit' to configure DropShell" << std::endl;
return false; return false;
} }

View File

@ -94,11 +94,11 @@ int edit_config()
std::string config_file = localfile::dropshell_json(); std::string config_file = localfile::dropshell_json();
if (!edit_file(config_file, false) || !std::filesystem::exists(config_file)) if (!edit_file(config_file, false) || !std::filesystem::exists(config_file))
return die("Error: Failed to edit config file."); return die("Failed to edit config file.");
gConfig().load_config(); gConfig().load_config();
if (!gConfig().is_config_set()) if (!gConfig().is_config_set())
return die("Error: Failed to load and parse edited config file!"); return die("Failed to load and parse edited config file!");
gConfig().save_config(true); gConfig().save_config(true);
@ -112,7 +112,7 @@ int edit_config()
int edit_server(const std::string &server_name) int edit_server(const std::string &server_name)
{ {
if (localpath::server(server_name).empty()) { if (localpath::server(server_name).empty()) {
std::cerr << "Error: Server not found: " << server_name << std::endl; error << "Server not found: " << server_name << std::endl;
return -1; return -1;
} }

View File

@ -334,7 +334,7 @@ complete -F _dropshell_completions ds
info << "Installing agent for user " << user.user << " on " << server.get_server_name() << std::endl; info << "Installing agent for user " << user.user << " on " << server.get_server_name() << std::endl;
std::string agent_path = remotepath(server.get_server_name(),user.user).agent(); std::string agent_path = remotepath(server.get_server_name(),user.user).agent();
ASSERT(agent_path == user.dir+"/agent", "Agent path does not match user directory for "+user.user+"@" + server.get_server_name() + " : " + agent_path + " != " + user.dir); ASSERT(agent_path == user.dir+"/agent", "Remote agent path does not match user directory for "+user.user+"@" + server.get_server_name() + " : " + agent_path + " != " + user.dir);
ASSERT(!agent_path.empty(), "Agent path is empty for " + user.user + "@" + server.get_server_name()); ASSERT(!agent_path.empty(), "Agent path is empty for " + user.user + "@" + server.get_server_name());
// now create the agent. // now create the agent.
@ -348,7 +348,7 @@ complete -F _dropshell_completions ds
bool okay = execute_ssh_command(server.get_SSH_INFO(user.user), sCommand(agent_path, "agent-install.sh",{}), cMode::Defaults | cMode::NoBB64, nullptr); bool okay = execute_ssh_command(server.get_SSH_INFO(user.user), sCommand(agent_path, "agent-install.sh",{}), cMode::Defaults | cMode::NoBB64, nullptr);
if (!okay) if (!okay)
{ {
error << "ERROR: Failed to install remote agent on " << server.get_server_name() << std::endl; error << "Failed to install remote agent on " << server.get_server_name() << std::endl;
return 1; return 1;
} }

View File

@ -147,7 +147,7 @@ void list_servers() {
void show_server_details(const std::string& server_name) { void show_server_details(const std::string& server_name) {
ServerConfig env(server_name); ServerConfig env(server_name);
if (!env.is_valid()) { if (!env.is_valid()) {
error << "Error: Invalid server environment file: " << server_name << std::endl; error << "Invalid server environment file: " << server_name << std::endl;
return; return;
} }

View File

@ -60,7 +60,7 @@ namespace dropshell
std::string local_backups_dir = localpath::backups(); std::string local_backups_dir = localpath::backups();
if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir)) if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir))
{ {
error << "Error: Local backups directory not found: " << local_backups_dir << std::endl; error << "Local backups directory not found: " << local_backups_dir << std::endl;
return {}; return {};
} }
@ -150,19 +150,19 @@ namespace dropshell
std::string local_backups_dir = localpath::backups(); std::string local_backups_dir = localpath::backups();
if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir)) if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir))
{ {
error << "Error: Local backups directory not found: " << local_backups_dir << std::endl; error << "Local backups directory not found: " << local_backups_dir << std::endl;
return 1; return 1;
} }
std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_details->get_filename()).string(); std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_details->get_filename()).string();
if (!std::filesystem::exists(local_backup_file_path)) if (!std::filesystem::exists(local_backup_file_path))
{ {
error << "Error: Backup file not found at " << local_backup_file_path << std::endl; error << "Backup file not found at " << local_backup_file_path << std::endl;
return 1; return 1;
} }
if (backup_details->get_template_name() != service_info.template_name) if (backup_details->get_template_name() != service_info.template_name)
{ {
error << "Error: Backup template does not match service template. Can't restore." << std::endl; error << "Backup template does not match service template. Can't restore." << std::endl;
info << "Backup template: " << backup_details->get_template_name() << std::endl; info << "Backup template: " << backup_details->get_template_name() << std::endl;
info << "Service template: " << service_info.template_name << std::endl; info << "Service template: " << service_info.template_name << std::endl;
return 1; return 1;

View File

@ -67,7 +67,7 @@ namespace dropshell
{ {
if (ctx.args.size() < 2) if (ctx.args.size() < 2)
{ {
std::cerr << "Error: Server name and service name are both required" << std::endl; error << "Server name and service name are both required" << std::endl;
return 1; return 1;
} }

View File

@ -37,7 +37,7 @@ bool config::load_config() { // load json config file.
} }
catch (nlohmann::json::parse_error& ex) catch (nlohmann::json::parse_error& ex)
{ {
std::cerr << "Error: Failed to parse config file: " << ex.what() << std::endl; error << "Failed to parse config file: " << ex.what() << std::endl;
return false; return false;
} }
@ -69,7 +69,7 @@ bool config::save_config(bool create_aux_directories)
if (!mIsConfigSet) if (!mIsConfigSet)
{ {
std::string homedir = localpath::current_user_home(); std::string homedir = localpath::current_user_home();
std::string dropshell_base = homedir + "/.local/dropshell_files"; std::string dropshell_base = homedir + "/.dropshell";
mConfig["server_definition_paths"] = { mConfig["server_definition_paths"] = {
dropshell_base + "/servers" dropshell_base + "/servers"
@ -81,6 +81,10 @@ bool config::save_config(bool create_aux_directories)
"https://templates.dropshell.app" "https://templates.dropshell.app"
}; };
mConfig["template_upload_token"] = "SECRETTOKEN"; mConfig["template_upload_token"] = "SECRETTOKEN";
mConfig["backups_path"] = {
dropshell_base + "/backups"
};
} }
config_file << mConfig.dump(4); config_file << mConfig.dump(4);
@ -175,4 +179,10 @@ std::string config::get_template_upload_token() {
return mConfig["template_upload_token"]; return mConfig["template_upload_token"];
} }
std::string config::get_backups_path()
{
return mConfig["backups_path"];
}
} // namespace dropshell } // namespace dropshell

View File

@ -28,6 +28,8 @@ class config {
std::string get_template_upload_url(); std::string get_template_upload_url();
std::string get_template_upload_token(); std::string get_template_upload_token();
std::string get_backups_path();
private: private:
nlohmann::json mConfig; nlohmann::json mConfig;
bool mIsConfigSet; bool mIsConfigSet;

View File

@ -80,7 +80,7 @@ int main(int argc, char* argv[]) {
} }
catch (const std::exception& e) { catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl; error << "Uncaught Exception: " << e.what() << std::endl;
return 1; return 1;
} }
} }

View File

@ -104,7 +104,7 @@ namespace dropshell
catch (const std::exception &e) catch (const std::exception &e)
{ {
error << "Failed to parse " << server_json_path << std::endl; error << "Failed to parse " << server_json_path << std::endl;
error << "Error: " << e.what() << std::endl; error << "Exception: " << e.what() << std::endl;
mValid = false; mValid = false;
} }
@ -241,7 +241,7 @@ namespace dropshell
bool okay = execute_ssh_command(sshinfo, scommand, cMode::Silent); bool okay = execute_ssh_command(sshinfo, scommand, cMode::Silent);
if (!okay) if (!okay)
{ {
std::cerr << "Error: Required items not found on remote server: " << file_names_str << std::endl; error << "Required items not found on remote server: " << file_names_str << std::endl;
return false; return false;
} }
return true; return true;
@ -341,7 +341,7 @@ namespace dropshell
std::map<std::string, std::string> env_vars; std::map<std::string, std::string> env_vars;
if (!get_all_service_env_vars(mServerName, service_name, env_vars)) if (!get_all_service_env_vars(mServerName, service_name, env_vars))
{ {
std::cerr << "Error: Failed to get all service env vars for " << service_name << std::endl; error << "Failed to get all service env vars for " << service_name << std::endl;
return std::nullopt; return std::nullopt;
} }
@ -358,7 +358,7 @@ namespace dropshell
if (sc.empty()) if (sc.empty())
{ {
std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl; error << "Failed to construct command for " << service_name << " " << command << std::endl;
return std::nullopt; return std::nullopt;
} }
return sc; return sc;
@ -388,7 +388,7 @@ namespace dropshell
ServerConfig env(server_name); ServerConfig env(server_name);
if (!env.is_valid()) if (!env.is_valid())
{ {
std::cerr << "Error: Invalid server environment file: " << entry.path().string() << std::endl; error << "Invalid server environment file: " << entry.path().string() << std::endl;
continue; continue;
} }
servers.push_back(env); servers.push_back(env);
@ -406,7 +406,7 @@ namespace dropshell
std::string server_existing_dir = localpath::server(server_name); std::string server_existing_dir = localpath::server(server_name);
if (!server_existing_dir.empty()) if (!server_existing_dir.empty())
{ {
error << "Error: Server name already exists: " << server_name << std::endl; error << "Server name already exists: " << server_name << std::endl;
info << "Current server path: " << server_existing_dir << std::endl; info << "Current server path: " << server_existing_dir << std::endl;
return false; return false;
} }
@ -415,7 +415,7 @@ namespace dropshell
auto lsdp = gConfig().get_local_server_definition_paths(); auto lsdp = gConfig().get_local_server_definition_paths();
if (lsdp.empty() || lsdp[0].empty()) if (lsdp.empty() || lsdp[0].empty())
{ {
error << "Error: Local server definition path not found" << std::endl; error << "Local server definition path not found" << std::endl;
info << "Run 'dropshell edit' to configure DropShell" << std::endl; info << "Run 'dropshell edit' to configure DropShell" << std::endl;
return false; return false;
} }
@ -434,7 +434,9 @@ namespace dropshell
server_env_file << " \"USER\": \"" << user << "\"," << std::endl; server_env_file << " \"USER\": \"" << user << "\"," << std::endl;
server_env_file << " \"DIR\": \"" << "/home/" + user << "/.dropshell\"" << std::endl; server_env_file << " \"DIR\": \"" << "/home/" + user << "/.dropshell\"" << std::endl;
server_env_file << " }" << std::endl; server_env_file << " }" << std::endl;
server_env_file << " ]" << std::endl; server_env_file << " ]," << std::endl;
server_env_file << " \"HAS_DOCKER\": \"true\"," << std::endl;
server_env_file << " \"DOCKER_ROOTLESS\": \"false\"" << std::endl;
server_env_file << "}" << std::endl; server_env_file << "}" << std::endl;
server_env_file.close(); server_env_file.close();

View File

@ -34,8 +34,8 @@ namespace dropshell
std::vector<std::string> local_server_definition_paths = gConfig().get_local_server_definition_paths(); std::vector<std::string> local_server_definition_paths = gConfig().get_local_server_definition_paths();
if (local_server_definition_paths.empty()) if (local_server_definition_paths.empty())
{ {
std::cerr << "Error: No local server definition paths found" << std::endl; error << "No local server definition paths found" << std::endl;
std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl; info << "Run 'dropshell edit' to configure DropShell" << std::endl;
return services; return services;
} }
@ -158,7 +158,7 @@ namespace dropshell
auto service_info = get_service_info(server_name, service_name); auto service_info = get_service_info(server_name, service_name);
if (service_info.local_template_path.empty()) if (service_info.local_template_path.empty())
{ {
std::cerr << "Error: Service not found: " << service_name << std::endl; error << "Service not found: " << service_name << std::endl;
return commands; return commands;
} }
@ -183,7 +183,7 @@ namespace dropshell
auto service_info = get_service_info(server_name, service_name); auto service_info = get_service_info(server_name, service_name);
if (service_info.local_template_path.empty()) if (service_info.local_template_path.empty())
{ {
std::cerr << "Error: Service not found: " << service_name << std::endl; error << "Service not found: " << service_name << std::endl;
return backups; return backups;
} }
@ -211,7 +211,7 @@ namespace dropshell
if (localpath::service(server_name, service_name).empty() || !fs::exists(localpath::service(server_name, service_name))) if (localpath::service(server_name, service_name).empty() || !fs::exists(localpath::service(server_name, service_name)))
{ {
std::cerr << "Error: Service not found: " << service_name << " on server " << server_name << std::endl; error << "Service not found: " << service_name << " on server " << server_name << std::endl;
return false; return false;
} }
@ -265,7 +265,7 @@ namespace dropshell
template_info tinfo = gTemplateManager().get_template_info(it->second); template_info tinfo = gTemplateManager().get_template_info(it->second);
if (!tinfo.is_set()) if (!tinfo.is_set())
{ {
std::cerr << "Error: Template '" << it->second << "' not found" << std::endl; error << "Template '" << it->second << "' not found" << std::endl;
return false; return false;
} }

View File

@ -163,7 +163,7 @@
ASSERT(mLoaded && mSources.size() > 0, "Template manager not loaded, or no template sources found."); ASSERT(mLoaded && mSources.size() > 0, "Template manager not loaded, or no template sources found.");
template_source_interface* source = get_source(template_name); template_source_interface* source = get_source(template_name);
if (!source) { if (!source) {
std::cerr << "Error: Template '" << template_name << "' not found" << std::endl; error << "Template '" << template_name << "' not found" << std::endl;
return false; return false;
} }
return source->template_command_exists(template_name, command); return source->template_command_exists(template_name, command);
@ -175,21 +175,21 @@
std::vector<std::string> local_server_definition_paths = gConfig().get_local_server_definition_paths(); std::vector<std::string> local_server_definition_paths = gConfig().get_local_server_definition_paths();
if (local_server_definition_paths.empty()) { if (local_server_definition_paths.empty()) {
std::cerr << "Error: No local server definition paths found" << std::endl; error << "No local server definition paths found" << std::endl;
std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl; info << "Run 'dropshell edit' to configure DropShell" << std::endl;
return false; return false;
} }
auto info = get_template_info(template_name); auto tinfo = get_template_info(template_name);
if (info.is_set()) { if (tinfo.is_set()) {
std::cerr << "Error: Template '" << template_name << "' already exists at " << info.locationID() << std::endl; error << "Template '" << template_name << "' already exists at " << tinfo.locationID() << std::endl;
return false; return false;
} }
auto local_template_paths = gConfig().get_local_template_paths(); auto local_template_paths = gConfig().get_local_template_paths();
if (local_template_paths.empty()) { if (local_template_paths.empty()) {
std::cerr << "Error: No local template paths found" << std::endl; error << "No local template paths found" << std::endl;
std::cerr << "Run 'dropshell edit' to add one to the DropShell config" << std::endl; info << "Run 'dropshell edit' to add one to the DropShell config" << std::endl;
return false; return false;
} }
std::string new_template_path = local_template_paths[0] + "/" + template_name; std::string new_template_path = local_template_paths[0] + "/" + template_name;
@ -200,7 +200,7 @@
// 2. Copy the example template from the system templates directory // 2. Copy the example template from the system templates directory
auto example_info = gTemplateManager().get_template_info("example-nginx"); auto example_info = gTemplateManager().get_template_info("example-nginx");
if (!example_info.is_set()) { if (!example_info.is_set()) {
std::cerr << "Error: Example template not found" << std::endl; error << "Example template not found" << std::endl;
return false; return false;
} }
std::string example_template_path = example_info.local_template_path(); std::string example_template_path = example_info.local_template_path();
@ -222,7 +222,7 @@
std::string replacement_line = "TEMPLATE=" + template_name; std::string replacement_line = "TEMPLATE=" + template_name;
std::string service_env_path = new_template_path + "/config/" + filenames::template_info_env; std::string service_env_path = new_template_path + "/config/" + filenames::template_info_env;
if (!replace_line_in_file(service_env_path, search_string, replacement_line)) { if (!replace_line_in_file(service_env_path, search_string, replacement_line)) {
std::cerr << "Error: Failed to replace TEMPLATE= line in the " << filenames::template_info_env <<" file" << std::endl; error << "Failed to replace TEMPLATE= line in the " << filenames::template_info_env <<" file" << std::endl;
return false; return false;
} }
@ -278,7 +278,7 @@
bool template_manager::required_file(std::string path, std::string template_name) bool template_manager::required_file(std::string path, std::string template_name)
{ {
if (!std::filesystem::exists(path)) { if (!std::filesystem::exists(path)) {
std::cerr << "Error: " << path << " file not found in template - REQUIRED." << template_name << std::endl; error << path << " file not found in template - REQUIRED." << template_name << std::endl;
return false; return false;
} }
return true; return true;
@ -323,7 +323,7 @@
std::filesystem::path path = template_path + "/" + file; std::filesystem::path path = template_path + "/" + file;
auto perms = std::filesystem::status(path).permissions(); auto perms = std::filesystem::status(path).permissions();
if ((perms & std::filesystem::perms::owner_exec) == std::filesystem::perms::none) if ((perms & std::filesystem::perms::owner_exec) == std::filesystem::perms::none)
std::cerr << "Error: " << file << " is not executable" << std::endl; error << file << " is not executable" << std::endl;
} }
} }
@ -347,18 +347,18 @@
// determine template name. // determine template name.
auto it = all_env_vars.find("TEMPLATE"); auto it = all_env_vars.find("TEMPLATE");
if (it == all_env_vars.end()) { if (it == all_env_vars.end()) {
std::cerr << "Error: TEMPLATE variable not found in " << template_path << std::endl; error << "TEMPLATE variable not found in " << template_path << std::endl;
return false; return false;
} }
std::string env_template_name = it->second; std::string env_template_name = it->second;
if (env_template_name.empty()) { if (env_template_name.empty()) {
std::cerr << "Error: TEMPLATE variable is empty in " << template_path << std::endl; error << "TEMPLATE variable is empty in " << template_path << std::endl;
return false; return false;
} }
if (env_template_name != template_name) { if (env_template_name != template_name) {
std::cerr << "Error: TEMPLATE variable is wrong in " << template_path << std::endl; error << "TEMPLATE variable is wrong in " << template_path << std::endl;
return false; return false;
} }

View File

@ -7,86 +7,85 @@
#include <string> #include <string>
#include <filesystem> #include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace dropshell { namespace dropshell
{
namespace localfile
{
namespace localfile { std::string dropshell_json()
{
std::string dropshell_json() { return localpath::dropshell_dir() + "/" + filenames::dropshell_json;
// Try ~/.config/dropshell/dropshell.json
std::string homedir = localpath::current_user_home();
if (!homedir.empty()) {
fs::path user_path = fs::path(homedir) / ".config" / "dropshell" / filenames::dropshell_json;
return user_path.string();
} }
return std::string();
}
std::string server_json(const std::string &server_name) { std::string server_json(const std::string &server_name)
std::string serverpath = localpath::server(server_name); {
return (serverpath.empty() ? "" : (fs::path(serverpath) / filenames::server_json).string()); std::string serverpath = localpath::server(server_name);
} return (serverpath.empty() ? "" : (fs::path(serverpath) / filenames::server_json).string());
}
std::string service_env(const std::string &server_name, const std::string &service_name) { std::string service_env(const std::string &server_name, const std::string &service_name)
std::string servicepath = localpath::service(server_name, service_name); {
return (servicepath.empty() ? "" : (fs::path(servicepath) / filenames::service_env).string()); std::string servicepath = localpath::service(server_name, service_name);
} return (servicepath.empty() ? "" : (fs::path(servicepath) / filenames::service_env).string());
}
std::string template_info_env(const std::string &server_name, const std::string &service_name) std::string template_info_env(const std::string &server_name, const std::string &service_name)
{
std::string servicepath = localpath::service(server_name, service_name);
return (servicepath.empty() ? "" : (fs::path(servicepath) / filenames::template_info_env).string());
}
std::string template_example()
{
return localpath::agent_local() + "/template_example";
}
std::string bb64()
{
return localpath::agent_local() + "/bb64";
}
} // namespace localfile
// ------------------------------------------------------------------------------------------
namespace localpath
{ {
std::string servicepath = localpath::service(server_name, service_name);
return (servicepath.empty() ? "" : (fs::path(servicepath) / filenames::template_info_env).string());
}
std::string template_example() std::string dropshell_dir()
{ {
return localpath::agent_local() + "/template_example"; return current_user_home() + "/.dropshell";
} }
std::string bb64() std::string server(const std::string &server_name)
{ {
return localpath::agent_local() + "/bb64"; for (std::filesystem::path dir : gConfig().get_local_server_definition_paths())
}
} // namespace localfile
// ------------------------------------------------------------------------------------------
namespace localpath {
std::string server(const std::string &server_name) {
for (std::filesystem::path dir : gConfig().get_local_server_definition_paths())
if (fs::exists(dir / server_name)) if (fs::exists(dir / server_name))
return dir / server_name; return dir / server_name;
return ""; return "";
} }
std::string service(const std::string &server_name, const std::string &service_name) { std::string service(const std::string &server_name, const std::string &service_name)
{
std::string serverpath = localpath::server(server_name); std::string serverpath = localpath::server(server_name);
return ((serverpath.empty() || service_name.empty()) ? "" : (serverpath+"/"+service_name)); return ((serverpath.empty() || service_name.empty()) ? "" : (serverpath + "/" + service_name));
} }
std::string remote_versions(const std::string &server_name, const std::string &service_name)
{
std::string template_cache_path = localpath::template_cache();
return ((template_cache_path.empty() || service_name.empty()) ? "" :
(template_cache_path+"/remote_versions/"+service_name+".json"));
}
std::string agent_local() std::string agent_local()
{ {
return current_user_home()+"/.local/dropshell_agent/agent-local"; return dropshell_dir() + "/agent-local";
} }
std::string agent_remote() std::string agent_remote()
{ {
return current_user_home() + "/.local/dropshell_agent/agent-remote"; return dropshell_dir() + "/agent-remote";
} }
std::string current_user_home() std::string current_user_home()
{ {
char * homedir = std::getenv("HOME"); char *homedir = std::getenv("HOME");
if (homedir) if (homedir)
{ {
std::filesystem::path homedir_path(homedir); std::filesystem::path homedir_path(homedir);
@ -96,37 +95,32 @@ namespace localpath {
return std::string(); return std::string();
} }
std::string dropshell_files()
{
return current_user_home() + "/.local/dropshell_files";
return std::string();
}
std::string backups() std::string backups()
{ {
return dropshell_files() + "/backups"; if (!gConfig().is_config_set())
return "";
return gConfig().get_backups_path();
} }
std::string temp_files() std::string temp_files()
{ {
return dropshell_files() + "/temp_files"; return dropshell_dir() + "/temp_files";
} }
std::string template_cache() std::string template_cache()
{ {
return dropshell_files() + "template_cache"; return dropshell_dir() + "/template_cache";
} }
bool create_directories() bool create_directories()
{ {
std::vector<std::filesystem::path> paths = { std::vector<std::filesystem::path> paths = {
dropshell_files(), dropshell_dir(),
agent_local(), agent_local(),
agent_remote(), agent_remote(),
template_cache(), template_cache(),
backups(), backups(),
temp_files() temp_files()};
};
for (auto &p : gConfig().get_local_server_definition_paths()) for (auto &p : gConfig().get_local_server_definition_paths())
paths.push_back(p); paths.push_back(p);
@ -139,7 +133,7 @@ namespace localpath {
return false; return false;
} }
} // namespace localpath } // namespace localpath
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
// remote paths // remote paths
@ -159,21 +153,26 @@ namespace localpath {
// |-- service.env (default service config) // |-- service.env (default service config)
// |-- (other config files for specific server&service) // |-- (other config files for specific server&service)
remotefile::remotefile(const std::string &server_name, const std::string &user) : mServer_name(server_name), mUser(user) {}
remotefile::remotefile(const std::string &server_name, const std::string &user) :
mServer_name(server_name), mUser(user) {}
std::string remotefile::service_env(const std::string &service_name) const std::string remotefile::service_env(const std::string &service_name) const
{ {
return remotepath(mServer_name,mUser).service_config(service_name) + "/" + filenames::service_env; return remotepath(mServer_name, mUser).service_config(service_name) + "/" + filenames::service_env;
} }
remotepath::remotepath(const std::string &server_name, const std::string &user) : mServer_name(server_name), mUser(user) {} remotepath::remotepath(const std::string &server_name, const std::string &user) : mServer_name(server_name), mUser(user) {}
std::string remotepath::DROPSHELL_DIR() const std::string remotepath::DROPSHELL_DIR() const
{ {
return ServerConfig(mServer_name).get_user_dir(mUser); try
{
return ServerConfig(mServer_name).get_user_dir(mUser);
} catch (const std::exception &e)
{
error << "Failed to get remote dropshell directory for " << mServer_name << "@" << mUser << std::endl;
error << "Exception: " << e.what() << std::endl;
return "";
}
} }
std::string remotepath::services() const std::string remotepath::services() const
@ -218,22 +217,21 @@ namespace localpath {
return (dsp.empty() ? "" : (dsp + "/agent")); return (dsp.empty() ? "" : (dsp + "/agent"));
} }
// ------------------------------------------------------------------------------------------
// Utility functions
// ------------------------------------------------------------------------------------------ std::string get_parent(const std::filesystem::path path)
// Utility functions {
if (path.empty())
return std::string();
return path.parent_path().string();
}
std::string get_parent(const std::filesystem::path path) std::string get_child(const std::filesystem::path path)
{ {
if (path.empty()) if (path.empty())
return std::string(); return std::string();
return path.parent_path().string(); return path.filename().string();
} }
std::string get_child(const std::filesystem::path path)
{
if (path.empty())
return std::string();
return path.filename().string();
}
} // namespace dropshell } // namespace dropshell

View File

@ -10,20 +10,15 @@ namespace dropshell {
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
// local user config directories // local user config directories
// ~/.config/dropshell/dropshell.json // ~/.dropshell
// |-- dropshell.json
// ~/.local/dropshell_agent
// |-- agent-local // |-- agent-local
// |-- agent-install.sh // |-- agent-install.sh
// |-- bb64 (only used locally, as it's for the local machine's architecture!) // |-- bb64 (only used locally, as it's for the local machine's architecture!)
// |-- template_example // |-- template_example
// |-- agent-remote // |-- agent-remote
// |-- (remote agent files, including _allservicesstatus.sh) // |-- (remote agent files, including _allservicesstatus.sh)
// ~/.local/dropshell_files
// |-- backups
// |-- katie-_-squashkiwi-_-squashkiwi-test-_-2025-04-28_21-23-59.tgz
// |-- temp_files // |-- temp_files
// |-- template_cache // |-- template_cache
// |-- templates // |-- templates
@ -35,6 +30,10 @@ namespace dropshell {
// | |-- .template_info.env // | |-- .template_info.env
// | |-- (...other service config files...) // | |-- (...other service config files...)
// backups_path
// |-- katie-_-squashkiwi-_-squashkiwi-test-_-2025-04-28_21-23-59.tgz
// server_definition_path // server_definition_path
// |-- <server_name> // |-- <server_name>
// |-- server.json // |-- server.json
@ -53,7 +52,6 @@ namespace dropshell {
} // namespace filenames. } // namespace filenames.
namespace localfile { namespace localfile {
// ~/.config/dropshell/dropshell.json
std::string dropshell_json(); std::string dropshell_json();
std::string server_json(const std::string &server_name); std::string server_json(const std::string &server_name);
std::string service_env(const std::string &server_name, const std::string &service_name); std::string service_env(const std::string &server_name, const std::string &service_name);
@ -63,16 +61,15 @@ namespace dropshell {
} // namespace localfile } // namespace localfile
namespace localpath { namespace localpath {
std::string dropshell_dir();
std::string server(const std::string &server_name); std::string server(const std::string &server_name);
std::string service(const std::string &server_name, const std::string &service_name); std::string service(const std::string &server_name, const std::string &service_name);
std::string remote_versions(const std::string &server_name, const std::string &service_name);
std::string agent_local(); std::string agent_local();
std::string agent_remote(); std::string agent_remote();
std::string current_user_home(); std::string current_user_home();
std::string dropshell_files();
std::string backups(); std::string backups();
std::string temp_files(); std::string temp_files();
std::string template_cache(); std::string template_cache();

View File

@ -183,7 +183,7 @@ namespace dropshell
if (!rval && !hasFlag(mode, cMode::Silent)) if (!rval && !hasFlag(mode, cMode::Silent))
{ {
error << "Error: Failed to execute ssh command:" << std::endl; error << "Failed to execute ssh command:" << std::endl;
debug << ssh_cmd.str() + " " + remote_command.construct_cmd(remote_bb64_path) << std::endl; debug << ssh_cmd.str() + " " + remote_command.construct_cmd(remote_bb64_path) << std::endl;
} }
return rval; return rval;

View File

@ -3,6 +3,8 @@
#define XXH_INLINE_ALL #define XXH_INLINE_ALL
#include "contrib/xxhash.hpp" #include "contrib/xxhash.hpp"
#include "output.hpp"
#include <fstream> #include <fstream>
#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
@ -13,7 +15,7 @@ uint64_t hash_file(const std::string &path) {
// Create hash state // Create hash state
XXH64_state_t* const state = XXH64_createState(); XXH64_state_t* const state = XXH64_createState();
if (state == nullptr) { if (state == nullptr) {
std::cerr << "Failed to create hash state" << std::endl; error << "Failed to create hash state" << std::endl;
return 0; return 0;
} }
@ -24,7 +26,7 @@ uint64_t hash_file(const std::string &path) {
// Open file // Open file
std::ifstream file(path, std::ios::binary); std::ifstream file(path, std::ios::binary);
if (!file.is_open()) { if (!file.is_open()) {
std::cerr << "Failed to open file: " << path << std::endl; error << "Failed to open file: " << path << std::endl;
XXH64_freeState(state); XXH64_freeState(state);
return 0; return 0;
} }
@ -34,7 +36,7 @@ uint64_t hash_file(const std::string &path) {
char buffer[buffer_size]; char buffer[buffer_size];
while (file.read(buffer, buffer_size)) { while (file.read(buffer, buffer_size)) {
if (XXH64_update(state, buffer, file.gcount()) == XXH_ERROR) { if (XXH64_update(state, buffer, file.gcount()) == XXH_ERROR) {
std::cerr << "Failed to update hash" << std::endl; error << "Failed to update hash" << std::endl;
XXH64_freeState(state); XXH64_freeState(state);
return 0; return 0;
} }
@ -43,7 +45,7 @@ uint64_t hash_file(const std::string &path) {
// Handle any remaining bytes // Handle any remaining bytes
if (file.gcount() > 0) { if (file.gcount() > 0) {
if (XXH64_update(state, buffer, file.gcount()) == XXH_ERROR) { if (XXH64_update(state, buffer, file.gcount()) == XXH_ERROR) {
std::cerr << "Failed to update hash" << std::endl; error << "Failed to update hash" << std::endl;
XXH64_freeState(state); XXH64_freeState(state);
return 0; return 0;
} }
@ -59,14 +61,14 @@ uint64_t hash_directory_recursive(const std::string &path) {
// Create hash state // Create hash state
XXH64_state_t* const state = XXH64_createState(); XXH64_state_t* const state = XXH64_createState();
if (state == nullptr) { if (state == nullptr) {
std::cerr << "Failed to create hash state" << std::endl; error << "Failed to create hash state" << std::endl;
return 0; return 0;
} }
// Initialize state with seed 0 // Initialize state with seed 0
XXH64_hash_t const seed = 0; /* or any other value */ XXH64_hash_t const seed = 0; /* or any other value */
if (XXH64_reset(state, seed) == XXH_ERROR) { if (XXH64_reset(state, seed) == XXH_ERROR) {
std::cerr << "Failed to reset hash state" << std::endl; error << "Failed to reset hash state" << std::endl;
XXH64_freeState(state); XXH64_freeState(state);
return 0; return 0;
} }
@ -81,7 +83,7 @@ uint64_t hash_directory_recursive(const std::string &path) {
} }
} }
} catch (const std::filesystem::filesystem_error& e) { } catch (const std::filesystem::filesystem_error& e) {
std::cerr << "Filesystem error: " << e.what() << std::endl; error << "Filesystem error: " << e.what() << std::endl;
XXH64_freeState(state); XXH64_freeState(state);
return 0; return 0;
} }
@ -94,7 +96,7 @@ uint64_t hash_directory_recursive(const std::string &path) {
uint64_t hash_path(const std::string &path) { uint64_t hash_path(const std::string &path) {
if (!std::filesystem::exists(path)) { if (!std::filesystem::exists(path)) {
std::cerr << "Path does not exist: " << path << std::endl; error << "Path does not exist: " << path << std::endl;
return 0; return 0;
} }
@ -103,28 +105,28 @@ uint64_t hash_path(const std::string &path) {
} else if (std::filesystem::is_regular_file(path)) { } else if (std::filesystem::is_regular_file(path)) {
return hash_file(path); return hash_file(path);
} else { } else {
std::cerr << "Path is neither a file nor a directory: " << path << std::endl; error << "Path is neither a file nor a directory: " << path << std::endl;
return 0; return 0;
} }
} }
void hash_demo(const std::string & path) void hash_demo(const std::string & path)
{ {
std::cout << "Hashing path: " << path << std::endl; info << "Hashing path: " << path << std::endl;
auto start = std::chrono::high_resolution_clock::now(); auto start = std::chrono::high_resolution_clock::now();
XXH64_hash_t hash = hash_path(path); XXH64_hash_t hash = hash_path(path);
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "Hash: " << hash << " (took " << duration.count() << "ms)" << std::endl; info << "Hash: " << hash << " (took " << duration.count() << "ms)" << std::endl;
} }
int hash_demo_raw(const std::string & path) int hash_demo_raw(const std::string & path)
{ {
if (!std::filesystem::exists(path)) { if (!std::filesystem::exists(path)) {
std::cout << 0 <<std::endl; return 1; info << 0 <<std::endl; return 1;
} }
XXH64_hash_t hash = hash_path(path); XXH64_hash_t hash = hash_path(path);
std::cout << hash << std::endl; info << hash << std::endl;
return 0; return 0;
} }

View File

@ -40,7 +40,7 @@ bool replace_line_in_file(const std::string& file_path, const std::string& searc
std::string line; std::string line;
if (!input_file.is_open()) { if (!input_file.is_open()) {
std::cerr << "Error: Unable to open file: " << file_path << std::endl; error << "Unable to open file: " << file_path << std::endl;
return false; return false;
} }
@ -55,7 +55,7 @@ bool replace_line_in_file(const std::string& file_path, const std::string& searc
std::ofstream output_file(file_path); std::ofstream output_file(file_path);
if (!output_file.is_open()) if (!output_file.is_open())
{ {
std::cerr << "Error: Unable to open file: " << file_path << std::endl; error << "Unable to open file: " << file_path << std::endl;
return false; return false;
} }
for (const auto& modified_line : file_lines) for (const auto& modified_line : file_lines)
@ -156,7 +156,7 @@ int str2int(const std::string &str)
try { try {
return std::stoi(str); return std::stoi(str);
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "Error: Invalid integer string: [" << str << "]" << std::endl; error << "Invalid integer string: [" << str << "]" << std::endl;
return 0; return 0;
} }
} }
@ -313,7 +313,8 @@ std::string requote(std::string str) {
int die(const std::string & msg) { int die(const std::string & msg) {
std::cerr << msg << std::endl; error << "Fatal error:" << std::endl;
error << msg << std::endl;
return 1; return 1;
} }
@ -553,23 +554,40 @@ bool match_line(const std::string &line, const std::string &pattern) {
// edits file in-place. // edits file in-place.
bool file_replace_or_add_segment(std::string filepath, std::string segment) bool file_replace_or_add_segment(std::string filepath, std::string segment)
{ {
std::string first_line = segment.substr(0, segment.find("\n")); // Create a backup of the original file
std::string backup_path = filepath + ".bak";
// look backwards until we get a non-empty line. try {
size_t last_line_pos = segment.rfind("\n"); std::filesystem::copy_file(filepath, backup_path, std::filesystem::copy_options::overwrite_existing);
while (last_line_pos != std::string::npos) { } catch (const std::exception& e) {
std::string last_line = segment.substr(last_line_pos + 1); std::cerr << "Error creating backup file: " << e.what() << std::endl;
if (!trim(last_line).empty()) { return false;
break; }
}
last_line_pos = segment.rfind("\n", last_line_pos - 1); // Handle empty segment
if (segment.empty()) {
error << "Empty segment provided" << std::endl;
return false;
}
// split the segment into lines
std::vector<std::string> segment_lines = split(segment, "\n");
// remove empty lines
segment_lines.erase(std::remove_if(segment_lines.begin(), segment_lines.end(), [](const std::string& line) {
return trim(line).empty();
}), segment_lines.end());
// remove any lines that are just whitespace
segment_lines.erase(std::remove_if(segment_lines.begin(), segment_lines.end(), [](const std::string& line) { return trim(line).empty(); }), segment_lines.end());
// check that the segment has at least two lines
if (segment_lines.size() < 2) {
error << "Segment must contain at least two non-empty lines" << std::endl;
return false;
} }
std::string last_line = segment.substr(last_line_pos + 1);
// Read the entire file into memory // Read the entire file into memory
std::ifstream input_file(filepath); std::ifstream input_file(filepath);
if (!input_file.is_open()) { if (!input_file.is_open()) {
std::cerr << "Error: Unable to open file: " << filepath << std::endl; error << "Unable to open file: " << filepath << std::endl;
return false; return false;
} }
@ -580,22 +598,21 @@ bool file_replace_or_add_segment(std::string filepath, std::string segment)
} }
input_file.close(); input_file.close();
// Store original file size for verification
size_t original_size = file_lines.size();
if (original_size == 0) {
warning << "File is empty" << std::endl;
}
// Try to find the matching block // Try to find the matching block
bool found_match = false; bool found_match = false;
for (size_t i = 0; i < file_lines.size(); i++) { for (size_t i = 0; i < file_lines.size(); i++) {
if (match_line(file_lines[i], first_line)) { if (match_line(file_lines[i], segment_lines[0])) {
// Found potential start, look for end // Found potential start, look for end
for (size_t j = i + 1; j < file_lines.size(); j++) { for (size_t j = i + 1; j < file_lines.size(); j++) {
if (match_line(file_lines[j], last_line)) { if (match_line(file_lines[j], segment_lines[segment_lines.size() - 1])) {
// Found matching block, replace it // Found matching block, replace it
file_lines.erase(file_lines.begin() + i, file_lines.begin() + j + 1); file_lines.erase(file_lines.begin() + i, file_lines.begin() + j + 1);
// Split segment into lines and insert them
std::vector<std::string> segment_lines;
std::istringstream segment_stream(segment);
while (std::getline(segment_stream, line)) {
segment_lines.push_back(line);
}
file_lines.insert(file_lines.begin() + i, segment_lines.begin(), segment_lines.end()); file_lines.insert(file_lines.begin() + i, segment_lines.begin(), segment_lines.end());
found_match = true; found_match = true;
@ -608,16 +625,13 @@ bool file_replace_or_add_segment(std::string filepath, std::string segment)
// If no match found, append the segment // If no match found, append the segment
if (!found_match) { if (!found_match) {
std::istringstream segment_stream(segment); file_lines.insert(file_lines.end(), segment_lines.begin(), segment_lines.end());
while (std::getline(segment_stream, line)) {
file_lines.push_back(line);
}
} }
// Write back to file // Write back to file
std::ofstream output_file(filepath); std::ofstream output_file(filepath);
if (!output_file.is_open()) { if (!output_file.is_open()) {
std::cerr << "Error: Unable to open file for writing: " << filepath << std::endl; error << "Unable to open file for writing: " << filepath << std::endl;
return false; return false;
} }
@ -626,6 +640,13 @@ bool file_replace_or_add_segment(std::string filepath, std::string segment)
} }
output_file.close(); output_file.close();
// If everything succeeded, remove the backup
try {
std::filesystem::remove(backup_path);
} catch (const std::exception& e) {
warning << "Could not remove backup file: " << e.what() << std::endl;
}
return true; return true;
} }