tools: add qvm-run --service

Make it convenient wrapper around qrexec-client{-vm}, which would start
a VM, wait for user session etc.
This commit is contained in:
Marek Marczykowski-Górecki 2017-05-19 19:41:39 +02:00
parent f5e102177c
commit 38abd81ea8
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
3 changed files with 57 additions and 8 deletions

View File

@ -63,6 +63,12 @@ Options
Run the command without GUI forwarding enabled. Can be switched back with Run the command without GUI forwarding enabled. Can be switched back with
:option:`--gui`. :option:`--gui`.
.. option:: --service
Start RPC service instead of shell command. Specify name of the service in
place of *COMMAND* argument. You can also specify service argument, appending
it to the service name after `+` character.
.. option:: --colour-output=COLOUR, --color-output=COLOR .. option:: --colour-output=COLOUR, --color-output=COLOR
Mark the qube output with given ANSI colour (ie. "31" for red). The exact Mark the qube output with given ANSI colour (ie. "31" for red). The exact

View File

@ -262,3 +262,35 @@ class TC_00_qvm_run(qubesadmin.tests.QubesTestCase):
('test-vm', 'qubes.VMShell', b'command; exit\n') ('test-vm', 'qubes.VMShell', b'command; exit\n')
]) ])
self.assertAllCalled() self.assertAllCalled()
def test_007_run_service_with_gui(self):
self.app.expected_calls[
('dom0', 'admin.vm.List', None, None)] = \
b'0\x00test-vm class=AppVM state=Running\n'
self.app.expected_calls[
('test-vm', 'admin.vm.property.Get', 'default_user', None)] = \
b'0\x00default=yes type=str user'
# self.app.expected_calls[
# ('test-vm', 'admin.vm.List', None, None)] = \
# b'0\x00test-vm class=AppVM state=Running\n'
ret = qubesadmin.tools.qvm_run.main(
['--service', 'test-vm', 'service.name'],
app=self.app)
self.assertEqual(ret, 0)
# make sure we have the same instance below
self.assertEqual(self.app.service_calls, [
('test-vm', 'qubes.WaitForSession', {
'stdout': subprocess.DEVNULL,
'stderr': subprocess.DEVNULL,
}),
('test-vm', 'qubes.WaitForSession', b'user'),
('test-vm', 'service.name', {
'filter_esc': True,
'localcmd': None,
'stdout': subprocess.DEVNULL,
'stderr': subprocess.DEVNULL,
'user': None,
}),
('test-vm', 'service.name', b''),
])
self.assertAllCalled()

View File

@ -86,6 +86,10 @@ parser.add_argument('--no-filter-escape-chars',
help='do not filter terminal escape sequences; DANGEROUS when output is a' help='do not filter terminal escape sequences; DANGEROUS when output is a'
' terminal emulator') ' terminal emulator')
parser.add_argument('--service',
action='store_true', dest='service',
help='run a qrexec service (named by COMMAND) instead of shell command')
parser.add_argument('cmd', metavar='COMMAND', parser.add_argument('cmd', metavar='COMMAND',
help='command to run') help='command to run')
@ -136,7 +140,7 @@ def main(args=None, app=None):
run_kwargs['stderr'] = None run_kwargs['stderr'] = None
if isinstance(args.app, qubesadmin.app.QubesLocal) and \ if isinstance(args.app, qubesadmin.app.QubesLocal) and \
not args.passio and not args.localcmd: not args.passio and not args.localcmd and args.service:
# wait=False works only in dom0; but it's still useful, to save on # wait=False works only in dom0; but it's still useful, to save on
# simultaneous vchan connections # simultaneous vchan connections
run_kwargs['wait'] = False run_kwargs['wait'] = False
@ -172,13 +176,20 @@ def main(args=None, app=None):
if args.passio and not args.localcmd: if args.passio and not args.localcmd:
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
loop.add_signal_handler(signal.SIGCHLD, loop.stop) loop.add_signal_handler(signal.SIGCHLD, loop.stop)
proc = vm.run_service('qubes.VMShell', if args.service:
user=args.user, proc = vm.run_service(args.cmd,
localcmd=args.localcmd, user=args.user,
filter_esc=args.filter_esc, localcmd=args.localcmd,
**run_kwargs) filter_esc=args.filter_esc,
proc.stdin.write(vm.prepare_input_for_vmshell(args.cmd)) **run_kwargs)
proc.stdin.flush() else:
proc = vm.run_service('qubes.VMShell',
user=args.user,
localcmd=args.localcmd,
filter_esc=args.filter_esc,
**run_kwargs)
proc.stdin.write(vm.prepare_input_for_vmshell(args.cmd))
proc.stdin.flush()
if args.passio and not args.localcmd: if args.passio and not args.localcmd:
asyncio.ensure_future(loop.connect_read_pipe( asyncio.ensure_future(loop.connect_read_pipe(
functools.partial(DataCopyProtocol, proc.stdin, functools.partial(DataCopyProtocol, proc.stdin,