mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
1633 lines
36 KiB
C++
1633 lines
36 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011-2017 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 <iostream>
|
|
#include <string>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include "Connector.h"
|
|
#include "../stringtools.h"
|
|
#include "../tclap/CmdLine.h"
|
|
#include "json/json.h"
|
|
#include "../urbackupcommon/os_functions.h"
|
|
|
|
#ifndef _WIN32
|
|
#include <sys/ioctl.h>
|
|
#include <unistd.h>
|
|
#include "../config.h"
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#define PWFILE VARDIR "/urbackup/pw.txt"
|
|
#define PWFILE_CHANGE VARDIR "/urbackup/pw_change.txt"
|
|
#else
|
|
#include <Windows.h>
|
|
#define PACKAGE_VERSION "$version_full_numeric$"
|
|
#define VARDIR ""
|
|
#define PWFILE "pw.txt"
|
|
#define PWFILE_CHANGE "pw_change.txt"
|
|
#endif
|
|
|
|
#ifdef __MACH__
|
|
#include <mach/clock.h>
|
|
#include <mach/mach.h>
|
|
#endif
|
|
|
|
void wait(unsigned int ms)
|
|
{
|
|
#ifdef _WIN32
|
|
Sleep(ms);
|
|
#else
|
|
usleep(ms * 1000);
|
|
#endif
|
|
}
|
|
|
|
int64 getTimeMS()
|
|
{
|
|
#ifdef _WIN32
|
|
return GetTickCount64();
|
|
#else
|
|
//return (unsigned int)(((double)clock()/(double)CLOCKS_PER_SEC)*1000.0);
|
|
/*
|
|
boost::xtime xt;
|
|
boost::xtime_get(&xt, boost::TIME_UTC);
|
|
static boost::int_fast64_t start_t=xt.sec;
|
|
xt.sec-=start_t;
|
|
unsigned int t=xt.sec*1000+(unsigned int)((double)xt.nsec/1000000.0);
|
|
return t;*/
|
|
/*timeval tp;
|
|
gettimeofday(&tp, NULL);
|
|
static long start_t=tp.tv_sec;
|
|
tp.tv_sec-=start_t;
|
|
return tp.tv_sec*1000+tp.tv_usec/1000;
|
|
*/
|
|
#ifdef __APPLE__
|
|
clock_serv_t cclock;
|
|
mach_timespec_t mts;
|
|
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
|
|
clock_get_time(cclock, &mts);
|
|
mach_port_deallocate(mach_task_self(), cclock);
|
|
return static_cast<int64>(mts.tv_sec) * 1000 + mts.tv_nsec / 1000000;
|
|
#else
|
|
timespec tp;
|
|
if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
|
|
{
|
|
timeval tv;
|
|
gettimeofday(&tv, nullptr);
|
|
static long start_t = tv.tv_sec;
|
|
tv.tv_sec -= start_t;
|
|
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
|
}
|
|
return static_cast<int64>(tp.tv_sec) * 1000 + tp.tv_nsec / 1000000;
|
|
#endif //__APPLE__
|
|
#endif
|
|
}
|
|
|
|
const std::string cmdline_version = PACKAGE_VERSION;
|
|
|
|
void show_version()
|
|
{
|
|
std::cout << "UrBackup Client Controller v" << cmdline_version << std::endl;
|
|
std::cout << "Copyright (C) 2011-2019 Martin Raiber" << std::endl;
|
|
std::cout << "This is free software; see the source for copying conditions. There is NO"<< std::endl;
|
|
std::cout << "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."<< std::endl;
|
|
}
|
|
|
|
void action_help(std::string cmd)
|
|
{
|
|
std::cout << std::endl;
|
|
std::cout << "USAGE:" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " [--help] [--version] <command> [<args>]" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "Get specific command help with " << cmd << " <command> --help" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " start" << std::endl;
|
|
std::cout << "\t\t" "Start an incremental/full image/file backup" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " status" << std::endl;
|
|
std::cout << "\t\t" "Get current backup status" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " browse" << std::endl;
|
|
std::cout << "\t\t" "Browse backups and files/folders in backups" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " restore-start" << std::endl;
|
|
std::cout << "\t\t" "Restore files/folders from backup" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " set-settings" << std::endl;
|
|
std::cout << "\t\t" "Set backup settings" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " reset-keep" << std::endl;
|
|
std::cout << "\t\t" "Reset keeping files during incremental backups" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " add-backupdir" << std::endl;
|
|
std::cout << "\t\t" "Add new directory to backup set" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " list-backupdirs" << std::endl;
|
|
std::cout << "\t\t" "List directories that are being backed up" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << cmd << " remove-backupdir" << std::endl;
|
|
std::cout << "\t\t" "Remove directory from backup set" << std::endl;
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
const size_t c_speed_size = 15;
|
|
const size_t c_max_l_length = 80;
|
|
|
|
size_t get_terminal_width()
|
|
{
|
|
#ifndef _WIN32
|
|
struct winsize w;
|
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != 0)
|
|
{
|
|
return c_max_l_length;
|
|
}
|
|
else
|
|
{
|
|
return w.ws_col;
|
|
}
|
|
#else
|
|
return c_max_l_length;
|
|
#endif
|
|
}
|
|
|
|
void draw_progress(int pc_done, double speed_bpms, int64 done_bytes, int64 total_bytes, std::string details, int detail_pc)
|
|
{
|
|
static size_t max_line_length = 0;
|
|
|
|
size_t term_width = get_terminal_width();
|
|
size_t draw_segments = term_width / 3;
|
|
|
|
size_t segments = (size_t)(pc_done / 100.*draw_segments);
|
|
|
|
std::string toc = "\r[";
|
|
for (size_t i = 0; i<draw_segments; ++i)
|
|
{
|
|
if (i<segments)
|
|
{
|
|
toc += "=";
|
|
}
|
|
else if (i == segments)
|
|
{
|
|
toc += ">";
|
|
}
|
|
else
|
|
{
|
|
toc += " ";
|
|
}
|
|
}
|
|
std::string speed_str = PrettyPrintSpeed(static_cast<size_t>(speed_bpms*1000.0));
|
|
while (speed_str.size()<c_speed_size)
|
|
speed_str += " ";
|
|
std::string pcdone = convert(pc_done);
|
|
if (pcdone.size() == 1)
|
|
pcdone = " " + pcdone;
|
|
|
|
if (!details.empty() && detail_pc >= 0)
|
|
{
|
|
std::string detailpc_s = convert(detail_pc);
|
|
if (detailpc_s.size() == 1)
|
|
detailpc_s = " " + detailpc_s;
|
|
|
|
details += " " + detailpc_s + "%";
|
|
}
|
|
|
|
toc += "] " + pcdone + "% ";
|
|
if (total_bytes >= 0)
|
|
{
|
|
toc += PrettyPrintBytes(done_bytes) + "/" + PrettyPrintBytes(total_bytes) + " ";
|
|
}
|
|
|
|
if (speed_bpms > 0)
|
|
{
|
|
toc += "at " + speed_str + " ";
|
|
}
|
|
|
|
if (!details.empty())
|
|
{
|
|
toc += details;
|
|
}
|
|
|
|
|
|
if (toc.size() >= term_width)
|
|
toc = toc.substr(0, term_width);
|
|
|
|
if (toc.size()>max_line_length)
|
|
max_line_length = toc.size();
|
|
|
|
max_line_length = (std::min)(max_line_length, term_width);
|
|
|
|
while (toc.size()<max_line_length)
|
|
toc += " ";
|
|
|
|
std::cout << toc;
|
|
std::cout.flush();
|
|
}
|
|
|
|
typedef int(*action_fun)(std::vector<std::string> args);
|
|
|
|
class PwClientCmd
|
|
{
|
|
public:
|
|
PwClientCmd(TCLAP::CmdLine& cmd, bool change)
|
|
: cmd(cmd),
|
|
pw_file_arg("p", "pw-file",
|
|
"Use password in file",
|
|
false, change ? PWFILE_CHANGE : PWFILE, "path", cmd),
|
|
client_arg("c", "client",
|
|
"Start backup on this client",
|
|
false, "127.0.0.1", "hostname/IP", cmd),
|
|
change(change)
|
|
{
|
|
|
|
}
|
|
|
|
void wait(int64 maxtimems)
|
|
{
|
|
int64 starttime = getTimeMS();
|
|
do
|
|
{
|
|
if (FileExists(pw_file_arg.getValue()))
|
|
{
|
|
return;
|
|
}
|
|
::wait(100);
|
|
} while (getTimeMS() - starttime < maxtimems);
|
|
}
|
|
|
|
bool set()
|
|
{
|
|
if (change)
|
|
{
|
|
Connector::setPWFileChange(pw_file_arg.getValue());
|
|
}
|
|
else
|
|
{
|
|
Connector::setPWFile(pw_file_arg.getValue());
|
|
}
|
|
|
|
Connector::setClient(client_arg.getValue());
|
|
|
|
if (trim(getFile(pw_file_arg.getValue())).empty())
|
|
{
|
|
if (errno != 0)
|
|
{
|
|
perror("urbackupclientctl");
|
|
}
|
|
std::cerr << "Cannot read backend password from " << pw_file_arg.getValue() << std::endl;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private:
|
|
TCLAP::CmdLine& cmd;
|
|
|
|
bool change;
|
|
TCLAP::ValueArg<std::string> pw_file_arg;
|
|
TCLAP::ValueArg<std::string> client_arg;
|
|
};
|
|
|
|
std::vector<int64> get_current_processes()
|
|
{
|
|
SStatusDetails sd = Connector::getStatusDetails();
|
|
|
|
std::vector<int64> ret;
|
|
|
|
for (size_t i = 0; i < sd.running_processes.size(); ++i)
|
|
{
|
|
ret.push_back(sd.running_processes[i].process_id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
const std::string spinner = "|/-\\";
|
|
|
|
int64 wait_for_new_process(std::string type, const std::vector<int64>& current_processes)
|
|
{
|
|
int tries = 60;
|
|
|
|
std::string message = "Waiting for server to start backup... ";
|
|
|
|
for (int i = 0; i < tries; ++i)
|
|
{
|
|
int tries = 20;
|
|
SStatusDetails sd = Connector::getStatusDetails();
|
|
|
|
while (!sd.ok && tries>0)
|
|
{
|
|
--tries;
|
|
wait(100);
|
|
sd = Connector::getStatusDetails();
|
|
}
|
|
|
|
if (!sd.ok)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for (size_t j = 0; j < sd.running_processes.size(); ++j)
|
|
{
|
|
if (sd.running_processes[j].action == type)
|
|
{
|
|
if (std::find(current_processes.begin(), current_processes.end(), sd.running_processes[j].process_id) == current_processes.end())
|
|
{
|
|
if (i > 0)
|
|
{
|
|
std::cout << "\r" << message << "done" << std::endl;
|
|
}
|
|
return sd.running_processes[j].process_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::cout << "\r" << message << spinner[i%spinner.size()];
|
|
std::cout.flush();
|
|
|
|
wait(1000);
|
|
}
|
|
|
|
std::cout << "\r" << message << "done" << std::endl;
|
|
return 0;
|
|
}
|
|
|
|
int follow_status(bool restore, int64 process_id)
|
|
{
|
|
bool found_once = false;
|
|
|
|
size_t preparing_idx = 0;
|
|
size_t waiting_for_id_idx = 0;
|
|
|
|
std::string waiting_msg;
|
|
std::string preparing_msg;
|
|
|
|
while (true)
|
|
{
|
|
int tries = 20;
|
|
SStatusDetails status = Connector::getStatusDetails();
|
|
|
|
while (!status.ok && tries>0)
|
|
{
|
|
--tries;
|
|
wait(100);
|
|
status = Connector::getStatusDetails();
|
|
}
|
|
|
|
if (!status.ok)
|
|
{
|
|
std::cerr << "Could not get status from backend" << std::endl;
|
|
return 3;
|
|
}
|
|
|
|
bool found = false;
|
|
|
|
for (size_t i = 0; i < status.running_processes.size(); ++i)
|
|
{
|
|
SRunningProcess& proc = status.running_processes[i];
|
|
if (status.running_processes[i].process_id == process_id)
|
|
{
|
|
if (!found_once && waiting_for_id_idx>0)
|
|
{
|
|
std::cout << "\r" << waiting_msg << " done" << std::endl;
|
|
}
|
|
|
|
found_once = true;
|
|
|
|
if (proc.percent_done < 0)
|
|
{
|
|
if (restore)
|
|
{
|
|
preparing_msg = "Preparing restore... ";
|
|
std::cout << "\r" << preparing_msg << spinner[preparing_idx%spinner.size()];
|
|
}
|
|
else
|
|
{
|
|
preparing_msg = "Preparing... ";
|
|
std::cout << "\r" << preparing_msg << spinner[preparing_idx%spinner.size()];
|
|
}
|
|
|
|
std::cout.flush();
|
|
|
|
++preparing_idx;
|
|
}
|
|
else
|
|
{
|
|
if (preparing_idx > 0)
|
|
{
|
|
std::cout << "\r" << preparing_msg << "done" << std::endl;
|
|
preparing_idx = 0;
|
|
}
|
|
draw_progress(proc.percent_done, proc.speed_bpms, proc.done_bytes, proc.total_bytes, proc.details, proc.detail_pc);
|
|
}
|
|
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
for (size_t i = 0; i < status.finished_processes.size(); ++i)
|
|
{
|
|
if (status.finished_processes[i].id == process_id)
|
|
{
|
|
if (status.finished_processes[i].success)
|
|
{
|
|
std::cout << std::endl;
|
|
if (restore)
|
|
{
|
|
std::cout << "Restore completed successfully." << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Completed successfully." << std::endl;
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
std::cout << std::endl;
|
|
if (restore)
|
|
{
|
|
std::cerr << "Restore failed." << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Failed." << std::endl;
|
|
}
|
|
return 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found_once)
|
|
{
|
|
if (restore)
|
|
{
|
|
waiting_msg = "Starting restore. Waiting for backup server... ";
|
|
std::cout << "\r" << waiting_msg << spinner[waiting_for_id_idx%spinner.size()];;
|
|
}
|
|
else
|
|
{
|
|
waiting_msg = "Waiting for process to become available... ";
|
|
std::cout << "\r" << waiting_msg << spinner[waiting_for_id_idx%spinner.size()];;
|
|
}
|
|
|
|
std::cout.flush();
|
|
|
|
++waiting_for_id_idx;
|
|
}
|
|
}
|
|
|
|
wait(1000);
|
|
}
|
|
}
|
|
|
|
int action_start(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("Start an incremental/full image/file backup", ' ', cmdline_version);
|
|
|
|
TCLAP::SwitchArg incr_backup("i", "incremental", "Start incremental backup");
|
|
TCLAP::SwitchArg full_backup("f", "full", "Start full backup");
|
|
|
|
cmd.xorAdd(incr_backup, full_backup);
|
|
|
|
#ifdef _WIN32
|
|
TCLAP::SwitchArg file_backup("l", "file", "Start file backup");
|
|
TCLAP::SwitchArg image_backup("m", "image", "Start image backup");
|
|
|
|
cmd.xorAdd(file_backup, image_backup);
|
|
#endif
|
|
|
|
TCLAP::SwitchArg non_blocking_arg("b", "non-blocking",
|
|
"Do not show backup progress and block till the backup is finished but return immediately after starting it", cmd);
|
|
|
|
TCLAP::ValueArg<std::string> virtual_client_arg("v", "virtual-client",
|
|
"Virtual client name",
|
|
false, "", "client name", cmd);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, false);
|
|
|
|
cmd.parse(args);
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
std::vector<int64> current_processes = get_current_processes();
|
|
|
|
std::string type;
|
|
int rc;
|
|
#ifdef _WIN32
|
|
if(file_backup.getValue())
|
|
{
|
|
#endif
|
|
type = full_backup.getValue() ? "FULL" : "INCR";
|
|
|
|
rc = Connector::startBackup(virtual_client_arg.getValue(), full_backup.getValue());
|
|
#ifdef _WIN32
|
|
}
|
|
else
|
|
{
|
|
type = full_backup.getValue() ? "FULLI" : "INCRI";
|
|
|
|
rc = Connector::startImage(virtual_client_arg.getValue(), full_backup.getValue());
|
|
}
|
|
#endif
|
|
|
|
if(rc==2)
|
|
{
|
|
std::cerr << "Backup is already running" << std::endl;
|
|
return 2;
|
|
}
|
|
else if(rc==1)
|
|
{
|
|
if (non_blocking_arg.getValue())
|
|
{
|
|
std::cout << "Backup started" << std::endl;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
int64 new_process = wait_for_new_process(type, current_processes);
|
|
|
|
if (new_process == 0)
|
|
{
|
|
std::cerr << "Timeout while waiting for server to start backup" << std::endl;
|
|
return 4;
|
|
}
|
|
|
|
return follow_status(false, new_process);
|
|
}
|
|
}
|
|
else if(rc==3)
|
|
{
|
|
std::cerr << "Error starting backup. No backup server found." << std::endl;
|
|
return 3;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Error starting backup." << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int action_status(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("Get current backup status", ' ', cmdline_version);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, false);
|
|
|
|
TCLAP::ValueArg<int> follow_arg("f", "follow",
|
|
"Follow proccess status",
|
|
false, 0, "process id", cmd);
|
|
|
|
cmd.parse(args);
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
if (follow_arg.getValue() == 0)
|
|
{
|
|
std::string status = Connector::getStatusDetailsRaw();
|
|
if (!status.empty())
|
|
{
|
|
std::cout << status << std::endl;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Error getting status" << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return follow_status(false, follow_arg.getValue());
|
|
}
|
|
}
|
|
|
|
int action_browse(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("Browse backups and files/folders in backups", ' ', cmdline_version);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, false);
|
|
|
|
TCLAP::ValueArg<std::string> backupid_arg("b", "backupid",
|
|
"Backupid of backup in which to browse files/folders or \"last\" for last complete backup",
|
|
false, "", "id", cmd);
|
|
|
|
TCLAP::ValueArg<std::string> path_arg("d", "path",
|
|
"Path of folder/file to which to browse",
|
|
false, "", "path", cmd);
|
|
|
|
TCLAP::ValueArg<std::string> virtual_client_arg("v", "virtual-client",
|
|
"Virtual client name",
|
|
false, "", "client name", cmd);
|
|
|
|
cmd.parse(args);
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
if(path_arg.getValue().empty() && !backupid_arg.isSet())
|
|
{
|
|
Connector::EAccessError access_error;
|
|
std::string filebackups = Connector::getFileBackupsList(virtual_client_arg.getValue(), access_error);
|
|
|
|
if(!filebackups.empty())
|
|
{
|
|
std::cout << filebackups << std::endl;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if(access_error==Connector::EAccessError_NoServer)
|
|
{
|
|
std::cerr << "Error getting file backups. No backup server found." << std::endl;
|
|
return 2;
|
|
}
|
|
else if (access_error == Connector::EAccessError_NoTokens)
|
|
{
|
|
std::cerr << "No file backup access tokens found. Did you run a file backup yet?" << std::endl;
|
|
return 3;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Error getting file backups" << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int* pbackupid = nullptr;
|
|
int backupid = 0;
|
|
if(backupid_arg.isSet())
|
|
{
|
|
if (backupid_arg.getValue() != "last"
|
|
&& convert(atoi(backupid_arg.getValue().c_str())) != backupid_arg.getValue())
|
|
{
|
|
std::cerr << "Not a valid backupid: \"" << backupid_arg.getValue() << "\"" << std::endl;
|
|
return 3;
|
|
}
|
|
|
|
if (backupid_arg.getValue() != "last")
|
|
{
|
|
backupid = atoi(backupid_arg.getValue().c_str());
|
|
}
|
|
pbackupid = &backupid;
|
|
}
|
|
Connector::EAccessError access_error;
|
|
std::string filelist = Connector::getFileList(path_arg.getValue(), pbackupid, virtual_client_arg.getValue(), access_error);
|
|
|
|
if(!filelist.empty())
|
|
{
|
|
std::cout << filelist << std::endl;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if (access_error == Connector::EAccessError_NoServer)
|
|
{
|
|
std::cerr << "Error getting file list. No backup server found." << std::endl;
|
|
return 2;
|
|
}
|
|
else if (access_error == Connector::EAccessError_NoTokens)
|
|
{
|
|
std::cerr << "No file backup access tokens found. Did you run a file backup yet?" << std::endl;
|
|
return 3;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Error getting file list" << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int wait_for_restore(std::string restore_info)
|
|
{
|
|
Json::Value root;
|
|
Json::Reader reader;
|
|
|
|
if (!reader.parse(restore_info, root, false))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (root.get("ok", false) == false)
|
|
{
|
|
std::cerr << "Error starting restore. Errorcode: " << root.get("err", -1).asInt() << std::endl;
|
|
return 2;
|
|
}
|
|
|
|
int64 process_id = root["process_id"].asInt64();
|
|
|
|
return follow_status(true, process_id);
|
|
}
|
|
|
|
std::string remove_ending_slash(const std::string& path)
|
|
{
|
|
if (path.size() > 1
|
|
&& path[path.size() - 1] == os_file_sep()[0])
|
|
{
|
|
return path.substr(0, path.size() - 1);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
int action_start_restore(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("Restore files/folders from backup", ' ', cmdline_version);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, false);
|
|
|
|
TCLAP::ValueArg<std::string> backupid_arg("b", "backupid",
|
|
"Backupid of backup from which to restore files/folders or \"last\" for last complete backup",
|
|
true, "", "id", cmd);
|
|
|
|
TCLAP::ValueArg<std::string> path_arg("d", "path",
|
|
"Path of folder/file to restore",
|
|
false, "", "path", cmd);
|
|
|
|
TCLAP::MultiArg<std::string> map_from_arg("m", "map-from",
|
|
"Map from local output path of folders/files to a different local path",
|
|
false, "path", cmd);
|
|
|
|
TCLAP::MultiArg<std::string> map_to_arg("t", "map-to",
|
|
"Map to local output path of folders/files to a different local path",
|
|
false, "path", cmd);
|
|
|
|
TCLAP::SwitchArg no_remove_arg("n", "no-remove",
|
|
"Do not remove files/directories not in backup", cmd);
|
|
|
|
TCLAP::SwitchArg consider_other_fs_arg("o", "consider-other-fs",
|
|
"Consider other file systems when removing files/directories not in backup", cmd);
|
|
|
|
TCLAP::SwitchArg non_blocking_arg("l", "non-blocking",
|
|
"Do not show restore progress and block till the restore is finished but return immediately after starting it", cmd);
|
|
|
|
TCLAP::SwitchArg no_follow_symlinks("s", "no-follow-symlinks",
|
|
"Do not follow symlinks outside of restored path during restore", cmd);
|
|
|
|
TCLAP::ValueArg<std::string> virtual_client_arg("v", "virtual-client",
|
|
"Virtual client name",
|
|
false, "", "client name", cmd);
|
|
|
|
cmd.parse(args);
|
|
|
|
if (map_from_arg.getValue().size() != map_to_arg.getValue().size())
|
|
{
|
|
std::cerr << "There need to be an equal amount of -m/--map-from and -t/--map-to arguments" << std::endl;
|
|
return 2;
|
|
}
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
if (backupid_arg.getValue() != "last"
|
|
&& convert(atoi(backupid_arg.getValue().c_str())) != backupid_arg.getValue())
|
|
{
|
|
std::cerr << "Not a valid backupid: \"" << backupid_arg.getValue() << "\"" << std::endl;
|
|
return 2;
|
|
}
|
|
|
|
std::vector<SPathMap> path_map;
|
|
for (size_t i = 0; i < map_from_arg.getValue().size(); ++i)
|
|
{
|
|
SPathMap new_pm;
|
|
new_pm.source = remove_ending_slash(map_from_arg.getValue()[i]);
|
|
new_pm.target = remove_ending_slash(map_to_arg.getValue()[i]);
|
|
|
|
if (new_pm.source == os_file_sep()
|
|
&& new_pm.target != os_file_sep())
|
|
{
|
|
new_pm.target += os_file_sep();
|
|
}
|
|
|
|
if (new_pm.target == os_file_sep()
|
|
&& new_pm.source != os_file_sep())
|
|
{
|
|
new_pm.target = std::string();
|
|
}
|
|
|
|
path_map.push_back(new_pm);
|
|
}
|
|
|
|
int backupid = 0;
|
|
if (backupid_arg.getValue() != "last")
|
|
{
|
|
backupid = atoi(backupid_arg.getValue().c_str());
|
|
}
|
|
|
|
Connector::EAccessError access_error;
|
|
std::string restore_info = Connector::startRestore(path_arg.getValue(), backupid, virtual_client_arg.getValue(),
|
|
path_map, access_error, !no_remove_arg.getValue(), !consider_other_fs_arg.getValue(),
|
|
!no_follow_symlinks.getValue());
|
|
|
|
if(!restore_info.empty())
|
|
{
|
|
if (non_blocking_arg.getValue())
|
|
{
|
|
std::cout << restore_info << std::endl;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return wait_for_restore(restore_info);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(access_error == Connector::EAccessError_NoServer)
|
|
{
|
|
std::cerr << "Error starting restore. No backup server found." << std::endl;
|
|
return 2;
|
|
}
|
|
else if (access_error == Connector::EAccessError_NoTokens)
|
|
{
|
|
std::cerr << "Error starting restore. No file backup access tokens found. Did you run a file backup yet?" << std::endl;
|
|
return 3;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Error starting restore" << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
int action_set_settings(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("Set backup settings", ' ', cmdline_version);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, true);
|
|
|
|
TCLAP::MultiArg<std::string> key_arg("k", "key",
|
|
"Key of the setting to set",
|
|
false, "setting key", cmd);
|
|
|
|
TCLAP::MultiArg<std::string> value_arg("v", "value",
|
|
"New value to set the setting to",
|
|
false, "setting value", cmd);
|
|
|
|
cmd.parse(args);
|
|
|
|
if (key_arg.getValue().size() != value_arg.getValue().size())
|
|
{
|
|
std::cerr << "There need to be an equal amount of -k/--key and -v/--value arguments" << std::endl;
|
|
return 2;
|
|
}
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
std::string s_settings;
|
|
for (size_t i = 0; i < key_arg.getValue().size(); ++i)
|
|
{
|
|
s_settings += key_arg.getValue()[i] + "=" + value_arg.getValue()[i] + "\n";
|
|
}
|
|
|
|
s_settings += "keep_old_settings=true\n";
|
|
|
|
bool no_perm;
|
|
bool b = Connector::updateSettings(s_settings, no_perm);
|
|
|
|
if (!b)
|
|
{
|
|
if (no_perm)
|
|
{
|
|
std::cerr << "Error setting settings. Client is not allowed to change settings." << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Error setting settings." << std::endl;
|
|
}
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int action_reset_keep(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("Reset keeping files during incremental backups", ' ', cmdline_version);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, true);
|
|
|
|
TCLAP::ValueArg<std::string> virtual_client_arg("v", "virtual-client",
|
|
"Virtual client name",
|
|
false, "", "client name", cmd);
|
|
|
|
TCLAP::ValueArg<std::string> backup_folder_arg("b", "backup-folder",
|
|
"Backup folder name",
|
|
false, "", "folder name", cmd);
|
|
|
|
TCLAP::ValueArg<int> group_arg("g", "backup-group",
|
|
"Backup group index",
|
|
false, 0, "group index", cmd);
|
|
|
|
cmd.parse(args);
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
std::string ret = Connector::resetKeep(virtual_client_arg.getValue(), backup_folder_arg.getValue(), group_arg.getValue());
|
|
|
|
if (ret == "OK")
|
|
{
|
|
return 0;
|
|
}
|
|
else if (ret == "err_virtual_client_not_found")
|
|
{
|
|
std::cerr << "Error: Virtual client not found" << std::endl;
|
|
return 4;
|
|
}
|
|
else if (ret == "err_backup_folder_not_found")
|
|
{
|
|
std::cerr << "Error: Backup folder not found" << std::endl;
|
|
return 5;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Error: " << ret << std::endl;
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
std::string removeChars(std::string in)
|
|
{
|
|
char illegalchars[] = { '*', ':', '/' , '\\' };
|
|
std::string ret;
|
|
for (size_t i = 0; i<in.size(); ++i)
|
|
{
|
|
bool found = false;
|
|
for (size_t j = 0; j<sizeof(illegalchars) / sizeof(illegalchars[0]); ++j)
|
|
{
|
|
if (illegalchars[j] == in[i])
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
ret += in[i];
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool findPathName(const std::vector<SBackupDir>& dirs, const std::string &pn)
|
|
{
|
|
for (size_t i = 0; i<dirs.size(); ++i)
|
|
{
|
|
if (dirs[i].name == pn)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string getDefaultDirname(const std::vector<SBackupDir>& dirs, const std::string &path)
|
|
{
|
|
std::string dirname = removeChars(ExtractFileName(path));
|
|
|
|
if (dirname.empty())
|
|
dirname = "rootfs";
|
|
|
|
if (findPathName(dirs, dirname))
|
|
{
|
|
for (int k = 0; k<100; ++k)
|
|
{
|
|
if (!findPathName(dirs, dirname + "_" + convert(k)))
|
|
{
|
|
dirname = dirname + "_" + convert(k);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return dirname;
|
|
}
|
|
|
|
int action_add_backupdir(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("Add new directory to backup set", ' ', cmdline_version);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, true);
|
|
|
|
TCLAP::ValueArg<std::string> virtual_client_arg("v", "virtual-client",
|
|
"Virtual client name",
|
|
false, "", "client name", cmd);
|
|
|
|
TCLAP::ValueArg<std::string> name_arg("n", "name",
|
|
"Backup directory name",
|
|
false, "", "name", cmd);
|
|
|
|
TCLAP::ValueArg<std::string> path_arg("d", "path",
|
|
"Backup path",
|
|
true, "", "path", cmd);
|
|
|
|
TCLAP::ValueArg<int> group_arg("g", "backup-group",
|
|
"Backup group index",
|
|
false, 0, "group index", cmd);
|
|
|
|
TCLAP::SwitchArg optional_arg("o", "optional",
|
|
"Do not fail backup if path does not exist",
|
|
cmd);
|
|
|
|
TCLAP::SwitchArg no_follow_symlinks_arg("f", "no-follow-symlinks",
|
|
"Do not follow symbolic links outside of backup path",
|
|
cmd);
|
|
|
|
TCLAP::SwitchArg symlinks_required_arg("r", "require-symlinks",
|
|
"Fail backup if symbolic link targets do not exist",
|
|
cmd);
|
|
|
|
TCLAP::SwitchArg one_filesystem_arg("x", "one-filesystem",
|
|
"Do not cross filesystem boundary during backup",
|
|
cmd);
|
|
|
|
TCLAP::SwitchArg require_snapshot_arg("s", "require-snapshot",
|
|
"Fail backup if snapshot of backup path cannot be created",
|
|
cmd);
|
|
|
|
TCLAP::SwitchArg separate_hashes_arg("a", "separate-hashes",
|
|
"Do not share local hashes with other virtual clients",
|
|
cmd);
|
|
|
|
TCLAP::SwitchArg keep_arg("k", "keep",
|
|
"Keep deleted files and directories during incremental backups. DO NOT USE",
|
|
cmd);
|
|
|
|
cmd.parse(args);
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
std::string flags;
|
|
|
|
if (optional_arg.getValue())
|
|
{
|
|
if (!flags.empty()) flags += ",";
|
|
flags += "optional";
|
|
}
|
|
|
|
if (!no_follow_symlinks_arg.getValue())
|
|
{
|
|
if (!flags.empty()) flags += ",";
|
|
flags += "follow_symlinks";
|
|
}
|
|
|
|
if (!symlinks_required_arg.getValue())
|
|
{
|
|
if (!flags.empty()) flags += ",";
|
|
flags += "symlinks_optional";
|
|
}
|
|
|
|
if (one_filesystem_arg.getValue())
|
|
{
|
|
if (!flags.empty()) flags += ",";
|
|
flags += "one_filesystem";
|
|
}
|
|
|
|
if (require_snapshot_arg.getValue())
|
|
{
|
|
if (!flags.empty()) flags += ",";
|
|
flags += "require_snapshot";
|
|
}
|
|
|
|
if (!separate_hashes_arg.getValue())
|
|
{
|
|
if (!flags.empty()) flags += ",";
|
|
flags += "share_hashes";
|
|
}
|
|
|
|
if (keep_arg.getValue())
|
|
{
|
|
if (!flags.empty()) flags += ",";
|
|
flags += "keep";
|
|
}
|
|
|
|
std::vector<SBackupDir> backup_dirs = Connector::getSharedPaths(true);
|
|
|
|
if (Connector::hasError())
|
|
{
|
|
std::cerr << "Error retrieving current backup directories from backend" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
SBackupDir new_dir;
|
|
new_dir.path = path_arg.getValue();
|
|
if (name_arg.getValue().empty())
|
|
{
|
|
new_dir.name = getDefaultDirname(backup_dirs, new_dir.path);
|
|
}
|
|
else
|
|
{
|
|
new_dir.name = name_arg.getValue();
|
|
}
|
|
|
|
new_dir.group = group_arg.getValue();
|
|
|
|
new_dir.flags = flags;
|
|
|
|
new_dir.virtual_client = virtual_client_arg.getValue();
|
|
|
|
backup_dirs.push_back(new_dir);
|
|
|
|
if (!Connector::saveSharedPaths(backup_dirs))
|
|
{
|
|
std::cerr << "Error adding new backup path via backend" << std::endl;
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void ouput_val(std::string val, size_t max_size)
|
|
{
|
|
if (val.size() > max_size)
|
|
{
|
|
val = val.substr(0, max_size);
|
|
}
|
|
std::cout << val;
|
|
for (size_t i = val.size(); i < max_size; ++i)
|
|
{
|
|
std::cout << ' ';
|
|
}
|
|
}
|
|
|
|
void display_table(const std::vector<std::vector<std::string> >& rows)
|
|
{
|
|
if (rows.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const size_t val_gap = 1;
|
|
|
|
std::vector<size_t> max_size;
|
|
max_size.resize(rows[0].size());
|
|
|
|
for (size_t i = 0; i < rows[0].size(); ++i)
|
|
{
|
|
for (size_t j = 0; j < rows.size(); ++j)
|
|
{
|
|
max_size[i] = (std::max)(rows[j][i].size(), max_size[i]);
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < rows[0].size(); ++i)
|
|
{
|
|
ouput_val(rows[0][i], max_size[i]+ val_gap);
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
|
|
for (size_t i = 0; i < rows[0].size(); ++i)
|
|
{
|
|
std::cout << std::string(max_size[i], '-');
|
|
std::cout << std::string(val_gap, ' ');
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
|
|
for (size_t i = 1; i < rows.size(); ++i)
|
|
{
|
|
for (size_t j = 0; j < rows[i].size(); ++j)
|
|
{
|
|
ouput_val(rows[i][j], max_size[j] + val_gap);
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
int action_list_backupdirs(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("List directories that are being backed up", ' ', cmdline_version);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, false);
|
|
|
|
TCLAP::ValueArg<std::string> virtual_client_arg("v", "virtual-client",
|
|
"Display only for virtual with name",
|
|
false, "", "client name", cmd);
|
|
|
|
TCLAP::SwitchArg raw_arg("r", "raw",
|
|
"Return raw JSON output", cmd);
|
|
|
|
cmd.parse(args);
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
if (raw_arg.getValue())
|
|
{
|
|
std::string ret = Connector::getSharedPathsRaw();
|
|
if (ret.empty())
|
|
{
|
|
std::cerr << "Error retrieving current backup directories from backend" << std::endl;
|
|
return 1;
|
|
}
|
|
std::cout << ret;
|
|
std::cout.flush();
|
|
return 0;
|
|
}
|
|
|
|
std::vector<SBackupDir> backup_dirs = Connector::getSharedPaths(false);
|
|
|
|
if (Connector::hasError())
|
|
{
|
|
std::cerr << "Error retrieving current backup directories from backend" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
if (backup_dirs.empty())
|
|
{
|
|
std::cout << "No directories are being backed up" << std::endl;
|
|
return 0;
|
|
}
|
|
|
|
bool has_virtual_client = false;
|
|
bool has_group = false;
|
|
bool has_server_default = false;
|
|
|
|
for (size_t i = 0; i < backup_dirs.size(); ++i)
|
|
{
|
|
if (!backup_dirs[i].virtual_client.empty())
|
|
{
|
|
has_virtual_client = true;
|
|
}
|
|
if (backup_dirs[i].group != 0)
|
|
{
|
|
has_group = true;
|
|
}
|
|
if (backup_dirs[i].server_default)
|
|
has_server_default = true;
|
|
}
|
|
|
|
std::vector<std::vector<std::string> > tab;
|
|
|
|
std::vector<std::string> tab_header;
|
|
tab_header.push_back("PATH");
|
|
tab_header.push_back("NAME");
|
|
if (has_group)
|
|
{
|
|
tab_header.push_back("GROUP");
|
|
}
|
|
if (has_virtual_client)
|
|
{
|
|
tab_header.push_back("VIRTUAL CLIENT");
|
|
}
|
|
tab_header.push_back("FLAGS");
|
|
if (has_server_default)
|
|
{
|
|
tab_header.push_back("CONFIGURED ON SERVER");
|
|
}
|
|
|
|
tab.push_back(tab_header);
|
|
|
|
for (size_t i = 0; i < backup_dirs.size(); ++i)
|
|
{
|
|
std::vector<std::string> row;
|
|
row.push_back(backup_dirs[i].path);
|
|
|
|
if (backup_dirs[i].name.empty())
|
|
{
|
|
backup_dirs[i].name = getDefaultDirname(backup_dirs, backup_dirs[i].path);
|
|
}
|
|
|
|
row.push_back(backup_dirs[i].name);
|
|
|
|
if (has_group)
|
|
{
|
|
row.push_back(convert(backup_dirs[i].group));
|
|
}
|
|
|
|
if (has_virtual_client)
|
|
{
|
|
if (backup_dirs[i].virtual_client.empty())
|
|
{
|
|
row.push_back("-");
|
|
}
|
|
else
|
|
{
|
|
row.push_back(backup_dirs[i].virtual_client);
|
|
}
|
|
}
|
|
|
|
row.push_back(backup_dirs[i].flags);
|
|
|
|
if (has_server_default)
|
|
{
|
|
if (backup_dirs[i].server_default)
|
|
{
|
|
row.push_back("Yes");
|
|
}
|
|
else
|
|
{
|
|
row.push_back("No");
|
|
}
|
|
}
|
|
|
|
tab.push_back(row);
|
|
}
|
|
|
|
display_table(tab);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int action_remove_backupdir(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("Remove directory from backup set", ' ', cmdline_version);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, true);
|
|
|
|
TCLAP::ValueArg<std::string> name_arg("n", "name",
|
|
"Backup directory name",
|
|
false, "", "name");
|
|
|
|
TCLAP::ValueArg<std::string> path_arg("d", "path",
|
|
"Backup path",
|
|
true, "", "path");
|
|
|
|
cmd.xorAdd(name_arg, path_arg);
|
|
|
|
cmd.parse(args);
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
std::vector<SBackupDir> backup_dirs = Connector::getSharedPaths(true);
|
|
|
|
if (Connector::hasError())
|
|
{
|
|
std::cerr << "Error retrieving current backup directories from backend" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
bool del_ok = false;
|
|
bool del_server_default = true;
|
|
|
|
for (size_t i = 0; i < backup_dirs.size();)
|
|
{
|
|
if (!name_arg.getValue().empty()
|
|
&& backup_dirs[i].name == name_arg.getValue())
|
|
{
|
|
if (backup_dirs[i].server_default)
|
|
{
|
|
del_server_default = true;
|
|
}
|
|
else
|
|
{
|
|
backup_dirs.erase(backup_dirs.begin() + i);
|
|
del_ok = true;
|
|
}
|
|
}
|
|
else if (!path_arg.getValue().empty()
|
|
&& backup_dirs[i].path == path_arg.getValue())
|
|
{
|
|
if (backup_dirs[i].server_default)
|
|
{
|
|
del_server_default = true;
|
|
}
|
|
else
|
|
{
|
|
backup_dirs.erase(backup_dirs.begin() + i);
|
|
del_ok = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++i;
|
|
}
|
|
}
|
|
|
|
if (!del_ok)
|
|
{
|
|
if (del_server_default)
|
|
{
|
|
std::cerr << "Backup directory was configured on the server. Please remove it there" << std::endl;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Backup directory to remove not found" << std::endl;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (!Connector::saveSharedPaths(backup_dirs))
|
|
{
|
|
std::cerr << "Error removing backup directory via backend" << std::endl;
|
|
return 2;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int action_wait_for_backend(std::vector<std::string> args)
|
|
{
|
|
TCLAP::CmdLine cmd("Wait for backend to become available", ' ', cmdline_version);
|
|
|
|
PwClientCmd pw_client_cmd(cmd, false);
|
|
|
|
TCLAP::ValueArg<int> time_arg("t", "time",
|
|
"Max time in seconds to wait",
|
|
false, 60, "seconds", cmd);
|
|
|
|
cmd.parse(args);
|
|
|
|
pw_client_cmd.wait(time_arg.getValue() * 1000);
|
|
|
|
if (!pw_client_cmd.set())
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
int64 starttime = getTimeMS();
|
|
do
|
|
{
|
|
int64 thistime = getTimeMS();
|
|
std::string d = Connector::getStatusRawNoWait();
|
|
if (!Connector::hasError()
|
|
&& !d.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
if (getTimeMS() - thistime < 30)
|
|
{
|
|
wait(100);
|
|
}
|
|
} while (getTimeMS() - starttime < time_arg.getValue() * 1000);
|
|
|
|
std::cerr << "Could not connect to backend in specified time" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
if(argc==0)
|
|
{
|
|
std::cerr << "Not enough arguments (zero arguments) -- no program name" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
HMODULE hModule = GetModuleHandleW(NULL);
|
|
if (hModule != INVALID_HANDLE_VALUE)
|
|
{
|
|
WCHAR path[MAX_PATH];
|
|
if (GetModuleFileNameW(hModule, path, MAX_PATH) != 0)
|
|
{
|
|
SetCurrentDirectoryW(path);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
std::vector<std::string> actions;
|
|
std::vector<action_fun> action_funs;
|
|
actions.push_back("start");
|
|
action_funs.push_back(action_start);
|
|
actions.push_back("status");
|
|
action_funs.push_back(action_status);
|
|
actions.push_back("browse");
|
|
action_funs.push_back(action_browse);
|
|
actions.push_back("restore-start");
|
|
action_funs.push_back(action_start_restore);
|
|
actions.push_back("set-settings");
|
|
action_funs.push_back(action_set_settings);
|
|
actions.push_back("reset-keep");
|
|
action_funs.push_back(action_reset_keep);
|
|
actions.push_back("add-backupdir");
|
|
action_funs.push_back(action_add_backupdir);
|
|
actions.push_back("list-backupdirs");
|
|
action_funs.push_back(action_list_backupdirs);
|
|
actions.push_back("remove-backupdir");
|
|
action_funs.push_back(action_remove_backupdir);
|
|
actions.push_back("wait-for-backend");
|
|
action_funs.push_back(action_wait_for_backend);
|
|
|
|
bool has_help=false;
|
|
bool has_version=false;
|
|
size_t action_idx=std::string::npos;
|
|
std::vector<std::string> args;
|
|
args.push_back(argv[0]);
|
|
for(int i=1;i<argc;++i)
|
|
{
|
|
std::string arg = argv[i];
|
|
|
|
if(arg=="--help" || arg=="-h")
|
|
{
|
|
has_help=true;
|
|
}
|
|
|
|
if(arg=="--version")
|
|
{
|
|
has_version=true;
|
|
}
|
|
|
|
if(!arg.empty() && arg[0]=='-')
|
|
{
|
|
args.push_back(arg);
|
|
continue;
|
|
}
|
|
|
|
bool found_action=false;
|
|
for(size_t j=0;j<actions.size();++j)
|
|
{
|
|
if(next(actions[j], 0, arg))
|
|
{
|
|
if(action_idx!=std::string::npos)
|
|
{
|
|
action_help(argv[0]);
|
|
exit(1);
|
|
}
|
|
action_idx=j;
|
|
found_action=true;
|
|
}
|
|
}
|
|
|
|
if(!found_action)
|
|
{
|
|
args.push_back(arg);
|
|
}
|
|
}
|
|
|
|
if(action_idx==std::string::npos)
|
|
{
|
|
if(has_help)
|
|
{
|
|
action_help(argv[0]);
|
|
exit(1);
|
|
}
|
|
if(has_version)
|
|
{
|
|
show_version();
|
|
exit(1);
|
|
}
|
|
action_help(argv[0]);
|
|
exit(1);
|
|
}
|
|
|
|
try
|
|
{
|
|
args[0]+=" "+actions[action_idx];
|
|
int rc = action_funs[action_idx](args);
|
|
return rc;
|
|
}
|
|
catch (TCLAP::ArgException &e)
|
|
{
|
|
std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|