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
|
||||||
|
|
||||||
=====================================================
|
:program:`qvm-run` -- Run a command in a specified VM
|
||||||
:program:`qvm-run` -- Run a command on a specified VM
|
|
||||||
=====================================================
|
=====================================================
|
||||||
|
|
||||||
Synopsis
|
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
|
Options
|
||||||
=======
|
-------
|
||||||
|
|
||||||
.. option:: --help, -h
|
.. option:: --help, -h
|
||||||
|
|
||||||
Show this help message and exit
|
Show the help message and exit.
|
||||||
|
|
||||||
|
.. option:: --verbose, -v
|
||||||
|
|
||||||
|
Increase verbosity.
|
||||||
|
|
||||||
.. option:: --quiet, -q
|
.. option:: --quiet, -q
|
||||||
|
|
||||||
Be quiet
|
Decrease verbosity.
|
||||||
|
|
||||||
.. 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
|
|
||||||
|
|
||||||
.. option:: --all
|
.. 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
|
Start the qube if it is not running.
|
||||||
: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`)
|
|
||||||
|
|
||||||
.. option:: --pass-io, -p
|
.. 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
|
Authors
|
||||||
=======
|
-------
|
||||||
|
|
||||||
| Joanna Rutkowska <joanna at invisiblethingslab dot com>
|
| Joanna Rutkowska <joanna at invisiblethingslab dot com>
|
||||||
| Rafal Wojtczuk <rafal at invisiblethingslab dot com>
|
| Rafal Wojtczuk <rafal at invisiblethingslab dot com>
|
||||||
| Marek Marczykowski <marmarek 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
|
@qubes.tests.skipUnlessDom0
|
||||||
class TC_00_qubes_create(qubes.tests.SystemTestsMixin, qubes.tests.QubesTestCase):
|
class TC_00_qubes_create(qubes.tests.SystemTestsMixin, qubes.tests.QubesTestCase):
|
||||||
def test_000_basic(self):
|
def test_000_basic(self):
|
||||||
self.assertTrue(qubes.tools.qubes_create.main((
|
self.assertEqual(0, qubes.tools.qubes_create.main((
|
||||||
'--qubesxml', qubes.tests.XMLPATH)))
|
'--qubesxml', qubes.tests.XMLPATH)))
|
||||||
|
|
||||||
def test_001_property(self):
|
def test_001_property(self):
|
||||||
self.assertTrue(qubes.tools.qubes_create.main((
|
self.assertEqual(0, qubes.tools.qubes_create.main((
|
||||||
'--qubesxml', qubes.tests.XMLPATH,
|
'--qubesxml', qubes.tests.XMLPATH,
|
||||||
'--property', 'default_kernel=testkernel')))
|
'--property', 'default_kernel=testkernel')))
|
||||||
|
|
||||||
|
@ -32,6 +32,9 @@ import os
|
|||||||
|
|
||||||
import qubes.log
|
import qubes.log
|
||||||
|
|
||||||
|
#: constant returned when some action should be performed on all qubes
|
||||||
|
VM_ALL = object()
|
||||||
|
|
||||||
|
|
||||||
class PropertyAction(argparse.Action):
|
class PropertyAction(argparse.Action):
|
||||||
'''Action for argument parser that stores a property.'''
|
'''Action for argument parser that stores a property.'''
|
||||||
@ -148,6 +151,7 @@ class QubesArgumentParser(argparse.ArgumentParser):
|
|||||||
want_app_no_instance=False,
|
want_app_no_instance=False,
|
||||||
want_force_root=False,
|
want_force_root=False,
|
||||||
want_vm=False,
|
want_vm=False,
|
||||||
|
want_vm_all=False,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
super(QubesArgumentParser, self).__init__(**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_app_no_instance = want_app_no_instance
|
||||||
self._want_force_root = want_force_root
|
self._want_force_root = want_force_root
|
||||||
self._want_vm = want_vm
|
self._want_vm = want_vm
|
||||||
|
self._want_vm_all = want_vm_all
|
||||||
|
|
||||||
if self._want_app:
|
if self._want_app:
|
||||||
self.add_argument('--qubesxml', metavar='FILE',
|
self.add_argument('--qubesxml', metavar='FILE',
|
||||||
@ -176,8 +181,21 @@ class QubesArgumentParser(argparse.ArgumentParser):
|
|||||||
help='force to run as root')
|
help='force to run as root')
|
||||||
|
|
||||||
if self._want_vm:
|
if self._want_vm:
|
||||||
self.add_argument('vm', metavar='VMNAME',
|
if self._want_vm_all:
|
||||||
action='store',
|
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')
|
help='name of the domain')
|
||||||
|
|
||||||
self.set_defaults(verbose=1, quiet=0)
|
self.set_defaults(verbose=1, quiet=0)
|
||||||
@ -191,6 +209,21 @@ class QubesArgumentParser(argparse.ArgumentParser):
|
|||||||
namespace.app = qubes.Qubes(namespace.app)
|
namespace.app = qubes.Qubes(namespace.app)
|
||||||
|
|
||||||
if self._want_vm:
|
if self._want_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:
|
try:
|
||||||
namespace.vm = namespace.app.domains[namespace.vm]
|
namespace.vm = namespace.app.domains[namespace.vm]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -49,8 +49,8 @@ def main(args=None):
|
|||||||
|
|
||||||
args = parser.parse_args(args)
|
args = parser.parse_args(args)
|
||||||
qubes.Qubes.create_empty_store(args.app, **args.properties)
|
qubes.Qubes.create_empty_store(args.app, **args.properties)
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(not main())
|
sys.exit(main())
|
||||||
|
@ -58,8 +58,8 @@ parser.add_argument('--unset', '--default', '--delete', '-D',
|
|||||||
' instead')
|
' instead')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(args=None):
|
||||||
args = parser.parse_args()
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
if args.property is None:
|
if args.property is None:
|
||||||
properties = args.app.property_list()
|
properties = args.app.property_list()
|
||||||
@ -80,24 +80,24 @@ def main():
|
|||||||
print('{name:{width}s} - {value!r}'.format(
|
print('{name:{width}s} - {value!r}'.format(
|
||||||
name=prop.__name__, width=width, value=value))
|
name=prop.__name__, width=width, value=value))
|
||||||
|
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if args.value is not None:
|
if args.value is not None:
|
||||||
setattr(args.app, args.property, args.value)
|
setattr(args.app, args.property, args.value)
|
||||||
args.app.save()
|
args.app.save()
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
if args.delete:
|
if args.delete:
|
||||||
delattr(args.app, args.property)
|
delattr(args.app, args.property)
|
||||||
args.app.save()
|
args.app.save()
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
print(str(getattr(args.app, args.property)))
|
print(str(getattr(args.app, args.property)))
|
||||||
|
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(not main())
|
sys.exit(main())
|
||||||
|
@ -147,8 +147,8 @@ def main(args=None):
|
|||||||
|
|
||||||
args.app.save()
|
args.app.save()
|
||||||
|
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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:
|
except (IOError, OSError, qubes.exc.QubesException) as e:
|
||||||
parser.error_runtime(str(e))
|
parser.error_runtime(str(e))
|
||||||
|
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys.exit(not main())
|
sys.exit(main())
|
||||||
|
@ -606,8 +606,8 @@ def main(args=None):
|
|||||||
table = Table(args.app, columns)
|
table = Table(args.app, columns)
|
||||||
table.write_table(sys.stdout)
|
table.write_table(sys.stdout)
|
||||||
|
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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')
|
' instead')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(args=None):
|
||||||
args = parser.parse_args()
|
args = parser.parse_args(args)
|
||||||
|
|
||||||
if args.property is None:
|
if args.property is None:
|
||||||
properties = args.vm.property_list()
|
properties = args.vm.property_list()
|
||||||
@ -83,24 +83,24 @@ def main():
|
|||||||
print('{name:{width}s} - {value!r}'.format(
|
print('{name:{width}s} - {value!r}'.format(
|
||||||
name=prop.__name__, width=width, value=value))
|
name=prop.__name__, width=width, value=value))
|
||||||
|
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if args.value is not None:
|
if args.value is not None:
|
||||||
setattr(args.vm, args.property, args.value)
|
setattr(args.vm, args.property, args.value)
|
||||||
args.app.save()
|
args.app.save()
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
if args.delete:
|
if args.delete:
|
||||||
delattr(args.vm, args.property)
|
delattr(args.vm, args.property)
|
||||||
args.app.save()
|
args.app.save()
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
print(str(getattr(args.vm, args.property)))
|
print(str(getattr(args.vm, args.property)))
|
||||||
|
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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:
|
except qubes.exc.QubesException as e:
|
||||||
parser.error_runtime('Qubes error: {!r}'.format(e))
|
parser.error_runtime('Qubes error: {!r}'.format(e))
|
||||||
|
|
||||||
return True
|
return 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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()):
|
if os.isatty(sys.stderr.fileno()):
|
||||||
args += ['-T']
|
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 = {}
|
call_kwargs = {}
|
||||||
if ignore_stderr:
|
if ignore_stderr or not passio:
|
||||||
null = open("/dev/null", "w")
|
null = open("/dev/null", "rw")
|
||||||
call_kwargs['stderr'] = null
|
call_kwargs['stderr'] = null
|
||||||
|
if not passio:
|
||||||
|
call_kwargs['stdin'] = null
|
||||||
|
call_kwargs['stdout'] = null
|
||||||
|
|
||||||
if passio_popen:
|
if passio_popen:
|
||||||
popen_kwargs = {'stdout': subprocess.PIPE}
|
popen_kwargs = {'stdout': subprocess.PIPE}
|
||||||
@ -959,7 +951,7 @@ class QubesVM(qubes.vm.BaseVM):
|
|||||||
if null:
|
if null:
|
||||||
null.close()
|
null.close()
|
||||||
return p
|
return p
|
||||||
if not wait:
|
if not wait and not passio:
|
||||||
args += ["-e"]
|
args += ["-e"]
|
||||||
retcode = subprocess.call(args, **call_kwargs)
|
retcode = subprocess.call(args, **call_kwargs)
|
||||||
if null:
|
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_create.py*
|
||||||
%{python_sitelib}/qubes/tools/qvm_kill.py*
|
%{python_sitelib}/qubes/tools/qvm_kill.py*
|
||||||
%{python_sitelib}/qubes/tools/qvm_ls.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_prefs.py*
|
||||||
|
%{python_sitelib}/qubes/tools/qvm_run.py*
|
||||||
%{python_sitelib}/qubes/tools/qvm_start.py*
|
%{python_sitelib}/qubes/tools/qvm_start.py*
|
||||||
|
%{python_sitelib}/qubes/tools/qvm_unpause.py*
|
||||||
|
|
||||||
%dir %{python_sitelib}/qubes/ext
|
%dir %{python_sitelib}/qubes/ext
|
||||||
%{python_sitelib}/qubes/ext/__init__.py*
|
%{python_sitelib}/qubes/ext/__init__.py*
|
||||||
|
Loading…
Reference in New Issue
Block a user