core-admin/qvm-tools/qvm-shutdown
Marek Marczykowski-Górecki 68460fb272 qvm-tools/qvm-shutdown: handle domains with xl daemon killed (#903)
When system is going down, systemd kills all the users processes,
including 'xl' daemons waiting for domain shutdown. This results in
zombie domains not cleaned up. The proper fix would be somehow extract
those processes from user session scope (most likely by starting them as
a service).

But because it applies only to system shutdown (qvm-shutdown
call there), it is simpler to add appropriate handling code to
qvm-shutdown.

In R3 the problem will vanish, because of use libvirtd deamon, so no
user processes required to track domains state.
2014-09-26 02:18:42 +02:00

121 lines
4.6 KiB
Python
Executable File

#!/usr/bin/python2
# -*- encoding: utf8 -*-
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2011 Marek Marczykowski <marmarek@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,QubesException
from qubes.qubes import defaults
from optparse import OptionParser;
import sys
import time
def main():
usage = "usage: %prog [options] <vm-name>"
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 ("--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 > defaults["shutdown_counter_max"]:
# 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()