diff --git a/doc/manpages/qvm-backup-restore.rst b/doc/manpages/qvm-backup-restore.rst index f3315f2..458dece 100644 --- a/doc/manpages/qvm-backup-restore.rst +++ b/doc/manpages/qvm-backup-restore.rst @@ -87,6 +87,10 @@ Options Read passphrase from file, or use '-' to read from stdin +.. option:: --location-is-service + + Provided backup location is a qrexec service name (optionally with an + argument, separated by ``+``), instead of file path or a command. Authors diff --git a/qubesadmin/backup/restore.py b/qubesadmin/backup/restore.py index f0196dc..fea3b08 100644 --- a/qubesadmin/backup/restore.py +++ b/qubesadmin/backup/restore.py @@ -910,7 +910,7 @@ class BackupRestore(object): self.username = os.path.basename(subdir) def __init__(self, app, backup_location, backup_vm, passphrase, - force_compression_filter=None): + location_is_service=False, force_compression_filter=None): super(BackupRestore, self).__init__() #: qubes.Qubes instance @@ -927,6 +927,10 @@ class BackupRestore(object): #: backup path, inside VM pointed by :py:attr:`backup_vm` self.backup_location = backup_location + #: use alternative qrexec service to retrieve backup data, instead of + #: ``qubes.Restore`` with *backup_location* given on stdin + self.location_is_service = location_is_service + #: force using specific application for (de)compression, instead of #: the one named in the backup header self.force_compression_filter = force_compression_filter @@ -973,11 +977,14 @@ class BackupRestore(object): vmproc = None if self.backup_vm is not None: # If APPVM, STDOUT is a PIPE - vmproc = self.backup_vm.run_service('qubes.Restore') - vmproc.stdin.write( - (self.backup_location.replace("\r", "").replace("\n", - "") + "\n").encode()) - vmproc.stdin.flush() + if self.location_is_service: + vmproc = self.backup_vm.run_service(self.backup_location) + else: + vmproc = self.backup_vm.run_service('qubes.Restore') + vmproc.stdin.write( + (self.backup_location.replace("\r", "").replace("\n", + "") + "\n").encode()) + vmproc.stdin.flush() # Send to tar2qfile the VMs that should be extracted vmproc.stdin.write((" ".join(filelist) + "\n").encode()) diff --git a/qubesadmin/tests/tools/qvm_backup_restore.py b/qubesadmin/tests/tools/qvm_backup_restore.py index b07304c..8ed1482 100644 --- a/qubesadmin/tests/tools/qvm_backup_restore.py +++ b/qubesadmin/tests/tools/qvm_backup_restore.py @@ -59,7 +59,7 @@ class TC_00_qvm_backup_restore(qubesadmin.tests.QubesTestCase): self.app, mock.ANY, mock_restore_info) mock_backup.assert_called_once_with( self.app, '/some/path', None, 'testpass', - force_compression_filter=None) + force_compression_filter=None, location_is_service=False) self.assertAllCalled() @mock.patch('qubesadmin.tools.qvm_backup_restore.input', create=True) @@ -94,7 +94,7 @@ class TC_00_qvm_backup_restore(qubesadmin.tests.QubesTestCase): app=self.app) mock_backup.assert_called_once_with( self.app, '/some/path', None, 'testpass', - force_compression_filter=None) + force_compression_filter=None, location_is_service=False) self.assertEqual(mock_backup.return_value.options.exclude, ['test-vm2']) self.assertAllCalled() diff --git a/qubesadmin/tools/qvm_backup_restore.py b/qubesadmin/tools/qvm_backup_restore.py index 61eaada..548b4a1 100644 --- a/qubesadmin/tools/qvm_backup_restore.py +++ b/qubesadmin/tools/qvm_backup_restore.py @@ -84,6 +84,10 @@ parser.add_argument("-p", "--passphrase-file", action="store", dest="pass_file", default=None, help="Read passphrase from file, or use '-' to read from stdin") +parser.add_argument("--location-is-service", action="store_true", + help="Interpret backup location as a qrexec service name," + "possibly with an argument separated by +.Requires -d option.") + parser.add_argument('backup_location', action='store', help="Backup directory name, or command to pipe from") @@ -205,6 +209,9 @@ def main(args=None, app=None): except KeyError: parser.error('no such domain: {!r}'.format(args.appvm)) + if args.location_is_service and not args.appvm: + parser.error('--location-is-service option requires -d') + if args.pass_file is not None: pass_f = open(args.pass_file) if args.pass_file != "-" else sys.stdin passphrase = pass_f.readline().rstrip() @@ -218,7 +225,7 @@ def main(args=None, app=None): try: backup = BackupRestore(args.app, args.backup_location, - appvm, passphrase, + appvm, passphrase, location_is_service=args.location_is_service, force_compression_filter=args.compression) except qubesadmin.exc.QubesException as e: parser.error_runtime(str(e))