virt-manager/virt-manager
Cole Robinson 20caba1831 Once again, fix issues with forking and gtk
This time it requires forking before doing any gtk imports. Hopefully
this is the last time these issues pop up.
2013-04-18 19:01:16 -04:00

310 lines
10 KiB
Python
Executable File

#!/usr/bin/python
#
# Copyright (C) 2006 Red Hat, Inc.
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.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.
#
import logging
import optparse
import os
import signal
import sys
import traceback
# pylint: disable=E0611
from gi.repository import GObject
from gi.repository import LibvirtGLib
# pylint: enable=E0611
from virtcli import cliutils, cliconfig
GObject.threads_init()
try:
# Make sure we have a default '_' implementation, in case something
# fails before gettext is set up
__builtins__._ = lambda msg: msg
except:
pass
logging_setup = False
def _show_startup_error(msg, details):
if logging_setup:
logging.exception("Error starting virt-manager")
from virtManager.error import vmmErrorDialog
err = vmmErrorDialog()
title = _("Error starting Virtual Machine Manager")
err.show_err(title + ": " + msg,
details=details,
title=title,
async=False,
debug=False)
def drop_tty():
# We fork and setsid so that we drop the controlling
# tty. This prevents libvirt's SSH tunnels from prompting
# for user input if SSH keys/agent aren't configured.
if os.fork() != 0:
os._exit(0) # pylint: disable=W0212
os.setsid()
def drop_stdio():
# We close STDIN/OUT/ERR since they're generally spewing
# junk to console when domains are in process of shutting
# down. Real errors will (hopefully) all be logged to the
# main log file. This is also again to stop SSH prompting
# for input
for fd in range(0, 2):
try:
os.close(fd)
except OSError:
pass
os.open(os.devnull, os.O_RDWR)
os.dup2(0, 1)
os.dup2(0, 2)
class PassThroughOptionParser(optparse.OptionParser):
# From http://stackoverflow.com/questions/1885161/how-can-i-get-optparses-optionparser-to-ignore-invalid-options
def _process_args(self, largs, rargs, values):
while rargs:
try:
optparse.OptionParser._process_args(self, largs, rargs, values)
except (optparse.BadOptionError, optparse.AmbiguousOptionError), e:
largs.append(e.opt_str)
def parse_commandline():
optParser = PassThroughOptionParser(version=cliconfig.__version__,
usage="virt-manager [options]")
optParser.set_defaults(uuid=None)
optParser.epilog = ("Also accepts standard GTK arguments like "
"--g-fatal-warnings")
# Generate runtime performance profile stats with hotshot
optParser.add_option("--profile", dest="profile",
help=optparse.SUPPRESS_HELP, metavar="FILE")
# Trace every libvirt API call to debug output
optParser.add_option("--trace-libvirt", dest="tracelibvirt",
help=optparse.SUPPRESS_HELP, action="store_true")
# Don't load any connections on startup to test first run
# PackageKit integration
optParser.add_option("--test-first-run", dest="testfirstrun",
help=optparse.SUPPRESS_HELP, action="store_true")
optParser.add_option("-c", "--connect", dest="uri",
help="Connect to hypervisor at URI", metavar="URI")
optParser.add_option("--debug", action="store_true", dest="debug",
help="Print debug output to stdout (implies --no-fork)",
default=False)
optParser.add_option("--no-fork", action="store_true", dest="nofork",
help="Don't fork into background on startup")
optParser.add_option("--no-conn-autostart", action="store_true",
dest="no_conn_auto",
help="Do not autostart connections")
optParser.add_option("--show-domain-creator", action="callback",
callback=opt_show_cb, dest="show",
help="Show 'New VM' wizard")
optParser.add_option("--show-domain-editor", type="string",
metavar="UUID", action="callback", callback=opt_show_cb,
help="Show domain details window")
optParser.add_option("--show-domain-performance", type="string",
metavar="UUID", action="callback", callback=opt_show_cb,
help="Show domain performance window")
optParser.add_option("--show-domain-console", type="string",
metavar="UUID", action="callback", callback=opt_show_cb,
help="Show domain graphical console window")
optParser.add_option("--show-host-summary", action="callback",
callback=opt_show_cb, help="Show connection details window")
return optParser.parse_args()
def launch_specific_window(engine, show, uri, uuid):
if not show:
return
logging.debug("Launching requested window '%s'", show)
if show == 'creator':
engine.show_domain_creator(uri)
elif show == 'editor':
engine.show_domain_editor(uri, uuid)
elif show == 'performance':
engine.show_domain_performance(uri, uuid)
elif show == 'console':
engine.show_domain_console(uri, uuid)
elif show == 'summary':
engine.show_host_summary(uri)
def _conn_state_changed(conn, engine, show, uri, uuid):
if conn.state == conn.STATE_DISCONNECTED:
return True
if conn.state != conn.STATE_ACTIVE:
return
launch_specific_window(engine, show, uri, uuid)
return True
def opt_show_cb(option, opt_str, value, parser):
# Generic OptionParser callback for all --show-* options
# This routine stores UUID to options.uuid for all --show-* options
# where is metavar="UUID" and also sets options.show
if option.metavar == "UUID":
setattr(parser.values, "uuid", value)
s = str(option)
show = s[s.rindex('-') + 1:]
setattr(parser.values, "show", show)
def main():
cliutils.setup_i18n()
(options, leftovers) = parse_commandline()
cliutils.setup_logging("virt-manager", options.debug)
global logging_setup
logging_setup = True
import virtManager
logging.debug("Launched as: %s", sys.argv)
logging.debug("virt-manager version: %s", cliconfig.__version__)
logging.debug("virtManager import: %s", str(virtManager))
if options.tracelibvirt:
logging.debug("Libvirt tracing requested")
import virtManager.module_trace
import libvirt
virtManager.module_trace.wrap_module(libvirt)
# Now we've got basic environment up & running we can fork
if not options.nofork and not options.debug:
drop_tty()
drop_stdio()
# Ignore SIGHUP, otherwise a serial console closing drops the whole app
signal.signal(signal.SIGHUP, signal.SIG_IGN)
# The never ending fork+gconf/gsettings problems now require
# us to import Gtk before the fork. This creates a funny race,
# since we need to parse the command line arguments to know if
# we need to fork, but need to import Gtk before cli processing
# so it can handle --g-fatal-args. We strip out our flags first
# and pass the left overs to gtk
origargv = sys.argv
try:
sys.argv = origargv[:1] + leftovers[:]
from gi.repository import Gtk # pylint: disable=E0611
globals()["Gtk"] = Gtk
leftovers = sys.argv[1:]
except:
# Don't just let the exception raise here. abrt reports bugs
# when users mess up su/sudo and DISPLAY isn't set. Printing
# it avoids the issue
print "".join(traceback.format_exc())
return 1
finally:
sys.argv = origargv
if leftovers:
raise RuntimeError("Unhandled command line options '%s'" % leftovers)
logging.debug("GTK version: %d.%d.%d",
Gtk.get_major_version(),
Gtk.get_minor_version(),
Gtk.get_micro_version())
import virtManager.config
import virtManager.util
config = virtManager.config.vmmConfig("virt-manager",
cliconfig.__version__,
os.path.join(cliconfig.asset_dir, "ui"),
options.testfirstrun)
virtManager.util.running_config = config
config.default_qemu_user = cliconfig.default_qemu_user
config.rhel6_defaults = cliconfig.rhel_enable_unsupported_opts
config.preferred_distros = cliconfig.preferred_distros
config.hv_packages = cliconfig.hv_packages
config.libvirt_packages = cliconfig.libvirt_packages
config.askpass_package = cliconfig.askpass_package
config.default_graphics_from_config = cliconfig.default_graphics
# Add our icon dir to icon theme
icon_theme = Gtk.IconTheme.get_default()
icon_theme.prepend_search_path(cliconfig.icon_dir)
from virtManager.engine import vmmEngine
Gtk.Window.set_default_icon_name("virt-manager")
if options.show and options.uri is None:
raise optparse.OptionValueError("can't use --show-* options "
"without --connect")
# Hook libvirt events into glib main loop
LibvirtGLib.init(None)
LibvirtGLib.event_register()
engine = vmmEngine()
engine.skip_autostart = options.no_conn_auto
engine.uri_at_startup = options.uri
if options.show:
def cb(conn):
return _conn_state_changed(conn, engine, options.show,
options.uri, options.uuid)
engine.uri_cb = cb
# Finally start the app for real
if options.profile is not None:
import hotshot
prof = hotshot.Profile(options.profile)
prof.runcall(engine.application.run)
prof.close()
else:
engine.application.run(None)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
logging.debug("Received KeyboardInterrupt. Exiting application.")
except SystemExit:
raise
except Exception, run_e:
if "Gtk" not in globals():
raise
_show_startup_error(str(run_e), "".join(traceback.format_exc()))