diff --git a/doc/manpages/qvm-shutdown.rst b/doc/manpages/qvm-shutdown.rst index 86cc2523..8ac86e0f 100644 --- a/doc/manpages/qvm-shutdown.rst +++ b/doc/manpages/qvm-shutdown.rst @@ -1,42 +1,64 @@ .. program:: qvm-shutdown -==================================================== -:program:`qvm-shutdown` -- Gracefully shut down a VM -==================================================== +:program:`qvm-shutdown` -- Gracefully shut down a qube +====================================================== + +.. warning:: + + This page was autogenerated from command-line parser. It shouldn't be 1:1 + conversion, because it would add little value. Please revise it and add + more descriptive help, which normally won't fit in standard ``--help`` + option. + + After rewrite, please remove this admonition. Synopsis -======== -:command:`qvm-shutdown` [*options*] <*vm-name*> +-------- + +:command:`qvm-shutdown` [-h] [--verbose] [--quiet] [--all] [--exclude *EXCLUDE*] [--force] [--wait] [--timeout *TIMEOUT*] [*VMNAME*] 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:: --force - - Force operation, even if may damage other VMs (eg. shutdown of NetVM) - -.. option:: --wait - - Wait for the VM(s) to shutdown + decrease verbosity .. option:: --all - Shutdown all running VMs + perform the action on all qubes -.. option:: --exclude=EXCLUDE_LIST +.. option:: --exclude=EXCLUDE - When :option:`--all` is used: exclude this VM name (might be repeated) + exclude the qube from :option:`--all` + +.. option:: --force + + force operation, even if may damage other VMs (eg. shutdown of network + provider) + +.. option:: --wait + + wait for the VMs to shut down + +.. option:: --timeout + + timeout after which domains are killed when using :option:`--wait` Authors -======= +------- + | Joanna Rutkowska | Rafal Wojtczuk | Marek Marczykowski +| Wojtek Porczyk + +.. vim: ts=3 sw=3 et tw=80 diff --git a/qubes/tools/__init__.py b/qubes/tools/__init__.py index 78757229..013efeb3 100644 --- a/qubes/tools/__init__.py +++ b/qubes/tools/__init__.py @@ -189,7 +189,7 @@ class QubesArgumentParser(argparse.ArgumentParser): vmchoice.add_argument('--all', action='store_const', const=VM_ALL, dest='vm', help='perform the action on all qubes') - vmchoice.add_argument('--exclude', + self.add_argument('--exclude', action='append', default=[], help='exclude the qube from --all') nargs = '?' diff --git a/qubes/tools/qvm_shutdown.py b/qubes/tools/qvm_shutdown.py new file mode 100644 index 00000000..40c88840 --- /dev/null +++ b/qubes/tools/qvm_shutdown.py @@ -0,0 +1,82 @@ +#!/usr/bin/python2 +# vim: fileencoding=utf8 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010-2016 Joanna Rutkowska +# Copyright (C) 2011-2016 Marek Marczykowski-Górecki +# +# Copyright (C) 2016 Wojtek Porczyk +# +# 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 qubes.config +import qubes.tools + +import sys +import time + + +parser = qubes.tools.QubesArgumentParser( + description='gracefully shut down a qube', + want_vm=True, + want_vm_all=True) + +parser.add_argument('--force', + action='store_true', default=False, + help='force operation, even if may damage other VMs (eg. shutdown of' + ' network provider)') + +parser.add_argument('--wait', + action='store_true', default=False, + help='wait for the VMs to shut down') + +parser.add_argument('--timeout', + action='store', type=float, + default=qubes.config.defaults['shutdown_counter_max'], + help='timeout after which domains are killed when using --wait' + ' (default: %d)') + + +def main(args=None): + args = parser.parse_args(args) + + for vm in args.vm: + vm.shutdown(force=args.force) + + if not args.wait: + return + + timeout = args.timeout + current_vms = list(sorted(args.vm)) + while timeout >= 0: + current_vms = [vm for vm in current_vms + if vm.get_power_state() != 'Halted'] + args.app.log.info('Waiting for shutdown ({}): {}'.format( + timeout, ', '.join(map(str, current_vms)))) + time.sleep(1) + timeout -= 1 + + args.app.log.notice( + 'Killing remaining qubes: {}'.format(', '.join(map(str, current_vms)))) + for vm in current_vms: + vm.force_shutdown() + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/qvm-tools/qvm-shutdown b/qvm-tools/qvm-shutdown deleted file mode 100755 index da3eeec2..00000000 --- a/qvm-tools/qvm-shutdown +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/python2 -# -*- encoding: utf8 -*- -# -# The Qubes OS Project, http://www.qubes-os.org -# -# Copyright (C) 2011 Marek Marczykowski -# -# 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,QubesException -from qubes.qubes import defaults -from optparse import OptionParser; -import sys -import time - -def main(): - usage = "usage: %prog [options] " - parser = OptionParser (usage) - - parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True) - parser.add_option ("--force", action="store_true", dest="force", default=False, - help="Force operation, even if may damage other VMs (eg shutdown of NetVM)") - parser.add_option ("--wait", action="store_true", dest="wait_for_shutdown", default=False, - help="Wait for the VM(s) to shutdown") - parser.add_option("--wait-time", action="store", dest="wait_time", - default=defaults["shutdown_counter_max"], - help="Timout after which VM will be killed when --wait " - "is used") - parser.add_option ("--all", action="store_true", dest="shutdown_all", default=False, - help="Shutdown all running VMs") - parser.add_option ("--exclude", action="append", dest="exclude_list", - help="When --all is used: exclude this VM name (may be " - "repeated)") - - (options, args) = parser.parse_args () - if not options.shutdown_all and (len (args) != 1): - parser.error ("You must specify VM name!") - - qvm_collection = QubesVmCollection() - qvm_collection.lock_db_for_reading() - qvm_collection.load() - qvm_collection.unlock_db() - - vms_list = [] - if options.shutdown_all: - 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 vm.is_running(): - vms_list.append (vm) - else: - vmname = args[0] - 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) - - # Check VMs network dependencies - if not options.force: - for vm in vms_list: - if vm.is_netvm(): - for connected_vm in vm.connected_vms.values(): - if connected_vm.is_running() and not connected_vm in vms_list: - print >> sys.stderr, "ERROR: There are other VMs connected to VM '%s'" % vm.name - exit(1) - - for vm in vms_list: - try: - if options.verbose: - print >> sys.stderr, "Shutting down VM: '{0}'...".format(vm.name) - # Dependencies already checked above - vm.shutdown(force=True) - except (IOError, OSError, QubesException) as err: - print >> sys.stderr, "ERROR: {0}".format(err) - exit (1) - - if options.wait_for_shutdown: - if options.verbose: - print >> sys.stderr, "Waiting for the VM(s) to shutdown..." - shutdown_counter = 0 - - halting_vms = [] - while len (vms_list): - if options.verbose: - print >> sys.stderr, "Waiting for VMs: ", [vm.name for vm in vms_list] - for vm in vms_list: - if not vm.is_running(): - vms_list.remove (vm) - continue - if vm.get_power_state() == "Halting": - if vm in halting_vms: - vm.force_shutdown() - continue - else: - halting_vms.append(vm) - if shutdown_counter > int(options.wait_time): - # kill the VM - if options.verbose: - print >> sys.stderr, "Killing the (apparently hanging) VM '{0}'...".format(vm.name) - vm.force_shutdown() - #vms_list.remove(vm) - - shutdown_counter += 1 - time.sleep (1) - -main() diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index a63a42c0..942e3f66 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -247,6 +247,7 @@ fi %{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_shutdown.py* %{python_sitelib}/qubes/tools/qvm_start.py* %{python_sitelib}/qubes/tools/qvm_unpause.py*