urbackup_backend/urbackupserver/cmdline_preprocessor.cpp

1354 lines
35 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 "../tclap/CmdLine.h"
#include <vector>
#include "../stringtools.h"
#include "../urbackupcommon/os_functions.h"
#include <stdlib.h>
#ifndef _WIN32
# include "../Server.h"
# include <sys/types.h>
# include <pwd.h>
# include <sys/wait.h>
# include <ctime>
# include <sys/time.h>
# include <unistd.h>
# include <sys/types.h>
# include <pwd.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <sys/fcntl.h>
# include <memory>
# include "../Interface/SettingsReader.h"
# include <sys/resource.h>
# include <errno.h>
#endif
#ifndef _WIN32
#include "config.h"
#define _getcwd getcwd
#else
#define PACKAGE_VERSION "unknown"
#define VARDIR ""
#define BINDIR ""
#define DATADIR ""
#include <direct.h>
#endif
const std::string cmdline_version = PACKAGE_VERSION;
void show_version()
{
std::cout << "UrBackup Server 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;
}
int64 get_time_ms()
{
#ifndef _WIN32
timespec tp;
if(clock_gettime(CLOCK_MONOTONIC, &tp)!=0)
{
timeval tv;
gettimeofday(&tv, NULL);
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;
#else
return 0;
#endif
}
int real_main(int argc, char* argv[])
#ifndef _WIN32
;
#else
{return 1;}
#endif
int run_real_main(std::vector<std::string> args)
{
char** argv = new char*[args.size()];
for(size_t i=0;i<args.size();++i)
{
argv[i] = const_cast<char*>(args[i].c_str());
}
int rc = real_main(static_cast<int>(args.size()), argv);
delete[] argv;
return rc;
}
typedef int(*action_fun)(std::vector<std::string> args);
std::string unquote_value(std::string val)
{
val = trim(val);
if(val[0]=='"')
{
size_t last_pos = val.find_last_of('"');
if(last_pos!=0)
{
val=val.substr(1, last_pos-1);
}
}
else if(val[0]=='\'')
{
size_t last_pos = val.find_last_of('\'');
if(last_pos!=0)
{
val=val.substr(1, last_pos-1);
}
}
return val;
}
#ifndef _WIN32
void read_config_file(std::string fn, std::vector<std::string>& real_args)
{
if (!FileExists(fn))
{
std::cout << "Config file at " << fn << " does not exist. Ignoring." << std::endl;
return;
}
bool destroy_server=false;
if(Server==NULL)
{
Server = new CServer;
destroy_server=true;
}
{
std::unique_ptr<ISettingsReader> settings(Server->createFileSettingsReader(fn));
std::string val;
if(settings->getValue("FASTCGI_PORT", &val))
{
val = unquote_value(val);
if(!val.empty())
{
real_args.push_back("--port");
real_args.push_back(val);
}
}
if(settings->getValue("HTTP_PORT", &val))
{
val = unquote_value(val);
if(!val.empty())
{
real_args.push_back("--http_port");
real_args.push_back(val);
}
}
if(settings->getValue("LOGFILE", &val))
{
val = unquote_value(val);
if(!val.empty())
{
if(val[0]!='/')
{
val = "/var/log/"+val;
}
real_args.push_back("--logfile");
real_args.push_back(val);
}
}
if(settings->getValue("LOGLEVEL", &val))
{
val = unquote_value(val);
if(!val.empty())
{
real_args.push_back("--loglevel");
real_args.push_back(unquote_value(val));
}
}
if(settings->getValue("DAEMON_TMPDIR", &val))
{
std::string tmpdir = unquote_value(val);
if(!tmpdir.empty())
{
if(setenv("TMPDIR", tmpdir.c_str(), 1)!=0)
{
std::cout << "Error setting TMPDIR" << std::endl;
exit(1);
}
}
}
if(settings->getValue("SQLITE_TMPDIR", &val))
{
val = unquote_value(val);
if(!val.empty())
{
real_args.push_back("--sqlite_tmpdir");
real_args.push_back(val);
}
}
if(settings->getValue("BROADCAST_INTERFACES", &val))
{
val = unquote_value(val);
if(!val.empty())
{
real_args.push_back("--broadcast_interfaces");
real_args.push_back(val);
}
}
if(settings->getValue("HTTP_SERVER", &val))
{
val = unquote_value(val);
if(!val.empty())
{
real_args.push_back("--http_server");
real_args.push_back(strlower(val));
}
}
if (settings->getValue("HTTP_LOCALHOST_ONLY", &val))
{
val = unquote_value(val);
if (!val.empty()
&& ( val=="1" ||
strlower(val)=="true" ||
strlower(val)=="yes") )
{
real_args.push_back("--http_localhost_only");
real_args.push_back("1");
}
}
if (settings->getValue("FASTCGI_LOCALHOST_ONLY", &val))
{
val = unquote_value(val);
if (!val.empty()
&& (val == "1" ||
strlower(val) == "true" ||
strlower(val) == "yes"))
{
real_args.push_back("--fastcgi_localhost_only");
real_args.push_back("1");
}
}
if (settings->getValue("LOG_ROTATE_FILESIZE", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
real_args.push_back("--rotate-filesize");
real_args.push_back(strlower(val));
}
}
if (settings->getValue("LOG_ROTATE_NUM", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
real_args.push_back("--rotate-numfiles");
real_args.push_back(strlower(val));
}
}
if (settings->getValue("SQLITE_MMAP_HUGE", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
real_args.push_back("--sqlite_mmap_huge");
real_args.push_back(strlower(val));
}
}
if (settings->getValue("SQLITE_MMAP_MEDIUM", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
real_args.push_back("--sqlite_mmap_medium");
real_args.push_back(strlower(val));
}
}
if (settings->getValue("SQLITE_MMAP_SMALL", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
real_args.push_back("--sqlite_mmap_small");
real_args.push_back(strlower(val));
}
}
if (settings->getValue("HTTP_PROXY", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
real_args.push_back("--http_proxy");
real_args.push_back(strlower(val));
}
}
if (settings->getValue("USER", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
real_args.push_back("--user");
real_args.push_back(val);
}
}
if (settings->getValue("INTERNET_ONLY", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
if (val == "1") val = "true";
real_args.push_back("--internet_only_mode");
real_args.push_back(strlower(val));
}
}
if (settings->getValue("LUA_SANDBOX", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
if (val == "1") val = "true";
real_args.push_back("--lua_sandbox");
real_args.push_back(strlower(val));
}
}
if (settings->getValue("INTERNET_MODE_DISABLED", &val))
{
val = trim(unquote_value(val));
if (!val.empty() &&
(val == "1" ||
strlower(val) == "true" ||
strlower(val) == "yes"))
{
real_args.push_back("--internet_mode_disabled");
real_args.push_back("1");
}
}
if (settings->getValue("INTERNET_PORT", &val))
{
val = trim(unquote_value(val));
if (!val.empty())
{
real_args.push_back("--internet_port");
real_args.push_back(val);
}
}
if (settings->getValue("INTERNET_LOCALHOST_ONLY", &val))
{
val = trim(unquote_value(val));
if (!val.empty() &&
(val == "1" ||
strlower(val) == "true" ||
strlower(val) == "yes"))
{
real_args.push_back("--internet_localhost_only");
real_args.push_back("1");
}
}
if (settings->getValue("INTERNET_DISABLE_WEBSOCKET", &val))
{
val = trim(unquote_value(val));
if (!val.empty() &&
(val == "1" ||
strlower(val) == "true" ||
strlower(val) == "yes"))
{
real_args.push_back("--internet_disable_websocket");
real_args.push_back("1");
}
}
}
if(destroy_server)
{
delete Server;
}
}
#endif
int action_run(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Run network file and image backup server", ' ', cmdline_version);
TCLAP::ValueArg<unsigned short> fastcgi_port_arg("f", "fastcgi-port",
"Specifies the port where UrBackup server will listen for FastCGI connections",
false, 55413, "port number", cmd);
TCLAP::ValueArg<unsigned short> http_port_arg("p", "http-port",
"Specifies the port where UrBackup server will listen for HTTP connections",
false, 55414, "port number", cmd);
TCLAP::ValueArg<std::string> logfile_arg("l", "logfile",
"Specifies the log file name",
false, "/var/log/urbackup.log", "path", cmd);
std::vector<std::string> loglevels;
loglevels.push_back("debug");
loglevels.push_back("info");
loglevels.push_back("warn");
loglevels.push_back("error");
TCLAP::ValuesConstraint<std::string> loglevels_constraint(loglevels);
TCLAP::ValueArg<std::string> loglevel_arg("v", "loglevel",
"Specifies the log level",
false, "info", &loglevels_constraint, cmd);
TCLAP::SwitchArg no_console_time_arg("t", "no-consoletime",
"Do not print time when logging to console",
cmd, false);
TCLAP::SwitchArg daemon_arg("d", "daemon", "Daemonize process", cmd, false);
TCLAP::SwitchArg disable_http_server_arg("e", "disable-http-server", "Do not start internal HTTP-server", cmd, false);
TCLAP::ValueArg<std::string> pidfile_arg("w", "pidfile",
"Save pid of daemon in file",
false, "/var/run/urbackupsrv.pid", "path", cmd);
TCLAP::MultiArg<std::string> broadcast_interface_arg("b", "broadcast-interface",
"Network interface from which to send broadcasts", false, "network interface name", cmd);
TCLAP::ValueArg<std::string> sqlite_tmpdir_arg("s", "sqlite-tmpdir",
"Specifies the directory SQLite uses to store temporary tables",
false, "", "path", cmd);
TCLAP::ValueArg<std::string> user_arg("u", "user",
"Change process to run as specific user",
false, "urbackup", "user", cmd);
#ifndef _WIN32
TCLAP::ValueArg<std::string> config_arg("c", "config",
"Read configuration parameters from config file",
false, "", "path", cmd);
#endif
TCLAP::ValueArg<int> rotate_filesize_arg("g", "rotate-filesize",
"Maximum size of log file before rotation",
false, 0, "bytes", cmd);
TCLAP::ValueArg<int> rotate_num_files_arg("j", "rotate-num-files",
"Max number of log files during rotation",
false, 0, "num", cmd);
TCLAP::SwitchArg internet_only_arg("i", "internet-only", "Do not discover clients in local network", cmd, false);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
#ifndef _WIN32
if(!config_arg.getValue().empty())
{
read_config_file(config_arg.getValue(), real_args);
}
#endif
if(std::find(real_args.begin(), real_args.end(), "--port")==real_args.end())
{
real_args.push_back("--port");
real_args.push_back(convert(fastcgi_port_arg.getValue()));
}
if(std::find(real_args.begin(), real_args.end(), "--http_port")==real_args.end())
{
real_args.push_back("--http_port");
real_args.push_back(convert(http_port_arg.getValue()));
}
real_args.push_back("--pidfile");
real_args.push_back(pidfile_arg.getValue());
real_args.push_back("--workingdir");
real_args.push_back(VARDIR);
real_args.push_back("--http_root");
real_args.push_back(DATADIR "/urbackup/www");
real_args.push_back("--urbackup_public_key");
real_args.push_back(DATADIR "/urbackup/urbackup_ecdsa409k1.pub");
real_args.push_back("--snapshot_helper");
real_args.push_back(BINDIR "/urbackup_snapshot_helper");
real_args.push_back("--mount_helper");
real_args.push_back(BINDIR "/urbackup_mount_helper");
if (std::find(real_args.begin(), real_args.end(), "--user") == real_args.end())
{
real_args.push_back("--user");
real_args.push_back(user_arg.getValue());
}
if(!sqlite_tmpdir_arg.getValue().empty())
{
real_args.push_back("--sqlite_tmpdir");
real_args.push_back(sqlite_tmpdir_arg.getValue());
}
std::string broadcast_interfaces;
const std::vector<std::string>& bi = broadcast_interface_arg.getValue();
for(size_t i=0;i<bi.size();++i)
{
if(!broadcast_interfaces.empty())
{
broadcast_interfaces+=",";
}
broadcast_interfaces+=bi[i];
}
if(!broadcast_interfaces.empty()
&& std::find(real_args.begin(), real_args.end(), "--broadcast_interfaces")==real_args.end())
{
real_args.push_back("--broadcast_interfaces");
real_args.push_back(broadcast_interfaces);
}
if(std::find(real_args.begin(), real_args.end(), "--logfile")==real_args.end())
{
real_args.push_back("--logfile");
real_args.push_back(logfile_arg.getValue());
}
if(std::find(real_args.begin(), real_args.end(), "--loglevel")==real_args.end())
{
real_args.push_back("--loglevel");
real_args.push_back(loglevel_arg.getValue());
}
if(daemon_arg.getValue())
{
real_args.push_back("--daemon");
}
if(no_console_time_arg.getValue())
{
real_args.push_back("--log_console_no_time");
}
if(std::find(real_args.begin(), real_args.end(), "--http_server")==real_args.end())
{
real_args.push_back("--http_server");
real_args.push_back(disable_http_server_arg.getValue() ? "false" : "true");
}
if (rotate_filesize_arg.getValue() > 0
&& std::find(real_args.begin(), real_args.end(), "--rotate-filesize") == real_args.end())
{
real_args.push_back("--rotate-filesize");
real_args.push_back(convert(rotate_filesize_arg.getValue()));
}
if (rotate_num_files_arg.getValue() > 0
&& std::find(real_args.begin(), real_args.end(), "--rotate-numfiles") == real_args.end())
{
real_args.push_back("--rotate-numfiles");
real_args.push_back(convert(rotate_num_files_arg.getValue()));
}
if (std::find(real_args.begin(), real_args.end(), "--internet_only_mode") == real_args.end()
&& internet_only_arg.getValue())
{
real_args.push_back("--internet_only_mode");
real_args.push_back("true");
}
#ifndef _WIN32
char* http_proxy = getenv("http_proxy");
if (http_proxy != NULL
&& std::find(real_args.begin(), real_args.end(), "--http_proxy") == real_args.end())
{
real_args.push_back("--http_proxy");
real_args.push_back(http_proxy);
}
struct rlimit limit;
limit.rlim_cur = 65535;
limit.rlim_max = 65535;
if (setrlimit(RLIMIT_NOFILE, &limit) != 0) {
std::cerr << "Raising maximum file descriptor to "<< limit.rlim_max << " failed. This may cause problems with many clients. (errno=" << errno <<")" << std::endl;
}
#ifdef RLIMIT_NICE
limit.rlim_cur = 35;
limit.rlim_max = 35;
if (setrlimit(RLIMIT_NICE, &limit) != 0) {
std::cerr << "Raising nice-ceiling to " << limit.rlim_max << " failed. (errno=" << errno << ")" << std::endl;
}
#endif
#endif
return run_real_main(real_args);
}
int action_verify_hashes(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Verify file backup hashes", ' ', cmdline_version);
TCLAP::SwitchArg delete_verify_failed_arg("d", "delete-verify-failed",
"Delete file entries of files with failed verification", cmd, false);
TCLAP::ValueArg<std::string> verify_arg("v", "verify",
"Specify file backup(s) to verify",
true, "all", "file backup set", cmd);
TCLAP::ValueArg<std::string> user_arg("u", "user",
"Change process to run as specific user",
false, "urbackup", "user", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--workingdir");
real_args.push_back(VARDIR);
real_args.push_back("--user");
real_args.push_back(user_arg.getValue());
real_args.push_back("--loglevel");
real_args.push_back("debug");
if(delete_verify_failed_arg.getValue())
{
real_args.push_back("--delete_verify_failed");
real_args.push_back("true");
}
if(verify_arg.getValue()=="all")
{
real_args.push_back("--verify_hashes");
real_args.push_back("true");
}
else
{
real_args.push_back("--verify_hashes");
real_args.push_back(verify_arg.getValue());
}
return run_real_main(real_args);
}
int action_remove_unknown(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Remove unknown files and directories from backup storage and fix symbolic links in backup storage", ' ', cmdline_version);
TCLAP::ValueArg<std::string> user_arg("u", "user",
"Change process to run as specific user",
false, "urbackup", "user", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--workingdir");
real_args.push_back(VARDIR);
real_args.push_back("--user");
real_args.push_back(user_arg.getValue());
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--app");
real_args.push_back("remove_unknown");
real_args.push_back("--snapshot_helper");
real_args.push_back(BINDIR "/urbackup_snapshot_helper");
return run_real_main(real_args);
}
int action_defrag_database(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Rebuild UrBackup database", ' ', cmdline_version);
TCLAP::ValueArg<std::string> user_arg("u", "user",
"Change process to run as specific user",
false, "urbackup", "user", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--workingdir");
real_args.push_back(VARDIR);
real_args.push_back("--user");
real_args.push_back(user_arg.getValue());
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--app");
real_args.push_back("defrag_database");
return run_real_main(real_args);
}
int action_repair_database(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Try to repair UrBackup database", ' ', cmdline_version);
TCLAP::ValueArg<std::string> user_arg("u", "user",
"Change process to run as specific user",
false, "urbackup", "user", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--workingdir");
real_args.push_back(VARDIR);
real_args.push_back("--user");
real_args.push_back(user_arg.getValue());
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--app");
real_args.push_back("repair_database");
return run_real_main(real_args);
}
int action_reset_admin_pw(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Reset web interface administrator password", ' ', cmdline_version);
TCLAP::ValueArg<std::string> password_arg("p", "password",
"New administrator password",
true, "", "password", cmd);
TCLAP::ValueArg<std::string> user_arg("u", "user",
"Change process to run as specific user",
false, "urbackup", "user", cmd);
TCLAP::ValueArg<std::string> admin_user_arg("a", "admin-user",
"Admin account user name",
false, "admin", "user account name", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--workingdir");
real_args.push_back(VARDIR);
real_args.push_back("--user");
real_args.push_back(user_arg.getValue());
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--set_admin_pw");
real_args.push_back(password_arg.getValue());
real_args.push_back("--set_admin_username");
real_args.push_back(admin_user_arg.getValue());
return run_real_main(real_args);
}
int action_cleanup(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Cleanup file/image backups from backup storage", ' ', cmdline_version);
TCLAP::ValueArg<std::string> cleanup_amount_arg("a", "amount",
"Amount of storage to cleanup",
true, "", "%|M|G|T", cmd);
TCLAP::ValueArg<std::string> user_arg("u", "user",
"Change process to run as specific user",
false, "urbackup", "user", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--workingdir");
real_args.push_back(VARDIR);
real_args.push_back("--user");
real_args.push_back(user_arg.getValue());
real_args.push_back("--snapshot_helper");
real_args.push_back(BINDIR "/urbackup_snapshot_helper");
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--app");
real_args.push_back("cleanup");
real_args.push_back("--cleanup_amount");
real_args.push_back(cleanup_amount_arg.getValue());
return run_real_main(real_args);
}
int action_export_auth_log(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Export authentication log to csv file", ' ', cmdline_version);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--workingdir");
real_args.push_back(VARDIR);
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--app");
real_args.push_back("export_auth_log");
return run_real_main(real_args);
}
std::string curr_dir()
{
char buf[4096];
char* cwd = _getcwd(buf, sizeof(buf));
if(cwd!=NULL)
{
return cwd;
}
else
{
return std::string();
}
}
std::string make_absolute(std::string fn)
{
std::string cdir = curr_dir();
bool is_absolute = (fn.empty() || fn[0]=='/');
if(!is_absolute && !cdir.empty())
{
fn = cdir + "/" + fn;
}
return fn;
}
int action_decompress_file(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Decompress UrBackup compressed file", ' ', cmdline_version);
TCLAP::ValueArg<std::string> file_arg("f", "file",
"File to decompress",
true, "", "path", cmd);
TCLAP::ValueArg<std::string> user_arg("u", "user",
"Change process to run as specific user",
false, "urbackup", "user", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--user");
real_args.push_back(user_arg.getValue());
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--decompress");
real_args.push_back(make_absolute(file_arg.getValue()));
return run_real_main(real_args);
}
#ifndef _WIN32
int action_mount_vhd(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Mount VHD file", ' ', cmdline_version);
TCLAP::ValueArg<std::string> file_arg("f", "file",
"VHD(z) file to mount",
true, "", "path", cmd);
TCLAP::ValueArg<std::string> mountpoint_arg("m", "mountpoint",
"Mount the VHD(z)-file at this mountpoint",
true, "", "path", cmd);
TCLAP::ValueArg<std::string> tempmount_arg("t", "tempmount",
"Use this directory as temporary mountpoint",
false, "", "path", cmd);
TCLAP::ValueArg<std::string> mount_options_arg("o", "mountoptions",
"Mount options when mounting device",
false, "ro", "mount options", cmd);
TCLAP::ValueArg<std::string> logfile_arg("l", "logfile",
"Specifies the log file name for the background VHD-reading process",
false, "/var/log/urbackup-fuse.log", "path", cmd);
TCLAP::SwitchArg guestmount_arg("g", "guestmount",
"Use libguestfs guestmount to mount the file system", cmd, false);
TCLAP::ValueArg<std::string> guestmount_user_arg("u", "guestmount-user",
"User when mounting device",
false, "urbackup", "mount options", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--logfile");
real_args.push_back(logfile_arg.getValue());
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--mount");
real_args.push_back(make_absolute(file_arg.getValue()));
std::string tmpmountpoint;
if(tempmount_arg.getValue().empty())
{
char *tmpdir=getenv("TMPDIR");
std::string stmpdir;
if(tmpdir==NULL )
stmpdir="/tmp";
else
stmpdir=tmpdir;
stmpdir=stmpdir+"/cps.XXXXXX";
char* made_tmpdir = mkdtemp(&stmpdir[0]);
if(made_tmpdir==NULL)
{
std::cout << "Error creating temporary directory " << stmpdir << std::endl;
exit(1);
}
tmpmountpoint = stmpdir;
}
else
{
tmpmountpoint = tempmount_arg.getValue();
}
real_args.push_back("--mountpoint");
real_args.push_back(tmpmountpoint);
std::cout << "Loading FUSE kernel module..." << std::endl;
system("modprobe fuse");
std::cout << "Starting VHD background process..." << std::endl;
size_t pid1;
if( (pid1=fork())==0 )
{
if(setsid()==-1)
{
perror("urbackupsrv");
exit(1);
}
if(fork()==0)
{
for (int i=getdtablesize();i>=0;--i) close(i);
int i=open("/dev/null",O_RDWR);
dup(i);
dup(i);
return run_real_main(real_args);
}
else
{
exit(0);
}
}
else
{
int status;
int rc = waitpid(pid1, &status, 0);
if(rc==-1)
{
perror("urbackupsrv");
exit(1);
}
}
if (guestmount_arg.getValue())
{
passwd* user_info = getpwnam(guestmount_user_arg.getValue().c_str());
if (user_info)
{
if (setgid(user_info->pw_gid))
{
Server->Log("Error setting uid", LL_ERROR);
}
if (setuid(user_info->pw_uid))
{
Server->Log("Error setting uid", LL_ERROR);
}
}
}
std::cout << "Waiting for background process to become available..." << std::endl;
int64 starttime = get_time_ms();
while(get_time_ms()-starttime<5*60*1000)
{
if(FileExists(tmpmountpoint+"/volume"))
{
std::cout << "Mounting..." << std::endl;
int rc;
if (guestmount_arg.getValue())
{
rc = system(("guestmount -r --format=raw -a \"" + tmpmountpoint + "/volume\" -o \"" + mount_options_arg.getValue() + "\" -m /dev/sda \"" + mountpoint_arg.getValue() + "\"").c_str());
}
else
{
rc = system(("mount -v -o \"" + mount_options_arg.getValue() + "\" \"" + tmpmountpoint + "/volume\" \"" + mountpoint_arg.getValue() + "\"").c_str());
}
if(rc!=0)
{
std::cout << "Mounting failed." << std::endl;
exit(1);
}
else
{
std::cout << "Mounted successfully." << std::endl;
exit(0);
}
}
usleep(100*1000);
}
std::cout << "Timeout while waiting for background process. Please see logfile (" << logfile_arg.getValue() << ") for error details." << std::endl;
exit(1);
}
#endif
int action_assemble(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Assemble VHD(Z) volumes into one disk VHD file", ' ', cmdline_version);
TCLAP::MultiArg<std::string> assemble_in_arg("a", "assemble-in",
"File to assemble into the output file",
true, "path", cmd);
TCLAP::ValueArg<std::string> assemble_out("o", "assemble-out",
"Location where disk VHD is written when assembling",
true, "", "path", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
std::string assemble_input;
for(size_t i=0;i<assemble_in_arg.getValue().size();++i)
{
if(!assemble_input.empty())
{
assemble_input+=";";
}
assemble_input+=make_absolute(assemble_in_arg.getValue()[i]);
}
real_args.push_back("--no-server");
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--assemble");
real_args.push_back(assemble_input);
real_args.push_back("--output_file");
real_args.push_back(assemble_out.getValue());
return run_real_main(real_args);
}
int action_internal(std::vector<std::string> args)
{
return run_real_main(args);
}
int action_blockalign(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Blockalign takes data from [input] and writes it to [output] "
"and uses hashes in [hash file] to align blocks in [output] such "
"that subsequent runs have the same blocks at the same positions.\n"
"Using the -r flag [input file] is restored to [output], such that the output is identical to the original input again.", ' ', cmdline_version);
TCLAP::ValueArg<std::string> input_arg("i", "input",
"Input (file path or - for stdin)",
true, "", "path", cmd);
TCLAP::ValueArg<std::string> output_arg("o", "output",
"Output (file path or - for stdout)",
true, "", "path", cmd);
TCLAP::SwitchArg restore_arg("r", "restore",
"Restore block aligned file to its original layout", false);
TCLAP::ValueArg<std::string> hash_arg("j", "hash-output",
"Hash output file",
true, "", "path");
TCLAP::SwitchArg verbose_arg("v", "verbose",
"Print some information after block alignment", cmd, false);
cmd.xorAdd(restore_arg, hash_arg);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--app");
real_args.push_back("blockalign");
real_args.push_back("--output");
real_args.push_back(output_arg.getValue());
if (restore_arg.getValue())
{
real_args.push_back("--restore");
real_args.push_back(input_arg.getValue());
}
else
{
real_args.push_back("--input");
real_args.push_back(input_arg.getValue());
if (verbose_arg.getValue())
{
real_args.push_back("--verbose");
real_args.push_back("1");
}
real_args.push_back("--hash_fn");
real_args.push_back(hash_arg.getValue());
}
return run_real_main(real_args);
}
int action_skiphash_copy(std::vector<std::string> args)
{
TCLAP::CmdLine cmd("Skiphash-copy copies a file [input] to [output] using hashes in file [hash file] to skip blocks that are unchanged. "
"A new hash file with updated hashes is created at [output path].hash. This file should be used as [hash file] input for the next skiphash-copy.", ' ', cmdline_version);
TCLAP::ValueArg<std::string> input_arg("i", "input",
"Input file",
true, "", "path", cmd);
TCLAP::ValueArg<std::string> output_arg("o", "output",
"Output file",
true, "", "path", cmd);
TCLAP::ValueArg<std::string> hash_arg("j", "hash-input",
"Hash input file",
false, "", "path", cmd);
std::vector<std::string> real_args;
real_args.push_back(args[0]);
cmd.parse(args);
real_args.push_back("--no-server");
real_args.push_back("--loglevel");
real_args.push_back("debug");
real_args.push_back("--app");
real_args.push_back("skiphash_copy");
real_args.push_back("--src");
real_args.push_back(input_arg.getValue());
real_args.push_back("--dst");
real_args.push_back(output_arg.getValue());
if (!hash_arg.getValue().empty())
{
real_args.push_back("--hashsrc");
real_args.push_back(hash_arg.getValue());
}
return run_real_main(real_args);
}
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 << " run" << std::endl;
std::cout << "\t\t" "Run UrBackup server" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " verify-hashes" << std::endl;
std::cout << "\t\t" "Verify file backup hashes" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " remove-unknown" << std::endl;
std::cout << "\t\t" "Remove unknown files and directories from backup storage and fix symbolic links in backup storage" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " reset-admin-pw" << std::endl;
std::cout << "\t\t" "Reset web interface administrator password" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " cleanup" << std::endl;
std::cout << "\t\t" "Cleanup file/image backups from backup storage" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " repair-database" << std::endl;
std::cout << "\t\t" "Try to repair UrBackup database" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " defrag-database" << std::endl;
std::cout << "\t\t" "Rebuild UrBackup database" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " export-auth-log" << std::endl;
std::cout << "\t\t" "Export authentication log to csv file" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " decompress-file" << std::endl;
std::cout << "\t\t" "Decompress UrBackup compressed file" << std::endl;
std::cout << std::endl;
#if !defined(_WIN32) && defined(WITH_FUSEPLUGIN)
std::cout << "\t" << cmd << " mount-vhd" << std::endl;
std::cout << "\t\t" "Mount VHD file" << std::endl;
std::cout << std::endl;
#endif
std::cout << "\t" << cmd << " assemble" << std::endl;
std::cout << "\t\t" "Assemble VHD(Z) volumes into one disk VHD file" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " blockalign" << std::endl;
std::cout << "\t\t" "Align file to block boundaries or reverse block boundary alignment" << std::endl;
std::cout << std::endl;
std::cout << "\t" << cmd << " skiphash-copy" << std::endl;
std::cout << "\t\t" "Copy a file to another file, using a hashes to copy only changed content" << std::endl;
std::cout << std::endl;
}
int main(int argc, char* argv[])
{
if(argc==0)
{
std::cout << "Not enough arguments (zero arguments) -- no program name" << std::endl;
return 1;
}
std::vector<std::string> actions;
std::vector<action_fun> action_funs;
actions.push_back("run");
action_funs.push_back(action_run);
actions.push_back("verify-hashes");
action_funs.push_back(action_verify_hashes);
actions.push_back("remove-unknown");
action_funs.push_back(action_remove_unknown);
actions.push_back("reset-admin-pw");
action_funs.push_back(action_reset_admin_pw);
actions.push_back("cleanup");
action_funs.push_back(action_cleanup);
actions.push_back("repair-database");
action_funs.push_back(action_repair_database);
actions.push_back("defrag-database");
action_funs.push_back(action_defrag_database);
actions.push_back("export-auth-log");
action_funs.push_back(action_export_auth_log);
actions.push_back("decompress-file");
action_funs.push_back(action_decompress_file);
#if !defined(_WIN32) && defined(WITH_FUSEPLUGIN)
actions.push_back("mount-vhd");
action_funs.push_back(action_mount_vhd);
#endif
actions.push_back("assemble");
action_funs.push_back(action_assemble);
actions.push_back("internal");
action_funs.push_back(action_internal);
actions.push_back("blockalign");
action_funs.push_back(action_blockalign);
actions.push_back("skiphash-copy");
action_funs.push_back(action_skiphash_copy);
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;
}
}