mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
1354 lines
35 KiB
C++
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;
|
|
}
|
|
}
|