#!/usr/bin/python2 # -*- encoding: utf8 -*- # # The Qubes OS Project, http://www.qubes-os.org # # Copyright (C) 2010 Joanna Rutkowska # # 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 2 # 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # from qubes.qubes import QubesVmCollection from qubes.qubes import QubesException from qubes.backup import backup_prepare, backup_do from qubes.qubesutils import size_to_human from optparse import OptionParser import qubes.backup import os import sys import getpass from locale import getpreferredencoding def print_progress(progress): print >> sys.stderr, "\r-> Backing up files: {0}%...".format (progress), def main(): usage = "usage: %prog [options] [vms-to-be-included ...]" parser = OptionParser (usage) parser.add_option ("-x", "--exclude", action="append", dest="exclude_list", default=[], help="Exclude the specified VM from backup (may be " "repeated)") parser.add_option ("--force-root", action="store_true", dest="force_root", default=False, help="Force to run, even with root privileges") parser.add_option ("-d", "--dest-vm", action="store", dest="appvm", help="The AppVM to send backups to (implies -e)") parser.add_option ("-e", "--encrypt", action="store_true", dest="encrypt", default=False, help="Encrypts the backup") parser.add_option ("--no-encrypt", action="store_true", dest="no_encrypt", default=False, help="Skip encryption even if sending the backup to VM") parser.add_option ("-p", "--passphrase-file", action="store", dest="pass_file", default=None, help="File containing the pass phrase to use, or '-' " "to read it from stdin") parser.add_option ("-E", "--enc-algo", action="store", dest="crypto_algorithm", default=None, help="Specify non-default encryption algorithm. For " "list of supported algos execute 'openssl " "list-cipher-algorithms' (implies -e)") parser.add_option ("-H", "--hmac-algo", action="store", dest="hmac_algorithm", default=None, help="Specify non-default hmac algorithm. For list of " "supported algos execute 'openssl " "list-message-digest-algorithms'") parser.add_option ("-z", "--compress", action="store_true", dest="compress", default=False, help="Compress the backup") parser.add_option ("-Z", "--compress-filter", action="store", dest="compress_filter", default=False, help="Compress the backup using specified filter " "program (default: gzip)") parser.add_option ("--debug", action="store_true", dest="debug", default=False, help="Enable (a lot of) debug output") (options, args) = parser.parse_args () if (len (args) < 1): print >> sys.stderr, "You must specify the target backup directory (e.g. /mnt/backup)" print >> sys.stderr, "qvm-backup will create a subdirectory there for each individual backup." exit (0) base_backup_dir = args[0] if hasattr(os, "geteuid") and os.geteuid() == 0: if not options.force_root: print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems." print >> sys.stderr, "Retry as unprivileged user." print >> sys.stderr, "... or use --force-root to continue anyway." exit(1) # Only for locking qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() vms = None if (len (args) > 1): vms = [qvm_collection.get_vm_by_name(vmname) for vmname in args[1:]] if options.appvm: options.exclude_list.append(options.appvm) if options.appvm or options.crypto_algorithm: options.encrypt = True if options.no_encrypt: options.encrypt = False if options.debug: qubes.backup.BACKUP_DEBUG = True files_to_backup = None try: files_to_backup = backup_prepare( vms_list=vms, exclude_list=options.exclude_list, hide_vm_names=options.encrypt) except QubesException as e: print >>sys.stderr, "ERROR: %s" % str(e) exit(1) total_backup_sz = reduce(lambda size, file: size+file["size"], files_to_backup, 0) if not options.appvm: appvm = None if os.path.isdir(base_backup_dir): stat = os.statvfs(base_backup_dir) else: stat = os.statvfs(os.path.dirname(base_backup_dir)) backup_fs_free_sz = stat.f_bsize * stat.f_bavail print if (total_backup_sz > backup_fs_free_sz): print >>sys.stderr, "ERROR: Not enough space available on the backup filesystem!" exit(1) print "-> Available space: {0}".format(size_to_human(backup_fs_free_sz)) else: appvm = qvm_collection.get_vm_by_name(options.appvm) if appvm is None: print >>sys.stderr, "ERROR: VM {0} does not exist".format(options.appvm) exit(1) stat = os.statvfs('/var/tmp') backup_fs_free_sz = stat.f_bsize * stat.f_bavail print if (backup_fs_free_sz < 1000000000): print >>sys.stderr, "ERROR: Not enough space available " \ "on the local filesystem (needs 1GB for temporary files)!" exit(1) if not appvm.is_running(): appvm.start(verbose=True) if options.appvm: print >>sys.stderr, ("WARNING: VM {} excluded because it's used to " "store the backup.").format(options.appvm) options.exclude_list.append(options.appvm) if not options.encrypt: print >>sys.stderr, "WARNING: encryption will not be used" if options.pass_file is not None: f = open(options.pass_file) if options.pass_file != "-" else sys.stdin passphrase = f.readline().rstrip() if f is not sys.stdin: f.close() else: if raw_input("Do you want to proceed? [y/N] ").upper() != "Y": exit(0) s = ("Please enter the pass phrase that will be used to {}verify " "the backup: ").format('encrypt and ' if options.encrypt else '') passphrase = getpass.getpass(s) if getpass.getpass("Enter again for verification: ") != passphrase: print >>sys.stderr, "ERROR: Password mismatch" exit(1) encoding = sys.stdin.encoding or getpreferredencoding() passphrase = passphrase.decode(encoding) kwargs = {} if options.hmac_algorithm: kwargs['hmac_algorithm'] = options.hmac_algorithm if options.crypto_algorithm: kwargs['crypto_algorithm'] = options.crypto_algorithm try: backup_do(base_backup_dir, files_to_backup, passphrase, progress_callback=print_progress, encrypted=options.encrypt, compressed=options.compress_filter or options.compress, appvm=appvm, **kwargs) except QubesException as e: print >>sys.stderr, "ERROR: %s" % str(e) exit(1) print print "-> Backup completed." qvm_collection.unlock_db() main()