172 lines
7.9 KiB
C++
172 lines
7.9 KiB
C++
#include <unistd.h>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <filesystem>
|
|
|
|
#include "utils/output.hpp"
|
|
#include <libassert/assert.hpp>
|
|
#include "utils/utils.hpp"
|
|
#include "command_registry.hpp"
|
|
#include "config.hpp"
|
|
#include "services.hpp"
|
|
#include "servers.hpp"
|
|
#include "servers.hpp"
|
|
#include "templates.hpp"
|
|
#include "utils/directories.hpp"
|
|
#include "shared_commands.hpp"
|
|
|
|
namespace dropshell
|
|
{
|
|
|
|
int backupdata_handler(const CommandContext &ctx);
|
|
|
|
static std::vector<std::string> backupdata_name_list = {"backupdata", "bd", "backup", "bup"};
|
|
|
|
// Static registration
|
|
struct BackupDataCommandRegister
|
|
{
|
|
BackupDataCommandRegister()
|
|
{
|
|
CommandRegistry::instance().register_command({backupdata_name_list,
|
|
backupdata_handler,
|
|
shared_commands::std_autocomplete_allowall,
|
|
false, // hidden
|
|
true, // requires_config
|
|
true, // requires_install
|
|
2, // min_args (after command)
|
|
2, // max_args (after command)
|
|
"backupdata SERVER SERVICE",
|
|
"Backup data for a service on a server.",
|
|
// heredoc
|
|
R"(
|
|
backupdata SERVER SERVICE Backup data for a service on a server.
|
|
backupdata SERVER all Backup data for all services on a server.
|
|
|
|
Note: This command will not create any data or configuration.
|
|
It will simply backup the data on the remote server, saving it to a local file.
|
|
Restore the data with restore.
|
|
)"});
|
|
}
|
|
} backupdata_command_register;
|
|
|
|
namespace shared_commands
|
|
{
|
|
|
|
bool backupdata_service(const ServerConfig &server_env, const std::string &service)
|
|
{
|
|
ASSERT(server_env.is_valid(), "Invalid server environment for " + server_env.get_server_name());
|
|
std::string server = server_env.get_server_name();
|
|
|
|
LocalServiceInfo sinfo = get_service_info(server, service);
|
|
if (!SIvalid(sinfo))
|
|
{
|
|
error << "Service " << service << " is not valid" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
const std::string command = "backup";
|
|
|
|
if (!gTemplateManager().template_command_exists(sinfo.template_name, command))
|
|
{
|
|
info << service << " has no data to backup" << std::endl;
|
|
debug << "(no backup script for " << sinfo.template_name << ")" << std::endl;
|
|
return true; // nothing to back up.
|
|
}
|
|
|
|
std::string user = server_env.get_user_for_service(service);
|
|
|
|
// Check if basic installed stuff is in place.
|
|
std::string remote_service_template_path = remotepath(server, user).service_template(service);
|
|
std::string remote_command_script_file = remote_service_template_path + "/" + command + ".sh";
|
|
std::string remote_service_config_path = remotepath(server, user).service_config(service);
|
|
if (!server_env.check_remote_items_exist({remotepath(server, user).service(service),
|
|
remote_command_script_file,
|
|
remotefile(server, user).service_env(service)}, user))
|
|
{
|
|
error << "Required service directories not found on remote server" << std::endl;
|
|
info << "Is the service installed?" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// Create backups directory on server if it doesn't exist
|
|
std::string remote_backups_dir = remotepath(server, user).backups();
|
|
debug << "Remote backups directory on " << server << ": " << remote_backups_dir << std::endl;
|
|
std::string mkdir_cmd = "mkdir -p " + quote(remote_backups_dir);
|
|
if (!execute_ssh_command(server_env.get_SSH_INFO(user), sCommand("", mkdir_cmd, {}), cMode::Defaults))
|
|
{
|
|
error << "Failed to create backups directory on server" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// Create backups directory locally if it doesn't exist
|
|
std::string local_backups_dir = localpath::backups();
|
|
if (local_backups_dir.empty())
|
|
{
|
|
error << "Local backups directory not found" << std::endl;
|
|
info << "Run 'dropshell edit' to configure DropShell" << std::endl;
|
|
return false;
|
|
}
|
|
if (!std::filesystem::exists(local_backups_dir))
|
|
std::filesystem::create_directories(local_backups_dir);
|
|
|
|
// Get current datetime for backup filename
|
|
shared_commands::cBackupFileName backup_filename_construction(server, service, sinfo.template_name);
|
|
if (!backup_filename_construction.is_valid())
|
|
{
|
|
error << "Invalid backup filename" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// Construct backup filename
|
|
std::string backup_filename = backup_filename_construction.get_filename();
|
|
std::string remote_backup_file_path = remote_backups_dir + "/" + backup_filename;
|
|
std::string local_backup_file_path = (std::filesystem::path(local_backups_dir) / backup_filename).string();
|
|
|
|
// assert that the backup filename is valid - -_- appears exactly 3 times in local_backup_file_path.
|
|
ASSERT(3 == count_substring(magic_string(), local_backup_file_path), "Invalid backup filename");
|
|
|
|
{ // Run backup script
|
|
shared_commands::cRemoteTempFolder remote_temp_folder(server_env, user);
|
|
if (!server_env.run_remote_template_command(service, command, {}, false, {{"BACKUP_FILE", remote_backup_file_path}, {"TEMP_DIR", remote_temp_folder.path()}}))
|
|
{
|
|
error << "Backup script failed on remote server: " << remote_backup_file_path << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// Copy backup file from server to local
|
|
if (!shared_commands::scp_file_from_remote(server_env, remote_backup_file_path, local_backup_file_path, false, sinfo.user))
|
|
{
|
|
error << "Failed to copy backup file from server" << std::endl;
|
|
return false;
|
|
}
|
|
} // dtor of remote_temp_folder will clean up the temp folder on the server
|
|
|
|
info << "Backup created successfully. Restore with:" << std::endl;
|
|
info << " dropshell restore " << server << " " << service << " " << backup_filename << std::endl;
|
|
return true;
|
|
}
|
|
} // namespace shared_commands
|
|
|
|
int backupdata_handler(const CommandContext &ctx)
|
|
{
|
|
ASSERT(ctx.args.size() == 2, "Invalid number of arguments");
|
|
|
|
std::string server = safearg(ctx.args, 0);
|
|
std::string service = safearg(ctx.args, 1);
|
|
|
|
if (service == "all")
|
|
{
|
|
// backup all services on the server
|
|
maketitle("Backing up data for all services on " + server);
|
|
bool okay = true;
|
|
std::vector<LocalServiceInfo> services = get_server_services_info(server);
|
|
for (const LocalServiceInfo &si : services)
|
|
okay &= shared_commands::backupdata_service(server, si.service_name);
|
|
return okay ? 0 : 1;
|
|
}
|
|
|
|
return shared_commands::backupdata_service(server, service) ? 0 : 1;
|
|
}
|
|
|
|
} // namespace dropshell
|