#!/usr/bin/env python2 # # Copyright (C) 2006, 2014 Red Hat, Inc. # Copyright (C) 2006 Daniel P. Berrange # # 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 argparse import logging import os import signal import sys import traceback import gi gi.require_version('LibvirtGLib', '1.0') from gi.repository import LibvirtGLib from virtinst import util as util from virtinst import cli as virtinstcli from virtcli import CLIConfig # This is massively heavy handed, but I can't figure out any way to shut # up the slew of gtk deprecation warnings that clog up our very useful # stdout --debug output. Of course we could drop use of deprecated APIs, # but it's a serious quantity of churn import warnings # pylint: disable=wrong-import-order warnings.simplefilter("ignore") try: gi.check_version("3.14.0") except (ValueError, AttributeError): print("pygobject3 3.14.0 or later is required.") sys.exit(1) def _show_startup_error(msg, details): logging.debug("Error starting virt-manager: %s\n%s", msg, details, exc_info=True) from virtManager.error import vmmErrorDialog err = vmmErrorDialog() title = _("Error starting Virtual Machine Manager") err.show_err(title + ": " + msg, details=details, title=title, modal=True, debug=False) def _import_gtk(leftovers): # The never ending fork+gsettings problems now require us to # import Gtk _after_ 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[:] gi.require_version('Gtk', '3.0') from gi.repository import Gtk leftovers = sys.argv[1:] if Gtk.check_version(3, 14, 0): print("gtk3 3.14.0 or later is required.") sys.exit(1) # This will error if Gtk wasn't correctly initialized Gtk.init() globals()["Gtk"] = Gtk import virtManager.config ignore = virtManager.config except Exception as e: # 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 display = os.environ.get("DISPLAY", "") msg = str(e) if display: msg += ": Could not open display: %s" % display logging.debug("".join(traceback.format_exc())) print(msg) sys.exit(1) finally: sys.argv = origargv return leftovers 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=protected-access os.setsid() def drop_stdio(): # This is part of the fork process described in drop_tty() 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) def parse_commandline(): epilog = ("Also accepts standard GTK arguments like --g-fatal-warnings") parser = argparse.ArgumentParser(usage="virt-manager [options]", epilog=epilog) parser.add_argument('--version', action='version', version=CLIConfig.version) parser.set_defaults(domain=None) # Trace every libvirt API call to debug output parser.add_argument("--trace-libvirt", help=argparse.SUPPRESS, action="store_true") # Don't load any connections on startup to test first run # PackageKit integration parser.add_argument("--test-first-run", help=argparse.SUPPRESS, action="store_true") # Force use of old style libvirt polling APIs parser.add_argument("--test-old-poll", help=argparse.SUPPRESS, action="store_true") # Force disable use of libvirt object events parser.add_argument("--test-no-events", help=argparse.SUPPRESS, action="store_true") parser.add_argument("-c", "--connect", dest="uri", help="Connect to hypervisor at URI", metavar="URI") parser.add_argument("--debug", action="store_true", help="Print debug output to stdout (implies --no-fork)", default=False) parser.add_argument("--no-fork", action="store_true", help="Don't fork into background on startup") parser.add_argument("--no-conn-autostart", action="store_true", dest="skip_autostart", help="Do not autostart connections") parser.add_argument("--spice-disable-auto-usbredir", action="store_true", dest="usbredir", help="Disable Auto USB redirection support") parser.add_argument("--show-domain-creator", action="store_true", help="Show 'New VM' wizard") parser.add_argument("--show-domain-editor", metavar="NAME|ID|UUID", help="Show domain details window") parser.add_argument("--show-domain-performance", metavar="NAME|ID|UUID", help="Show domain performance window") parser.add_argument("--show-domain-console", metavar="NAME|ID|UUID", help="Show domain graphical console window") parser.add_argument("--show-host-summary", action="store_true", help="Show connection details window") return parser.parse_known_args() def main(): (options, leftovers) = parse_commandline() virtinstcli.setupLogging("virt-manager", options.debug, False, False) import virtManager logging.debug("virt-manager version: %s", CLIConfig.version) logging.debug("virtManager import: %s", str(virtManager)) if options.trace_libvirt: logging.debug("Libvirt tracing requested") import virtManager.module_trace import libvirt virtManager.module_trace.wrap_module(libvirt, regex=None) # Now we've got basic environment up & running we can fork do_drop_stdio = False if not options.no_fork and not options.debug: drop_tty() do_drop_stdio = True # Ignore SIGHUP, otherwise a serial console closing drops the whole app signal.signal(signal.SIGHUP, signal.SIG_IGN) leftovers = _import_gtk(leftovers) Gtk = globals()["Gtk"] # Do this after the Gtk import so the user has a chance of seeing any error if do_drop_stdio: drop_stdio() if leftovers: raise RuntimeError("Unhandled command line options '%s'" % leftovers) logging.debug("PyGObject version: %d.%d.%d", gi.version_info[0], gi.version_info[1], gi.version_info[2]) logging.debug("GTK version: %d.%d.%d", Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version()) config = virtManager.config.vmmConfig( "virt-manager", CLIConfig, options.test_first_run) if not util.local_libvirt_version() >= 6000: # We need this version for threaded virConnect access _show_startup_error( _("virt-manager requires libvirt 0.6.0 or later."), "") return if options.usbredir and config.get_auto_redirection(): config.cli_usbredir = False # 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") import virtManager.connection virtManager.connection.FORCE_DISABLE_EVENTS = bool(options.test_no_events) import virtinst.pollhelpers virtinst.pollhelpers.FORCE_OLD_POLL = bool(options.test_old_poll) show_window = None domain = None if options.show_domain_creator: show_window = vmmEngine.CLI_SHOW_DOMAIN_CREATOR elif options.show_host_summary: show_window = vmmEngine.CLI_SHOW_HOST_SUMMARY elif options.show_domain_editor: show_window = vmmEngine.CLI_SHOW_DOMAIN_EDITOR domain = options.show_domain_editor elif options.show_domain_performance: show_window = vmmEngine.CLI_SHOW_DOMAIN_PERFORMANCE domain = options.show_domain_performance elif options.show_domain_console: show_window = vmmEngine.CLI_SHOW_DOMAIN_CONSOLE domain = options.show_domain_console if show_window and options.uri is None: raise RuntimeError("can't use --show-* options without --connect") if show_window: options.skip_autostart = True # Hook libvirt events into glib main loop LibvirtGLib.init(None) LibvirtGLib.event_register() # Actually exit when we receive ctrl-c from gi.repository import GLib def _sigint_handler(user_data): ignore = user_data logging.debug("Received KeyboardInterrupt. Exiting application.") sys.exit(0) GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, _sigint_handler, None) engine = vmmEngine() engine.start(options.uri, show_window, domain, options.skip_autostart) if __name__ == "__main__": try: main() except KeyboardInterrupt: logging.debug("Received KeyboardInterrupt. Exiting application.") except SystemExit: raise except Exception as run_e: if "Gtk" not in globals(): raise _show_startup_error(str(run_e), "".join(traceback.format_exc()))