urbackup_backend/urbackupclient/client_restore_http.cpp
2021-07-18 13:12:33 +02:00

505 lines
11 KiB
C++

#include "client_restore_http.h"
#include "client_restore.h"
#include <mutex>
#include <thread>
#include "../urbackupcommon/fileclient/tcpstack.h"
#include "../urbackupcommon/mbrdata.h"
#include "../fsimageplugin/IFSImageFactory.h"
extern IFSImageFactory* image_fak;
namespace
{
std::mutex g_restore_data_mutex;
restore::LoginData g_login_data;
int64 res_id = 0;
struct SRestoreRes
{
restore::DownloadStatus dl_status;
restore::EDownloadResult ec;
bool finished;
};
std::map<int64, SRestoreRes> restore_results;
void setGLoginData(restore::LoginData login_data)
{
std::lock_guard<std::mutex> lock(g_restore_data_mutex);
g_login_data = login_data;
}
}
ACTION_IMPL(status)
{
std::unique_ptr<IPipe> c(restore::connectToService());
JSON::Object ret;
ret.set("ok", true);
if (!c)
{
ret.set("err", "Error connecting to restore service");
restore::writeJsonResponse(tid, ret);
return;
}
std::string pw = restore::restorePw();
CTCPStack tcpstack;
tcpstack.Send(c.get(), "STATUS DETAIL#pw=" + pw);
std::string r = restore::getResponse(c);
if (r.empty())
{
ret.set("err", "No response from restore service");
restore::writeJsonResponse(tid, ret);
return;
}
Server->setContentType(tid, "application/json");
Server->Write(tid, r);
}
ACTION_IMPL(login)
{
restore::LoginData login_data;
login_data.has_login_data = POST["has_login_data"]=="1";
login_data.username = POST["username"];
login_data.password = POST["password"];
JSON::Object ret;
ret.set("ok", true);
if (restore::do_login(login_data, false))
{
ret.set("success", true);
setGLoginData(login_data);
}
else
{
ret.set("success", false);
}
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(get_clientnames)
{
int rc = 0;
std::vector<std::string> clients = restore::getBackupclients(rc);
JSON::Object ret;
ret.set("ok", true);
ret.set("rc", rc);
if (rc != 0)
ret.set("err", "Error getting clients on server. Rc=" + std::to_string(rc));
JSON::Array j_clients;
for (auto clientname : clients)
{
j_clients.add(clientname);
}
ret.set("clients", j_clients);
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(get_backupimages)
{
std::unique_ptr<IPipe> c(restore::connectToService());
JSON::Object ret;
ret.set("ok", true);
if (!c)
{
ret.set("err", "Error connecting to restore service");
restore::writeJsonResponse(tid, ret);
return;
}
std::string pw = restore::restorePw();
CTCPStack tcpstack;
tcpstack.Send(c.get(), "GET BACKUPIMAGES " + POST["restore_name"]+ "#pw=" + pw);
std::string r = restore::getResponse(c);
if (r.empty())
{
ret.set("err", "No response from restore service");
}
else
{
if (r[0] == '0')
{
ret.set("err", "No backup server found");
}
else
{
ret.set("images", restore::backup_images_output_to_json(r.substr(1)));
}
}
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(start_download)
{
int img_id = watoi(POST["img_id"]);
std::string img_time = POST["img_time"];
std::string out = POST["out"];
bool mbr = POST["mbr"] == "1";
SRestoreRes* restore_res;
int64 curr_res_id;
{
std::lock_guard<std::mutex> lock(g_restore_data_mutex);
curr_res_id = res_id++;
restore_res = &restore_results[curr_res_id];
}
if(mbr && !FileExists(out))
{
writestring("", out);
}
Server->Log("Start_download: id = \""+std::to_string(img_id)+" time = \""+img_time+ "\"", LL_INFO);
std::thread dl_thread([img_id, img_time, mbr, out, restore_res]() {
restore_res->ec = restore::downloadImage(img_id, img_time, out, mbr, g_login_data, restore_res->dl_status);
std::lock_guard<std::mutex> lock(g_restore_data_mutex);
restore_res->finished = true;
});
dl_thread.detach();
JSON::Object ret;
ret.set("ok", true);
ret.set("res_id", curr_res_id);
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(download_progress)
{
int64 res_id = watoi64(POST["res_id"]);
SRestoreRes* restore_res = nullptr;
{
std::lock_guard<std::mutex> lock(g_restore_data_mutex);
auto it = restore_results.find(res_id);
if (it != restore_results.end())
restore_res = &it->second;
}
JSON::Object ret;
ret.set("ok", true);
if (restore_res == nullptr)
{
ret.set("err", "Restore not found");
restore::writeJsonResponse(tid, ret);
return;
}
if (restore_res->finished)
{
ret.set("finished", true);
ret.set("ec", restore_res->ec);
restore::writeJsonResponse(tid, ret);
return;
}
std::unique_ptr<IPipe> c(restore::connectToService());
if (!c)
{
ret.set("err", "Error connecting to restore service");
restore::writeJsonResponse(tid, ret);
return;
}
CTCPStack tcpstack;
std::string pw = restore::restorePw();
tcpstack.Send(c.get(), "STATUS DETAIL#pw=" + pw);
std::string r = restore::getResponse(c);
if (r.empty())
{
ret.set("err", "No response from restore service");
restore::writeJsonResponse(tid, ret);
return;
}
Server->setContentType(tid, "application/json");
Server->Write(tid, r);
/*std::unique_ptr<IPipe> c(restore::connectToService());
if (!c)
{
ret.set("err", "Error connecting to restore service");
restore::writeJsonResponse(tid, ret);
return;
}
CTCPStack tcpstack;
std::string pw = restore::restorePw();
tcpstack.Send(c.get(), "GET DOWNLOADPROGRESS#pw=" + pw);
int lpc = 0;
bool got_pc = false;
std::string curr;
size_t r = c->Read(&curr, 10000);
for (int i = 0; i < linecount(curr); ++i)
{
std::string l = getline(i, curr);
if (!restore::trim2(l).empty())
{
int npc = atoi(restore::trim2(l).c_str());
got_pc = true;
if (npc != lpc)
{
lpc = npc;
}
}
}
if (got_pc)
{
ret.set("pc", lpc);
}
restore::writeJsonResponse(tid, ret); */
}
ACTION_IMPL(has_network_device)
{
JSON::Object ret;
ret.set("ok", true);
ret.set("ret", restore::has_network_device());
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(ping_server)
{
std::string servername = POST["servername"];
std::thread t([servername]() {
system(("./urbackuprestoreclient --ping-server \"" + servername + "\"").c_str());
});
t.detach();
JSON::Object ret;
ret.set("ok", true);
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(has_internet_connection)
{
JSON::Object ret;
ret.set("ok", true);
int rc;
std::string errstatus;
ret.set("ret", restore::has_internet_connection(rc, errstatus));
ret.set("rc", rc);
ret.set("errstatus", errstatus);
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(configure_server)
{
bool active_client = POST["active"] == "1";
if (active_client)
{
restore::configure_internet_server(POST["url"], POST["authkey"], POST["proxy"], false);
}
else
{
restore::configure_local_server();
}
JSON::Object ret;
ret.set("ok", true);
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(get_disks)
{
std::vector<restore::SLsblk> drives = restore::lsblk("");
JSON::Array j_disks;
for (restore::SLsblk& blk : drives)
{
if (blk.type == "disk")
{
JSON::Object disk;
disk.set("model", blk.model);
disk.set("maj_min", blk.maj_min);
disk.set("path", blk.path);
disk.set("size", blk.size);
disk.set("type", blk.type);
j_disks.add(disk);
}
}
JSON::Object ret;
ret.set("ok", true);
ret.set("disks", j_disks);
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(get_is_disk_mbr)
{
JSON::Object ret;
ret.set("ok", true);
ret.set("res", is_disk_mbr(POST["mbrfn"]));
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(write_mbr)
{
std::string errmsg;
bool b = restore::do_restore_write_mbr(POST["mbrfn"],
POST["out_device"], true, errmsg);
JSON::Object ret;
ret.set("ok", true);
ret.set("success", b);
ret.set("errmsg", errmsg);
restore::writeJsonResponse(tid, ret);
}
ACTION_IMPL(get_partition)
{
std::unique_ptr<IFsFile> f(Server->openFile(POST["mbrfn"], MODE_READ));
if(f==nullptr)
{
JSON::Object ret;
ret.set("ok", true);
ret.set("success", false);
ret.set("errmsg", "Cannot open mbr file. "+os_last_error_str());
restore::writeJsonResponse(tid, ret);
return;
}
size_t fsize=(size_t)f->Size();
std::vector<char> buf(fsize);
f->Read(buf.data(), static_cast<_u32>(fsize));
CRData mbr(buf.data(), fsize);
SMBRData mbrdata(mbr);
if(mbrdata.hasError())
{
JSON::Object ret;
ret.set("ok", true);
ret.set("success", false);
ret.set("errmsg", "Error while reading MBR file");
restore::writeJsonResponse(tid, ret);
return;
}
bool t_gpt_style;
std::vector<IFSImageFactory::SPartition> partitions
= image_fak->readPartitions(mbrdata.mbr_data, mbrdata.gpt_header, mbrdata.gpt_table, t_gpt_style);
std::string seldrive = POST["out_device"];
Server->Log("Selected device: "+seldrive+" Partition: "+convert(mbrdata.partition_number));
std::string partpath = restore::getPartitionPath(seldrive, mbrdata.partition_number);
Server->Log("Partition path: "+partpath);
std::unique_ptr<IFsFile> dev;
if(!partpath.empty())
{
dev.reset(Server->openFile(partpath, MODE_RW));
}
int try_c=0;
int delete_parts = 0;
bool fix_gpt=true;
while(!dev && try_c<10)
{
system(("partprobe "+seldrive+" > /dev/null 2>&1").c_str());
Server->wait(10000);
partpath = restore::getPartitionPath(seldrive, mbrdata.partition_number);
if(!partpath.empty())
{
dev.reset(Server->openFile(partpath, MODE_RW));
}
if(!dev)
{
if (!mbrdata.gpt_style)
{
Server->Log("Trying to fix LBA partitioning scheme via fdisk...");
//Fix LBA partition signature
system(("echo w | fdisk " + seldrive + " > /dev/null 2>&1").c_str());
system(("echo -e \"w\\nY\" | fdisk " + seldrive + " > /dev/null 2>&1").c_str());
}
else
{
Server->Log("Trying to fix GPT partitioning scheme via gdisk...");
system(("echo -e \"w\\nY\" | gdisk " + seldrive).c_str());
}
if (fix_gpt && try_c>5
&& !partitions.empty())
{
//TODO: the last partition might not be the one with the highest offset
Server->Log("Deleting last GPT partition...");
++delete_parts;
std::string cmd;
for (int i = 0; i < delete_parts; ++i)
{
if (partitions.size() - i == mbrdata.partition_number)
{
Server->Log("Cannot delete last GPT partition. This is the partition to be restored. DISK IS PROBABLY TOO SMALL TO RESTORE TO.", LL_ERROR);
system("read -n1 -r -p \"Press any key to continue...\" key");
fix_gpt = false;
try_c = 100;
}
cmd += "d\\n" + convert(partitions.size() - i);
}
cmd += "\\nw\\nY";
if(fix_gpt)
system(("echo -e \""+cmd+"\" | gdisk " + seldrive).c_str());
}
}
++try_c;
}
if(dev==nullptr)
{
JSON::Object ret;
ret.set("ok", true);
ret.set("success", false);
ret.set("errmsg", "Could not open restore partition");
restore::writeJsonResponse(tid, ret);
return;
}
JSON::Object ret;
ret.set("ok", true);
ret.set("success", true);
ret.set("partpath", partpath);
restore::writeJsonResponse(tid, ret);
return;
}
ACTION_IMPL(restart)
{
system("init 6");
JSON::Object ret;
ret.set("ok", true);
restore::writeJsonResponse(tid, ret);
}