mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
2849 lines
69 KiB
C++
2849 lines
69 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011-2016 Martin Raiber
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
**************************************************************************/
|
|
|
|
#include <string>
|
|
#include "../Interface/Server.h"
|
|
#include "../Interface/ThreadPool.h"
|
|
#include "../Interface/Thread.h"
|
|
#include "../Interface/File.h"
|
|
#include "../urbackupcommon/fileclient/tcpstack.h"
|
|
#include "../common/data.h"
|
|
#include "../stringtools.h"
|
|
#include "../urbackupcommon/os_functions.h"
|
|
#include "client_restore.h"
|
|
#include "client_restore_http.h"
|
|
#include <iostream>
|
|
#include <stdlib.h>
|
|
#include <memory.h>
|
|
#include <algorithm>
|
|
#include <thread>
|
|
#include "../urbackupcommon/fileclient/socket_header.h"
|
|
#ifndef _WIN32
|
|
#include <net/if.h>
|
|
#include <sys/ioctl.h>
|
|
#else
|
|
#include <ws2tcpip.h>
|
|
#endif
|
|
#include "../urbackupcommon/mbrdata.h"
|
|
#include "../fileservplugin/settings.h"
|
|
#include "../fileservplugin/packet_ids.h"
|
|
#include <memory>
|
|
#include "../cryptoplugin/ICryptoFactory.h"
|
|
#include "../fsimageplugin/IFSImageFactory.h"
|
|
|
|
#ifndef _WIN32
|
|
#include "../config.h"
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#ifdef HAVE_LINUX_FS_H
|
|
#include <linux/fs.h>
|
|
#endif
|
|
|
|
#ifndef BLKRRPART
|
|
#define BLKRRPART _IO(0x12,95)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
using namespace restore;
|
|
|
|
#ifdef _WIN32
|
|
const std::string pw_file="pw.txt";
|
|
#else
|
|
const std::string pw_file="urbackup/pw.txt";
|
|
#endif
|
|
|
|
const std::string configure_wlan="wicd-curses";
|
|
const std::string configure_networkcard=configure_wlan;
|
|
|
|
#define UDP_SOURCE_PORT 35623
|
|
|
|
extern ICryptoFactory *crypto_fak;
|
|
extern IFSImageFactory* image_fak;
|
|
|
|
namespace
|
|
{
|
|
bool rereadPartitionLayout(const std::string& devfn)
|
|
{
|
|
bool ret = true;
|
|
#ifndef _WIN32
|
|
int fd = open(devfn.c_str(), O_NONBLOCK | O_RDWR);
|
|
if (fd == -1)
|
|
return false;
|
|
|
|
int rc = ioctl(fd, BLKRRPART, NULL);
|
|
|
|
if (rc == -1)
|
|
{
|
|
ret = false;
|
|
Server->Log("Error re-reading partition layout. " + os_last_error_str(), LL_WARNING);
|
|
}
|
|
|
|
close(fd);
|
|
#endif
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
namespace restore
|
|
{
|
|
std::string trim2(const std::string& str)
|
|
{
|
|
size_t startpos = str.find_first_not_of(" \t\n");
|
|
size_t endpos = str.find_last_not_of(" \t\n");
|
|
if (std::string::npos == startpos || std::string::npos == endpos)
|
|
{
|
|
return "";
|
|
}
|
|
else
|
|
{
|
|
return str.substr(startpos, endpos - startpos + 1);
|
|
}
|
|
}
|
|
|
|
std::string getResponse(IPipe* c)
|
|
{
|
|
CTCPStack tcpstack;
|
|
char* resp = nullptr;
|
|
char buffer[1024];
|
|
size_t packetsize;
|
|
while (resp == nullptr)
|
|
{
|
|
size_t rc = c->Read(buffer, 1024, 60000);
|
|
if (rc == 0)
|
|
{
|
|
return "";
|
|
}
|
|
tcpstack.AddData(buffer, rc);
|
|
|
|
resp = tcpstack.getPacket(&packetsize);
|
|
if (resp != nullptr && packetsize == 0)
|
|
{
|
|
delete[]resp;
|
|
return "";
|
|
}
|
|
}
|
|
|
|
std::string ret;
|
|
ret.resize(packetsize);
|
|
memcpy(&ret[0], resp, packetsize);
|
|
delete[]resp;
|
|
return ret;
|
|
}
|
|
|
|
std::string restorePw()
|
|
{
|
|
return getFile(pw_file);
|
|
}
|
|
|
|
std::vector<std::string> getBackupclients(int& ec)
|
|
{
|
|
std::string pw = getFile(pw_file);
|
|
CTCPStack tcpstack;
|
|
std::vector<std::string> ret;
|
|
ec = 0;
|
|
|
|
IPipe* c = Server->ConnectStream("localhost", 35623, 60000);
|
|
if (c == nullptr)
|
|
{
|
|
Server->Log("Error connecting to client service -1", LL_ERROR);
|
|
ec = 10;
|
|
return ret;
|
|
}
|
|
|
|
tcpstack.Send(c, "GET BACKUPCLIENTS#pw=" + pw);
|
|
std::string r = getResponse(c);
|
|
if (r.empty())
|
|
{
|
|
Server->Log("No response from ClientConnector", LL_ERROR);
|
|
ec = 1;
|
|
}
|
|
else
|
|
{
|
|
if (r[0] == '0')
|
|
{
|
|
Server->Log("No backupserver found", LL_ERROR);
|
|
ec = 2;
|
|
}
|
|
else
|
|
{
|
|
std::vector<std::string> toks;
|
|
std::string t = r.substr(1);
|
|
Tokenize(t, toks, "\n");
|
|
for (size_t i = 0; i < toks.size(); ++i)
|
|
{
|
|
std::string nam = trim2(getafter("|", toks[i]));
|
|
if (!nam.empty())
|
|
{
|
|
if (std::find(ret.begin(), ret.end(), nam) == ret.end())
|
|
{
|
|
ret.push_back(nam);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Server->destroy(c);
|
|
return ret;
|
|
}
|
|
|
|
IPipe* connectToService(int& ec)
|
|
{
|
|
IPipe* c = Server->ConnectStream("localhost", 35623, 60000);
|
|
if (c == nullptr)
|
|
{
|
|
Server->Log("Error connecting to client service -1", LL_ERROR);
|
|
ec = 10;
|
|
return nullptr;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
IPipe* connectToService()
|
|
{
|
|
int ec = 0;
|
|
return connectToService(ec);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
struct SPasswordSalt
|
|
{
|
|
std::string salt;
|
|
std::string rnd;
|
|
int pbkdf2_rounds;
|
|
};
|
|
|
|
std::vector<SPasswordSalt> getSalt(const std::string& username, int tries, int *ec)
|
|
{
|
|
std::string pw=getFile(pw_file);
|
|
CTCPStack tcpstack;
|
|
*ec=0;
|
|
std::vector<SPasswordSalt> ret;
|
|
|
|
IPipe *c=connectToService(*ec);
|
|
if(c==nullptr)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
bool has_ok = false;
|
|
tcpstack.Send(c, "GET SALT#pw="+pw+"&username="+username);
|
|
std::string r=getResponse(c);
|
|
if(r.empty() )
|
|
{
|
|
Server->Log("No response from ClientConnector", LL_ERROR);
|
|
*ec=1;
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Salt Response: "+r, LL_INFO);
|
|
|
|
std::vector<std::string> toks;
|
|
Tokenize(r, toks, "/");
|
|
|
|
for(size_t i=0;i<toks.size();++i)
|
|
{
|
|
if(toks[i].find("ok;")==0)
|
|
{
|
|
has_ok = true;
|
|
std::vector<std::string> salt_toks;
|
|
Tokenize(toks[i], salt_toks, ";");
|
|
|
|
SPasswordSalt new_salts;
|
|
new_salts.pbkdf2_rounds=0;
|
|
|
|
if(salt_toks.size()==3)
|
|
{
|
|
new_salts.salt = salt_toks[1];
|
|
new_salts.rnd = salt_toks[2];
|
|
}
|
|
else if(salt_toks.size()==4)
|
|
{
|
|
new_salts.salt = salt_toks[1];
|
|
new_salts.rnd = salt_toks[2];
|
|
new_salts.pbkdf2_rounds = atoi(salt_toks[3].c_str());
|
|
}
|
|
|
|
ret.push_back(new_salts);
|
|
}
|
|
else
|
|
{
|
|
Server->Log("SALT error: "+toks[i], LL_ERROR);
|
|
Server->Log("Username not found on server", LL_ERROR);
|
|
SPasswordSalt new_salts = {};
|
|
ret.push_back(new_salts);
|
|
tries = 0;
|
|
}
|
|
}
|
|
}
|
|
Server->destroy(c);
|
|
|
|
if (!has_ok && tries>0)
|
|
{
|
|
Server->wait(10000);
|
|
return getSalt(username, tries - 1, ec);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool tryLogin(const std::string& username, const std::string& password, std::vector<SPasswordSalt> salts, int tries, int *ec)
|
|
{
|
|
std::string pw=getFile(pw_file);
|
|
CTCPStack tcpstack;
|
|
*ec=0;
|
|
|
|
IPipe *c=connectToService(*ec);
|
|
if(c==nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::string auth_str;
|
|
if(!username.empty())
|
|
{
|
|
auth_str="&username="+username;
|
|
|
|
for(size_t i=0;i<salts.size();++i)
|
|
{
|
|
if(!salts[i].salt.empty()
|
|
&& !salts[i].rnd.empty())
|
|
{
|
|
|
|
std::string pw_md5 = Server->GenerateHexMD5(salts[i].salt+password);
|
|
|
|
if(salts[i].pbkdf2_rounds>0)
|
|
{
|
|
pw_md5 = strlower(crypto_fak->generatePasswordHash(hexToBytes(pw_md5), salts[i].salt, salts[i].pbkdf2_rounds));
|
|
}
|
|
|
|
auth_str+="&password"+convert(i)+"="+
|
|
Server->GenerateHexMD5(salts[i].rnd+pw_md5);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool do_retry = false;
|
|
bool ret=false;
|
|
tcpstack.Send(c, "LOGIN FOR DOWNLOAD#pw="+pw+auth_str);
|
|
std::string r=getResponse(c);
|
|
if(r.empty() )
|
|
{
|
|
Server->Log("No response from ClientConnector", LL_ERROR);
|
|
*ec=1;
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Login Response: "+r, LL_INFO);
|
|
|
|
if(r=="ok")
|
|
{
|
|
ret=true;
|
|
}
|
|
else if (r == "no channels available")
|
|
{
|
|
ret = true;
|
|
do_retry = true;
|
|
}
|
|
else
|
|
{
|
|
if (r == "err")
|
|
{
|
|
tries = 0;
|
|
}
|
|
Server->Log("Error during login: "+r, LL_ERROR);
|
|
}
|
|
}
|
|
Server->destroy(c);
|
|
|
|
if ((!ret || do_retry) && tries>0)
|
|
{
|
|
Server->wait(10000);
|
|
return tryLogin(username, password, salts, tries - 1, ec);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
std::vector<SImage> getBackupimages(std::string clientname, int *ec)
|
|
{
|
|
std::string pw=getFile(pw_file);
|
|
CTCPStack tcpstack;
|
|
std::vector<SImage> ret;
|
|
*ec=0;
|
|
|
|
IPipe *c=Server->ConnectStream("localhost", 35623, 60000);
|
|
if(c==nullptr)
|
|
{
|
|
Server->Log("Error connecting to client service -1", LL_ERROR);
|
|
*ec=10;
|
|
return ret;
|
|
}
|
|
|
|
tcpstack.Send(c, "GET BACKUPIMAGES "+clientname+"#pw="+pw);
|
|
std::string r=getResponse(c);
|
|
if(r.empty() )
|
|
{
|
|
Server->Log("No response from ClientConnector", LL_ERROR);
|
|
*ec=1;
|
|
}
|
|
else
|
|
{
|
|
if(r[0]=='0')
|
|
{
|
|
Server->Log("No backupserver found", LL_ERROR);
|
|
*ec=1;
|
|
}
|
|
else
|
|
{
|
|
ret = parse_backup_images_output(r.substr(1));
|
|
}
|
|
}
|
|
Server->destroy(c);
|
|
return ret;
|
|
}
|
|
|
|
struct SFileBackup
|
|
{
|
|
bool operator<(const SFileBackup &other) const
|
|
{
|
|
return other.time_s<time_s;
|
|
}
|
|
std::string time_str;
|
|
_i64 time_s;
|
|
int id;
|
|
int tgroup;
|
|
};
|
|
|
|
std::vector<SFileBackup> getFileBackups(std::string clientname, int *ec)
|
|
{
|
|
std::string pw=getFile(pw_file);
|
|
CTCPStack tcpstack;
|
|
std::vector<SFileBackup> ret;
|
|
*ec=0;
|
|
|
|
IPipe *c=Server->ConnectStream("localhost", 35623, 60000);
|
|
if(c==nullptr)
|
|
{
|
|
Server->Log("Error connecting to client service -1", LL_ERROR);
|
|
*ec=10;
|
|
return ret;
|
|
}
|
|
|
|
tcpstack.Send(c, "GET FILE BACKUPS "+clientname+"#pw="+pw);
|
|
std::string r=getResponse(c);
|
|
if(r.empty() )
|
|
{
|
|
Server->Log("No response from ClientConnector", LL_ERROR);
|
|
*ec=1;
|
|
}
|
|
else
|
|
{
|
|
if(r[0]=='0')
|
|
{
|
|
Server->Log("No backupserver found", LL_ERROR);
|
|
*ec=1;
|
|
}
|
|
else
|
|
{
|
|
std::vector<std::string> toks;
|
|
std::string t=r.substr(1);
|
|
Tokenize(t, toks, "\n");
|
|
for(size_t i=0;i<toks.size();++i)
|
|
{
|
|
std::vector<std::string> t2;
|
|
Tokenize(toks[i], t2, "|");
|
|
if(t2.size()==4 )
|
|
{
|
|
SFileBackup si;
|
|
si.id=atoi(t2[0].c_str());
|
|
si.time_s=os_atoi64(t2[1]);
|
|
si.time_str=t2[2];
|
|
si.tgroup=atoi(t2[3].c_str());
|
|
ret.push_back(si);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Server->destroy(c);
|
|
return ret;
|
|
}
|
|
|
|
volatile bool restore_retry_ok=false;
|
|
|
|
namespace restore
|
|
{
|
|
EDownloadResult retryDownload(EDownloadResult errrc, int img_id, std::string img_time, std::string outfile,
|
|
bool mbr, LoginData login_data, DownloadStatus& dl_status, int recur_depth, int64* o_imgsize, int64* o_output_file_size)
|
|
{
|
|
if (recur_depth == 0)
|
|
{
|
|
Server->Log("Read Timeout: Retrying -1", LL_WARNING);
|
|
|
|
int total_tries = 20;
|
|
int tries = total_tries;
|
|
EDownloadResult rc;
|
|
do
|
|
{
|
|
Server->wait(30000);
|
|
int64 starttime = Server->getTimeMS();
|
|
rc = downloadImage(img_id, img_time, outfile, mbr, login_data, dl_status, recur_depth + 1, o_imgsize, o_output_file_size);
|
|
if (rc == EDownloadResult_Ok)
|
|
{
|
|
return rc;
|
|
}
|
|
--tries;
|
|
if (Server->getTimeMS() - starttime > 180000)
|
|
{
|
|
tries = total_tries;
|
|
}
|
|
|
|
if (rc == EDownloadResult_SizeReadError)
|
|
{
|
|
do_login(login_data);
|
|
Server->wait(10000);
|
|
}
|
|
} while (tries > 0);
|
|
}
|
|
Server->Log("Read Timeout.", LL_ERROR);
|
|
return errrc;
|
|
}
|
|
|
|
EDownloadResult downloadImage(int img_id, std::string img_time, std::string outfile, bool mbr, LoginData login_data,
|
|
DownloadStatus& dl_status, int recur_depth, int64* o_imgsize, int64* o_output_file_size)
|
|
{
|
|
std::string pw = getFile(pw_file);
|
|
CTCPStack tcpstack;
|
|
std::vector<SImage> ret;
|
|
|
|
std::unique_ptr<IPipe> client_pipe(Server->ConnectStream("localhost", 35623, 60000));
|
|
if (client_pipe.get() == nullptr)
|
|
{
|
|
Server->Log("Error connecting to client service -1", LL_ERROR);
|
|
return EDownloadResult_ConnectError;
|
|
}
|
|
|
|
std::string s_offset;
|
|
if (dl_status.offset != -1)
|
|
{
|
|
s_offset = "&offset=" + convert(dl_status.offset) + "&received_bytes=" + convert(dl_status.received);
|
|
}
|
|
|
|
std::string dl_args;
|
|
if (img_id != 0 || !img_time.empty())
|
|
{
|
|
dl_args = "&img_id=" + convert(img_id) + "&time=" + img_time;
|
|
}
|
|
else if (login_data.has_login_data
|
|
&& !login_data.token.empty())
|
|
{
|
|
dl_args = "&token=" + EscapeParamString(login_data.token);
|
|
}
|
|
|
|
tcpstack.Send(client_pipe.get(), "DOWNLOAD IMAGE#pw=" + pw + dl_args + "&mbr=" + convert(mbr) + s_offset);
|
|
|
|
std::string restore_out = outfile;
|
|
Server->Log("Restoring to " + restore_out);
|
|
std::unique_ptr<IFile> out_file(Server->openFile(restore_out, MODE_RW_READNONE));
|
|
if (out_file.get() == nullptr)
|
|
{
|
|
Server->Log("Could not open \"" + restore_out + "\" for writing. "+os_last_error_str(), LL_ERROR);
|
|
return EDownloadResult_OpenError;
|
|
}
|
|
else if (o_output_file_size != nullptr)
|
|
{
|
|
*o_output_file_size = out_file->Size();
|
|
}
|
|
|
|
_i64 imgsize = -1;
|
|
client_pipe->Read((char*)&imgsize, sizeof(_i64), 60000);
|
|
|
|
if (o_imgsize != nullptr)
|
|
{
|
|
*o_imgsize = imgsize;
|
|
}
|
|
|
|
if (imgsize == -1)
|
|
{
|
|
Server->Log("Error reading size", LL_ERROR);
|
|
return EDownloadResult_SizeReadError;
|
|
}
|
|
if (imgsize == -2)
|
|
{
|
|
Server->Log("Connection timeout", LL_ERROR);
|
|
EDownloadResult rc = retryDownload(EDownloadResult_TimeoutError1, img_id, img_time,
|
|
outfile, mbr, login_data, dl_status, recur_depth, o_imgsize, o_output_file_size);
|
|
return rc;
|
|
}
|
|
|
|
if (!mbr && imgsize > out_file->Size())
|
|
{
|
|
Server->Log("Output device too small. File size = " + convert(out_file->Size()) + " needed = " + convert(imgsize), LL_ERROR);
|
|
return EDownloadResult_DeviceTooSmall;
|
|
}
|
|
|
|
const size_t c_buffer_size = 32768;
|
|
const unsigned int c_block_size = 4096;
|
|
|
|
char buf[c_buffer_size];
|
|
if (mbr == true)
|
|
{
|
|
_i64 read = 0;
|
|
while (read < imgsize)
|
|
{
|
|
size_t c_read = client_pipe->Read(buf, c_buffer_size, 180000);
|
|
if (c_read == 0)
|
|
{
|
|
EDownloadResult rc = retryDownload(EDownloadResult_TimeoutError2, img_id, img_time, outfile,
|
|
mbr, login_data, dl_status, recur_depth, o_imgsize, o_output_file_size);
|
|
return rc;
|
|
}
|
|
out_file->Write(buf, (_u32)c_read);
|
|
read += c_read;
|
|
}
|
|
return EDownloadResult_Ok;
|
|
}
|
|
else
|
|
{
|
|
_i64 read = 0;
|
|
unsigned int blockleft = 0;
|
|
unsigned int off = 0;
|
|
char blockdata[c_block_size];
|
|
bool first = true;
|
|
bool has_data = false;
|
|
_i64 pos = 0;
|
|
while (pos < imgsize)
|
|
{
|
|
size_t r = client_pipe->Read(&buf[off], c_buffer_size - off, 180000);
|
|
if (r != 0)
|
|
r += off;
|
|
off = 0;
|
|
if (r == 0)
|
|
{
|
|
Server->Log("Read Timeout: Retrying", LL_WARNING);
|
|
client_pipe.reset(nullptr);
|
|
out_file.reset(nullptr);
|
|
if (has_data)
|
|
{
|
|
return retryDownload(EDownloadResult_TimeoutError2, img_id, img_time,
|
|
outfile, mbr, login_data, dl_status, recur_depth, o_imgsize, o_output_file_size);
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Read Timeout: No data", LL_ERROR);
|
|
return EDownloadResult_TimeoutError2;
|
|
}
|
|
}
|
|
while (true)
|
|
{
|
|
if (blockleft == 0)
|
|
{
|
|
if (!first)
|
|
{
|
|
//Only write one block
|
|
_u32 tw = c_block_size;
|
|
//Don't write over blockdev boundary
|
|
if (imgsize >= pos && imgsize - pos < c_block_size)
|
|
tw = (_u32)(imgsize - pos);
|
|
|
|
//Write to blockdev
|
|
_u32 woff = 0;
|
|
do
|
|
{
|
|
bool has_write_error = false;
|
|
_u32 w = out_file->Write(&blockdata[woff], tw - woff, &has_write_error);
|
|
if (w == 0)
|
|
{
|
|
Server->Log("Writing to output file failed", LL_ERROR);
|
|
return EDownloadResult_WriteFailed;
|
|
}
|
|
if (has_write_error)
|
|
{
|
|
Server->Log("Writing to output file failed -2", LL_ERROR);
|
|
return EDownloadResult_WriteFailed;
|
|
}
|
|
woff += w;
|
|
} while (tw - woff > 0);
|
|
|
|
if (pos > dl_status.offset)
|
|
{
|
|
dl_status.offset = pos;
|
|
}
|
|
|
|
has_data = true;
|
|
}
|
|
else
|
|
{
|
|
first = false;
|
|
|
|
if (pos != -1 && !restore_retry_ok)
|
|
{
|
|
restore_retry_ok = true;
|
|
}
|
|
}
|
|
|
|
if (r - off >= sizeof(_i64))
|
|
{
|
|
blockleft = c_block_size;
|
|
_i64* s = reinterpret_cast<_i64*>(&buf[off]);
|
|
if (*s == 0x7fffffffffffffffLL)
|
|
{
|
|
Server->Log("Restore finished", LL_INFO);
|
|
pos = *s;
|
|
break;
|
|
}
|
|
if (*s > imgsize)
|
|
{
|
|
Server->Log("Invalid seek value: " + convert(*s), LL_ERROR);
|
|
//TODO return error code here after deprecation period
|
|
pos = *s;
|
|
break;
|
|
}
|
|
else if (*s < pos)
|
|
{
|
|
Server->Log("Position out of order!", LL_ERROR);
|
|
}
|
|
else
|
|
{
|
|
if (!out_file->Seek(*s))
|
|
{
|
|
Server->Log("Seeking in output file failed (to position " + convert(*s) + ")", LL_ERROR);
|
|
return EDownloadResult_WriteFailed;
|
|
}
|
|
pos = *s;
|
|
}
|
|
off += sizeof(_i64);
|
|
}
|
|
else if (r - off > 0)
|
|
{
|
|
memmove(buf, &buf[off], r - off);
|
|
off = (_u32)r - off;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
off = 0;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned int available = (std::min)((unsigned int)r - off, blockleft);
|
|
if (available > 0)
|
|
{
|
|
memcpy(&blockdata[c_block_size - blockleft], &buf[off], (_u32)available);
|
|
}
|
|
read += available;
|
|
blockleft -= available;
|
|
off += available;
|
|
dl_status.received += available;
|
|
if (off >= r)
|
|
{
|
|
off = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return EDownloadResult_Ok;
|
|
}
|
|
return EDownloadResult_Ok;
|
|
}
|
|
|
|
int downloadFiles(int backupid, std::string backup_time)
|
|
{
|
|
std::string pw=getFile(pw_file);
|
|
CTCPStack tcpstack;
|
|
std::vector<SImage> ret;
|
|
|
|
std::unique_ptr<IPipe> client_pipe(Server->ConnectStream("localhost", 35623, 60000));
|
|
if(client_pipe.get()==nullptr)
|
|
{
|
|
Server->Log("Error connecting to client service -1", LL_ERROR);
|
|
return 10;
|
|
}
|
|
|
|
tcpstack.Send(client_pipe.get(), "DOWNLOAD FILES#pw="+pw+"&backupid="+convert(backupid)+"&time="+backup_time);
|
|
int64 starttime = Server->getTimeMS();
|
|
|
|
while(Server->getTimeMS()-starttime<60000)
|
|
{
|
|
std::string msg;
|
|
if(client_pipe->Read(&msg, 60000)>0)
|
|
{
|
|
tcpstack.AddData(&msg[0], msg.size());
|
|
|
|
if(tcpstack.getPacket(msg) )
|
|
{
|
|
if(msg=="ok")
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Error: "+msg, LL_ERROR);
|
|
return 1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Server->Log("Timeout", LL_ERROR);
|
|
return 2;
|
|
}
|
|
|
|
} //namespace restore
|
|
|
|
namespace restore
|
|
{
|
|
|
|
bool LookupBlocking(std::string pServer, in_addr *dest)
|
|
{
|
|
const char* host=pServer.c_str();
|
|
unsigned int addr = inet_addr(host);
|
|
if (addr != INADDR_NONE)
|
|
{
|
|
dest->s_addr = addr;
|
|
}
|
|
else
|
|
{
|
|
addrinfo hints;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
addrinfo* hp;
|
|
if(getaddrinfo(host, nullptr, &hints, &hp)==0 && hp!=nullptr)
|
|
{
|
|
if(hp->ai_addrlen<=sizeof(sockaddr_in))
|
|
{
|
|
memcpy(dest, &reinterpret_cast<sockaddr_in*>(hp->ai_addr)->sin_addr, sizeof(in_addr));
|
|
freeaddrinfo(hp);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
freeaddrinfo(hp);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ping_server(void)
|
|
{
|
|
int type = SOCK_DGRAM;
|
|
#if !defined(_WIN32) && defined(SOCK_CLOEXEC)
|
|
type |= SOCK_CLOEXEC;
|
|
#endif
|
|
SOCKET udpsock=socket(AF_INET, type,0);
|
|
|
|
#if !defined(_WIN32) && !defined(SOCK_CLOEXEC)
|
|
fcntl(udpsock, F_SETFD, fcntl(udpsock, F_GETFD, 0) | FD_CLOEXEC);
|
|
#endif
|
|
|
|
std::string server=Server->getServerParameter("ping_server");
|
|
|
|
if(server.empty())
|
|
return false;
|
|
sockaddr_in server_addr;
|
|
if(!LookupBlocking(server, &server_addr.sin_addr))
|
|
return false;
|
|
|
|
server_addr.sin_family=AF_INET;
|
|
server_addr.sin_port=htons(UDP_SOURCE_PORT);
|
|
|
|
std::string ping_clientname=getFile("clientname.txt");
|
|
|
|
std::vector<char> buffer;
|
|
buffer.resize(ping_clientname.size()+2);
|
|
buffer[0]=ID_PONG;
|
|
buffer[1]=FILESERV_VERSION;
|
|
memcpy(&buffer[2], ping_clientname.c_str(), ping_clientname.size());
|
|
|
|
while(true)
|
|
{
|
|
int rc = sendto(udpsock, &buffer[0], static_cast<int>(buffer.size()), 0, (sockaddr*)&server_addr, sizeof(server_addr));
|
|
if(rc == -1)
|
|
{
|
|
Server->Log("Error sending to UrBackup server on UDP socket", LL_ERROR);
|
|
return false;
|
|
}
|
|
Server->wait(10000);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::vector<SLsblk> lsblk(const std::string& dev)
|
|
{
|
|
int rc = system(("lsblk -x MAJ:MIN -o MAJ:MIN,MODEL,SIZE,TYPE,PATH -P "+dev+" 1> out").c_str());
|
|
|
|
std::vector<SLsblk> ret;
|
|
|
|
if(rc!=0)
|
|
{
|
|
Server->Log("Error while running 'lsblk'", LL_ERROR);
|
|
return ret;
|
|
}
|
|
|
|
std::vector<std::string> lines;
|
|
Tokenize(getFile("out"), lines, "\n");
|
|
|
|
for(size_t i=0;i<lines.size();++i)
|
|
{
|
|
SLsblk c;
|
|
c.maj_min = getbetween("MAJ:MIN=\"", "\"", lines[i]);
|
|
c.model = getbetween("MODEL=\"", "\"", lines[i]);
|
|
c.size = getbetween("SIZE=\"", "\"", lines[i]);
|
|
c.type = getbetween("TYPE=\"", "\"", lines[i]);
|
|
c.path = getbetween("PATH=\"", "\"", lines[i]);
|
|
|
|
if(next(c.path, 0, "/dev/fd"))
|
|
continue;
|
|
|
|
ret.push_back(c);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string getPartitionPath(const std::string& dev, int partnum)
|
|
{
|
|
std::vector<SLsblk> parts = lsblk(dev);
|
|
|
|
std::map<size_t, SLsblk> parts_maj_min;
|
|
|
|
for(auto& it: parts)
|
|
{
|
|
if(it.type=="part")
|
|
{
|
|
int maj = watoi(getuntil(":", it.maj_min));
|
|
int min = watoi(getafter(":", it.maj_min));
|
|
parts_maj_min[maj*100000ULL + min] = it;
|
|
}
|
|
}
|
|
|
|
int idx=1;
|
|
for(auto it: parts_maj_min)
|
|
{
|
|
if(idx==partnum)
|
|
{
|
|
return it.second.path;
|
|
}
|
|
++idx;
|
|
}
|
|
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
namespace restore
|
|
{
|
|
std::vector<SImage> parse_backup_images_output(const std::string& data)
|
|
{
|
|
std::vector<SImage> ret;
|
|
std::vector<std::string> toks;
|
|
Tokenize(data, toks, "\n");
|
|
for (size_t i = 0; i < toks.size(); ++i)
|
|
{
|
|
std::vector<std::string> t2;
|
|
Tokenize(toks[i], t2, "|");
|
|
if (t2.size() == 3 || (t2.size() == 4 && t2[0] != "#"))
|
|
{
|
|
SImage si;
|
|
si.id = atoi(t2[0].c_str());
|
|
si.time_s = os_atoi64(t2[1]);
|
|
si.time_str = t2[2];
|
|
if (t2.size() == 4)
|
|
{
|
|
si.letter = t2[3];
|
|
}
|
|
ret.push_back(si);
|
|
}
|
|
else if ( (t2.size() == 4 || t2.size() == 5) &&
|
|
t2[0] == "#" && !ret.empty())
|
|
{
|
|
SImage si;
|
|
si.id = atoi(t2[1].c_str());
|
|
si.time_s = os_atoi64(t2[2]);
|
|
si.time_str = t2[3];
|
|
if (t2.size() == 5)
|
|
{
|
|
si.letter = t2[4];
|
|
}
|
|
ret[ret.size() - 1].assoc.push_back(si);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
JSON::Array backup_images_output_to_json(const std::string& data)
|
|
{
|
|
std::vector<SImage> images = parse_backup_images_output(data);
|
|
|
|
JSON::Array ret;
|
|
for (size_t i = 0; i < images.size(); ++i)
|
|
{
|
|
JSON::Object image;
|
|
image.set("id", images[i].id);
|
|
image.set("time_s", images[i].time_s);
|
|
image.set("time_str", images[i].time_str);
|
|
image.set("letter", images[i].letter);
|
|
|
|
JSON::Array assoc;
|
|
for (size_t j = 0; j < images[i].assoc.size(); ++j)
|
|
{
|
|
JSON::Object image_assoc;
|
|
image_assoc.set("id", images[i].assoc[j].id);
|
|
image_assoc.set("time_s", images[i].assoc[j].time_s);
|
|
image_assoc.set("time_str", images[i].assoc[j].time_str);
|
|
image_assoc.set("letter", images[i].assoc[j].letter);
|
|
assoc.add(image_assoc);
|
|
}
|
|
image.set("assoc", assoc);
|
|
|
|
ret.add(image);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string backup_images_output_to_json_str(const std::string& data)
|
|
{
|
|
return backup_images_output_to_json(data).stringify(false);
|
|
}
|
|
|
|
struct SgdiskPart
|
|
{
|
|
size_t number;
|
|
int64 start;
|
|
int64 end;
|
|
std::string size;
|
|
std::string code;
|
|
std::string type_name;
|
|
std::string part_guid;
|
|
std::string unique_guid;
|
|
std::string name;
|
|
std::string flags;
|
|
};
|
|
|
|
std::vector<SgdiskPart> getSgdiskParts(const std::string& dev)
|
|
{
|
|
std::vector<SgdiskPart> ret;
|
|
std::string out;
|
|
int rc = os_popen("sgdisk -p \""+dev+"\" 2>&1", out);
|
|
if(rc!=0)
|
|
return ret;
|
|
|
|
if(out.find("Number")==std::string::npos)
|
|
return ret;
|
|
|
|
std::string table = getafter("\n", getafter("Number", out));
|
|
|
|
std::vector<std::string> lines;
|
|
Tokenize(table, lines, "\n");
|
|
|
|
for(auto& line: lines)
|
|
{
|
|
std::vector<std::string> cols;
|
|
Tokenize(line, cols, " ");
|
|
|
|
SgdiskPart new_part;
|
|
new_part.number = 0;
|
|
new_part.start = -1;
|
|
new_part.end = -1;
|
|
size_t idx=0;
|
|
for(auto& col: cols)
|
|
{
|
|
std::string cd = trim(col);
|
|
if(!cd.empty())
|
|
{
|
|
if(idx==0)
|
|
new_part.number = watoi(cd);
|
|
else if(idx==1)
|
|
new_part.start = watoi64(cd);
|
|
else if(idx==2)
|
|
new_part.end = watoi64(cd);
|
|
else if(idx==3)
|
|
new_part.size = cd;
|
|
else if(idx==4)
|
|
new_part.size += " "+ cd;
|
|
else if(idx==5)
|
|
new_part.code = cd;
|
|
else
|
|
new_part.type_name += cd;
|
|
++idx;
|
|
}
|
|
}
|
|
|
|
if(new_part.start!=-1 &&
|
|
new_part.number!=0 &&
|
|
new_part.end!=-1)
|
|
{
|
|
std::string iout;
|
|
rc = os_popen("sgdisk -i "+std::to_string(new_part.number)+" \""+dev+"\" 2>&1", iout);
|
|
if(rc==0)
|
|
{
|
|
new_part.part_guid=trim(getbetween("Partition GUID code: ", " ", iout));
|
|
if(new_part.part_guid.empty())
|
|
new_part.part_guid=trim(getbetween("Partition GUID code: ", "\n", iout));
|
|
|
|
new_part.unique_guid = trim(getbetween("Partition unique GUID: ", "\n", iout));
|
|
new_part.flags = trim(getbetween("Attribute flags: ", "\n", iout));
|
|
new_part.name = trim(getbetween("Partition name: '", "\n", iout));
|
|
if(!new_part.name.empty() && new_part.name[new_part.name.size()-1]=='\'')
|
|
new_part.name = new_part.name.substr(0, new_part.name.size()-1);
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Getting detailed info for partition "+std::to_string(new_part.number)+" failed: "+iout, LL_ERROR);
|
|
}
|
|
|
|
ret.push_back(new_part);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
std::string createSgdiskPart(const SgdiskPart& lp, const std::string& out_device)
|
|
{
|
|
std::string out;
|
|
int rc = os_popen("sgdisk -n "+std::to_string(lp.number)+":"+std::to_string(lp.start)+":0 "
|
|
"-A "+std::to_string(lp.number)+":=:"+lp.flags+" "
|
|
"-c "+std::to_string(lp.number)+":\""+lp.name+"\" "
|
|
"-t "+std::to_string(lp.number)+":"+lp.part_guid+" "
|
|
"-u "+std::to_string(lp.number)+":"+lp.unique_guid+" "
|
|
+out_device, out);
|
|
|
|
if(rc!=0)
|
|
{
|
|
return "Error creating partition "+std::to_string(lp.number)+": "+trim(out);
|
|
}
|
|
|
|
return std::string();
|
|
}
|
|
|
|
int64 getSgdiskDiskSize(const std::string& dev)
|
|
{
|
|
std::string out;
|
|
int rc = os_popen("sgdisk -p \""+dev+"\" 2>&1", out);
|
|
if(rc!=0)
|
|
return -1;
|
|
|
|
std::string s = trim(getbetween("Disk "+dev+": ", " sectors", out));
|
|
if(s.empty())
|
|
return -1;
|
|
|
|
return watoi64(s);
|
|
}
|
|
|
|
bool do_restore_write_mbr(const std::string& mbr_filename, const std::string& out_device,
|
|
bool fix_gpt, std::string& errmsg)
|
|
{
|
|
IFile *f=Server->openFile(mbr_filename, MODE_READ);
|
|
if(f==nullptr)
|
|
{
|
|
errmsg = "Could not open MBR file";
|
|
return false;
|
|
}
|
|
size_t fsize=(size_t)f->Size();
|
|
char *buf=new char[fsize];
|
|
f->Read(buf, (_u32)fsize);
|
|
Server->destroy(f);
|
|
|
|
CRData mbr(buf, fsize);
|
|
SMBRData mbrdata(mbr);
|
|
if(mbrdata.hasError())
|
|
{
|
|
errmsg = "Error while parsing MBR data";
|
|
delete []buf;
|
|
return false;
|
|
}
|
|
|
|
IFile *dev=Server->openFile(out_device, MODE_RW);
|
|
if(dev==nullptr)
|
|
{
|
|
errmsg = "Could not open device file for writing";
|
|
delete []buf;
|
|
return false;
|
|
}
|
|
dev->Seek(0);
|
|
Server->Log("Writing MBR data...", LL_INFO);
|
|
if (dev->Write(mbrdata.mbr_data) != mbrdata.mbr_data.size())
|
|
{
|
|
errmsg = "Writing MBR data failed. " + os_last_error_str();
|
|
Server->Log(errmsg, LL_ERROR);
|
|
}
|
|
else
|
|
{
|
|
Server->Log("done.", LL_INFO);
|
|
}
|
|
|
|
bool curr_fix_gpt=false;
|
|
if (mbrdata.gpt_style)
|
|
{
|
|
Server->Log("Writing GPT header...");
|
|
if (dev->Write(mbrdata.gpt_header_pos, mbrdata.gpt_header) != mbrdata.gpt_header.size() )
|
|
{
|
|
errmsg = "Writing GPT header failed. " + os_last_error_str();
|
|
Server->Log(errmsg, LL_ERROR);
|
|
}
|
|
|
|
Server->Log("Writing GPT table...");
|
|
if (dev->Write(mbrdata.gpt_table_pos, mbrdata.gpt_table) != mbrdata.gpt_table.size())
|
|
{
|
|
errmsg = "Writing GPT table failed. " + os_last_error_str();
|
|
Server->Log(errmsg, LL_ERROR);
|
|
}
|
|
|
|
if (mbrdata.backup_gpt_header_pos != -1)
|
|
{
|
|
Server->Log("Writing GPT backup header...");
|
|
if (dev->Write(mbrdata.backup_gpt_header_pos, mbrdata.backup_gpt_header) != mbrdata.backup_gpt_header.size())
|
|
{
|
|
errmsg = "Writing GPT backup header failed. " + os_last_error_str();
|
|
Server->Log(errmsg, LL_ERROR);
|
|
curr_fix_gpt = true;
|
|
}
|
|
}
|
|
|
|
if (mbrdata.backup_gpt_table_pos != -1)
|
|
{
|
|
Server->Log("Writing GPT backup table...");
|
|
if (dev->Write(mbrdata.backup_gpt_table_pos, mbrdata.backup_gpt_table) != mbrdata.backup_gpt_table.size())
|
|
{
|
|
errmsg = "Writing backup GPT table failed. " + os_last_error_str();
|
|
Server->Log(errmsg, LL_ERROR);
|
|
curr_fix_gpt = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mbrdata.extra_data_pos != -1)
|
|
{
|
|
Server->Log("Writing extra data at position "+convert(mbrdata.extra_data_pos)+" size "+convert(mbrdata.extra_data.size())+" ...");
|
|
if (dev->Write(mbrdata.extra_data_pos, mbrdata.extra_data) != mbrdata.extra_data.size())
|
|
{
|
|
errmsg = "Writing extra data failed. " + os_last_error_str();
|
|
Server->Log(errmsg, LL_ERROR);
|
|
}
|
|
}
|
|
|
|
if (curr_fix_gpt && fix_gpt)
|
|
{
|
|
std::string out;
|
|
int rc = os_popen("sgdisk --version", out);
|
|
if (rc != 0 || out.find("sgdisk") == std::string::npos)
|
|
{
|
|
Server->Log("sgdisk not available. Not able to fixup GPT table", LL_WARNING);
|
|
fix_gpt = false;
|
|
}
|
|
}
|
|
|
|
if(curr_fix_gpt && fix_gpt)
|
|
{
|
|
std::string fix_output;
|
|
int rc = os_popen("sgdisk -e " + out_device + " 2>&1", fix_output);
|
|
errmsg="Fixed GPT backup table: "+trim(fix_output);
|
|
if(fix_output.find("too big for the disk")!=std::string::npos)
|
|
{
|
|
std::vector<SgdiskPart> partitions = getSgdiskParts(out_device);
|
|
|
|
int64 sgdisk_size = getSgdiskDiskSize(out_device);
|
|
while(!partitions.empty())
|
|
{
|
|
if(sgdisk_size<0)
|
|
break;
|
|
|
|
SgdiskPart lp = partitions[partitions.size()-1];
|
|
|
|
if(lp.start>=sgdisk_size)
|
|
{
|
|
Server->Log("Partition "+std::to_string(lp.number)+" not on disk. Removing...");
|
|
std::string out;
|
|
rc = os_popen("sgdisk -d "+std::to_string(lp.number)+" "+out_device+" 2>&1", out);
|
|
|
|
if(rc!=0)
|
|
{
|
|
errmsg="Error deleting last partition (1): "+trim(out);
|
|
Server->Log(errmsg, LL_ERROR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!partitions.empty())
|
|
{
|
|
Server->Log("Partition too big. Resizing last partition", LL_WARNING);
|
|
SgdiskPart lp = partitions[partitions.size()-1];
|
|
|
|
Server->Log("Last partition num="+std::to_string(lp.number)+" start="+std::to_string(lp.start)+" end="+std::to_string(lp.end)
|
|
+" size="+lp.size, LL_INFO);
|
|
|
|
std::string out;
|
|
rc = os_popen("sgdisk -d "+std::to_string(lp.number)+" "+out_device+" 2>&1", out);
|
|
|
|
if(rc!=0)
|
|
{
|
|
errmsg="Error deleting last partition: "+trim(out);
|
|
Server->Log(errmsg, LL_ERROR);
|
|
}
|
|
|
|
std::string err = createSgdiskPart(lp, out_device);
|
|
|
|
if(!err.empty())
|
|
{
|
|
errmsg="Error re-creating last partition: "+trim(err);
|
|
Server->Log(errmsg, LL_ERROR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("No partitions found", LL_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
Server->destroy(dev);
|
|
|
|
if (out_device.find("/dev/") == 0)
|
|
{
|
|
rereadPartitionLayout(out_device);
|
|
}
|
|
|
|
delete []buf;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void do_restore(void)
|
|
{
|
|
std::string cmd=Server->getServerParameter("restore_cmd");
|
|
|
|
if(cmd=="write_mbr")
|
|
{
|
|
std::string mbr_filename=Server->getServerParameter("mbr_filename");
|
|
std::string out_device=Server->getServerParameter("out_device");
|
|
if(mbr_filename.empty())
|
|
{
|
|
Server->Log("MBR filename not specified (mbr_filename parameter)", LL_ERROR);
|
|
exit(1);
|
|
}
|
|
|
|
if(out_device.empty())
|
|
{
|
|
Server->Log("Output device not specified (out_device paramter)", LL_ERROR);
|
|
exit(1);
|
|
}
|
|
|
|
std::string errmsg;
|
|
if(!do_restore_write_mbr(mbr_filename, out_device, true, errmsg))
|
|
{
|
|
Server->Log(errmsg, LL_ERROR);
|
|
exit(2);
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
else if(cmd=="mbrinfo")
|
|
{
|
|
std::string mbr_filename=Server->getServerParameter("mbr_filename");
|
|
if(mbr_filename.empty())
|
|
{
|
|
Server->Log("MBR filename not specified (mbr_filename parameter)", LL_ERROR);
|
|
exit(1);
|
|
}
|
|
IFile *f=Server->openFile(mbr_filename, MODE_READ);
|
|
if(f==nullptr)
|
|
{
|
|
Server->Log("Could not open MBR file", LL_ERROR);
|
|
exit(1);
|
|
}
|
|
size_t fsize=(size_t)f->Size();
|
|
char *buf=new char[fsize];
|
|
f->Read(buf, (_u32)fsize);
|
|
Server->destroy(f);
|
|
|
|
CRData mbr(buf, fsize);
|
|
SMBRData mbrdata(mbr);
|
|
if(mbrdata.hasError())
|
|
{
|
|
Server->Log("Error while parsing MBR data", LL_ERROR);
|
|
delete []buf; exit(1);
|
|
}
|
|
|
|
std::cout << mbrdata.infoString();
|
|
delete []buf;
|
|
exit(0);
|
|
}
|
|
else if (cmd == "login")
|
|
{
|
|
LoginData login_data;
|
|
login_data.has_login_data = true;
|
|
login_data.username = Server->getServerParameter("username");
|
|
login_data.password = Server->getServerParameter("password");
|
|
|
|
char* pw = getenv("LOGIN_PASSWORD");
|
|
if (pw != nullptr)
|
|
{
|
|
login_data.password = pw;
|
|
}
|
|
if (do_login(login_data))
|
|
{
|
|
exit(0);
|
|
}
|
|
else
|
|
{
|
|
exit(1);
|
|
}
|
|
}
|
|
else if (cmd == "read_mbr")
|
|
{
|
|
std::string dev_fn = Server->getServerParameter("device_fn");
|
|
std::unique_ptr<IFile> dev(Server->openFile(dev_fn, MODE_READ_DEVICE));
|
|
|
|
if (dev.get() == nullptr)
|
|
{
|
|
Server->Log("Error opening device at " + dev_fn+". "+os_last_error_str(), LL_ERROR);
|
|
exit(5);
|
|
}
|
|
|
|
bool gpt_style;
|
|
std::vector<IFSImageFactory::SPartition> partitions = image_fak->readPartitions(dev.get(), gpt_style);
|
|
|
|
if (partitions.empty())
|
|
{
|
|
Server->Log("No partitions found", LL_ERROR);
|
|
exit(4);
|
|
}
|
|
|
|
for (size_t i = 0; i < partitions.size(); ++i)
|
|
{
|
|
std::cout << "partition " << (i+1) << " off " << partitions[i].offset << " size " << partitions[i].length << std::endl;
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
else if(cmd=="resize_ntfs")
|
|
{
|
|
std::string dev_fn = Server->getServerParameter("device_fn");
|
|
std::unique_ptr<IFile> dev(Server->openFile(dev_fn, MODE_READ_DEVICE));
|
|
|
|
if (dev.get() == nullptr)
|
|
{
|
|
Server->Log("Error opening device at " + dev_fn+". "+os_last_error_str(), LL_ERROR);
|
|
exit(5);
|
|
}
|
|
|
|
dev.reset();
|
|
|
|
std::string s_new_size = Server->getServerParameter("new_size");
|
|
if(s_new_size.empty())
|
|
{
|
|
Server->Log("new_size paramater missing", LL_ERROR);
|
|
exit(6);
|
|
}
|
|
|
|
int64 new_size = watoi64(s_new_size);
|
|
|
|
std::string err;
|
|
std::atomic<int> complete_pc;
|
|
std::atomic<bool> done(false);
|
|
std::thread t( [&err, dev_fn, new_size, &complete_pc, &done]()
|
|
{
|
|
err = restore::resize_ntfs(new_size, dev_fn, complete_pc);
|
|
done=true;
|
|
});
|
|
|
|
t.detach();
|
|
|
|
int last_pc = complete_pc;
|
|
while(!done)
|
|
{
|
|
Server->wait(1000);
|
|
if(complete_pc!=last_pc)
|
|
{
|
|
last_pc = complete_pc;
|
|
Server->Log("Resize "+std::to_string(last_pc)+"% complete", LL_INFO);
|
|
}
|
|
}
|
|
|
|
if(!err.empty())
|
|
{
|
|
Server->Log("Resize error: "+err, LL_ERROR);
|
|
exit(1);
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
else if(cmd=="get_sgparts")
|
|
{
|
|
std::string dev_fn = Server->getServerParameter("device_fn");
|
|
|
|
Server->Log("Device size: "+PrettyPrintBytes(getSgdiskDiskSize(dev_fn)*512), LL_INFO);
|
|
|
|
std::vector<SgdiskPart> parts = getSgdiskParts(dev_fn);
|
|
|
|
for(const SgdiskPart& part: parts)
|
|
{
|
|
Server->Log("part number="+std::to_string(part.number)
|
|
+" start="+std::to_string(part.start)+" end="+std::to_string(part.end)
|
|
+" size="+part.size+" part_uuid="+part.part_guid+" unique_uuid="+part.unique_guid
|
|
+" name=\""+part.name+"\" flags="+part.flags, LL_INFO);
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
else if(cmd=="help")
|
|
{
|
|
Server->Log("restore_cmd commands are...", LL_INFO);
|
|
Server->Log("write_mbr(mbr_filename,out_device)", LL_INFO);
|
|
Server->Log("get_clientnames", LL_INFO);
|
|
Server->Log("get_backupimages(restore_name)", LL_INFO);
|
|
Server->Log("get_backupimages_json(restore_name)", LL_INFO);
|
|
Server->Log("get_file_backups(restore_name)", LL_INFO);
|
|
Server->Log("download_mbr(restore_img_id,restore_time,restore_out)", LL_INFO);
|
|
Server->Log("download_image(restore_img_id,restore_time,restore_out)", LL_INFO);
|
|
Server->Log("download_files(restore_backupid,restore_time,restore_out)", LL_INFO);
|
|
Server->Log("download_progress(mbr_filename,out_device)", LL_INFO);
|
|
Server->Log("login(username,password)", LL_INFO);
|
|
Server->Log("read_mbr(device_fn)", LL_INFO);
|
|
Server->Log("resize_ntfs(device_fn,new_size)", LL_INFO);
|
|
exit(0);
|
|
}
|
|
else if(cmd=="ping_server")
|
|
{
|
|
bool b=ping_server();
|
|
exit(b?0:1);
|
|
}
|
|
|
|
IPipe *c=Server->ConnectStream("localhost", 35623, 60000);
|
|
if(c==nullptr)
|
|
{
|
|
Server->Log("Error connecting to client service -1", LL_ERROR);
|
|
exit(1);return;
|
|
}
|
|
|
|
std::string pw=getFile(pw_file);
|
|
|
|
CTCPStack tcpstack;
|
|
if(cmd=="get_clientnames")
|
|
{
|
|
tcpstack.Send(c, "GET BACKUPCLIENTS#pw="+pw);
|
|
std::string r=getResponse(c);
|
|
if(r.empty() )
|
|
{
|
|
Server->Log("No response from ClientConnector", LL_ERROR);
|
|
Server->destroy(c);exit(2);return;
|
|
}
|
|
else
|
|
{
|
|
if(r[0]=='0')
|
|
{
|
|
Server->Log("No backupserver found", LL_ERROR);
|
|
Server->destroy(c);exit(3);return;
|
|
}
|
|
else
|
|
{
|
|
std::cout << r.substr(1) ;
|
|
Server->destroy(c);exit(0);return;
|
|
}
|
|
}
|
|
}
|
|
else if(cmd=="get_backupimages"
|
|
|| cmd == "get_backupimages_json")
|
|
{
|
|
tcpstack.Send(c, "GET BACKUPIMAGES "+Server->getServerParameter("restore_name")+"#pw="+pw);
|
|
std::string r=getResponse(c);
|
|
if(r.empty() )
|
|
{
|
|
Server->Log("No response from ClientConnector", LL_ERROR);
|
|
Server->destroy(c);exit(2);return;
|
|
}
|
|
else
|
|
{
|
|
if(r[0]=='0')
|
|
{
|
|
Server->Log("No backupserver found", LL_ERROR);
|
|
Server->destroy(c);exit(3);return;
|
|
}
|
|
else
|
|
{
|
|
if (cmd == "get_backupimages_json")
|
|
{
|
|
std::cout << backup_images_output_to_json_str(r.substr(1));
|
|
}
|
|
else
|
|
{
|
|
std::cout << r.substr(1);
|
|
}
|
|
Server->destroy(c);exit(0);return;
|
|
}
|
|
}
|
|
}
|
|
else if(cmd=="get_file_backups" )
|
|
{
|
|
tcpstack.Send(c, "GET FILE BACKUPS "+Server->getServerParameter("restore_name")+"#pw="+pw);
|
|
std::string r=getResponse(c);
|
|
if(r.empty() )
|
|
{
|
|
Server->Log("No response from ClientConnector", LL_ERROR);
|
|
Server->destroy(c);exit(2);return;
|
|
}
|
|
else
|
|
{
|
|
if(r[0]=='0')
|
|
{
|
|
Server->Log("No backupserver found", LL_ERROR);
|
|
Server->destroy(c);exit(3);return;
|
|
}
|
|
else
|
|
{
|
|
std::cout << r.substr(1) ;
|
|
Server->destroy(c);exit(0);return;
|
|
}
|
|
}
|
|
}
|
|
else if(cmd=="download_mbr" || cmd=="download_image" )
|
|
{
|
|
bool mbr=false;
|
|
if(cmd=="download_mbr")
|
|
mbr=true;
|
|
|
|
LoginData login_data;
|
|
login_data.has_login_data = false;
|
|
|
|
std::string restore_token = Server->getServerParameter("restore_token");
|
|
if (!restore_token.empty())
|
|
{
|
|
login_data.has_login_data = true;
|
|
login_data.token = restore_token;
|
|
}
|
|
|
|
DownloadStatus dl_status;
|
|
int ec=downloadImage(atoi(Server->getServerParameter("restore_img_id").c_str()), Server->getServerParameter("restore_time"), Server->getServerParameter("restore_out"), mbr, login_data, dl_status);
|
|
exit(ec);
|
|
}
|
|
else if(cmd=="download_files")
|
|
{
|
|
int ec=downloadFiles(atoi(Server->getServerParameter("restore_backupid").c_str()), Server->getServerParameter("restore_time"));
|
|
exit(ec);
|
|
}
|
|
else if(cmd=="download_progress")
|
|
{
|
|
std::string decorate = Server->getServerParameter("decorate");
|
|
|
|
tcpstack.Send(c, "GET DOWNLOADPROGRESS#pw="+pw);
|
|
int lpc=0;
|
|
while(true)
|
|
{
|
|
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(!trim2(l).empty())
|
|
{
|
|
int npc=atoi(trim2(l).c_str());
|
|
if(npc!=lpc)
|
|
{
|
|
if(decorate.empty())
|
|
std::cout << npc << std::endl;
|
|
else
|
|
std::cout << "Image restore complete: "<< npc << "%" << std::endl;
|
|
lpc=npc;
|
|
}
|
|
}
|
|
}
|
|
if(r==0)
|
|
break;
|
|
}
|
|
if(lpc!=100)
|
|
{
|
|
if (decorate.empty())
|
|
std::cout << "100" << std::endl;
|
|
else
|
|
std::cout << "Image restore complete: 100%" << std::endl;
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
class RestoreThread : public IThread
|
|
{
|
|
public:
|
|
RestoreThread(int pImg_id, std::string pImg_time, std::string pOutfile, LoginData login_data)
|
|
: img_id(pImg_id), img_time(pImg_time), outfile(pOutfile), imgsize(0), output_file_size(0), login_data(login_data)
|
|
{
|
|
done=false;
|
|
}
|
|
|
|
void operator()(void)
|
|
{
|
|
DownloadStatus dl_status;
|
|
rc=downloadImage(img_id, img_time, outfile, false, login_data, dl_status, 0, &imgsize, &output_file_size);
|
|
done=true;
|
|
}
|
|
|
|
bool isDone(void)
|
|
{
|
|
return done;
|
|
}
|
|
|
|
EDownloadResult getRC(void)
|
|
{
|
|
return rc;
|
|
}
|
|
|
|
int64 getImgsize()
|
|
{
|
|
return imgsize;
|
|
}
|
|
|
|
int64 getOutputFileSize()
|
|
{
|
|
return output_file_size;
|
|
}
|
|
|
|
private:
|
|
EDownloadResult rc;
|
|
volatile bool done;
|
|
int img_id;
|
|
std::string img_time;
|
|
std::string outfile;
|
|
LoginData login_data;
|
|
|
|
int64 imgsize;
|
|
int64 output_file_size;
|
|
};
|
|
|
|
namespace restore
|
|
{
|
|
bool has_network_device(void)
|
|
{
|
|
#ifdef _WIN32
|
|
return true;
|
|
#else
|
|
#ifdef sun
|
|
return true;
|
|
#else
|
|
char buf[1024];
|
|
struct ifconf ifc;
|
|
struct ifreq* ifr;
|
|
int sck;
|
|
int nInterfaces;
|
|
int i;
|
|
|
|
int type = SOCK_DGRAM;
|
|
#if !defined(_WIN32) && defined(SOCK_CLOEXEC)
|
|
type |= SOCK_CLOEXEC;
|
|
#endif
|
|
/* Get a socket handle. */
|
|
sck = socket(AF_INET, type, 0);
|
|
if (sck < 0)
|
|
{
|
|
return true;
|
|
}
|
|
#if !defined(_WIN32) && !defined(SOCK_CLOEXEC)
|
|
fcntl(sck, F_SETFD, fcntl(sck, F_GETFD, 0) | FD_CLOEXEC);
|
|
#endif
|
|
|
|
/* Query available interfaces. */
|
|
ifc.ifc_len = sizeof(buf);
|
|
ifc.ifc_buf = buf;
|
|
if (ioctl(sck, SIOCGIFCONF, &ifc) < 0)
|
|
{
|
|
close(sck);
|
|
return true;
|
|
}
|
|
|
|
/* Iterate through the list of interfaces. */
|
|
ifr = ifc.ifc_req;
|
|
nInterfaces = ifc.ifc_len / sizeof(struct ifreq);
|
|
for (i = 0; i < nInterfaces; i++)
|
|
{
|
|
struct ifreq* item = &ifr[i];
|
|
|
|
if (htonl(INADDR_LOOPBACK) != ((struct sockaddr_in*)&item->ifr_addr)->sin_addr.s_addr)
|
|
{
|
|
close(sck);
|
|
return true;
|
|
}
|
|
}
|
|
close(sck);
|
|
return false;
|
|
#endif //sun
|
|
#endif //_WIN32
|
|
}
|
|
}
|
|
|
|
void ping_named_server(void)
|
|
{
|
|
std::string out;
|
|
while(out.empty())
|
|
{
|
|
int rc=system("dialog --inputbox \"`cat urbackup/restore/enter_server_ip_input`\" 8 30 2> out");
|
|
out=getFile("out");
|
|
|
|
if(rc!=0)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(!out.empty())
|
|
{
|
|
system(("./urbackuprestoreclient --ping-server \""+out+"\" &").c_str());
|
|
}
|
|
}
|
|
|
|
namespace restore
|
|
{
|
|
bool has_internet_connection(int& ec, std::string& errstatus)
|
|
{
|
|
ec = 0;
|
|
|
|
IPipe* c = connectToService(ec);
|
|
|
|
if (c == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::string pw = getFile(pw_file);
|
|
CTCPStack tcpstack;
|
|
tcpstack.Send(c, "STATUS DETAIL#pw=" + pw);
|
|
std::string r = getResponse(c);
|
|
if (r.empty())
|
|
{
|
|
Server->Log("No response from ClientConnector", LL_ERROR);
|
|
ec = 1;
|
|
return false;
|
|
}
|
|
|
|
std::string internet_connected = getbetween("\"internet_connected\": ", ",", r);
|
|
std::string internet_status = getbetween("\"internet_status\": \"", "\",", r);
|
|
|
|
if (internet_connected == "true"
|
|
&& r.find("\"servers\": []") == std::string::npos)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
errstatus = internet_status;
|
|
ec = 2;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool internet_server_configured = false;
|
|
}
|
|
|
|
namespace restore
|
|
{
|
|
void configure_internet_server()
|
|
{
|
|
std::string internet_server;
|
|
while (internet_server.empty())
|
|
{
|
|
int rc = system("dialog --inputbox \"`cat urbackup/restore/enter_internet_server_name`\" 8 30 2> out");
|
|
internet_server = trim(getFile("out"));
|
|
|
|
if (rc != 0)
|
|
{
|
|
system("dialog --msgbox \"`cat urbackup/restore/no_internet_server_configured`\" 10 70");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (internet_server.empty())
|
|
{
|
|
system("dialog --msgbox \"`cat urbackup/restore/no_internet_server_configured`\" 10 70");
|
|
return;
|
|
}
|
|
|
|
std::string internet_authkey;
|
|
while (internet_authkey.empty())
|
|
{
|
|
int rc = system("dialog --inputbox \"`cat urbackup/restore/enter_internet_server_authkey`\" 8 30 2> out");
|
|
internet_authkey = getFile("out");
|
|
|
|
if (rc != 0)
|
|
{
|
|
system("dialog --msgbox \"`cat urbackup/restore/no_internet_server_configured`\" 10 70");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (internet_authkey.empty())
|
|
{
|
|
system("dialog --msgbox \"`cat urbackup/restore/no_internet_server_configured`\" 10 70");
|
|
return;
|
|
}
|
|
|
|
system("clear");
|
|
std::cout << "Configuring UrBackup restore client as Internet client..." << std::endl;
|
|
|
|
configure_internet_server(internet_server, internet_authkey, "", true);
|
|
}
|
|
|
|
void configure_internet_server(std::string server_url, const std::string& server_authkey, const std::string& server_proxy,
|
|
bool with_cli)
|
|
{
|
|
system("systemctl stop restore-client");
|
|
|
|
int internet_server_port = 55415;
|
|
|
|
if (next(server_url, 0, "urbackup://"))
|
|
{
|
|
server_url.erase(0, 11);
|
|
if (server_url.find(":") != std::string::npos)
|
|
{
|
|
internet_server_port = watoi(getafter(":", server_url));
|
|
server_url = getuntil(":", server_url);
|
|
}
|
|
}
|
|
|
|
std::string rnd;
|
|
rnd.resize(16);
|
|
Server->secureRandomFill(&rnd[0], 16);
|
|
|
|
std::string clientname = "##restore##" + bytesToHex(rnd);
|
|
|
|
os_create_dir_recursive("urbackup/data_1");
|
|
|
|
writestring("internet_mode_enabled=true\n"
|
|
"internet_server=" + server_url + "\n"
|
|
"internet_server_port=" + std::to_string(internet_server_port) + "\n"
|
|
"internet_authkey=" + server_authkey + "\n"
|
|
"computername=" + clientname + "\n", "urbackup/data_1/settings.cfg");
|
|
|
|
if (with_cli)
|
|
std::cout << "Starting UrBackup Internet restore client..." << std::endl;
|
|
|
|
if (system("systemctl restart restore-client-internet") != 0)
|
|
{
|
|
std::cout << "Error starting Internet restore client." << std::endl;
|
|
char ch;
|
|
std::cin >> ch;
|
|
return;
|
|
}
|
|
|
|
if (!with_cli)
|
|
return;
|
|
|
|
std::cout << "Waiting for Internet restore client to connect..." << std::endl;
|
|
|
|
Server->wait(1000);
|
|
|
|
int64 starttime = Server->getTimeMS();
|
|
int ec;
|
|
std::string errstatus;
|
|
while (Server->getTimeMS() - starttime < 60000)
|
|
{
|
|
if (has_internet_connection(ec, errstatus))
|
|
{
|
|
internet_server_configured = true;
|
|
return;
|
|
}
|
|
if (ec == 2 && next(errstatus, 0, "error:"))
|
|
break;
|
|
Server->wait(1000);
|
|
}
|
|
|
|
std::cout << "Connecting to Internet server failed: " << errstatus << std::endl;
|
|
std::cout << "Restarting local UrBackup client... " << errstatus << std::endl;
|
|
system("systemctl stop restore-client-internet");
|
|
Server->deleteFile("urbackup/data_1/settings.cfg");
|
|
system("systemctl start restore-client");
|
|
|
|
std::string errmsg;
|
|
switch (ec)
|
|
{
|
|
case 10:
|
|
case 1:
|
|
errmsg = "`cat urbackup/restore/internal_error`";
|
|
break;
|
|
case 2:
|
|
errmsg = getFile("urbackup/restore/internet_authentification_failed");
|
|
errmsg = greplace("_STATUS_", errstatus, errmsg);
|
|
break;
|
|
}
|
|
|
|
int r = system(("dialog --menu \"`cat urbackup/restore/error_happend` " + errmsg + ". `cat urbackup/restore/how_to_continue`\" 15 50 10 "
|
|
"\"r\" \"`cat urbackup/restore/search_again`\" "
|
|
"\"i\" \"`cat urbackup/restore/connect_to_internet_server`\" "
|
|
"\"s\" \"`cat urbackup/restore/start_shell`\" \"s\" "
|
|
"\"`cat urbackup/restore/stop_restore`\" 2> out").c_str());
|
|
|
|
if (r != 0)
|
|
{
|
|
return;
|
|
}
|
|
std::string out = getFile("out");
|
|
if (out == "s")
|
|
{
|
|
system("bash");
|
|
}
|
|
else if (out == "i")
|
|
{
|
|
configure_internet_server();
|
|
}
|
|
}
|
|
|
|
void configure_local_server()
|
|
{
|
|
system("systemctl stop restore-client-internet");
|
|
Server->deleteFile("urbackup/data_1/settings.cfg");
|
|
system("systemctl restart restore-client");
|
|
}
|
|
}
|
|
|
|
namespace restore
|
|
{
|
|
|
|
bool do_login(LoginData& login_data, bool with_cli_output)
|
|
{
|
|
if (login_data.has_login_data)
|
|
{
|
|
if (login_data.username.empty())
|
|
{
|
|
int ec;
|
|
return tryLogin("", "", std::vector<SPasswordSalt>(), 10, &ec);
|
|
}
|
|
|
|
int ec;
|
|
std::vector<SPasswordSalt> salts = getSalt(login_data.username, 10, &ec);
|
|
|
|
bool found_salt = false;
|
|
for (size_t i = 0; i < salts.size(); ++i)
|
|
{
|
|
if (!salts[i].salt.empty() &&
|
|
!salts[i].rnd.empty())
|
|
{
|
|
found_salt = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found_salt)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return tryLogin(login_data.username, login_data.password, salts, 10, &ec);
|
|
}
|
|
|
|
if (with_cli_output)
|
|
{
|
|
system("clear");
|
|
system("cat urbackup/restore/trying_to_login");
|
|
}
|
|
|
|
int ec;
|
|
if (!tryLogin("", "", std::vector<SPasswordSalt>(), 0, &ec))
|
|
{
|
|
if (ec == 1)
|
|
{
|
|
//Server probably does not support logging in
|
|
std::vector<std::string> clients = getBackupclients(ec);
|
|
if (ec == 0 && !clients.empty())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (!with_cli_output)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
int rc = system("dialog --inputbox \"`cat urbackup/restore/enter_username`\" 8 30 2> out");
|
|
std::string username = getFile("out");
|
|
|
|
if (rc != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::vector<SPasswordSalt> salts = getSalt(username, 5, &ec);
|
|
|
|
bool found_salt = false;
|
|
for (size_t i = 0; i < salts.size(); ++i)
|
|
{
|
|
if (!salts[i].salt.empty() &&
|
|
!salts[i].rnd.empty())
|
|
{
|
|
found_salt = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found_salt)
|
|
{
|
|
rc = system("dialog --yesno \"`cat urbackup/restore/user_not_found`\" 7 50");
|
|
if (rc != 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = system("dialog --insecure --passwordbox \"`cat urbackup/restore/enter_password`\" 8 30 2> out");
|
|
std::string password = getFile("out");
|
|
|
|
if (rc != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!tryLogin(username, password, salts, 5, &ec))
|
|
{
|
|
rc = system("dialog --yesno \"`cat urbackup/restore/login_failed`\" 7 50");
|
|
if (rc != 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
login_data.has_login_data = true;
|
|
login_data.username = username;
|
|
login_data.password = password;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
login_data.has_login_data = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
const int start_state=-2;
|
|
|
|
void restore_wizard(void)
|
|
{
|
|
int state=start_state;
|
|
std::vector<std::string> clients;
|
|
std::string clientname;
|
|
std::vector<SImage> images;
|
|
SImage selimage;
|
|
SImage r_selimage;
|
|
std::string seldrive;
|
|
std::string windows_partition;
|
|
std::string selpart;
|
|
std::string err;
|
|
bool res_sysvol=false;
|
|
LoginData login_data;
|
|
while(true)
|
|
{
|
|
|
|
switch(state)
|
|
{
|
|
case -2:
|
|
{
|
|
system("dialog --msgbox \"`cat urbackup/restore/welcome`\" 10 70");
|
|
++state;
|
|
}break;
|
|
case -1:
|
|
{
|
|
if(!has_network_device())
|
|
{
|
|
system("clear");
|
|
system("cat urbackup/restore/search_network");
|
|
int tries=20;
|
|
bool has_interface=false;
|
|
do
|
|
{
|
|
bool b=has_network_device();
|
|
if(b)
|
|
{
|
|
has_interface=true;
|
|
break;
|
|
}
|
|
--tries;
|
|
Server->wait(1000);
|
|
} while(tries>=0);
|
|
|
|
if(has_interface==false)
|
|
{
|
|
int r=system("dialog --menu \"`cat urbackup/restore/no_network_device`\" 15 50 10 \"n\" \"`cat urbackup/restore/configure_networkcard`\" \"w\" \"`cat urbackup/restore/configure_wlan`\" \"e\" \"`cat urbackup/restore/start_shell`\" \"c\" \"`cat urbackup/restore/continue_restore`\" 2> out");
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
std::string out=getFile("out");
|
|
if(out=="n")
|
|
{
|
|
system(configure_networkcard.c_str());
|
|
state=0;
|
|
}
|
|
else if(out=="w")
|
|
{
|
|
system(configure_wlan.c_str());
|
|
state=0;
|
|
}
|
|
else if(out=="e")
|
|
{
|
|
system("bash");
|
|
state=0;
|
|
}
|
|
else if(out=="c")
|
|
state=0;
|
|
else
|
|
state=99;
|
|
}
|
|
else
|
|
{
|
|
state=0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state=0;
|
|
}
|
|
}break;
|
|
case 0:
|
|
{
|
|
if (!internet_server_configured)
|
|
{
|
|
system("urbackup/restore/progress-start.sh | dialog --backtitle \"`cat urbackup/restore/search`\" --gauge \"`cat urbackup/restore/t_progress`\" 6 60 0");
|
|
}
|
|
++state;
|
|
}break;
|
|
case 1:
|
|
{
|
|
std::string errmsg;
|
|
int ec;
|
|
login_data = LoginData();
|
|
if(do_login(login_data))
|
|
{
|
|
clients=getBackupclients(ec);
|
|
|
|
switch(ec)
|
|
{
|
|
case 10:
|
|
case 1:
|
|
errmsg="`cat urbackup/restore/internal_error`";
|
|
break;
|
|
case 2:
|
|
errmsg="`cat urbackup/restore/no_server_found`";
|
|
break;
|
|
}
|
|
|
|
if(errmsg.empty() && clients.empty())
|
|
{
|
|
ec=3;
|
|
errmsg="`cat urbackup/restore/no_clients_found`";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
errmsg="login failed";
|
|
ec=1;
|
|
}
|
|
|
|
if(ec!=0)
|
|
{
|
|
int r=system(("dialog --menu \"`cat urbackup/restore/error_happend` "+errmsg+". `cat urbackup/restore/how_to_continue`\" 15 50 10 "
|
|
"\"r\" \"`cat urbackup/restore/search_again`\" "
|
|
"\"n\" \"`cat urbackup/restore/configure_networkcard`\" "
|
|
"\"w\" \"`cat urbackup/restore/configure_wlan`\" "
|
|
"\"e\" \"`cat urbackup/restore/enter_server_ip`\" "
|
|
"\"i\" \"`cat urbackup/restore/connect_to_internet_server`\" "
|
|
"\"s\" \"`cat urbackup/restore/start_shell`\" \"s\" "
|
|
"\"`cat urbackup/restore/stop_restore`\" 2> out").c_str());
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
std::string out=getFile("out");
|
|
if(out=="r")
|
|
state=0;
|
|
else if(out=="n")
|
|
{
|
|
system(configure_networkcard.c_str());
|
|
state=0;
|
|
}
|
|
else if(out=="w")
|
|
{
|
|
system(configure_wlan.c_str());
|
|
state=0;
|
|
}
|
|
else if(out=="e")
|
|
{
|
|
ping_named_server();
|
|
state=0;
|
|
}
|
|
else if (out == "i")
|
|
{
|
|
configure_internet_server();
|
|
state = 0;
|
|
}
|
|
else if(out=="s")
|
|
{
|
|
system("bash");
|
|
state=0;
|
|
}
|
|
else
|
|
state=99;
|
|
}
|
|
else
|
|
{
|
|
std::string mi;
|
|
for(size_t i=0;i<clients.size();++i)
|
|
{
|
|
mi+="\""+convert((int)i+1)+"\" \""+clients[i]+"\" ";
|
|
}
|
|
int r=system(("dialog --menu \"`cat urbackup/restore/select`\" 15 50 10 "+mi+"2> out").c_str());
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
|
|
std::string out=getFile("out");
|
|
clientname=clients[atoi(out.c_str())-1];
|
|
++state;
|
|
}
|
|
}break;
|
|
case 2:
|
|
{
|
|
int ec;
|
|
images=getBackupimages(clientname, &ec);
|
|
std::string errmsg;
|
|
switch(ec)
|
|
{
|
|
case 10:
|
|
case 1:
|
|
errmsg="`cat urbackup/restore/internal_error`";
|
|
break;
|
|
case 2:
|
|
errmsg="`cat urbackup/restore/no_server_found`";
|
|
break;
|
|
}
|
|
|
|
if(images.empty())
|
|
{
|
|
ec=3;
|
|
errmsg="`cat urbackup/restore/no_images_for_client1` '"+clientname+"' `cat urbackup/restore/no_images_for_client2`";
|
|
}
|
|
|
|
std::sort(images.begin(), images.end());
|
|
|
|
if(ec!=0)
|
|
{
|
|
int r=system(("dialog --menu \"`cat urbackup/restore/error_happend` "+errmsg+". `cat urbackup/restore/how_to_continue`\" 15 50 10 \"a\" \"`cat urbackup/restore/error_j_select`\" \"r\" \"`cat urbackup/restore/search_again`\" \"s\" \"`cat urbackup/restore/stop_restore`\" 2> out").c_str());
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
std::string out=getFile("out");
|
|
if(out=="r")
|
|
state=0;
|
|
else if(out=="a")
|
|
state=1;
|
|
else
|
|
state=99;
|
|
}
|
|
else
|
|
{
|
|
std::string mi;
|
|
for(size_t i=0;i<images.size();++i)
|
|
{
|
|
if(!images[i].letter.empty())
|
|
images[i].letter=" "+images[i].letter;
|
|
|
|
mi+="\""+convert((int)i+1)+"\" \""+images[i].time_str+images[i].letter+"\" ";
|
|
}
|
|
int r=system(("dialog --menu \"`cat urbackup/restore/select_date`\" 15 50 10 "+mi+"2> out").c_str());
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
std::string out=getFile("out");
|
|
selimage=images[atoi(out.c_str())-1];
|
|
r_selimage=selimage;
|
|
++state;
|
|
}
|
|
}break;
|
|
case 3:
|
|
{
|
|
std::vector<SLsblk> drives = lsblk("");
|
|
|
|
bool has_disk=false;
|
|
|
|
for(size_t i=0;i<drives.size();++i)
|
|
{
|
|
if(drives[i].type=="disk" && !drives[i].path.empty())
|
|
{
|
|
has_disk=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if(!has_disk)
|
|
{
|
|
int r=system("dialog --menu \"`cat urbackup/restore/no_disks_found`. `cat urbackup/restore/how_to_continue`\" 15 50 10 \"r\" \"`cat urbackup/restore/search_disk_again`\" \"s\" \"`cat urbackup/restore/stop_restore`\" 2> out");
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
std::string out=getFile("out");
|
|
if(out=="r")
|
|
state=3;
|
|
else
|
|
state=99;
|
|
break;
|
|
}
|
|
|
|
std::string mi;
|
|
for(size_t i=0;i<drives.size();++i)
|
|
{
|
|
if(drives[i].type=="disk")
|
|
{
|
|
mi+="\""+convert((int)i+1)+"\" \""+drives[i].model+" `cat urbackup/restore/size`: "+drives[i].size+"\" ";
|
|
}
|
|
}
|
|
std::string scmd="dialog --menu \"`cat urbackup/restore/select_drive`\" 15 50 10 "+mi+"2> out";
|
|
writestring(scmd, "scmd.sh");
|
|
int r=system(scmd.c_str());
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
std::string out=getFile("out");
|
|
int driveidx=atoi(out.c_str())-1;
|
|
seldrive=drives[driveidx].path;
|
|
r=system(("dialog --yesno \"`cat urbackup/restore/select_certain`\\n"+drives[driveidx].model+" `cat urbackup/restore/size`: "+drives[driveidx].size+"\" 10 50").c_str());
|
|
if(r!=0)
|
|
{
|
|
break;
|
|
}
|
|
++state;
|
|
}break;
|
|
case 4:
|
|
{
|
|
system("clear");
|
|
system("cat urbackup/restore/loading_mbr");
|
|
system("echo");
|
|
if(FileExists("mbr.dat"))
|
|
{
|
|
Server->deleteFile("mbr.dat");
|
|
}
|
|
system("touch mbr.dat");
|
|
DownloadStatus dl_status;
|
|
EDownloadResult rc = downloadImage(selimage.id, convert(selimage.time_s), "mbr.dat", true, login_data, dl_status);
|
|
if (rc != EDownloadResult_Ok )
|
|
{
|
|
Server->Log("Error downloading MBR", LL_ERROR);
|
|
err="cannot_read_mbr";
|
|
state=101;
|
|
break;
|
|
}
|
|
system("cat urbackup/restore/reading_mbr");
|
|
system("echo");
|
|
if (is_disk_mbr("mbr.dat"))
|
|
{
|
|
std::cout << "Restoring whole disk..." << std::endl;
|
|
++state;
|
|
selpart = seldrive;
|
|
break;
|
|
}
|
|
IFile *f=Server->openFile("mbr.dat", MODE_READ);
|
|
if(f==nullptr)
|
|
{
|
|
err="cannot_read_mbr";
|
|
state=101;
|
|
break;
|
|
}
|
|
size_t fsize=(size_t)f->Size();
|
|
char *buf=new char[fsize];
|
|
f->Read(buf, (_u32)fsize);
|
|
Server->destroy(f);
|
|
|
|
CRData mbr(buf, fsize);
|
|
SMBRData mbrdata(mbr);
|
|
if(mbrdata.hasError())
|
|
{
|
|
err="error_while_reading_mbr";
|
|
exit(3);
|
|
state=101;
|
|
break;
|
|
}
|
|
|
|
system("cat urbackup/restore/writing_mbr");
|
|
system("echo");
|
|
IFile *dev=Server->openFile(seldrive, MODE_RW);
|
|
if(dev==nullptr)
|
|
{
|
|
err="cannot_open_disk";
|
|
state=101;
|
|
break;
|
|
}
|
|
dev->Seek(0);
|
|
if(dev->Write(mbrdata.mbr_data)!=mbrdata.mbr_data.size())
|
|
{
|
|
err="error_writing_mbr";
|
|
state=101;
|
|
break;
|
|
}
|
|
|
|
bool fix_gpt = false;
|
|
std::vector<IFSImageFactory::SPartition> partitions;
|
|
|
|
if(mbrdata.gpt_style)
|
|
{
|
|
std::string onlypart = ExtractFileName(seldrive);
|
|
std::string logical_block_size_str = getFile("/sys/block/"+onlypart+"/queue/logical_block_size");
|
|
|
|
if(!logical_block_size_str.empty())
|
|
{
|
|
unsigned int logical_block_size = atoi(logical_block_size_str.c_str());
|
|
|
|
if(logical_block_size!=mbrdata.gpt_header_pos)
|
|
{
|
|
err="gpt_logical_blocksize_change";
|
|
state=101;
|
|
break;
|
|
}
|
|
Server->Log("Logical block size: "+convert(logical_block_size));
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Could not get logical block size");
|
|
}
|
|
|
|
system("cat urbackup/restore/writing_gpt_header");
|
|
system("echo");
|
|
|
|
if(!dev->Seek(mbrdata.gpt_header_pos) || dev->Write(mbrdata.gpt_header)!=mbrdata.gpt_header.size())
|
|
{
|
|
err="error_writing_gpt";
|
|
state=101;
|
|
break;
|
|
}
|
|
|
|
system("cat urbackup/restore/writing_gpt_table");
|
|
system("echo");
|
|
|
|
if(!dev->Seek(mbrdata.gpt_table_pos) || dev->Write(mbrdata.gpt_table)!=mbrdata.gpt_table.size())
|
|
{
|
|
err="error_writing_gpt";
|
|
state=101;
|
|
break;
|
|
}
|
|
|
|
system("cat urbackup/restore/writing_backup_gpt_header");
|
|
system("echo");
|
|
|
|
if(!dev->Seek(mbrdata.backup_gpt_header_pos) || dev->Write(mbrdata.backup_gpt_header)!=mbrdata.backup_gpt_header.size())
|
|
{
|
|
std::cout << "Error writing backup GPT header. " << os_last_error_str() << ". Fixing with GNU parted..." << std::endl;
|
|
fix_gpt = true;
|
|
}
|
|
|
|
system("cat urbackup/restore/writing_backup_gpt_table");
|
|
system("echo");
|
|
|
|
if(!dev->Seek(mbrdata.backup_gpt_table_pos) || dev->Write(mbrdata.backup_gpt_table)!=mbrdata.backup_gpt_table.size())
|
|
{
|
|
std::cout << "Error writing backup GPT table. " << os_last_error_str() << ". Fixing with GNU parted..." << std::endl;
|
|
fix_gpt = true;
|
|
}
|
|
|
|
if (fix_gpt)
|
|
{
|
|
system(("echo -e \"w\\nY\\nY\" | gdisk " + seldrive).c_str());
|
|
|
|
bool t_gpt_style;
|
|
partitions = image_fak->readPartitions(mbrdata.mbr_data, mbrdata.gpt_header, mbrdata.gpt_table, t_gpt_style);
|
|
}
|
|
else
|
|
{
|
|
system(("echo -e \"w\\nY\\nY\" | gdisk " + seldrive + " > /dev/null 2>&1").c_str());
|
|
}
|
|
}
|
|
|
|
if (mbrdata.extra_data_pos != -1)
|
|
{
|
|
std::cout << "Writing extra data..." << std::endl;
|
|
if (dev->Write(mbrdata.extra_data_pos,
|
|
mbrdata.extra_data) != mbrdata.extra_data.size())
|
|
{
|
|
std::cout << "Error writing extra data. Ignoring...";
|
|
}
|
|
}
|
|
|
|
Server->destroy(dev);
|
|
|
|
system("cat urbackup/restore/reading_partition_table");
|
|
system("echo");
|
|
Server->Log("Selected device: "+seldrive+" Partition: "+convert(mbrdata.partition_number));
|
|
system(("partprobe "+seldrive+" > /dev/null 2>&1").c_str());
|
|
Server->wait(10000);
|
|
system("cat urbackup/restore/testing_partition");
|
|
system("echo");
|
|
dev=nullptr;
|
|
std::string partpath = getPartitionPath(seldrive, mbrdata.partition_number);
|
|
if(!partpath.empty())
|
|
{
|
|
dev=Server->openFile(partpath, MODE_RW);
|
|
}
|
|
int try_c=0;
|
|
int delete_parts = 0;
|
|
while(dev==nullptr && try_c<10)
|
|
{
|
|
system(("partprobe "+seldrive+" > /dev/null 2>&1").c_str());
|
|
Server->wait(10000);
|
|
system("cat urbackup/restore/testing_partition");
|
|
system("echo");
|
|
partpath = getPartitionPath(seldrive, mbrdata.partition_number);
|
|
if(!partpath.empty())
|
|
{
|
|
dev=Server->openFile(partpath, MODE_RW);
|
|
}
|
|
|
|
if(dev==nullptr)
|
|
{
|
|
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)
|
|
{
|
|
err="no_restore_partition";
|
|
state=101;
|
|
break;
|
|
}
|
|
selpart=partpath;
|
|
Server->destroy(dev);
|
|
delete []buf;
|
|
++state;
|
|
}break;
|
|
case 5:
|
|
{
|
|
if(windows_partition.empty())
|
|
{
|
|
windows_partition=selpart;
|
|
}
|
|
RestoreThread rt(selimage.id, convert(selimage.time_s), selpart, login_data);
|
|
THREADPOOL_TICKET rt_ticket=Server->getThreadPool()->execute(&rt, "image restore");
|
|
while(true)
|
|
{
|
|
system(("./urbackuprestoreclient --image-download-progress | dialog --backtitle \"`cat urbackup/restore/restoration"+(std::string)(res_sysvol?"_sysvol":"")+"`\" --gauge \"`cat urbackup/restore/t_progress`\" 6 60 0").c_str());
|
|
while(!rt.isDone() && !restore_retry_ok)
|
|
{
|
|
Server->wait(1000);
|
|
}
|
|
if(rt.isDone())
|
|
break;
|
|
if(restore_retry_ok)
|
|
restore_retry_ok=false;
|
|
}
|
|
Server->getThreadPool()->waitFor(rt_ticket);
|
|
EDownloadResult rc=rt.getRC();
|
|
std::string errmsg;
|
|
switch(rc)
|
|
{
|
|
case EDownloadResult_ConnectError: errmsg="`cat urbackup/restore/no_connection`"; break;
|
|
case EDownloadResult_OpenError: errmsg="`cat urbackup/restore/cannot_write_on_partition`"; break;
|
|
case EDownloadResult_SizeReadError: errmsg="`cat urbackup/restore/wrong_size`"; break;
|
|
case EDownloadResult_TimeoutError2: errmsg="`cat urbackup/restore/server_doesnot_respond`"; break;
|
|
case EDownloadResult_TimeoutError1: errmsg="`cat urbackup/restore/server_connection_timeout`"; break;
|
|
case EDownloadResult_WriteFailed: errmsg="`cat urbackup/restore/writing_failed`"; break;
|
|
case EDownloadResult_DeviceTooSmall: errmsg = greplace("_1_",
|
|
PrettyPrintBytes(rt.getImgsize()), greplace("_2_", PrettyPrintBytes(rt.getOutputFileSize()), getFile("urbackup/restore/device_too_small"))); break;
|
|
};
|
|
|
|
if(rc!=0)
|
|
{
|
|
int r=system(("dialog --menu \"`cat urbackup/restore/error_happend` "+errmsg+". `cat urbackup/restore/how_to_continue`\" 15 50 10 \"r\" \"`cat urbackup/restore/restart_restore`\" \"s\" \"`cat urbackup/restore/start_shell`\" \"o\" \"`cat urbackup/restore/restore_other`\" \"s\" \"`cat urbackup/restore/stop_restore`\" 2> out").c_str());
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
std::string out=getFile("out");
|
|
if(out=="r")
|
|
state=5;
|
|
else if(out=="o")
|
|
state=2;
|
|
else if(out=="s")
|
|
{
|
|
system("bash");
|
|
state=0;
|
|
}
|
|
else
|
|
state=99;
|
|
}
|
|
else
|
|
{
|
|
if(r_selimage.assoc.size()>0)
|
|
{
|
|
selimage=r_selimage.assoc[0];
|
|
r_selimage.assoc.erase(r_selimage.assoc.begin());
|
|
res_sysvol=true;
|
|
state=4;
|
|
}
|
|
else
|
|
{
|
|
++state;
|
|
}
|
|
}
|
|
}break;
|
|
case 6:
|
|
{
|
|
int r=system("dialog --menu \"`cat urbackup/restore/restore_success`\" 15 70 10 \"r\" \"`cat urbackup/restore/restart_computer`\" \"b\" \"`cat urbackup/restore/bootable_on_different_hardware`\" \"o\" \"`cat urbackup/restore/restore_other`\" \"s\" \"`cat urbackup/restore/stop_restore`\" 2> out");
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
std::string out=getFile("out");
|
|
if(out=="r")
|
|
system("init 6");
|
|
else if(out=="o")
|
|
state=2;
|
|
else if(out=="s")
|
|
system("bash");
|
|
else if(out=="b")
|
|
system(("python3 driver_edit.py "+windows_partition).c_str());
|
|
else
|
|
system("init 6");
|
|
|
|
}break;
|
|
case 99:
|
|
{
|
|
system("dialog --msgbox \"`cat urbackup/restore/computer_halt`\" 7 50");
|
|
system("init 0");
|
|
exit(1);
|
|
}break;
|
|
case 101:
|
|
{
|
|
int r=system(("dialog --menu \"`cat urbackup/restore/error_happend` `cat urbackup/restore/"+err+"`. `cat urbackup/restore/how_to_continue`\" 15 50 10 \"r\" \"`cat urbackup/restore/restart_restore`\" \"s\" \"`cat urbackup/restore/stop_restore`\" 2> out").c_str());
|
|
if(r!=0)
|
|
{
|
|
state=start_state;
|
|
break;
|
|
}
|
|
|
|
std::string out=getFile("out");
|
|
if(out=="r")
|
|
state=0;
|
|
else
|
|
state=99;
|
|
}break;
|
|
default:
|
|
{
|
|
system("dialog --msgbox \"`cat urbackup/restore/internal_error`!!!!\" 7 50");
|
|
exit(99);
|
|
}break;
|
|
}
|
|
}
|
|
}
|