// Copyright © 2011-18 Richard Kettlewell. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include "Command.h" #include "Defaults.h" #include "Errors.h" #include "Conf.h" #include "IO.h" #include "Utils.h" #include #include // Long-only options enum { RETIRE_DEVICE = 256, RETIRE = 257, WARN_UNKNOWN = 258, WARN_STORE = 259, WARN_UNREACHABLE = 261, WARN_PARTIAL = 262, REPEAT_ERRORS = 263, NO_REPEAT_ERRORS = 264, NO_WARN_PARTIAL = 265, LOG_VERBOSITY = 266, DUMP_CONFIG = 267, FORGET_ONLY = 268, UNMOUNTED_STORE = 269, }; const struct option Command::options[] = { { "help", no_argument, nullptr, 'h' }, { "version", no_argument, nullptr, 'V' }, { "backup", no_argument, nullptr, 'b' }, { "html", required_argument, nullptr, 'H' }, { "text", required_argument, nullptr, 'T' }, { "email", required_argument, nullptr, 'e' }, { "prune", no_argument, nullptr, 'p' }, { "prune-incomplete", no_argument, nullptr, 'P' }, { "store", required_argument, nullptr, 's' }, { "unmounted-store", required_argument, nullptr, UNMOUNTED_STORE }, { "retire-device", no_argument, nullptr, RETIRE_DEVICE }, { "retire", no_argument, nullptr, RETIRE }, { "config", required_argument, nullptr, 'c' }, { "wait", no_argument, nullptr, 'w' }, { "force", no_argument, nullptr, 'f' }, { "dry-run", no_argument, nullptr, 'n' }, { "verbose", no_argument, nullptr, 'v' }, { "warn-unknown", no_argument, nullptr, WARN_UNKNOWN }, { "warn-store", no_argument, nullptr, WARN_STORE }, { "warn-unreachable", no_argument, nullptr, WARN_UNREACHABLE }, { "warn-partial", no_argument, nullptr, WARN_PARTIAL }, { "no-warn-partial", no_argument, nullptr, NO_WARN_PARTIAL }, { "errors", no_argument, nullptr, REPEAT_ERRORS }, { "no-errors", no_argument, nullptr, NO_REPEAT_ERRORS }, { "warn-all", no_argument, nullptr, 'W' }, { "debug", no_argument, nullptr, 'd' }, { "logs", required_argument, nullptr, LOG_VERBOSITY }, { "dump-config", no_argument, nullptr, DUMP_CONFIG }, { "database", required_argument, nullptr, 'D' }, { "forget-only", no_argument, nullptr, FORGET_ONLY }, { nullptr, 0, nullptr, 0 } }; void Command::help() { IO::out.writef(helpString()); IO::out.close(); exit(0); } const char *Command::helpString() { return "Usage:\n" " rsbackup [OPTIONS] [--] [[-]HOST...] [[-]HOST:VOLUME...]\n" " rsbackup --retire [OPTIONS] [--] [HOST...] [HOST:VOLUME...]\n" " rsbackup --retire-device [OPTIONS] [--] DEVICES...\n" "\n" "At least one action option is required:\n" " --backup, -b Back up selected volumes (default: all)\n" " --html, -H PATH Write an HTML report to PATH\n" " --text, -T PATH Write a text report to PATH\n" " --email, -e ADDRESS Mail HTML report to ADDRESS\n" " --prune, -p Prune old backups of selected volumes (default: all)\n" " --prune-incomplete, -P Prune incomplete backups\n" " --retire Retire volumes (must specify at least one)\n" " --forget-only Retire from database but not disk (with --retire)\n" " --retire-device Retire devices (must specify at least one)\n" " --dump-config Dump parsed configuration\n" "\n" "Additional options:\n" " --logs all|errors|recent|latest|failed Log verbosity in report\n" " --store, -s DIR Override directory(s) to store backups in\n" " --unmounted-store DIR Override directory(s) to store backups in\n" " --config, -c PATH Set config file (default: /etc/rsbackup/config)\n" " --wait, -w Wait until running rsbackup finishes\n" " --force, -f Don't prompt when retiring\n" " --dry-run, -n Dry run only\n" " --verbose, -v Verbose output\n" " --debug, -d Debug output\n" " --database, -D PATH Override database path\n" " --help, -h Display usage message\n" " --version, -V Display version number\n" "\n" "Warning options:\n" " --warn-unknown Warn about unknown devices/volumes\n" " --warn-store Warn about bad stores/unavailable devices\n" " --warn-unreachable Warn about unreachable hosts\n" " --warn-partial Warn about partial transfers (default)\n" " --no-warn-partial Suppress warnings about partial transfers\n" " --warn-all, -W Enable all warnings\n" " --errors Display rsync errors (default)\n" " --no-errors Don't display rsync errors\n" ; } void Command::version() { IO::out.writef("%s\n", VERSION); IO::out.close(); exit(0); } void Command::parse(int argc, const char *const *argv) { int n; // Override debug if(getenv("RSBACKUP_DEBUG")) debug = true; // Parse options optind = 1; while((n = getopt_long(argc, (char *const *)argv, "+hVbH:T:e:pPs:c:wnfvdWD:", options, nullptr)) >= 0) { switch(n) { case 'h': help(); case 'V': version(); case 'b': backup = true; break; case 'H': html = new std::string(optarg); break; case 'T': text = new std::string(optarg); break; case 'e': email = new std::string(optarg); break; case 'p': prune = true; break; case 'P': pruneIncomplete = true; break; case 's': stores.push_back(optarg); enable_warning(WARNING_STORE); break; case UNMOUNTED_STORE: unmountedStores.push_back(optarg); enable_warning(WARNING_STORE); break; case 'c': configPath = optarg; break; case 'w': wait = true; break; case 'n': act = false; enable_warning(WARNING_VERBOSE); break; case 'f': force = true; break; case 'v': enable_warning(WARNING_VERBOSE); break; case 'd': debug = true; break; case 'D': database = optarg; break; case RETIRE_DEVICE: retireDevice = true; break; case RETIRE: retire = true; break; case WARN_UNKNOWN: enable_warning(WARNING_UNKNOWN); break; case WARN_STORE: enable_warning(WARNING_STORE); break; case WARN_UNREACHABLE: enable_warning(WARNING_UNREACHABLE); break; case WARN_PARTIAL: enable_warning(WARNING_PARTIAL); break; case NO_WARN_PARTIAL: disable_warning(WARNING_PARTIAL); break; case REPEAT_ERRORS: enable_warning(WARNING_ERRORLOGS); break; case NO_REPEAT_ERRORS: disable_warning(WARNING_ERRORLOGS); break; case LOG_VERBOSITY: logVerbosity = getVerbosity(optarg); break; case 'W': enable_warning(static_cast(-1)); break; case DUMP_CONFIG: dumpConfig = true; break; case FORGET_ONLY: forgetOnly = true; break; default: exit(1); } } // Various options are incompatible with one another if(retire && retireDevice) throw CommandError("--retire and --retire-device cannot be used together"); if(backup && retire) throw CommandError("--retire and --backup cannot be used together"); if(backup && retireDevice) throw CommandError("--retire-device and --backup cannot be used together"); if(forgetOnly && !retire) throw CommandError("--forget-only may only be used with --retire"); if(dumpConfig && (backup || html || text || email || prune || pruneIncomplete || retireDevice || retire)) throw CommandError("--dump-config cannot be used with any other action"); // We have to do *something* if(!backup && !html && !text && !email && !prune && !pruneIncomplete && !retireDevice && !retire && !dumpConfig) throw CommandError("no action specified"); if(backup || prune || pruneIncomplete || retire) { // Volumes to back up, prune or retire if(optind < argc) { for(n = optind; n < argc; ++n) selections.add(argv[n]); } else { if(retire) throw CommandError("no volumes specified to retire"); } } if(retireDevice) { if(optind >= argc) throw CommandError("no devices specified to retire"); for(n = optind; n < argc; ++n) devices.push_back(argv[n]); } if(dumpConfig) { if(optind < argc) throw CommandError("no arguments allowed to --dump-config"); } } Command::LogVerbosity Command::getVerbosity(const std::string &v) { if(v == "all") return All; if(v == "errors") return Errors; if(v == "recent") return Recent; if(v == "latest") return Latest; if(v == "failed") return Failed; throw CommandError("invalid argument to --logs: " + v); } Command::~Command() { delete html; delete text; delete email; } Command command; std::string configPath = DEFAULT_CONFIG; std::string database;