qubes/tools: add qvm-run, qvm-{,un}pause
Also change convention of calling main(): now command returns its numeric value instead of bool. Also fixed QSB#13 fixes QubesOS/qubes-issues#1226
This commit is contained in:
parent
7b30361fa6
commit
f1a0b1af39
34
doc/manpages/qvm-pause.rst
Normal file
34
doc/manpages/qvm-pause.rst
Normal file
@ -0,0 +1,34 @@
|
||||
.. program:: qvm-pause
|
||||
|
||||
:program:`qvm-pause` -- pause a domain
|
||||
======================================
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
:command:`qvm-pause` [-h] [--verbose] [--quiet] *VMNAME*
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
.. option:: --help, -h
|
||||
|
||||
Show the help message and exit.
|
||||
|
||||
.. option:: --verbose, -v
|
||||
|
||||
Increase verbosity.
|
||||
|
||||
.. option:: --quiet, -q
|
||||
|
||||
Decrease verbosity.
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
| Joanna Rutkowska <joanna at invisiblethingslab dot com>
|
||||
| Rafal Wojtczuk <rafal at invisiblethingslab dot com>
|
||||
| Marek Marczykowski <marmarek at invisiblethingslab dot com>
|
||||
| Wojtek Porczyk <woju at invisiblethingslab dot com>
|
||||
|
||||
.. vim: ts=3 sw=3 et tw=80
|
@ -1,80 +1,97 @@
|
||||
.. program:: qvm-run
|
||||
|
||||
=====================================================
|
||||
:program:`qvm-run` -- Run a command on a specified VM
|
||||
:program:`qvm-run` -- Run a command in a specified VM
|
||||
=====================================================
|
||||
|
||||
Synopsis
|
||||
========
|
||||
:command:`qvm-run` [*options*] [<*vm-name*>] [<*cmd*>]
|
||||
--------
|
||||
|
||||
:command:`qvm-run` [-h] [--verbose] [--quiet] [--all] [--exclude *EXCLUDE*] [--user *USER*] [--autostart] [--pass-io] [--localcmd *COMMAND*] [--gui] [--no-gui] [--colour-output *COLOR*] [--no-color-output] [--filter-escape-chars] [--no-filter-escape-chars] [*VMNAME*] *COMMAND*
|
||||
|
||||
Options
|
||||
=======
|
||||
-------
|
||||
|
||||
.. option:: --help, -h
|
||||
|
||||
Show this help message and exit
|
||||
Show the help message and exit.
|
||||
|
||||
.. option:: --verbose, -v
|
||||
|
||||
Increase verbosity.
|
||||
|
||||
.. option:: --quiet, -q
|
||||
|
||||
Be quiet
|
||||
|
||||
.. option:: --auto, -a
|
||||
|
||||
Auto start the VM if not running
|
||||
|
||||
.. option:: --user=USER, -u USER
|
||||
|
||||
Run command in a VM as a specified user
|
||||
|
||||
.. option:: --tray
|
||||
|
||||
Use tray notifications instead of stdout
|
||||
Decrease verbosity.
|
||||
|
||||
.. option:: --all
|
||||
|
||||
Run command on all currently running VMs (or all paused, in case of :option:`--unpause`)
|
||||
Run the command on all qubes. You can use :option:`--exclude` to limit the
|
||||
qubes set. Command is never run on the dom0.
|
||||
|
||||
.. option:: --exclude=EXCLUDE_LIST
|
||||
.. option:: --exclude
|
||||
|
||||
When :option:`--all` is used: exclude this VM name (might be repeated)
|
||||
Exclude the qube from :option:`--all`.
|
||||
|
||||
.. option:: --wait
|
||||
.. option:: --user=USER, -u USER
|
||||
|
||||
Wait for the VM(s) to shutdown
|
||||
Run command in a qube as *USER*.
|
||||
|
||||
.. option:: --shutdown
|
||||
.. option:: --auto, --autostart, -a
|
||||
|
||||
Do 'xl shutdown' for the VM(s) (can be combined with :option:`--all` and
|
||||
:option:`--wait`)
|
||||
|
||||
.. deprecated:: R2
|
||||
Use :manpage:`qvm-shutdown(1)` instead.
|
||||
|
||||
.. option:: --pause
|
||||
|
||||
Do 'xl pause' for the VM(s) (can be combined with :option:`--all` and
|
||||
:option:`--wait`)
|
||||
|
||||
.. option:: --unpause
|
||||
|
||||
Do 'xl unpause' for the VM(s) (can be combined with :option:`--all` and
|
||||
:option:`--wait`)
|
||||
Start the qube if it is not running.
|
||||
|
||||
.. option:: --pass-io, -p
|
||||
|
||||
Pass stdin/stdout/stderr from remote program
|
||||
Pass standard input and output to and from the remote program.
|
||||
|
||||
.. option:: --localcmd=LOCALCMD
|
||||
.. option:: --localcmd=COMMAND
|
||||
|
||||
With :option:`--pass-io`, pass stdin/stdout/stderr to the given program
|
||||
With :option:`--pass-io`, pass standard input and output to and from the
|
||||
given program.
|
||||
|
||||
.. option:: --force
|
||||
.. option:: --gui
|
||||
|
||||
Force operation, even if may damage other VMs (eg. shutdown of NetVM)
|
||||
Run the command with GUI forwarding enabled, which is the default. This
|
||||
switch can be used to counter :option:`--no-gui`.
|
||||
|
||||
.. option:: --no-gui, --nogui
|
||||
|
||||
Run the command without GUI forwarding enabled. Can be switched back with
|
||||
:option:`--gui`.
|
||||
|
||||
.. option:: --colour-output=COLOUR, --color-output=COLOR
|
||||
|
||||
Mark the qube output with given ANSI colour (ie. "31" for red). The exact
|
||||
apping of numbers to colours and styles depends of the particular terminal
|
||||
emulator.
|
||||
|
||||
Colouring can be disabled with :option:`--no-colour-output`.
|
||||
|
||||
.. option:: --no-colour-output, --no-color-output
|
||||
|
||||
Disable colouring the stdio.
|
||||
|
||||
.. option:: --filter-escape-chars
|
||||
|
||||
Filter terminal escape sequences (default if output is terminal).
|
||||
|
||||
Terminal control characters are a security issue, which in worst case amount
|
||||
to arbitrary command execution. In the simplest case this requires two often
|
||||
found codes: terminal title setting (which puts arbitrary string in the
|
||||
window title) and title repo reporting (which puts that string on the shell's
|
||||
standard input.
|
||||
|
||||
.. option:: --no-filter-escape-chars
|
||||
|
||||
Do not filter terminal escape sequences. This is DANGEROUS when output is
|
||||
a terminal emulator. See :option:`--filter-escape-chars` for explanation.
|
||||
|
||||
Authors
|
||||
=======
|
||||
-------
|
||||
|
||||
| Joanna Rutkowska <joanna at invisiblethingslab dot com>
|
||||
| Rafal Wojtczuk <rafal at invisiblethingslab dot com>
|
||||
| Marek Marczykowski <marmarek at invisiblethingslab dot com>
|
||||
| Wojtek Porczyk <woju at invisiblethingslab dot com>
|
||||
|
||||
.. vim: ts=3 sw=3 et tw=80
|
||||
|
34
doc/manpages/qvm-unpause.rst
Normal file
34
doc/manpages/qvm-unpause.rst
Normal file
@ -0,0 +1,34 @@
|
||||
.. program:: qvm-unpause
|
||||
|
||||
:program:`qvm-unpause` -- unpause a domain
|
||||
==========================================
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
:command:`qvm-unpause` [-h] [--verbose] [--quiet] *VMNAME*
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
.. option:: --help, -h
|
||||
|
||||
Show the help message and exit.
|
||||
|
||||
.. option:: --verbose, -v
|
||||
|
||||
Increase verbosity.
|
||||
|
||||
.. option:: --quiet, -q
|
||||
|
||||
Decrease verbosity.
|
||||
|
||||
Authors
|
||||
-------
|
||||
|
||||
| Joanna Rutkowska <joanna at invisiblethingslab dot com>
|
||||
| Rafal Wojtczuk <rafal at invisiblethingslab dot com>
|
||||
| Marek Marczykowski <marmarek at invisiblethingslab dot com>
|
||||
| Wojtek Porczyk <woju at invisiblethingslab dot com>
|
||||
|
||||
.. vim: ts=3 sw=3 et tw=80
|
@ -31,11 +31,11 @@ import qubes.tests
|
||||
@qubes.tests.skipUnlessDom0
|
||||
class TC_00_qubes_create(qubes.tests.SystemTestsMixin, qubes.tests.QubesTestCase):
|
||||
def test_000_basic(self):
|
||||
self.assertTrue(qubes.tools.qubes_create.main((
|
||||
self.assertEqual(0, qubes.tools.qubes_create.main((
|
||||
'--qubesxml', qubes.tests.XMLPATH)))
|
||||
|
||||
def test_001_property(self):
|
||||
self.assertTrue(qubes.tools.qubes_create.main((
|
||||
self.assertEqual(0, qubes.tools.qubes_create.main((
|
||||
'--qubesxml', qubes.tests.XMLPATH,
|
||||
'--property', 'default_kernel=testkernel')))
|
||||
|
||||
|
@ -32,6 +32,9 @@ import os
|
||||
|
||||
import qubes.log
|
||||
|
||||
#: constant returned when some action should be performed on all qubes
|
||||
VM_ALL = object()
|
||||
|
||||
|
||||
class PropertyAction(argparse.Action):
|
||||
'''Action for argument parser that stores a property.'''
|
||||
@ -148,6 +151,7 @@ class QubesArgumentParser(argparse.ArgumentParser):
|
||||
want_app_no_instance=False,
|
||||
want_force_root=False,
|
||||
want_vm=False,
|
||||
want_vm_all=False,
|
||||
**kwargs):
|
||||
|
||||
super(QubesArgumentParser, self).__init__(**kwargs)
|
||||
@ -156,6 +160,7 @@ class QubesArgumentParser(argparse.ArgumentParser):
|
||||
self._want_app_no_instance = want_app_no_instance
|
||||
self._want_force_root = want_force_root
|
||||
self._want_vm = want_vm
|
||||
self._want_vm_all = want_vm_all
|
||||
|
||||
if self._want_app:
|
||||
self.add_argument('--qubesxml', metavar='FILE',
|
||||
@ -176,8 +181,21 @@ class QubesArgumentParser(argparse.ArgumentParser):
|
||||
help='force to run as root')
|
||||
|
||||
if self._want_vm:
|
||||
self.add_argument('vm', metavar='VMNAME',
|
||||
action='store',
|
||||
if self._want_vm_all:
|
||||
vmchoice = self.add_mutually_exclusive_group()
|
||||
vmchoice.add_argument('--all',
|
||||
action='store_const', const=VM_ALL, dest='vm',
|
||||
help='perform the action on all qubes')
|
||||
vmchoice.add_argument('--exclude',
|
||||
action='append', default=[],
|
||||
help='exclude the qube from --all')
|
||||
nargs = '?'
|
||||
else:
|
||||
vmchoice = self
|
||||
nargs = None
|
||||
|
||||
vmchoice.add_argument('vm', metavar='VMNAME',
|
||||
action='store', nargs=nargs,
|
||||
help='name of the domain')
|
||||
|
||||
self.set_defaults(verbose=1, quiet=0)
|
||||
@ -191,10 +209,25 @@ class QubesArgumentParser(argparse.ArgumentParser):
|
||||
namespace.app = qubes.Qubes(namespace.app)
|
||||
|
||||
if self._want_vm:
|
||||
try:
|
||||
namespace.vm = namespace.app.domains[namespace.vm]
|
||||
except KeyError:
|
||||
self.error('no such domain: {!r}'.format(namespace.vm))
|
||||
if self._want_vm_all:
|
||||
if namespace.vm is VM_ALL:
|
||||
namespace.vm = [vm for vm in namespace.app.domains
|
||||
if vm.qid != 0 and vm.name not in namespace.exclude]
|
||||
else:
|
||||
if namespace.exclude:
|
||||
self.error('--exclude can only be used with --all')
|
||||
try:
|
||||
namespace.vm = \
|
||||
(namespace.app.domains[namespace.vm],)
|
||||
except KeyError:
|
||||
self.error(
|
||||
'no such domain: {!r}'.format(namespace.vm))
|
||||
|
||||
else:
|
||||
try:
|
||||
namespace.vm = namespace.app.domains[namespace.vm]
|
||||
except KeyError:
|
||||
self.error('no such domain: {!r}'.format(namespace.vm))
|
||||
|
||||
if self._want_force_root:
|
||||
self.dont_run_as_root(namespace)
|
||||
|
@ -49,8 +49,8 @@ def main(args=None):
|
||||
|
||||
args = parser.parse_args(args)
|
||||
qubes.Qubes.create_empty_store(args.app, **args.properties)
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(not main())
|
||||
sys.exit(main())
|
||||
|
@ -58,8 +58,8 @@ parser.add_argument('--unset', '--default', '--delete', '-D',
|
||||
' instead')
|
||||
|
||||
|
||||
def main():
|
||||
args = parser.parse_args()
|
||||
def main(args=None):
|
||||
args = parser.parse_args(args)
|
||||
|
||||
if args.property is None:
|
||||
properties = args.app.property_list()
|
||||
@ -80,24 +80,24 @@ def main():
|
||||
print('{name:{width}s} - {value!r}'.format(
|
||||
name=prop.__name__, width=width, value=value))
|
||||
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
if args.value is not None:
|
||||
setattr(args.app, args.property, args.value)
|
||||
args.app.save()
|
||||
return True
|
||||
return 0
|
||||
|
||||
if args.delete:
|
||||
delattr(args.app, args.property)
|
||||
args.app.save()
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
print(str(getattr(args.app, args.property)))
|
||||
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(not main())
|
||||
sys.exit(main())
|
||||
|
@ -147,8 +147,8 @@ def main(args=None):
|
||||
|
||||
args.app.save()
|
||||
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(not main())
|
||||
sys.exit(main())
|
||||
|
@ -48,8 +48,8 @@ def main(args=None):
|
||||
except (IOError, OSError, qubes.exc.QubesException) as e:
|
||||
parser.error_runtime(str(e))
|
||||
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(not main())
|
||||
sys.exit(main())
|
||||
|
@ -606,8 +606,8 @@ def main(args=None):
|
||||
table = Table(args.app, columns)
|
||||
table.write_table(sys.stdout)
|
||||
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(not main())
|
||||
sys.exit(main())
|
||||
|
50
qubes/tools/qvm_pause.py
Normal file
50
qubes/tools/qvm_pause.py
Normal file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/python2 -O
|
||||
# vim: fileencoding=utf-8
|
||||
|
||||
#
|
||||
# The Qubes OS Project, https://www.qubes-os.org/
|
||||
#
|
||||
# Copyright (C) 2010-2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2015 Wojtek Porczyk <woju@invisiblethingslab.com>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
'''qvm-pause - Pause a domain'''
|
||||
|
||||
import sys
|
||||
import qubes
|
||||
|
||||
|
||||
parser = qubes.tools.QubesArgumentParser(
|
||||
want_vm=True,
|
||||
description='pause a domain')
|
||||
|
||||
|
||||
def main(args=None):
|
||||
'''Main routine of :program:`qvm-pause`.
|
||||
|
||||
:param list args: Optional arguments to override those delivered from \
|
||||
command line.
|
||||
'''
|
||||
|
||||
args = parser.parse_args(args)
|
||||
args.vm.pause()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -61,8 +61,8 @@ parser.add_argument('--unset', '--default', '--delete', '-D',
|
||||
' instead')
|
||||
|
||||
|
||||
def main():
|
||||
args = parser.parse_args()
|
||||
def main(args=None):
|
||||
args = parser.parse_args(args)
|
||||
|
||||
if args.property is None:
|
||||
properties = args.vm.property_list()
|
||||
@ -83,24 +83,24 @@ def main():
|
||||
print('{name:{width}s} - {value!r}'.format(
|
||||
name=prop.__name__, width=width, value=value))
|
||||
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
if args.value is not None:
|
||||
setattr(args.vm, args.property, args.value)
|
||||
args.app.save()
|
||||
return True
|
||||
return 0
|
||||
|
||||
if args.delete:
|
||||
delattr(args.vm, args.property)
|
||||
args.app.save()
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
print(str(getattr(args.vm, args.property)))
|
||||
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(not main())
|
||||
sys.exit(main())
|
||||
|
133
qubes/tools/qvm_run.py
Normal file
133
qubes/tools/qvm_run.py
Normal file
@ -0,0 +1,133 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010-2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2015 Wojtek Porczyk <woju@invisiblethingslab.com>
|
||||
#
|
||||
# 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 __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import qubes
|
||||
import qubes.exc
|
||||
import qubes.tools
|
||||
|
||||
|
||||
parser = qubes.tools.QubesArgumentParser(
|
||||
want_vm=True,
|
||||
want_vm_all=True)
|
||||
|
||||
parser.add_argument('--user', '-u', metavar='USER',
|
||||
help='run command in a qube as USER')
|
||||
|
||||
parser.add_argument('--autostart', '--auto', '-a',
|
||||
action='store_true', default=False,
|
||||
help='start the qube if it is not running')
|
||||
|
||||
parser.add_argument('--pass-io', '-p',
|
||||
action='store_true', dest='passio', default=False,
|
||||
help='pass stdio from remote program')
|
||||
|
||||
parser.add_argument('--localcmd', metavar='COMMAND',
|
||||
help='with --pass-io, pass stdio to the given program')
|
||||
|
||||
parser.add_argument('--gui',
|
||||
action='store_true', default=True,
|
||||
help='run the command with GUI (default on)')
|
||||
|
||||
parser.add_argument('--no-gui', '--nogui',
|
||||
action='store_false', dest='gui',
|
||||
help='run the command without GUI')
|
||||
|
||||
parser.add_argument('--colour-output', '--color-output', metavar='COLOUR',
|
||||
action='store', dest='color_output', default=None,
|
||||
help='mark the qube output with given ANSI colour (ie. "31" for red)')
|
||||
|
||||
parser.add_argument('--no-colour-output', '--no-color-output',
|
||||
action='store_false', dest='color_output',
|
||||
help='disable colouring the stdio')
|
||||
|
||||
parser.add_argument('--filter-escape-chars',
|
||||
action='store_true', dest='filter_esc',
|
||||
default=os.isatty(sys.stdout.fileno()),
|
||||
help='filter terminal escape sequences (default if output is terminal)')
|
||||
|
||||
parser.add_argument('--no-filter-escape-chars',
|
||||
action='store_false', dest='filter_esc',
|
||||
help='do not filter terminal escape sequences; DANGEROUS when output is a'
|
||||
' terminal emulator')
|
||||
|
||||
parser.add_argument('cmd', metavar='COMMAND',
|
||||
help='command to run')
|
||||
|
||||
#
|
||||
# parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
|
||||
# parser.add_option ("--tray", action="store_true", dest="tray", default=False,
|
||||
# help="Use tray notifications instead of stdout" )
|
||||
# parser.add_option ("--pause", action="store_true", dest="pause", default=False,
|
||||
# help="Do 'xl pause' for the VM(s) (can be combined this with --all)")
|
||||
# parser.add_option ("--unpause", action="store_true", dest="unpause", default=False,
|
||||
# help="Do 'xl unpause' for the VM(s) (can be combined this with --all)")
|
||||
# parser.add_option ("--nogui", action="store_false", dest="gui", default=True,
|
||||
# help="Run command without gui")
|
||||
##
|
||||
|
||||
def main(args=None):
|
||||
args = parser.parse_args(args)
|
||||
if args.color_output is None and args.filter_esc:
|
||||
args.color_output = '31'
|
||||
|
||||
if args.vm is qubes.tools.VM_ALL and args.passio:
|
||||
parser.error('--all and --passio are mutually exclusive')
|
||||
if args.color_output and not args.filter_esc:
|
||||
parser.error('--color-output must be used with --filter-escape-chars')
|
||||
|
||||
retcode = 0
|
||||
for vm in args.vm:
|
||||
if args.color_output:
|
||||
sys.stdout.write('\033[0;{}m'.format(args.color_output))
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
retcode = max(retcode, vm.run(args.cmd,
|
||||
user=args.user,
|
||||
autostart=args.autostart,
|
||||
passio=args.passio,
|
||||
localcmd=args.localcmd,
|
||||
gui=args.gui,
|
||||
filter_esc=args.filter_esc))
|
||||
|
||||
except qubes.exc.QubesException as e:
|
||||
if args.color_output:
|
||||
sys.stdout.write('\033[0m')
|
||||
sys.stdout.flush()
|
||||
vm.log.error(str(e))
|
||||
return -1
|
||||
|
||||
finally:
|
||||
if args.color_output:
|
||||
sys.stdout.write('\033[0m')
|
||||
sys.stdout.flush()
|
||||
|
||||
return retcode
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -134,8 +134,8 @@ def main(args=None):
|
||||
except qubes.exc.QubesException as e:
|
||||
parser.error_runtime('Qubes error: {!r}'.format(e))
|
||||
|
||||
return True
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(not main())
|
||||
sys.exit(main())
|
||||
|
49
qubes/tools/qvm_unpause.py
Normal file
49
qubes/tools/qvm_unpause.py
Normal file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/python2 -O
|
||||
# vim: fileencoding=utf-8
|
||||
|
||||
#
|
||||
# The Qubes OS Project, https://www.qubes-os.org/
|
||||
#
|
||||
# Copyright (C) 2010-2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2015 Wojtek Porczyk <woju@invisiblethingslab.com>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
'''qvm-unpause - Unause a domain'''
|
||||
|
||||
import sys
|
||||
import qubes
|
||||
|
||||
|
||||
parser = qubes.tools.QubesArgumentParser(
|
||||
want_vm=True,
|
||||
description='unpause a domain')
|
||||
|
||||
def main(args=None):
|
||||
'''Main routine of :program:`qvm-unpause`.
|
||||
|
||||
:param list args: Optional arguments to override those delivered from \
|
||||
command line.
|
||||
'''
|
||||
|
||||
args = parser.parse_args(args)
|
||||
args.vm.unpause()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -932,21 +932,13 @@ class QubesVM(qubes.vm.BaseVM):
|
||||
if os.isatty(sys.stderr.fileno()):
|
||||
args += ['-T']
|
||||
|
||||
# TODO: QSB#13
|
||||
if passio:
|
||||
if os.name == 'nt':
|
||||
# wait for qrexec-client to exit, otherwise client is not
|
||||
# properly attached to console if qvm-run is executed from
|
||||
# cmd.exe
|
||||
ret = subprocess.call(args)
|
||||
exit(ret)
|
||||
os.execv(qubes.config.system_path['qrexec_client_path'], args)
|
||||
exit(1)
|
||||
|
||||
call_kwargs = {}
|
||||
if ignore_stderr:
|
||||
null = open("/dev/null", "w")
|
||||
if ignore_stderr or not passio:
|
||||
null = open("/dev/null", "rw")
|
||||
call_kwargs['stderr'] = null
|
||||
if not passio:
|
||||
call_kwargs['stdin'] = null
|
||||
call_kwargs['stdout'] = null
|
||||
|
||||
if passio_popen:
|
||||
popen_kwargs = {'stdout': subprocess.PIPE}
|
||||
@ -959,7 +951,7 @@ class QubesVM(qubes.vm.BaseVM):
|
||||
if null:
|
||||
null.close()
|
||||
return p
|
||||
if not wait:
|
||||
if not wait and not passio:
|
||||
args += ["-e"]
|
||||
retcode = subprocess.call(args, **call_kwargs)
|
||||
if null:
|
||||
|
@ -1,199 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
#
|
||||
# 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.notify import notify_error_qubes_manager
|
||||
from qubes.notify import tray_notify,tray_notify_error,tray_notify_init
|
||||
from optparse import OptionParser
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
|
||||
def vm_run_cmd(vm, cmd, options):
|
||||
if options.pause:
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "Pausing VM: '{0}'...".format(vm.name)
|
||||
vm.pause()
|
||||
return 0
|
||||
|
||||
if options.unpause:
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "UnPausing VM: '{0}'...".format(vm.name)
|
||||
vm.unpause()
|
||||
return 0
|
||||
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "Running command on VM: '{0}'...".format(vm.name)
|
||||
if options.passio and options.color_output is not None:
|
||||
print "\033[0;%dm" % options.color_output,
|
||||
|
||||
try:
|
||||
def tray_notify_generic(level, str):
|
||||
if level == "info":
|
||||
tray_notify(str, label=vm.label)
|
||||
elif level == "error":
|
||||
tray_notify_error(str)
|
||||
|
||||
return vm.run(cmd, autostart = options.auto,
|
||||
verbose = options.verbose,
|
||||
user = options.user,
|
||||
notify_function = tray_notify_generic if options.tray else None,
|
||||
passio = options.passio, localcmd = options.localcmd,
|
||||
gui = options.gui, filter_esc = options.filter_esc)
|
||||
except QubesException as err:
|
||||
if options.passio and options.color_output is not None:
|
||||
sys.stdout.write("\033[0m")
|
||||
if options.tray:
|
||||
tray_notify_error(str(err))
|
||||
notify_error_qubes_manager(vm.name, str(err))
|
||||
print >> sys.stderr, "ERROR(%s): %s" % (str(vm.name), str(err))
|
||||
return 1
|
||||
finally:
|
||||
if options.passio and options.color_output is not None:
|
||||
sys.stdout.write("\033[0m")
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] [<vm-name>] [<cmd>]"
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
|
||||
parser.add_option ("-a", "--auto", action="store_true", dest="auto", default=False,
|
||||
help="Auto start the VM if not running")
|
||||
parser.add_option ("-u", "--user", action="store", dest="user", default=None,
|
||||
help="Run command in a VM as a specified user")
|
||||
parser.add_option ("--tray", action="store_true", dest="tray", default=False,
|
||||
help="Use tray notifications instead of stdout" )
|
||||
|
||||
parser.add_option ("--all", action="store_true", dest="run_on_all_running", default=False,
|
||||
help="Run command on all currently running VMs (or all paused, in case of --unpause)")
|
||||
|
||||
parser.add_option ("--exclude", action="append", dest="exclude_list",
|
||||
help="When --all is used: exclude this VM name (may be "
|
||||
"repeated)")
|
||||
|
||||
parser.add_option ("--pause", action="store_true", dest="pause", default=False,
|
||||
help="Do 'xl pause' for the VM(s) (can be combined this with --all)")
|
||||
|
||||
parser.add_option ("--unpause", action="store_true", dest="unpause", default=False,
|
||||
help="Do 'xl unpause' for the VM(s) (can be combined this with --all)")
|
||||
|
||||
parser.add_option ("-p", "--pass-io", action="store_true", dest="passio", default=False,
|
||||
help="Pass stdin/stdout/stderr from remote program (implies -q)")
|
||||
|
||||
parser.add_option ("--localcmd", action="store", dest="localcmd", default=None,
|
||||
help="With --pass-io, pass stdin/stdout/stderr to the given program")
|
||||
|
||||
parser.add_option ("--nogui", action="store_false", dest="gui", default=True,
|
||||
help="Run command without gui")
|
||||
|
||||
parser.add_option ("--filter-escape-chars", action="store_true",
|
||||
dest="filter_esc",
|
||||
default=os.isatty(sys.stdout.fileno()),
|
||||
help="Filter terminal escape sequences (default if "
|
||||
"output is terminal)")
|
||||
parser.add_option("--no-filter-escape-chars", action="store_false",
|
||||
dest="filter_esc",
|
||||
help="Do not filter terminal escape sequences - "
|
||||
"overrides --filter-escape-chars, DANGEROUS when "
|
||||
"output is terminal")
|
||||
|
||||
parser.add_option("--no-color-output", action="store_false",
|
||||
dest="color_output", default=None,
|
||||
help="Disable marking VM output with red color")
|
||||
|
||||
parser.add_option("--color-output", action="store", type="int",
|
||||
dest="color_output",
|
||||
help="Force marking VM output with given ANSI style ("
|
||||
"use 31 for red)")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
|
||||
if options.passio and options.run_on_all_running:
|
||||
parser.error ("Options --all and --pass-io cannot be used together")
|
||||
|
||||
if options.passio:
|
||||
options.verbose = False
|
||||
|
||||
if options.color_output is None:
|
||||
if os.isatty(sys.stdout.fileno()):
|
||||
options.color_output = 31
|
||||
elif options.color_output is False:
|
||||
options.color_output = None
|
||||
|
||||
if (options.pause or options.unpause):
|
||||
takes_cmd_argument = False
|
||||
else:
|
||||
takes_cmd_argument = True
|
||||
|
||||
if options.run_on_all_running:
|
||||
if len(args) < 1 and takes_cmd_argument:
|
||||
parser.error ("You must provide a command to execute on all the VMs.")
|
||||
if len(args) > 1 or ((not takes_cmd_argument) and len(args) > 0):
|
||||
parser.error ("To many arguments...")
|
||||
cmdstr = args[0] if takes_cmd_argument else None
|
||||
else:
|
||||
if len (args) < 1 and not takes_cmd_argument:
|
||||
parser.error ("You must specify the VM name to pause/unpause.")
|
||||
if len (args) < 2 and takes_cmd_argument:
|
||||
parser.error ("You must specify the VM name and the command to execute in the VM.")
|
||||
if len (args) > 2 or ((not takes_cmd_argument) and len(args) > 1):
|
||||
parser.error ("To many arguments...")
|
||||
vmname = args[0]
|
||||
cmdstr = args[1] if takes_cmd_argument else None
|
||||
|
||||
if options.tray:
|
||||
tray_notify_init()
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vms_list = []
|
||||
if options.run_on_all_running:
|
||||
all_vms = [vm for vm in qvm_collection.values()]
|
||||
for vm in all_vms:
|
||||
if options.exclude_list is not None and vm.name in options.exclude_list:
|
||||
continue
|
||||
if vm.qid == 0:
|
||||
continue
|
||||
if (options.unpause and vm.is_paused()) or (not options.unpause and vm.is_running()):
|
||||
vms_list.append (vm)
|
||||
# disable options incompatible with --all
|
||||
options.passio = False
|
||||
else:
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
if vm is None:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system!".format(vmname)
|
||||
exit(1)
|
||||
vms_list.append(vm)
|
||||
|
||||
retcode = 0
|
||||
for vm in vms_list:
|
||||
r = vm_run_cmd(vm, cmdstr, options)
|
||||
retcode = max(r, retcode)
|
||||
|
||||
exit(retcode)
|
||||
|
||||
main()
|
@ -236,8 +236,11 @@ fi
|
||||
%{python_sitelib}/qubes/tools/qvm_create.py*
|
||||
%{python_sitelib}/qubes/tools/qvm_kill.py*
|
||||
%{python_sitelib}/qubes/tools/qvm_ls.py*
|
||||
%{python_sitelib}/qubes/tools/qvm_pause.py*
|
||||
%{python_sitelib}/qubes/tools/qvm_prefs.py*
|
||||
%{python_sitelib}/qubes/tools/qvm_run.py*
|
||||
%{python_sitelib}/qubes/tools/qvm_start.py*
|
||||
%{python_sitelib}/qubes/tools/qvm_unpause.py*
|
||||
|
||||
%dir %{python_sitelib}/qubes/ext
|
||||
%{python_sitelib}/qubes/ext/__init__.py*
|
||||
|
Loading…
Reference in New Issue
Block a user