1# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4; encoding:utf8 -*- 2# 3# Copyright 2002 Ben Escoto <ben@emerose.org> 4# Copyright 2007 Kenneth Loafman <kenneth@loafman.com> 5# 6# This file is part of duplicity. 7# 8# Duplicity is free software; you can redistribute it and/or modify it 9# under the terms of the GNU General Public License as published by the 10# Free Software Foundation; either version 2 of the License, or (at your 11# option) any later version. 12# 13# Duplicity is distributed in the hope that it will be useful, but 14# WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16# General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with duplicity; if not, write to the Free Software Foundation, 20# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 22u"""Parse command line, check for consistency, and set config""" 23 24from __future__ import print_function 25from future import standard_library 26standard_library.install_aliases() 27from builtins import str 28from builtins import range 29 30from copy import copy 31import optparse 32import os 33import re 34import sys 35import socket 36import io 37 38try: 39 from hashlib import md5 40except ImportError: 41 from md5 import new as md5 42 43from duplicity import backend 44from duplicity import dup_time 45from duplicity import config 46from duplicity import gpg 47from duplicity import log 48from duplicity import path 49from duplicity import selection 50from duplicity import util 51 52 53select_opts = [] # Will hold all the selection options 54select_files = [] # Will hold file objects when filelist given 55 56full_backup = None # Will be set to true if full command given 57list_current = None # Will be set to true if list-current command given 58collection_status = None # Will be set to true if collection-status command given 59cleanup = None # Set to true if cleanup command given 60verify = None # Set to true if verify command given 61replicate = None # Set to true if replicate command given 62 63commands = [u"cleanup", 64 u"collection-status", 65 u"full", 66 u"incremental", 67 u"list-current-files", 68 u"remove-older-than", 69 u"remove-all-but-n-full", 70 u"remove-all-inc-of-but-n-full", 71 u"restore", 72 u"verify", 73 u"replicate" 74 ] 75 76 77def old_fn_deprecation(opt): 78 log.Log(_(u"Warning: Option %s is pending deprecation " 79 u"and will be removed in version 0.9.0.\n" 80 u"Use of default filenames is strongly suggested.") % opt, 81 log.ERROR, force_print=True) 82 83 84def old_globbing_filelist_deprecation(opt): 85 log.Log(_(u"Warning: Option %s is pending deprecation and will be removed in a future release.\n" 86 u"--include-filelist and --exclude-filelist now accept globbing characters and should " 87 u"be used instead.") % opt, 88 log.ERROR, force_print=True) 89 90 91def stdin_deprecation(opt): 92 # See https://bugs.launchpad.net/duplicity/+bug/1423367 93 # In almost all Linux distros stdin is a file represented by /dev/stdin, 94 # so --exclude-file=/dev/stdin will work as a substitute. 95 log.Log(_(u"Warning: Option %s is pending deprecation and will be removed in a future release.\n" 96 u"On many GNU/Linux systems, stdin is represented by /dev/stdin and\n" 97 u"--include-filelist=/dev/stdin or --exclude-filelist=/dev/stdin could\n" 98 u"be used as a substitute.") % opt, 99 log.ERROR, force_print=True) 100 101 102# log options handled in log.py. Add noop to make optparse happy 103def noop(): 104 pass 105 106 107def expand_fn(filename): 108 return os.path.expanduser(os.path.expandvars(filename)) 109 110 111def expand_archive_dir(archdir, backname): 112 u""" 113 Return expanded version of archdir joined with backname. 114 """ 115 assert config.backup_name is not None, \ 116 u"expand_archive_dir() called prior to config.backup_name being set" 117 118 return expand_fn(os.path.join(archdir, backname)) 119 120 121def generate_default_backup_name(backend_url): 122 u""" 123 @param backend_url: URL to backend. 124 @returns A default backup name (string). 125 """ 126 # For default, we hash args to obtain a reasonably safe default. 127 # We could be smarter and resolve things like relative paths, but 128 # this should actually be a pretty good compromise. Normally only 129 # the destination will matter since you typically only restart 130 # backups of the same thing to a given destination. The inclusion 131 # of the source however, does protect against most changes of 132 # source directory (for whatever reason, such as 133 # /path/to/different/snapshot). If the user happens to have a case 134 # where relative paths are used yet the relative path is the same 135 # (but duplicity is run from a different directory or similar), 136 # then it is simply up to the user to set --archive-dir properly. 137 burlhash = md5() 138 burlhash.update(backend_url.encode()) 139 return burlhash.hexdigest() 140 141 142def check_file(option, opt, value): # pylint: disable=unused-argument 143 return expand_fn(value) 144 145 146def check_time(option, opt, value): # pylint: disable=unused-argument 147 try: 148 return dup_time.genstrtotime(value) 149 except dup_time.TimeException as e: 150 raise optparse.OptionValueError(str(e)) 151 152 153def check_verbosity(option, opt, value): # pylint: disable=unused-argument 154 fail = False 155 156 value = value.lower() 157 if value in [u'e', u'error']: 158 verb = log.ERROR 159 elif value in [u'w', u'warning']: 160 verb = log.WARNING 161 elif value in [u'n', u'notice']: 162 verb = log.NOTICE 163 elif value in [u'i', u'info']: 164 verb = log.INFO 165 elif value in [u'd', u'debug']: 166 verb = log.DEBUG 167 else: 168 try: 169 verb = int(value) 170 if verb < 0 or verb > 9: 171 fail = True 172 except ValueError: 173 fail = True 174 175 if fail: 176 # TRANSL: In this portion of the usage instructions, "[ewnid]" indicates which 177 # characters are permitted (e, w, n, i, or d); the brackets imply their own 178 # meaning in regex; i.e., only one of the characters is allowed in an instance. 179 raise optparse.OptionValueError(u"Verbosity must be one of: digit [0-9], character [ewnid], " 180 u"or word ['error', 'warning', 'notice', 'info', 'debug']. " 181 u"The default is 4 (Notice). It is strongly recommended " 182 u"that verbosity level is set at 2 (Warning) or higher.") 183 184 return verb 185 186 187class DupOption(optparse.Option): 188 TYPES = optparse.Option.TYPES + (u"file", u"time", u"verbosity",) 189 TYPE_CHECKER = copy(optparse.Option.TYPE_CHECKER) 190 TYPE_CHECKER[u"file"] = check_file 191 TYPE_CHECKER[u"time"] = check_time 192 TYPE_CHECKER[u"verbosity"] = check_verbosity 193 194 ACTIONS = optparse.Option.ACTIONS + (u"extend",) 195 STORE_ACTIONS = optparse.Option.STORE_ACTIONS + (u"extend",) 196 TYPED_ACTIONS = optparse.Option.TYPED_ACTIONS + (u"extend",) 197 ALWAYS_TYPED_ACTIONS = optparse.Option.ALWAYS_TYPED_ACTIONS + (u"extend",) 198 199 def take_action(self, action, dest, opt, value, values, parser): 200 if action == u"extend": 201 if not value: 202 return 203 if hasattr(values, dest) and getattr(values, dest): 204 setattr(values, dest, getattr(values, dest) + u' ' + value) 205 else: 206 setattr(values, dest, value) 207 else: 208 optparse.Option.take_action( 209 self, action, dest, opt, value, values, parser) 210 211 212def parse_cmdline_options(arglist): 213 u"""Parse argument list""" 214 global select_opts, select_files, full_backup 215 global list_current, collection_status, cleanup, remove_time, verify, replicate 216 217 def set_log_fd(fd): 218 if fd < 1: 219 raise optparse.OptionValueError(u"log-fd must be greater than zero.") 220 log.add_fd(fd) 221 222 def set_time_sep(sep, opt): 223 if sep == u'-': 224 raise optparse.OptionValueError(u"Dash ('-') not valid for time-separator.") 225 config.time_separator = sep 226 old_fn_deprecation(opt) 227 228 def add_selection(o, option, additional_arg, p): # pylint: disable=unused-argument 229 if o.type in (u"string", u"file"): 230 addarg = util.fsdecode(additional_arg) 231 else: 232 addarg = additional_arg 233 select_opts.append((util.fsdecode(option), addarg)) 234 235 def add_filelist(o, s, filename, p): # pylint: disable=unused-argument 236 select_opts.append((util.fsdecode(s), util.fsdecode(filename))) 237 try: 238 select_files.append(io.open(filename, u"rt", encoding=u"UTF-8")) 239 except IOError: 240 log.FatalError(_(u"Error opening file %s") % filename, 241 log.ErrorCode.cant_open_filelist) 242 243 def print_ver(o, s, v, p): # pylint: disable=unused-argument 244 print(u"duplicity %s" % (config.version)) 245 sys.exit(0) 246 247 def add_rename(o, s, v, p): # pylint: disable=unused-argument 248 key = util.fsencode(os.path.normcase(os.path.normpath(v[0]))) 249 config.rename[key] = util.fsencode(v[1]) 250 251 parser = optparse.OptionParser(option_class=DupOption, usage=usage()) 252 253 # If this is true, only warn and don't raise fatal error when backup 254 # source directory doesn't match previous backup source directory. 255 parser.add_option(u"--allow-source-mismatch", action=u"store_true") 256 257 # Set to the path of the archive directory (the directory which 258 # contains the signatures and manifests of the relevent backup 259 # collection), and for checkpoint state between volumes. 260 # TRANSL: Used in usage help to represent a Unix-style path name. Example: 261 # --archive-dir <path> 262 parser.add_option(u"--archive-dir", type=u"file", metavar=_(u"path")) 263 264 # Asynchronous put/get concurrency limit 265 # (default of 0 disables asynchronicity). 266 parser.add_option(u"--asynchronous-upload", action=u"store_const", const=1, 267 dest=u"async_concurrency") 268 269 parser.add_option(u"--compare-data", action=u"store_true") 270 271 # config dir for future use 272 parser.add_option(u"--config-dir", type=u"file", metavar=_(u"path"), 273 help=optparse.SUPPRESS_HELP) 274 275 # When symlinks are encountered, the item they point to is copied rather than 276 # the symlink. 277 parser.add_option(u"--copy-links", action=u"store_true") 278 279 # for testing -- set current time 280 parser.add_option(u"--current-time", type=u"int", 281 dest=u"current_time", help=optparse.SUPPRESS_HELP) 282 283 # Don't actually do anything, but still report what would be done 284 parser.add_option(u"--dry-run", action=u"store_true") 285 286 # TRANSL: Used in usage help to represent an ID for a GnuPG key. Example: 287 # --encrypt-key <gpg_key_id> 288 parser.add_option(u"--encrypt-key", type=u"string", metavar=_(u"gpg-key-id"), 289 dest=u"", action=u"callback", 290 callback=lambda o, s, v, p: config.gpg_profile.recipients.append(v)) 291 292 # secret keyring in which the private encrypt key can be found 293 parser.add_option(u"--encrypt-secret-keyring", type=u"string", metavar=_(u"path")) 294 295 parser.add_option(u"--encrypt-sign-key", type=u"string", metavar=_(u"gpg-key-id"), 296 dest=u"", action=u"callback", 297 callback=lambda o, s, v, p: (config.gpg_profile.recipients.append(v), set_sign_key(v))) 298 299 # TRANSL: Used in usage help to represent a "glob" style pattern for 300 # matching one or more files, as described in the documentation. 301 # Example: 302 # --exclude <shell_pattern> 303 parser.add_option(u"--exclude", action=u"callback", metavar=_(u"shell_pattern"), 304 dest=u"", type=u"string", callback=add_selection) 305 306 parser.add_option(u"--exclude-device-files", action=u"callback", 307 dest=u"", callback=add_selection) 308 309 parser.add_option(u"--exclude-filelist", type=u"file", metavar=_(u"filename"), 310 dest=u"", action=u"callback", callback=add_filelist) 311 312 parser.add_option(u"--exclude-filelist-stdin", action=u"callback", dest=u"", 313 callback=lambda o, s, v, p: (select_opts.append((u"--exclude-filelist", u"standard input")), 314 select_files.append(sys.stdin), 315 stdin_deprecation(o)), 316 help=optparse.SUPPRESS_HELP) 317 318 parser.add_option(u"--exclude-globbing-filelist", type=u"file", metavar=_(u"filename"), 319 dest=u"", action=u"callback", callback=lambda o, s, v, p: (add_filelist(o, s, v, p), 320 old_globbing_filelist_deprecation(s)), 321 help=optparse.SUPPRESS_HELP) 322 323 # TRANSL: Used in usage help to represent the name of a file. Example: 324 # --log-file <filename> 325 parser.add_option(u"--exclude-if-present", metavar=_(u"filename"), dest=u"", 326 type=u"file", action=u"callback", callback=add_selection) 327 328 parser.add_option(u"--exclude-other-filesystems", action=u"callback", 329 dest=u"", callback=add_selection) 330 331 # TRANSL: Used in usage help to represent a regular expression (regexp). 332 parser.add_option(u"--exclude-regexp", metavar=_(u"regular_expression"), 333 dest=u"", type=u"string", action=u"callback", callback=add_selection) 334 335 # Exclude any files with modification dates older than this from the backup 336 parser.add_option(u"--exclude-older-than", type=u"time", metavar=_(u"time"), 337 dest=u"", action=u"callback", callback=add_selection) 338 339 # used in testing only - raises exception after volume 340 parser.add_option(u"--fail-on-volume", type=u"int", 341 help=optparse.SUPPRESS_HELP) 342 343 # used to provide a prefix on top of the defaul tar file name 344 parser.add_option(u"--file-prefix", type=u"string", dest=u"file_prefix", action=u"store") 345 346 # used to provide a suffix for manifest files only 347 parser.add_option(u"--file-prefix-manifest", type=u"string", dest=u"file_prefix_manifest", action=u"store") 348 349 # used to provide a suffix for archive files only 350 parser.add_option(u"--file-prefix-archive", type=u"string", dest=u"file_prefix_archive", action=u"store") 351 352 # used to provide a suffix for sigature files only 353 parser.add_option(u"--file-prefix-signature", type=u"string", dest=u"file_prefix_signature", action=u"store") 354 355 # used in testing only - skips upload for a given volume 356 parser.add_option(u"--skip-volume", type=u"int", 357 help=optparse.SUPPRESS_HELP) 358 359 # If set, restore only the subdirectory or file specified, not the 360 # whole root. 361 # TRANSL: Used in usage help to represent a Unix-style path name. Example: 362 # --archive-dir <path> 363 parser.add_option(u"--file-to-restore", u"-r", action=u"callback", type=u"file", 364 metavar=_(u"path"), dest=u"restore_dir", 365 callback=lambda o, s, v, p: setattr(p.values, u"restore_dir", util.fsencode(v.strip(u'/')))) 366 367 # Used to confirm certain destructive operations like deleting old files. 368 parser.add_option(u"--force", action=u"store_true") 369 370 # FTP data connection type 371 parser.add_option(u"--ftp-passive", action=u"store_const", const=u"passive", dest=u"ftp_connection") 372 parser.add_option(u"--ftp-regular", action=u"store_const", const=u"regular", dest=u"ftp_connection") 373 374 # If set, forces a full backup if the last full backup is older than 375 # the time specified 376 parser.add_option(u"--full-if-older-than", type=u"time", dest=u"full_force_time", metavar=_(u"time")) 377 378 parser.add_option(u"--gio", action=u"callback", dest=u"use_gio", 379 callback=lambda o, s, v, p: (setattr(p.values, o.dest, True), 380 old_fn_deprecation(s))) 381 382 parser.add_option(u"--gpg-binary", type=u"file", metavar=_(u"path")) 383 384 parser.add_option(u"--gpg-options", action=u"extend", metavar=_(u"options")) 385 386 # TRANSL: Used in usage help to represent an ID for a hidden GnuPG key. Example: 387 # --hidden-encrypt-key <gpg_key_id> 388 parser.add_option(u"--hidden-encrypt-key", type=u"string", metavar=_(u"gpg-key-id"), 389 dest=u"", action=u"callback", 390 callback=lambda o, s, v, p: config.gpg_profile.hidden_recipients.append(v)) 391 392 # Fake-root for iDrived backend 393 parser.add_option(u"--idr-fakeroot", dest=u"fakeroot", type=u"file", metavar=_(u"path")) 394 395 # ignore (some) errors during operations; supposed to make it more 396 # likely that you are able to restore data under problematic 397 # circumstances. the default should absolutely always be False unless 398 # you know what you are doing. 399 parser.add_option(u"--ignore-errors", action=u"callback", 400 dest=u"ignore_errors", 401 callback=lambda o, s, v, p: (log.Warn( 402 _(u"Running in 'ignore errors' mode due to %s; please " 403 u"re-consider if this was not intended") % s), 404 setattr(p.values, u"ignore_errors", True))) 405 406 # Whether to use the full email address as the user name when 407 # logging into an imap server. If false just the user name 408 # part of the email address is used. 409 parser.add_option(u"--imap-full-address", action=u"store_true") 410 411 # Name of the imap folder where we want to store backups. 412 # Can be changed with a command line argument. 413 # TRANSL: Used in usage help to represent an imap mailbox 414 parser.add_option(u"--imap-mailbox", metavar=_(u"imap_mailbox")) 415 416 parser.add_option(u"--include", action=u"callback", metavar=_(u"shell_pattern"), 417 dest=u"", type=u"string", callback=add_selection) 418 parser.add_option(u"--include-filelist", type=u"file", metavar=_(u"filename"), 419 dest=u"", action=u"callback", callback=add_filelist) 420 parser.add_option(u"--include-filelist-stdin", action=u"callback", dest=u"", 421 callback=lambda o, s, v, p: (select_opts.append((u"--include-filelist", u"standard input")), 422 select_files.append(sys.stdin), 423 stdin_deprecation(o)), 424 help=optparse.SUPPRESS_HELP) 425 parser.add_option(u"--include-globbing-filelist", type=u"file", metavar=_(u"filename"), 426 dest=u"", action=u"callback", callback=lambda o, s, v, p: (add_filelist(o, s, v, p), 427 old_globbing_filelist_deprecation(s)), 428 help=optparse.SUPPRESS_HELP) 429 parser.add_option(u"--include-regexp", metavar=_(u"regular_expression"), dest=u"", 430 type=u"string", action=u"callback", callback=add_selection) 431 432 parser.add_option(u"--log-fd", type=u"int", metavar=_(u"file_descriptor"), 433 dest=u"", action=u"callback", 434 callback=lambda o, s, v, p: set_log_fd(v)) 435 436 # TRANSL: Used in usage help to represent the name of a file. Example: 437 # --log-file <filename> 438 parser.add_option(u"--log-file", type=u"file", metavar=_(u"filename"), 439 dest=u"", action=u"callback", 440 callback=lambda o, s, v, p: log.add_file(v)) 441 442 # log option to add timestamp and level to log entries 443 parser.add_option(u"--log-timestamp", action=u"callback", 444 callback=lambda o, s, v, p: noop()) 445 446 # Maximum block size for large files 447 parser.add_option(u"--max-blocksize", type=u"int", metavar=_(u"number")) 448 449 # TRANSL: Used in usage help (noun) 450 parser.add_option(u"--name", dest=u"backup_name", metavar=_(u"backup name")) 451 452 # If set to false, then do not encrypt files on remote system 453 parser.add_option(u"--no-encryption", action=u"store_false", dest=u"encryption") 454 455 # If set to false, then do not compress files on remote system 456 parser.add_option(u"--no-compression", action=u"store_false", dest=u"compression") 457 458 # If set, print the statistics after every backup session 459 parser.add_option(u"--no-print-statistics", action=u"store_false", dest=u"print_statistics") 460 461 # If true, filelists and directory statistics will be split on 462 # nulls instead of newlines. 463 parser.add_option(u"--null-separator", action=u"store_true") 464 465 # number of retries on network operations 466 # TRANSL: Used in usage help to represent a desired number of 467 # something. Example: 468 # --num-retries <number> 469 parser.add_option(u"--num-retries", type=u"int", metavar=_(u"number")) 470 471 # File owner uid keeps number from tar file. Like same option in GNU tar. 472 parser.add_option(u"--numeric-owner", action=u"store_true") 473 474 # Do no restore the uid/gid when finished, useful if you're restoring 475 # data without having root privileges or Unix users support 476 parser.add_option(u"--do-not-restore-ownership", action=u"store_true") 477 478 # Whether the old filename format is in effect. 479 parser.add_option(u"--old-filenames", action=u"callback", 480 dest=u"old_filenames", 481 callback=lambda o, s, v, p: (setattr(p.values, o.dest, True), 482 old_fn_deprecation(s))) 483 484 # Sync only required metadata 485 parser.add_option(u"--metadata-sync-mode", 486 default=u"partial", 487 choices=(u"full", u"partial")) 488 489 # Level of Redundancy in % for Par2 files 490 parser.add_option(u"--par2-redundancy", type=u"int", metavar=_(u"number")) 491 492 # Verbatim par2 options 493 parser.add_option(u"--par2-options", action=u"extend", metavar=_(u"options")) 494 495 # Used to display the progress for the full and incremental backup operations 496 parser.add_option(u"--progress", action=u"store_true") 497 498 # Used to control the progress option update rate in seconds. Default: prompts each 3 seconds 499 parser.add_option(u"--progress-rate", type=u"int", metavar=_(u"number")) 500 501 # option to trigger Pydev debugger 502 parser.add_option(u"--pydevd", action=u"store_true") 503 504 # option to rename files during restore 505 parser.add_option(u"--rename", type=u"file", action=u"callback", nargs=2, 506 callback=add_rename) 507 508 # Restores will try to bring back the state as of the following time. 509 # If it is None, default to current time. 510 # TRANSL: Used in usage help to represent a time spec for a previous 511 # point in time, as described in the documentation. Example: 512 # duplicity remove-older-than time [options] target_url 513 parser.add_option(u"--restore-time", u"--time", u"-t", type=u"time", metavar=_(u"time")) 514 515 # user added rsync options 516 parser.add_option(u"--rsync-options", action=u"extend", metavar=_(u"options")) 517 518 # Whether to create European buckets (sorry, hard-coded to only 519 # support european for now). 520 parser.add_option(u"--s3-european-buckets", action=u"store_true") 521 522 # Whether to use S3 Reduced Redundancy Storage 523 parser.add_option(u"--s3-use-rrs", action=u"store_true") 524 525 # Whether to use S3 Infrequent Access Storage 526 parser.add_option(u"--s3-use-ia", action=u"store_true") 527 528 # Whether to use S3 Glacier Storage 529 parser.add_option(u"--s3-use-glacier", action=u"store_true") 530 531 # Whether to use S3 Glacier Deep Archive Storage 532 parser.add_option(u"--s3-use-deep-archive", action=u"store_true") 533 534 # Whether to use S3 One Zone Infrequent Access Storage 535 parser.add_option(u"--s3-use-onezone-ia", action=u"store_true") 536 537 # Whether to use "new-style" subdomain addressing for S3 buckets. Such 538 # use is not backwards-compatible with upper-case buckets, or buckets 539 # that are otherwise not expressable in a valid hostname. 540 parser.add_option(u"--s3-use-new-style", action=u"store_true") 541 542 # Whether to use plain HTTP (without SSL) to send data to S3 543 # See <https://bugs.launchpad.net/duplicity/+bug/433970>. 544 parser.add_option(u"--s3-unencrypted-connection", action=u"store_true") 545 546 # Chunk size used for S3 multipart uploads.The number of parallel uploads to 547 # S3 be given by chunk size / volume size. Use this to maximize the use of 548 # your bandwidth. Defaults to 25MB 549 parser.add_option(u"--s3-multipart-chunk-size", type=u"int", action=u"callback", metavar=_(u"number"), 550 callback=lambda o, s, v, p: setattr(p.values, u"s3_multipart_chunk_size", v * 1024 * 1024)) 551 552 # Number of processes to set the Processor Pool to when uploading multipart 553 # uploads to S3. Use this to control the maximum simultaneous uploads to S3. 554 parser.add_option(u"--s3-multipart-max-procs", type=u"int", metavar=_(u"number")) 555 556 # Number of seconds to wait for each part of a multipart upload to S3. Use this 557 # to prevent hangups when doing a multipart upload to S3. 558 parser.add_option(u"--s3-multipart-max-timeout", type=u"int", metavar=_(u"number")) 559 560 # Option to allow the s3/boto backend use the multiprocessing version. 561 parser.add_option(u"--s3-use-multiprocessing", action=u"store_true") 562 563 # Option to allow use of server side encryption in s3 564 parser.add_option(u"--s3-use-server-side-encryption", action=u"store_true", dest=u"s3_use_sse") 565 566 # Options to allow use of server side KMS encryption 567 parser.add_option(u"--s3-use-server-side-kms-encryption", action=u"store_true", dest=u"s3_use_sse_kms") 568 parser.add_option(u"--s3-kms-key-id", action=u"store", dest=u"s3_kms_key_id") 569 parser.add_option(u"--s3-kms-grant", action=u"store", dest=u"s3_kms_grant") 570 571 # Options for specifying region and endpoint of s3 572 parser.add_option(u"--s3-region-name", type=u"string", dest=u"s3_region_name", action=u"store") 573 parser.add_option(u"--s3-endpoint-url", type=u"string", dest=u"s3_endpoint_url", action=u"store") 574 575 # Option to specify a Swift container storage policy. 576 parser.add_option(u"--swift-storage-policy", type=u"string", metavar=_(u"policy")) 577 578 # Number of the largest supported upload size where the Azure library makes only one put call. 579 # This is used to upload a single block if the content length is known and is less than this value. 580 # The default is 67108864 (64MiB) 581 parser.add_option(u"--azure-max-single-put-size", type=u"int", metavar=_(u"number")) 582 583 # Number for the block size used by the Azure library to upload a blob if the length is unknown 584 # or is larger than the value set by --azure-max-single-put-size". 585 # The maximum block size the service supports is 100MiB. 586 # The default is 4 * 1024 * 1024 (4MiB) 587 parser.add_option(u"--azure-max-block-size", type=u"int", metavar=_(u"number")) 588 589 # The number for the maximum parallel connections to use when the blob size exceeds 64MB. 590 # max_connections (int) - Maximum number of parallel connections to use when the blob size exceeds 64MB. 591 parser.add_option(u"--azure-max-connections", type=u"int", metavar=_(u"number")) 592 593 # Standard storage tier used for storring backup files (Hot|Cool|Archive). 594 parser.add_option(u"--azure-blob-tier", type=u"string", metavar=_(u"Hot|Cool|Archive")) 595 596 # scp command to use (ssh pexpect backend) 597 parser.add_option(u"--scp-command", metavar=_(u"command")) 598 599 # sftp command to use (ssh pexpect backend) 600 parser.add_option(u"--sftp-command", metavar=_(u"command")) 601 602 # allow the user to switch cloudfiles backend 603 parser.add_option(u"--cf-backend", metavar=_(u"pyrax|cloudfiles")) 604 605 # Option that causes the B2 backend to hide files instead of deleting them 606 parser.add_option(u"--b2-hide-files", action=u"store_true") 607 608 # If set, use short (< 30 char) filenames for all the remote files. 609 parser.add_option(u"--short-filenames", action=u"callback", 610 dest=u"short_filenames", 611 callback=lambda o, s, v, p: (setattr(p.values, o.dest, True), 612 old_fn_deprecation(s))) 613 614 # TRANSL: Used in usage help to represent an ID for a GnuPG key. Example: 615 # --encrypt-key <gpg_key_id> 616 parser.add_option(u"--sign-key", type=u"string", metavar=_(u"gpg-key-id"), 617 dest=u"", action=u"callback", 618 callback=lambda o, s, v, p: set_sign_key(v)) 619 620 # default to batch mode using public-key encryption 621 parser.add_option(u"--ssh-askpass", action=u"store_true") 622 623 # user added ssh options 624 parser.add_option(u"--ssh-options", action=u"extend", metavar=_(u"options")) 625 626 # user added ssl options (used by webdav, lftp backend) 627 parser.add_option(u"--ssl-cacert-file", metavar=_(u"pem formatted bundle of certificate authorities")) 628 parser.add_option(u"--ssl-cacert-path", metavar=_(u"path to a folder with certificate authority files")) 629 parser.add_option(u"--ssl-no-check-certificate", action=u"store_true") 630 631 # Working directory for the tempfile module. Defaults to /tmp on most systems. 632 parser.add_option(u"--tempdir", dest=u"temproot", type=u"file", metavar=_(u"path")) 633 634 # network timeout value 635 # TRANSL: Used in usage help. Example: 636 # --timeout <seconds> 637 parser.add_option(u"--timeout", type=u"int", metavar=_(u"seconds")) 638 639 # Character used like the ":" in time strings like 640 # 2002-08-06T04:22:00-07:00. The colon isn't good for filenames on 641 # windows machines. 642 # TRANSL: abbreviation for "character" (noun) 643 parser.add_option(u"--time-separator", type=u"string", metavar=_(u"char"), 644 action=u"callback", 645 callback=lambda o, s, v, p: set_time_sep(v, s)) 646 647 # Whether to specify --use-agent in GnuPG options 648 parser.add_option(u"--use-agent", action=u"store_true") 649 650 parser.add_option(u"--verbosity", u"-v", type=u"verbosity", metavar=u"[0-9]", 651 dest=u"", action=u"callback", 652 callback=lambda o, s, v, p: log.setverbosity(v)) 653 654 parser.add_option(u"-V", u"--version", action=u"callback", callback=print_ver) 655 656 # option for mediafire to purge files on delete instead of sending to trash 657 parser.add_option(u"--mf-purge", action=u"store_true") 658 659 def set_mpsize(o, s, v, p): # pylint: disable=unused-argument 660 setattr(p.values, u"mp_segment_size", v * 1024 * 1024) 661 setattr(p.values, u"mp_set", True) 662 parser.add_option(u"--mp-segment-size", type=u"int", action=u"callback", metavar=_(u"number"), 663 callback=set_mpsize) 664 # volume size 665 # TRANSL: Used in usage help to represent a desired number of 666 # something. Example: 667 # --num-retries <number> 668 669 def set_volsize(o, s, v, p): # pylint: disable=unused-argument 670 setattr(p.values, u"volsize", v * 1024 * 1024) 671 # if mp_size was not explicity given, default it to volsize 672 if not getattr(p.values, u'mp_set', False): 673 setattr(p.values, u"mp_segment_size", int(config.mp_factor * p.values.volsize)) 674 675 parser.add_option(u"--volsize", type=u"int", action=u"callback", metavar=_(u"number"), 676 callback=set_volsize) 677 678 # If set, collect only the file status, not the whole root. 679 parser.add_option(u"--file-changed", action=u"callback", type=u"file", 680 metavar=_(u"path"), dest=u"file_changed", 681 callback=lambda o, s, v, p: setattr(p.values, u"file_changed", v.rstrip(u'/'))) 682 683 # delay time before next try after a failure of a backend operation 684 # TRANSL: Used in usage help. Example: 685 # --backend-retry-delay <seconds> 686 parser.add_option(u"--backend-retry-delay", type=u"int", metavar=_(u"seconds")) 687 688 # parse the options 689 (options, args) = parser.parse_args(arglist) 690 691 # Copy all arguments and their values to the config module. Don't copy 692 # attributes that are 'hidden' (start with an underscore) or whose name is 693 # the empty string (used for arguments that don't directly store a value 694 # by using dest="") 695 for f in [x for x in dir(options) if x and not x.startswith(u"_")]: 696 v = getattr(options, f) 697 # Only set if v is not None because None is the default for all the 698 # variables. If user didn't set it, we'll use defaults in config.py 699 if v is not None: 700 setattr(config, f, v) 701 702 # convert file_prefix* string 703 if sys.version_info.major >= 3: 704 if isinstance(config.file_prefix, str): 705 config.file_prefix = bytes(config.file_prefix, u'utf-8') 706 if isinstance(config.file_prefix_manifest, str): 707 config.file_prefix_manifest = bytes(config.file_prefix_manifest, u'utf-8') 708 if isinstance(config.file_prefix_archive, str): 709 config.file_prefix_archive = bytes(config.file_prefix_archive, u'utf-8') 710 if isinstance(config.file_prefix_signature, str): 711 config.file_prefix_signature = bytes(config.file_prefix_signature, u'utf-8') 712 713 # todo: this should really NOT be done here 714 socket.setdefaulttimeout(config.timeout) 715 716 # expect no cmd and two positional args 717 cmd = u"" 718 num_expect = 2 719 720 # process first arg as command 721 if args: 722 cmd = args.pop(0) 723 possible = [c for c in commands if c.startswith(cmd)] 724 # no unique match, that's an error 725 if len(possible) > 1: 726 command_line_error(u"command '%s' not unique, could be %s" % (cmd, possible)) 727 # only one match, that's a keeper 728 elif len(possible) == 1: 729 cmd = possible[0] 730 # no matches, assume no cmd 731 elif not possible: 732 args.insert(0, cmd) 733 734 if cmd == u"cleanup": 735 cleanup = True 736 num_expect = 1 737 elif cmd == u"collection-status": 738 collection_status = True 739 num_expect = 1 740 elif cmd == u"full": 741 full_backup = True 742 num_expect = 2 743 elif cmd == u"incremental": 744 config.incremental = True 745 num_expect = 2 746 elif cmd == u"list-current-files": 747 list_current = True 748 num_expect = 1 749 elif cmd == u"remove-older-than": 750 try: 751 arg = args.pop(0) 752 except Exception: 753 command_line_error(u"Missing time string for remove-older-than") 754 config.remove_time = dup_time.genstrtotime(arg) 755 num_expect = 1 756 elif cmd == u"remove-all-but-n-full" or cmd == u"remove-all-inc-of-but-n-full": 757 if cmd == u"remove-all-but-n-full": 758 config.remove_all_but_n_full_mode = True 759 if cmd == u"remove-all-inc-of-but-n-full": 760 config.remove_all_inc_of_but_n_full_mode = True 761 try: 762 arg = args.pop(0) 763 except Exception: 764 command_line_error(u"Missing count for " + cmd) 765 config.keep_chains = int(arg) 766 if not config.keep_chains > 0: 767 command_line_error(cmd + u" count must be > 0") 768 num_expect = 1 769 elif cmd == u"verify": 770 verify = True 771 elif cmd == u"replicate": 772 replicate = True 773 num_expect = 2 774 775 if len(args) != num_expect: 776 command_line_error(u"Expected %d args, got %d" % (num_expect, len(args))) 777 778 # expand pathname args, but not URL 779 for loc in range(len(args)): 780 if isinstance(args[loc], bytes): 781 args[loc] = args[loc].decode(u'utf8') 782 if u'://' not in args[loc]: 783 args[loc] = expand_fn(args[loc]) 784 785 # Note that ProcessCommandLine depends on us verifying the arg 786 # count here; do not remove without fixing it. We must make the 787 # checks here in order to make enough sense of args to identify 788 # the backend URL/lpath for args_to_path_backend(). 789 if len(args) < 1: 790 command_line_error(u"Too few arguments") 791 elif len(args) == 1: 792 backend_url = args[0] 793 elif len(args) == 2: 794 if replicate: 795 if not backend.is_backend_url(args[0]) or not backend.is_backend_url(args[1]): 796 command_line_error(u"Two URLs expected for replicate.") 797 src_backend_url, backend_url = args[0], args[1] 798 else: 799 lpath, backend_url = args_to_path_backend(args[0], args[1]) 800 else: 801 command_line_error(u"Too many arguments") 802 803 if config.backup_name is None: 804 config.backup_name = generate_default_backup_name(backend_url) 805 806 # set and expand archive dir 807 set_archive_dir(expand_archive_dir(config.archive_dir, 808 config.backup_name)) 809 810 log.Info(_(u"Using archive dir: %s") % (config.archive_dir_path.uc_name,)) 811 log.Info(_(u"Using backup name: %s") % (config.backup_name,)) 812 813 return args 814 815 816def command_line_error(message): 817 u"""Indicate a command line error and exit""" 818 log.FatalError(_(u"Command line error: %s") % (message,) + u"\n" + 819 _(u"Enter 'duplicity --help' for help screen."), 820 log.ErrorCode.command_line) 821 822 823def usage(): 824 u"""Returns terse usage info. The code is broken down into pieces for ease of 825 translation maintenance. Any comments that look extraneous or redundant should 826 be assumed to be for the benefit of translators, since they can get each string 827 (paired with its preceding comment, if any) independently of the others.""" 828 829 trans = { 830 # TRANSL: Used in usage help to represent a Unix-style path name. Example: 831 # rsync://user[:password]@other_host[:port]//absolute_path 832 u'absolute_path': _(u"absolute_path"), 833 834 # TRANSL: Used in usage help. Example: 835 # tahoe://alias/some_dir 836 u'alias': _(u"alias"), 837 838 # TRANSL: Used in help to represent a "bucket name" for Amazon Web 839 # Services' Simple Storage Service (S3). Example: 840 # s3://other.host/bucket_name[/prefix] 841 u'bucket_name': _(u"bucket_name"), 842 843 # TRANSL: abbreviation for "character" (noun) 844 u'char': _(u"char"), 845 846 # TRANSL: noun 847 u'command': _(u"command"), 848 849 # TRANSL: Used in usage help to represent the name of a container in 850 # Amazon Web Services' Cloudfront. Example: 851 # cf+http://container_name 852 u'container_name': _(u"container_name"), 853 854 # TRANSL: noun 855 u'count': _(u"count"), 856 857 # TRANSL: Used in usage help to represent the name of a file directory 858 u'directory': _(u"directory"), 859 860 # TRANSL: Used in usage help to represent the name of a file. Example: 861 # --log-file <filename> 862 u'filename': _(u"filename"), 863 864 # TRANSL: Used in usage help to represent an ID for a GnuPG key. Example: 865 # --encrypt-key <gpg_key_id> 866 u'gpg_key_id': _(u"gpg-key-id"), 867 868 # TRANSL: Used in usage help, e.g. to represent the name of a code 869 # module. Example: 870 # rsync://user[:password]@other.host[:port]::/module/some_dir 871 u'module': _(u"module"), 872 873 # TRANSL: Used in usage help to represent a desired number of 874 # something. Example: 875 # --num-retries <number> 876 u'number': _(u"number"), 877 878 # TRANSL: Used in usage help. (Should be consistent with the "Options:" 879 # header.) Example: 880 # duplicity [full|incremental] [options] source_dir target_url 881 u'options': _(u"options"), 882 883 # TRANSL: Used in usage help to represent an internet hostname. Example: 884 # ftp://user[:password]@other.host[:port]/some_dir 885 u'other_host': _(u"other.host"), 886 887 # TRANSL: Used in usage help. Example: 888 # ftp://user[:password]@other.host[:port]/some_dir 889 u'password': _(u"password"), 890 891 # TRANSL: Used in usage help to represent a Unix-style path name. Example: 892 # --archive-dir <path> 893 u'path': _(u"path"), 894 895 # TRANSL: Used in usage help to represent a TCP port number. Example: 896 # ftp://user[:password]@other.host[:port]/some_dir 897 u'port': _(u"port"), 898 899 # TRANSL: Used in usage help. This represents a string to be used as a 900 # prefix to names for backup files created by Duplicity. Example: 901 # s3://other.host/bucket_name[/prefix] 902 u'prefix': _(u"prefix"), 903 904 # TRANSL: Used in usage help to represent a Unix-style path name. Example: 905 # rsync://user[:password]@other.host[:port]/relative_path 906 u'relative_path': _(u"relative_path"), 907 908 # TRANSL: Used in usage help. Example: 909 # --timeout <seconds> 910 u'seconds': _(u"seconds"), 911 912 # TRANSL: Used in usage help to represent a "glob" style pattern for 913 # matching one or more files, as described in the documentation. 914 # Example: 915 # --exclude <shell_pattern> 916 u'shell_pattern': _(u"shell_pattern"), 917 918 # TRANSL: Used in usage help to represent the name of a single file 919 # directory or a Unix-style path to a directory. Example: 920 # file:///some_dir 921 u'some_dir': _(u"some_dir"), 922 923 # TRANSL: Used in usage help to represent the name of a single file 924 # directory or a Unix-style path to a directory where files will be 925 # coming FROM. Example: 926 # duplicity [full|incremental] [options] source_dir target_url 927 u'source_dir': _(u"source_dir"), 928 929 # TRANSL: Used in usage help to represent a URL files will be coming 930 # FROM. Example: 931 # duplicity [restore] [options] source_url target_dir 932 u'source_url': _(u"source_url"), 933 934 # TRANSL: Used in usage help to represent the name of a single file 935 # directory or a Unix-style path to a directory. where files will be 936 # going TO. Example: 937 # duplicity [restore] [options] source_url target_dir 938 u'target_dir': _(u"target_dir"), 939 940 # TRANSL: Used in usage help to represent a URL files will be going TO. 941 # Example: 942 # duplicity [full|incremental] [options] source_dir target_url 943 u'target_url': _(u"target_url"), 944 945 # TRANSL: Used in usage help to represent a time spec for a previous 946 # point in time, as described in the documentation. Example: 947 # duplicity remove-older-than time [options] target_url 948 u'time': _(u"time"), 949 950 # TRANSL: Used in usage help to represent a user name (i.e. login). 951 # Example: 952 # ftp://user[:password]@other.host[:port]/some_dir 953 u'user': _(u"user"), 954 955 # TRANSL: account id for b2. Example: b2://account_id@bucket/ 956 u'account_id': _(u"account_id"), 957 958 # TRANSL: application_key for b2. 959 # Example: b2://account_id:application_key@bucket/ 960 u'application_key': _(u"application_key"), 961 962 # TRANSL: remote name for rclone. 963 # Example: rclone://remote:/some_dir 964 u'remote': _(u"remote"), 965 } 966 967 # TRANSL: Header in usage help 968 msg = u""" 969 duplicity [full|incremental] [%(options)s] %(source_dir)s %(target_url)s 970 duplicity [restore] [%(options)s] %(source_url)s %(target_dir)s 971 duplicity verify [%(options)s] %(source_url)s %(target_dir)s 972 duplicity collection-status [%(options)s] %(target_url)s 973 duplicity list-current-files [%(options)s] %(target_url)s 974 duplicity cleanup [%(options)s] %(target_url)s 975 duplicity remove-older-than %(time)s [%(options)s] %(target_url)s 976 duplicity remove-all-but-n-full %(count)s [%(options)s] %(target_url)s 977 duplicity remove-all-inc-of-but-n-full %(count)s [%(options)s] %(target_url)s 978 duplicity replicate %(source_url)s %(target_url)s 979 980""" % trans 981 982 # TRANSL: Header in usage help 983 msg = msg + _(u"Backends and their URL formats:") + u""" 984 azure://%(container_name)s 985 b2://%(account_id)s[:%(application_key)s]@%(bucket_name)s/[%(some_dir)s/] 986 boto3+s3://%(bucket_name)s[/%(prefix)s] 987 cf+http://%(container_name)s 988 dpbx:///%(some_dir)s 989 file:///%(some_dir)s 990 ftp://%(user)s[:%(password)s]@%(other_host)s[:%(port)s]/%(some_dir)s 991 ftps://%(user)s[:%(password)s]@%(other_host)s[:%(port)s]/%(some_dir)s 992 gdocs://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s 993 for gdrive:// a <service-account-url> like the following is required 994 <serviceaccount-name>@<serviceaccount-name>.iam.gserviceaccount.com 995 gdrive://<service-account-url>/target-folder/?driveID=<SHARED DRIVE ID> (for GOOGLE Shared Drive) 996 gdrive://<service-account-url>/target-folder/?myDriveFolderID=<google-myDrive-folder-id> (for GOOGLE MyDrive) 997 hsi://%(user)s[:%(password)s]@%(other_host)s[:%(port)s]/%(some_dir)s 998 imap://%(user)s[:%(password)s]@%(other_host)s[:%(port)s]/%(some_dir)s 999 mega://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s 1000 megav2://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s 1001 mf://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s 1002 onedrive://%(some_dir)s 1003 pca://%(container_name)s 1004 pydrive://%(user)s@%(other_host)s/%(some_dir)s 1005 rclone://%(remote)s:/%(some_dir)s 1006 rsync://%(user)s[:%(password)s]@%(other_host)s[:%(port)s]/%(relative_path)s 1007 rsync://%(user)s[:%(password)s]@%(other_host)s[:%(port)s]//%(absolute_path)s 1008 rsync://%(user)s[:%(password)s]@%(other_host)s[:%(port)s]::/%(module)s/%(some_dir)s 1009 s3+http://%(bucket_name)s[/%(prefix)s] 1010 s3://%(other_host)s[:%(port)s]/%(bucket_name)s[/%(prefix)s] 1011 scp://%(user)s[:%(password)s]@%(other_host)s[:%(port)s]/%(some_dir)s 1012 ssh://%(user)s[:%(password)s]@%(other_host)s[:%(port)s]/%(some_dir)s 1013 swift://%(container_name)s 1014 tahoe://%(alias)s/%(directory)s 1015 webdav://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s 1016 webdavs://%(user)s[:%(password)s]@%(other_host)s/%(some_dir)s 1017 1018""" % trans 1019 1020 # TRANSL: Header in usage help 1021 msg = msg + _(u"Commands:") + u""" 1022 cleanup <%(target_url)s> 1023 collection-status <%(target_url)s> 1024 full <%(source_dir)s> <%(target_url)s> 1025 incr <%(source_dir)s> <%(target_url)s> 1026 list-current-files <%(target_url)s> 1027 remove-all-but-n-full <%(count)s> <%(target_url)s> 1028 remove-all-inc-of-but-n-full <%(count)s> <%(target_url)s> 1029 remove-older-than <%(time)s> <%(target_url)s> 1030 replicate <%(source_url)s> <%(target_url)s> 1031 restore <%(source_url)s> <%(target_dir)s> 1032 verify <%(target_url)s> <%(source_dir)s> 1033 1034""" % trans 1035 1036 return msg 1037 1038 1039def set_archive_dir(dirstring): 1040 u"""Check archive dir and set global""" 1041 if not os.path.exists(dirstring): 1042 try: 1043 os.makedirs(dirstring) 1044 except Exception: 1045 pass 1046 archive_dir_path = path.Path(dirstring) 1047 if not archive_dir_path.isdir(): 1048 log.FatalError(_(u"Specified archive directory '%s' does not exist, " 1049 u"or is not a directory") % (archive_dir_path.uc_name,), 1050 log.ErrorCode.bad_archive_dir) 1051 config.archive_dir_path = archive_dir_path 1052 1053 1054def set_sign_key(sign_key): 1055 u"""Set config.sign_key assuming proper key given""" 1056 if not re.search(u"^(0x)?([0-9A-Fa-f]{8}|[0-9A-Fa-f]{16}|[0-9A-Fa-f]{40})$", sign_key): 1057 log.FatalError(_(u"Sign key should be an 8, 16 alt. 40 character hex string, like " 1058 u"'AA0E73D2'.\nReceived '%s' instead.") % (sign_key,), 1059 log.ErrorCode.bad_sign_key) 1060 config.gpg_profile.sign_key = sign_key 1061 1062 1063def set_selection(): 1064 u"""Return selection iter starting at filename with arguments applied""" 1065 global select_opts, select_files 1066 sel = selection.Select(config.local_path) 1067 sel.ParseArgs(select_opts, select_files) 1068 config.select = sel.set_iter() 1069 1070 1071def args_to_path_backend(arg1, arg2): 1072 u""" 1073 Given exactly two arguments, arg1 and arg2, figure out which one 1074 is the backend URL and which one is a local path, and return 1075 (local, backend). 1076 """ 1077 arg1_is_backend, arg2_is_backend = backend.is_backend_url(arg1), backend.is_backend_url(arg2) 1078 1079 if not arg1_is_backend and not arg2_is_backend: 1080 command_line_error(u"""\ 1081One of the arguments must be an URL. Examples of URL strings are 1082"scp://user@host.net:1234/path" and "file:///usr/local". See the man 1083page for more information.""") 1084 if arg1_is_backend and arg2_is_backend: 1085 command_line_error(u"Two URLs specified. " 1086 u"One argument should be a path.") 1087 if arg1_is_backend: 1088 return (arg2, arg1) 1089 elif arg2_is_backend: 1090 return (arg1, arg2) 1091 else: 1092 raise AssertionError(u'should not be reached') 1093 1094 1095def set_backend(arg1, arg2): 1096 u"""Figure out which arg is url, set backend 1097 1098 Return value is pair (path_first, path) where is_first is true iff 1099 path made from arg1. 1100 1101 """ 1102 path, bend = args_to_path_backend(arg1, arg2) 1103 1104 config.backend = backend.get_backend(bend) 1105 1106 if path == arg2: 1107 return (None, arg2) # False? 1108 else: 1109 return (1, arg1) # True? 1110 1111 1112def process_local_dir(action, local_pathname): 1113 u"""Check local directory, set config.local_path""" 1114 local_path = path.Path(path.Path(local_pathname).get_canonical()) 1115 if action == u"restore": 1116 if (local_path.exists() and not local_path.isemptydir()) and not config.force: 1117 log.FatalError(_(u"Restore destination directory %s already " 1118 u"exists.\nWill not overwrite.") % (local_path.uc_name,), 1119 log.ErrorCode.restore_dir_exists) 1120 elif action == u"verify": 1121 if not local_path.exists(): 1122 log.FatalError(_(u"Verify directory %s does not exist") % 1123 (local_path.uc_name,), 1124 log.ErrorCode.verify_dir_doesnt_exist) 1125 else: 1126 assert action == u"full" or action == u"inc" 1127 if not local_path.exists(): 1128 log.FatalError(_(u"Backup source directory %s does not exist.") 1129 % (local_path.uc_name,), 1130 log.ErrorCode.backup_dir_doesnt_exist) 1131 1132 config.local_path = local_path 1133 1134 1135def check_consistency(action): 1136 u"""Final consistency check, see if something wrong with command line""" 1137 global full_backup, select_opts, list_current, collection_status, cleanup, replicate 1138 1139 def assert_only_one(arglist): 1140 u"""Raises error if two or more of the elements of arglist are true""" 1141 n = 0 1142 for m in arglist: 1143 if m: 1144 n += 1 1145 assert n <= 1, u"Invalid syntax, two conflicting modes specified" 1146 1147 if action in [u"list-current", u"collection-status", 1148 u"cleanup", u"remove-old", u"remove-all-but-n-full", u"remove-all-inc-of-but-n-full", u"replicate"]: 1149 assert_only_one([list_current, collection_status, cleanup, replicate, 1150 config.remove_time is not None]) 1151 elif action == u"restore" or action == u"verify": 1152 if full_backup: 1153 command_line_error(u"--full option cannot be used when " 1154 u"restoring or verifying") 1155 elif config.incremental: 1156 command_line_error(u"--incremental option cannot be used when " 1157 u"restoring or verifying") 1158 if select_opts and action == u"restore": 1159 log.Warn(_(u"Command line warning: %s") % _(u"Selection options --exclude/--include\n" 1160 u"currently work only when backing up," 1161 u"not restoring.")) 1162 else: 1163 assert action == u"inc" or action == u"full" 1164 if verify: 1165 command_line_error(u"--verify option cannot be used " 1166 u"when backing up") 1167 if config.restore_dir: 1168 command_line_error(u"restore option incompatible with %s backup" 1169 % (action,)) 1170 if sum([config.s3_use_rrs, config.s3_use_ia, config.s3_use_onezone_ia]) >= 2: 1171 command_line_error(u"only one of --s3-use-rrs, --s3-use-ia, and --s3-use-onezone-ia may be used") 1172 1173 1174def ProcessCommandLine(cmdline_list): 1175 u"""Process command line, set config, return action 1176 1177 action will be "list-current", "collection-status", "cleanup", 1178 "remove-old", "restore", "verify", "full", or "inc". 1179 1180 """ 1181 # build initial gpg_profile 1182 config.gpg_profile = gpg.GPGProfile() 1183 1184 # parse command line 1185 args = parse_cmdline_options(cmdline_list) 1186 1187 # if we get a different gpg-binary from the commandline then redo gpg_profile 1188 if config.gpg_binary is not None: 1189 src = config.gpg_profile 1190 config.gpg_profile = gpg.GPGProfile( 1191 passphrase=src.passphrase, 1192 sign_key=src.sign_key, 1193 recipients=src.recipients, 1194 hidden_recipients=src.hidden_recipients) 1195 log.Debug(_(u"GPG binary is %s, version %s") % 1196 ((config.gpg_binary or u'gpg'), config.gpg_profile.gpg_version)) 1197 1198 # we can now try to import all the backends 1199 backend.import_backends() 1200 1201 # parse_cmdline_options already verified that we got exactly 1 or 2 1202 # non-options arguments 1203 assert len(args) >= 1 and len(args) <= 2, u"arg count should have been checked already" 1204 1205 if len(args) == 1: 1206 if list_current: 1207 action = u"list-current" 1208 elif collection_status: 1209 action = u"collection-status" 1210 elif cleanup: 1211 action = u"cleanup" 1212 elif config.remove_time is not None: 1213 action = u"remove-old" 1214 elif config.remove_all_but_n_full_mode: 1215 action = u"remove-all-but-n-full" 1216 elif config.remove_all_inc_of_but_n_full_mode: 1217 action = u"remove-all-inc-of-but-n-full" 1218 else: 1219 command_line_error(u"Too few arguments") 1220 config.backend = backend.get_backend(args[0]) 1221 if not config.backend: 1222 log.FatalError(_(u"""Bad URL '%s'. 1223Examples of URL strings are "scp://user@host.net:1234/path" and 1224"file:///usr/local". See the man page for more information.""") % (args[0],), 1225 log.ErrorCode.bad_url) 1226 elif len(args) == 2: 1227 if replicate: 1228 config.src_backend = backend.get_backend(args[0]) 1229 config.backend = backend.get_backend(args[1]) 1230 action = u"replicate" 1231 else: 1232 # Figure out whether backup or restore 1233 backup, local_pathname = set_backend(args[0], args[1]) 1234 if backup: 1235 if full_backup: 1236 action = u"full" 1237 else: 1238 action = u"inc" 1239 else: 1240 if verify: 1241 action = u"verify" 1242 else: 1243 action = u"restore" 1244 1245 process_local_dir(action, local_pathname) 1246 if action in [u'full', u'inc', u'verify']: 1247 set_selection() 1248 elif len(args) > 2: 1249 raise AssertionError(u"this code should not be reachable") 1250 1251 check_consistency(action) 1252 log.Info(_(u"Main action: ") + action) 1253 return action 1254