# Copyright 2002 Ben Escoto # # This file is part of duplicity. # # Duplicity 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. # # Duplicity 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 duplicity; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Parse command line, check for consistency, and set globals""" import getopt, re, sys, os import backends, globals, log, path, selection, gpg, dup_time select_opts = [] # Will hold all the selection options select_files = [] # Will hold file objects when filelist given full_backup = None # Will be set to true if -f or --full option given list_current = None # Will be set to true if --list-current option given collection_status = None # Will be set to true if --collection-status given cleanup = None # Set to true if --cleanup option given verify = None # Set to true if --verify option given commands = ["cleanup", "collection-status", "full", "incremental", "list-current-files", "remove-older-than", "remove-all-but-n-full", "verify", ] options = ["allow-source-mismatch", "archive-dir=", "current-time=", "encrypt-key=", "exclude=", "exclude-device-files", "exclude-filelist=", "exclude-globbing-filelist=", "exclude-filelist-stdin", "exclude-other-filesystems", "exclude-regexp=", "file-to-restore=", "force", "ftp-passive", "ftp-regular", "full-if-older-than=", "gpg-options=", "help", "include=", "include-filelist=", "include-filelist-stdin", "include-globbing-filelist=", "include-regexp=", "no-encryption", "no-print-statistics", "null-separator", "num-retries=", "restore-dir=", "restore-time=", "scp-command=", "sftp-command=", "short-filenames", "sign-key=", "ssh-askpass", "ssh-options=", "timeout=", "time-separator=", "verbosity=", "version", "volsize=", ] def parse_cmdline_options(arglist): """Parse argument list""" global select_opts, select_files, full_backup global list_current, collection_status, cleanup, remove_time, verify def sel_fl(filename): """Helper function for including/excluding filelists below""" try: return open(filename, "r") except IOError: log.FatalError("Error opening file %s" % filename) # expect no cmd and two positional args cmd = "" num_expect = 2 # process first arg as command if arglist and arglist[0][0] != '-': cmd = arglist.pop(0) possible = [c for c in commands if c.startswith(cmd)] # no unique match, that's an error if len(possible) > 1: log.FatalError("command '%s' not unique, could be %s" % (cmd, possible)) # only one match, that's a keeper elif len(possible) == 1: cmd = possible[0] # no matches, assume no cmd elif not possible: arglist.insert(0, cmd) if cmd == "cleanup": cleanup = True num_expect = 1 elif cmd == "collection-status": collection_status = True num_expect = 1 elif cmd == "full": full_backup = True num_expect = 2 elif cmd == "incremental": globals.incremental = True num_expect = 2 elif cmd == "list-current-files": list_current = True num_expect = 1 elif cmd == "remove-older-than": try: arg = arglist.pop(0) except: command_line_error("Missing time string for remove-older-than") globals.remove_time = dup_time.genstrtotime(arg) num_expect = 1 elif cmd == "remove-all-but-n-full": try: arg = arglist.pop(0) except: command_line_error("Missing count for remove-all-but-n-full") globals.keep_chains = int(arg) if not globals.keep_chains > 0: command_line_error("remove-all-but-n-full count must be > 0") num_expect = 1 elif cmd == "verify": verify = True num_expect = 2 # parse the remaining args try: optlist, args = getopt.gnu_getopt(arglist, "rt:v:V", options) except getopt.error, e: command_line_error("%s" % (str(e),)) for opt, arg in optlist: if opt == "--allow-source-mismatch": globals.allow_source_mismatch = 1 elif opt == "--archive-dir": set_archive_dir(arg) elif opt == "--current-time": dup_time.setcurtime(get_int(arg, "current-time")) elif opt == "--encrypt-key": globals.gpg_profile.recipients.append(arg) elif (opt == "--exclude" or opt == "--exclude-regexp" or opt == "--include" or opt == "--include-regexp"): select_opts.append((opt, arg)) elif (opt == "--exclude-device-files" or opt == "--exclude-other-filesystems"): select_opts.append((opt, None)) elif (opt == "--exclude-filelist" or opt == "--include-filelist" or opt == "--exclude-globbing-filelist" or opt == "--include-globbing-filelist"): select_opts.append((opt, arg)) select_files.append(sel_fl(arg)) elif opt == "--exclude-filelist-stdin": select_opts.append(("--exclude-filelist", "standard input")) select_files.append(sys.stdin) elif opt == "--full-if-older-than": globals.full_force_time = dup_time.genstrtotime(arg) elif opt == "--force": globals.force = 1 elif opt == "--ftp-passive": globals.ftp_connection = 'passive' elif opt == "--ftp-regular": globals.ftp_connection = 'regular' elif opt == "--gpg-options": gpg.gpg_options = (gpg.gpg_options + ' ' + arg).strip() elif opt == "--help": usage(); sys.exit(1); elif opt == "--include-filelist-stdin": select_opts.append(("--include-filelist", "standard input")) select_files.append(sys.stdin) elif opt == "--no-encryption": globals.encryption = 0 elif opt == "--no-print-statistics": globals.print_statistics = 0 elif opt == "--null-separator": globals.null_separator = 1 elif opt == "--num-retries": globals.num_retries = int(arg) elif (opt == "-r" or opt == "--file-to-restore"): globals.restore_dir = arg elif (opt == "-t" or opt == "--restore-time"): globals.restore_time = dup_time.genstrtotime(arg) elif opt == "--scp-command": backends.scp_command = arg elif opt == "--sftp-command": backends.sftp_command = arg elif opt == "--short-filenames": globals.short_filenames = 1 elif opt == "--sign-key": set_sign_key(arg) elif opt == "--ssh-askpass": backends.ssh_askpass = True elif opt == "--ssh-options": backends.ssh_options = (backends.ssh_options + ' ' + arg).strip() elif opt == "--timeout": globals.timeout = int(arg) elif opt == "--time-separator": if arg == '-': command_line_error("Dash ('-') not valid for time-separator.") globals.time_separator = arg dup_time.curtimestr = dup_time.timetostring(dup_time.curtime) elif opt == "-V" or opt == "--version": print "duplicity", str(globals.version) sys.exit(0) elif (opt == "-v" or opt == "--verbosity"): log.setverbosity(int(arg)) elif opt == "--volsize": globals.volsize = int(arg)*1024*1024 else: command_line_error("Unknown option %s" % opt) if len(args) != num_expect: command_line_error("Expected %d args, got %d" % (num_expect, len(args))) return args def command_line_error(message): """Indicate a command line error and exit""" sys.stderr.write("Command line error: %s\n" % (message,)) sys.stderr.write("Enter 'duplicity --help' for help screen.\n") sys.exit(1) def usage(): """Print terse usage info""" sys.stdout.write(""" duplicity version %s running on %s. Usage: duplicity [full|incremental] [options] source_dir target_url duplicity [restore] [options] source_url target_dir duplicity verify [options] source_url target_dir duplicity collection-status [options] target_url duplicity list-current-files [options] target_url duplicity cleanup [options] target_url duplicity remove-older-than time [options] target_url duplicity remove-all-but-n-full count [options] target_url Backends and their URL formats: ssh://user@other.host:port/some_dir scp://user@other.host:port/some_dir ftp://user@other.host/some_dir hsi://user@other.host/some_dir file:///some_dir rsync://user@host:/module/some_dir rsync://user@host/some_non_module_path s3://host/bucket_name[/prefix] s3+http://bucket_name[/prefix] webdav://user@other.host/some_dir webdavs://user@other.host/some_dir Commands: cleanup collection-status full incr list-current-files restore remove-older-than