#!@PYTHON@ # -*- mode: Python; indent-tabs-mode: nil; -*- import atexit import datetime import dbus from dbus.mainloop.glib import DBusGMainLoop import gobject try: import json except ImportError: try: import simplejson as json except ImportError: json = None import optparse import os import random import re import shutil import signal import subprocess import sys import tempfile import termios import time import errno def show_version(option, opt_str, value, parser): print "GNOME Shell @VERSION@" sys.exit() def get_running_session_environs(): wanted_environment = ['DBUS_SESSION_BUS_ADDRESS', 'DISPLAY', 'XDG_DATA_DIRS', 'XAUTHORITY', 'XDG_SESSION_COOKIE', 'ORBIT_SOCKETDIR', 'SESSION_MANAGER'] num_re = re.compile('^[0-9]+$') myuid = os.getuid() if not os.path.isdir('/proc'): return {} for filename in os.listdir('/proc'): if not num_re.match(filename): continue piddir = '/proc/' + filename try: stat = os.stat(piddir) except OSError, e: continue if not stat.st_uid == myuid: continue try: f = open(piddir + "/cmdline") command = f.read() f.close() except IOError, e: continue # /proc/cmdline is separated and terminated by NULs command = command.split("\x00")[0] command = os.path.basename(command) if command != 'gnome-session': continue try: f = open(os.path.join(piddir, 'environ')) except OSError, e: continue environ_data = f.read() f.close() # There's a trailing null at the last one, so remove the # empty string environs = environ_data.split('\0')[:-1] # Rumor has it the presence of just FOO (instead of FOO=bar) # represents a deleted environment variable environs = filter(lambda x: '=' in x, environs) # Turn it into a dictionary environs = dict(map(lambda x: x.split('=', 1), environs)) result = {} for key in wanted_environment: if key in environs: result[key] = environs[key] return result def start_shell(): self_dir = os.path.dirname(os.path.abspath(sys.argv[0])) if os.path.exists(os.path.join(self_dir, 'gnome-shell-jhbuild.in')): running_from_source_tree = True top_dir = os.path.dirname(self_dir) typelib_dir = '@JHBUILD_TYPELIBDIR@:' + os.path.join(top_dir, "src") js_dir = os.path.join(top_dir, "js") data_dir = os.path.join(top_dir, "data") else: running_from_source_tree = False js_dir = os.path.join('@pkgdatadir@', 'js') typelib_dir = '@JHBUILD_TYPELIBDIR@' if os.environ.has_key('GI_TYPELIB_PATH'): typelib_dir = typelib_dir + ":" + os.environ.get('GI_TYPELIB_PATH') # Set up environment env = dict(os.environ) if running_from_source_tree: env.update({'GNOME_SHELL_JS' : js_dir, 'GNOME_SHELL_BINDIR' : self_dir, 'GNOME_SHELL_DATADIR' : data_dir, 'GSETTINGS_SCHEMA_DIR' : data_dir }) args = [] if options.debug: debug_command = options.debug_command.split() if running_from_source_tree: args += [os.path.join(top_dir, 'libtool'), '--mode=execute'] args += debug_command args.append(os.path.join(self_dir, 'gnome-shell-real')) if options.replace: args.append('--replace') if options.sync: args.append('--sync') return subprocess.Popen(args, env=env) def run_shell(): if options.debug: # Record initial terminal state so we can reset it to that # later, in case we kill gdb at a bad time termattrs = termios.tcgetattr(0); normal_exit = False if options.verbose: print "Starting shell" try: shell = start_shell() # Wait for shell to exit if options.verbose: print "Waiting for shell to exit" shell.wait() except KeyboardInterrupt, e: try: os.kill(shell.pid, signal.SIGKILL) except: pass shell.wait() finally: if shell is None: print "Failed to start shell" elif shell.returncode == 0: normal_exit = True if options.verbose: print "Shell exited normally" elif shell.returncode < 0: # Python has no mapping for strsignal; not worth using # ctypes for this. print "Shell killed with signal %d" % - shell.returncode else: # Normal reason here would be losing connection the X server if options.verbose: print "Shell exited with return code %d" % shell.returncode if options.debug: termios.tcsetattr(0, termios.TCSANOW, termattrs); return normal_exit def restore_gnome(): # Do imports lazily to save time and memory import gio import gconf # We don't want to start the new gnome-panel in the current # directory; $HOME is better for stuff launched from it os.chdir(os.path.expanduser("~")) def launch_component(gconf_path): client = gconf.client_get_default() component = client.get_string(gconf_path) if component == None or component == "": return False # See gnome-session/gsm-util.c:gsm_util_find_desktop_file_for_app_name() # The one difference is that we don't search the autostart directories, # and just search normal application search path. (Gio doesnt' know # how to search the autostart dirs, so we'd have to do that ourselves.) appinfo = None try: appinfo = gio.unix.DesktopAppInfo(component + ".desktop") except: try: appinfo = gio.unix.DesktopAppInfo("gnome-" + component + ".desktop") except: pass if appinfo: appinfo.launch() return True return False # GNOME2 fallback wm = launch_component("/desktop/gnome/session/required_components/windowmanager") panel = launch_component("/desktop/gnome/session/required_components/panel") if not wm and not panel: # Probably GNOME3 subprocess.Popen(['gnome-shell']) # Main program parser = optparse.OptionParser() parser.add_option("-r", "--replace", action="store_true", help="Replace the running metacity/gnome-panel") parser.add_option("-g", "--debug", action="store_true", help="Run under a debugger") parser.add_option("", "--debug-command", metavar="COMMAND", help="Command to use for debugging (defaults to 'gdb --args')") parser.add_option("-v", "--verbose", action="store_true") parser.add_option("", "--sync", action="store_true") parser.add_option("", "--version", action="callback", callback=show_version, help="Display version and exit") options, args = parser.parse_args() if args: parser.print_usage() sys.exit(1) # Handle ssh logins if 'DISPLAY' not in os.environ: running_env = get_running_session_environs() os.environ.update(running_env) if options.debug_command: options.debug = True elif options.debug: options.debug_command = "gdb --args" # We only respawn the previous environment on abnormal exit; # for a clean exit, we assume that gnome-shell was replaced with # something else. normal_exit = False try: normal_exit = run_shell() finally: if options.replace and not normal_exit: restore_gnome() if normal_exit: sys.exit(0) else: sys.exit(1)