urbackup_backend/urbackupserver/LocalBackup.cpp

563 lines
14 KiB
C++

#include "LocalBackup.h"
#include "dao/ServerBackupDao.h"
#include "server_settings.h"
#include "../urbackupcommon/os_functions.h"
#include "ClientMain.h"
#include "database.h"
#include "server.h"
#include <time.h>
extern std::string server_identity;
bool LocalBackup::doBackup()
{
logid = ServerLogger::getLogId(clientid);
db = nullptr;
if (is_file_backup)
{
if (r_incremental)
{
status_id = ServerStatus::startProcess(clientname, sa_incr_file, details, logid, true);
}
else
{
status_id = ServerStatus::startProcess(clientname, sa_full_file, details, logid, true);
}
}
else
{
if (r_incremental)
{
status_id = ServerStatus::startProcess(clientname, sa_incr_image, details, logid, true);
}
else
{
status_id = ServerStatus::startProcess(clientname, sa_full_image, details, logid, true);
}
ServerStatus::setProcessPcDone(clientname, status_id, 0);
}
IDatabase* db = Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER);
server_settings.reset(new ServerSettings(db, clientid));
ServerBackupDao backup_dao(db);
if (!constructBackupPath())
return false;
ServerBackupDao::SLastIncremental last_incremental = backup_dao.getLastIncrementalCompleteFileBackup(clientid, group);
int incremental_ref = 0;
int incremental = 0;
if (last_incremental.exists)
{
incremental = last_incremental.incremental + 1;
incremental_ref = last_incremental.id;
}
if (!backup_dao.newFileBackup(incremental, clientid, backuppath_single, 0, 0, group, incremental_ref, 1))
return false;
backupid = static_cast<int>(db->getLastInsertID());
std::string pver = "3";
std::string identity = client_main->getIdentity();
std::string start_backup_cmd = identity + pver;
if (!isIncrementalBackup())
{
start_backup_cmd += "START FULL BACKUP";
}
else
{
start_backup_cmd += "START BACKUP";
}
start_backup_cmd += " group=" + convert(group);
if (!clientsubname.empty())
{
start_backup_cmd += "&clientsubname=" + EscapeParamString((clientsubname));
}
start_backup_cmd += "&running_jobs=" + convert(ServerStatus::numRunningJobs(clientname));
/*if (resume)
{
start_backup_cmd += "&resume=";
if (full)
start_backup_cmd += "full";
else
start_backup_cmd += "incr";
}*/
if (BackupServer::useTreeHashing())
{
start_backup_cmd += "&sha=528";
}
else
{
start_backup_cmd += "&sha=512";
}
start_backup_cmd += "&with_permissions=1&with_scripts=1&with_orig_path=1&with_sequence=1&with_proper_symlinks=1";
start_backup_cmd += "&status_id=" + convert(status_id);
start_backup_cmd += "&log_id=" + convert(logid.first);
start_backup_cmd += "&async=1";
start_backup_cmd += "#token=" + server_token;
ServerLogger::Log(logid, clientname + ": Connecting to startup backup...", LL_DEBUG);
std::unique_ptr<IPipe> cc( client_main->getClientCommandConnection(server_settings.get(), 60000));
if (!cc)
{
ServerLogger::Log(logid, "Connecting to client \"" + clientname + "\" to startup backup failed", LL_ERROR);
has_early_error = true;
return false;
}
CTCPStack tcpstack(client_main->isOnInternetConnection());
if (tcpstack.Send(cc.get(), start_backup_cmd) != start_backup_cmd.size())
{
ServerLogger::Log(logid, "Requesting backup start failed", LL_ERROR);
has_early_error = true;
return false;
}
const unsigned int timeout_time = 10 * 60 * 1000;
bool has_total_timeout;
std::string async_id;
int64 starttime = Server->getTimeMS();
bool no_backup_dirs = false;
while (!(has_total_timeout = Server->getTimeMS() - starttime > timeout_time))
{
if (ServerStatus::getProcess(clientname, status_id).stop)
{
ServerLogger::Log(logid, "Sever admin stopped backup during start", LL_WARNING);
return false;
}
std::string ret;
size_t rc = cc->Read(&ret, 60000);
if (rc == 0)
{
ServerLogger::Log(logid, "Timeout while requesting backup start", LL_ERROR);
has_timeout_error = true;
should_backoff = false;
return false;
}
tcpstack.AddData((char*)ret.c_str(), ret.size());
bool has_error = false;
while (tcpstack.getPacket(ret))
{
if (ret != "DONE")
{
if (async_id.empty()
&& next(ret, 0, "ASYNC-"))
{
str_map params;
ParseParamStrHttp(ret.substr(6), &params);
async_id = params["async_id"];
starttime = Server->getTimeMS();
break;
}
else if (ret == "BUSY")
{
starttime = Server->getTimeMS();
}
else if (ret != "no backup dirs")
{
if (ret == "ERR")
{
client_main->forceReauthenticate();
}
ServerLogger::Log(logid, "Starting backup on \"" + clientname + "\" failed: " + ret, LL_ERROR);
if (ret.find("Async indexing process not found") != std::string::npos)
{
ServerLogger::Log(logid, "Hint: The most likely explanation for this error is that the client was restarted/rebooted during indexing", LL_ERROR);
should_backoff = false;
has_timeout_error = true;
}
return false;
}
else
{
ServerLogger::Log(logid, "Starting backup on \"" + clientname + "\" failed: " + ret + ". Please add paths to backup on the client (via tray icon) or configure default paths to backup.", LL_ERROR);
no_backup_dirs = true;
break;
}
}
else
{
return true;
}
}
if (!async_id.empty())
{
break;
}
}
if (has_total_timeout)
{
ServerLogger::Log(logid, "Starting backup of \"" + clientname + "\" failed - TIMEOUT(3)", LL_ERROR);
has_timeout_error = true;
should_backoff = false;
return false;
}
return true;
}
bool LocalBackup::queryBackupFinished(int64 timeout_time, bool& finished)
{
IDatabase* db = Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER);
server_settings.reset(new ServerSettings(db, clientid));
finished = false;
int64 starttime = Server->getTimeMS();
std::unique_ptr<IPipe> cc;
CTCPStack tcpstack(client_main->isOnInternetConnection());
while (Server->getTimeMS() - starttime <= timeout_time)
{
if (cc.get() == NULL)
{
while (cc.get() == NULL
&& Server->getTimeMS() - starttime <= timeout_time)
{
ServerLogger::Log(logid, clientname + ": Connecting for getting backup status...", LL_DEBUG);
cc.reset(client_main->getClientCommandConnection(server_settings.get(), 60000));
if (ServerStatus::getProcess(clientname, status_id).stop)
{
cc.reset();
ServerLogger::Log(logid, "Sever admin stopped backup)", LL_WARNING);
break;
}
if (cc.get() == NULL)
{
ServerLogger::Log(logid, clientname + ": Failed to connect to client. Retrying in 10s", LL_DEBUG);
Server->wait(10000);
}
}
if (cc.get() == NULL)
{
if (!ServerStatus::getProcess(clientname, status_id).stop)
{
ServerLogger::Log(logid, "Connecting to ClientService of \"" + clientname + "\" failed - CONNECT error while getting backup status(2)", LL_ERROR);
has_timeout_error = true;
}
return false;
}
starttime = Server->getTimeMS();
tcpstack.reset();
std::string cmd = client_main->getIdentity() + "WAIT FOR INDEX async_id=" + async_id + "#token=" + server_token;
tcpstack.Send(cc.get(), cmd);
}
std::string ret;
size_t rc = cc->Read(&ret, 60000);
if (rc == 0)
{
cc.reset();
continue;
}
tcpstack.AddData((char*)ret.c_str(), ret.size());
while (tcpstack.getPacket(ret))
{
if (ret == "DONE")
{
backup_result = true;
finished = true;
return true;
}
else if (ret == "BUSY")
{
finished = false;
starttime = Server->getTimeMS();
return true;
}
else
{
if (ret == "ERR")
{
client_main->forceReauthenticate();
Server->wait(10000);
cc.reset();
break;
}
else
{
finished = true;
backup_result = false;
return true;
}
ServerLogger::Log(logid, "Backup status of \"" + clientname + "\" failed: " + ret, LL_ERROR);
return false;
}
}
}
return false;
}
bool LocalBackup::finishBackup()
{
if (!os_create_dir(backuppath + os_file_sep() + ".hashes"))
{
return false;
}
std::vector<std::string> del_files;
if (!finishLocalBackup(del_files))
{
ServerLogger::Log(logid, "Error getting objects to be deleted", LL_ERROR);
return false;
}
if (!del_files.empty())
{
Server->Log(clientname + ": Connecting to client...", LL_DEBUG);
std::string identity = client_main->getIdentity();
FileClient fc(false, identity, client_main->getProtocolVersions().filesrv_protocol_version,
client_main->isOnInternetConnection(), client_main, nullptr);
_u32 rc = client_main->getClientFilesrvConnection(&fc, server_settings.get(), 60000);
if (rc != ERR_CONNECTED)
{
ServerLogger::Log(logid, "Error connecting to download objects to be deleted", LL_ERROR);
return false;
}
fc.setProgressLogCallback(this);
ServerLogger::Log(logid, clientname + ": Loading objects to be deleted", LL_INFO);
size_t idx = 0;
for (std::string del_file : del_files)
{
std::unique_ptr<IFsFile> del_f(Server->openFile(backuppath + os_file_sep()
+ "del_objs_" + std::to_string(idx) + ".dat", MODE_WRITE));
if (!del_f)
{
ServerLogger::Log(logid, "Error opening del_objs fn. "+os_last_error_str(), LL_ERROR);
return false;
}
rc = fc.GetFile("urbackup/"+ del_file, del_f.get(), true, false, 0, false, 0);
if (rc != ERR_SUCCESS)
{
ServerLogger::Log(logid, "Error objects to be deleted "+ del_file+" from " + clientname +
". Errorcode: " + fc.getErrorString(rc) + " (" + convert(rc) + ")", LL_ERROR);
return false;
}
++idx;
}
}
writestring("1", backuppath + os_file_sep()
+ ".hashes" + os_file_sep() + ".local_backup_7267f182-4341-4cda-ada1-4c0c554322d8");
const char* sync_fn = ".sync_f3a50226-f49a-4195-afef-c75b21781ae1";
std::unique_ptr<IFile> syncf(Server->openFile(backuppath + os_file_sep()
+ ".hashes" + os_file_sep()+sync_fn, MODE_WRITE));
if (!syncf)
return false;
if (!os_sync(backuppath))
return false;
if (!del_files.empty())
{
if (!finishLocalBackup(del_files))
{
ServerLogger::Log(logid, "Error finishing local backup on client", LL_ERROR);
return false;
}
}
return true;
}
bool LocalBackup::constructBackupPath()
{
if (!createDirectoryForClient())
{
return false;
}
time_t tt = time(NULL);
#ifdef _WIN32
tm lt;
tm* t = &lt;
localtime_s(t, &tt);
#else
tm* t = localtime(&tt);
#endif
char buffer[500];
strftime(buffer, 500, "%y%m%d-%H%M", t);
backuppath_single = (std::string)buffer;
std::string backupfolder = server_settings->getSettings()->backupfolder;
backuppath = backupfolder + os_file_sep() + clientname + os_file_sep() + backuppath_single;
return os_create_dir(os_file_prefix(backuppath));
}
bool LocalBackup::finishLocalBackup(std::vector<std::string>& del_files)
{
std::string params;
if (del_files.empty())
{
params = "step=1";
}
else
{
params = "step=2";
size_t idx = 0;
for (std::string fn : del_files)
{
params += "&del_fn" + std::to_string(idx) + "=" + EscapeParamString(fn);
++idx;
}
}
IDatabase* db = Server->getDatabase(Server->getThreadID(), URBACKUPDB_SERVER);
server_settings.reset(new ServerSettings(db, clientid));
const int64 timeout_time = 60000;
int64 starttime = Server->getTimeMS();
std::unique_ptr<IPipe> cc;
CTCPStack tcpstack(client_main->isOnInternetConnection());
while (Server->getTimeMS() - starttime <= timeout_time)
{
if (cc.get() == NULL)
{
while (cc.get() == NULL
&& Server->getTimeMS() - starttime <= timeout_time)
{
ServerLogger::Log(logid, clientname + ": Connecting for finishing backup...", LL_DEBUG);
cc.reset(client_main->getClientCommandConnection(server_settings.get(), 60000));
if (ServerStatus::getProcess(clientname, status_id).stop)
{
cc.reset();
ServerLogger::Log(logid, "Sever admin stopped backup", LL_WARNING);
break;
}
if (cc.get() == NULL)
{
ServerLogger::Log(logid, clientname + ": Failed to connect to client. Retrying in 10s", LL_DEBUG);
Server->wait(10000);
}
}
if (cc.get() == NULL)
{
if (!ServerStatus::getProcess(clientname, status_id).stop)
{
ServerLogger::Log(logid, "Connecting to ClientService of \"" + clientname + "\" failed - CONNECT error while getting backup status(2)", LL_ERROR);
has_timeout_error = true;
}
return false;
}
starttime = Server->getTimeMS();
tcpstack.reset();
std::string cmd = client_main->getIdentity() + "FINISH LBACKUP?"+ params+"#token=" + server_token;
tcpstack.Send(cc.get(), cmd);
}
std::string ret;
size_t rc = cc->Read(&ret, 60000);
if (rc == 0)
{
cc.reset();
continue;
}
tcpstack.AddData((char*)ret.c_str(), ret.size());
while (tcpstack.getPacket(ret))
{
if (ret == "ERR")
{
client_main->forceReauthenticate();
Server->wait(10000);
cc.reset();
break;
}
else
{
str_map res;
ParseParamStrHttp(ret, &res);
if (res["ok"] != "1")
{
return false;
}
size_t idx = 0;
while (res.find("del_fn" + std::to_string(idx))!=res.end())
{
del_files.push_back(res["del_fn" + std::to_string(idx)]);
++idx;
}
return true;
}
}
}
return false;
}
void LocalBackup::log_progress(const std::string& fn, int64 total, int64 downloaded, int64 speed_bps)
{
std::string fn_wo_token = fn;
std::string share = getuntil("/", fn);
if (!share.empty())
{
if (share.find("|") != std::string::npos)
{
fn_wo_token = getafter("|", fn);
}
}
if (total > 0 && total != LLONG_MAX)
{
int pc_complete = 0;
if (total > 0)
{
pc_complete = static_cast<int>((static_cast<float>(downloaded) / total) * 100.f);
}
ServerLogger::Log(logid, "Loading \"" + fn_wo_token + "\". " + convert(pc_complete) + "% finished " + PrettyPrintBytes(downloaded) + "/" + PrettyPrintBytes(total) + " at " + PrettyPrintSpeed(static_cast<size_t>(speed_bps)), LL_DEBUG);
}
else
{
ServerLogger::Log(logid, "Loading \"" + fn_wo_token + "\". Loaded " + PrettyPrintBytes(downloaded) + " at " + PrettyPrintSpeed(static_cast<size_t>(speed_bps)), LL_DEBUG);
}
}