2009-08-31 11:52:18 -04:00
|
|
|
#!@PYTHON@
|
2010-05-11 16:43:38 -04:00
|
|
|
# -*- mode: Python; indent-tabs-mode: nil; -*-
|
2009-02-23 14:52:49 -05:00
|
|
|
|
|
|
|
import atexit
|
2010-05-12 18:14:14 -04:00
|
|
|
import datetime
|
2010-06-25 10:10:18 -04:00
|
|
|
import dbus
|
|
|
|
from dbus.mainloop.glib import DBusGMainLoop
|
|
|
|
import gobject
|
2010-05-25 10:31:15 -04:00
|
|
|
try:
|
|
|
|
import json
|
|
|
|
except ImportError:
|
|
|
|
try:
|
|
|
|
import simplejson as json
|
|
|
|
except ImportError:
|
|
|
|
json = None
|
2009-02-23 14:52:49 -05:00
|
|
|
import optparse
|
|
|
|
import os
|
|
|
|
import random
|
|
|
|
import re
|
|
|
|
import shutil
|
|
|
|
import signal
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tempfile
|
2009-04-13 11:58:05 -04:00
|
|
|
import termios
|
2009-02-23 14:52:49 -05:00
|
|
|
import time
|
2009-04-06 10:10:03 -04:00
|
|
|
import errno
|
2009-02-23 14:52:49 -05:00
|
|
|
|
2010-03-17 13:02:24 -04:00
|
|
|
def show_version(option, opt_str, value, parser):
|
|
|
|
print "GNOME Shell @VERSION@"
|
|
|
|
sys.exit()
|
|
|
|
|
2010-02-25 13:35:32 -05:00
|
|
|
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:
|
2010-04-24 09:41:03 -04:00
|
|
|
f = open(piddir + "/cmdline")
|
|
|
|
command = f.read()
|
|
|
|
f.close()
|
|
|
|
except IOError, e:
|
2010-02-25 13:35:32 -05:00
|
|
|
continue
|
2010-04-24 09:41:03 -04:00
|
|
|
# /proc/cmdline is separated and terminated by NULs
|
|
|
|
command = command.split("\x00")[0]
|
|
|
|
command = os.path.basename(command)
|
|
|
|
if command != 'gnome-session':
|
2010-02-25 13:35:32 -05:00
|
|
|
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
|
|
|
|
|
2012-04-12 13:43:08 -04:00
|
|
|
def start_shell():
|
2011-02-25 11:20:27 -05:00
|
|
|
self_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
|
|
|
if os.path.exists(os.path.join(self_dir, 'gnome-shell-jhbuild.in')):
|
2009-03-11 09:59:02 -04:00
|
|
|
running_from_source_tree = True
|
2011-02-25 11:20:27 -05:00
|
|
|
top_dir = os.path.dirname(self_dir)
|
2011-03-16 09:40:25 -04:00
|
|
|
typelib_dir = '@JHBUILD_TYPELIBDIR@:' + os.path.join(top_dir, "src")
|
2009-03-11 09:59:02 -04:00
|
|
|
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')
|
2011-03-16 09:40:25 -04:00
|
|
|
typelib_dir = '@JHBUILD_TYPELIBDIR@'
|
|
|
|
|
|
|
|
if os.environ.has_key('GI_TYPELIB_PATH'):
|
|
|
|
typelib_dir = typelib_dir + ":" + os.environ.get('GI_TYPELIB_PATH')
|
2009-03-11 09:59:02 -04:00
|
|
|
|
|
|
|
# Set up environment
|
|
|
|
env = dict(os.environ)
|
|
|
|
if running_from_source_tree:
|
2012-04-12 13:48:55 -04:00
|
|
|
env.update({'GNOME_SHELL_JS' : js_dir,
|
|
|
|
'GNOME_SHELL_BINDIR' : self_dir,
|
2011-02-25 17:42:25 -05:00
|
|
|
'GNOME_SHELL_DATADIR' : data_dir,
|
2010-05-05 17:05:42 -04:00
|
|
|
'GSETTINGS_SCHEMA_DIR' : data_dir })
|
2010-05-11 18:08:50 -04:00
|
|
|
|
2011-02-25 11:20:27 -05:00
|
|
|
args = []
|
2009-02-23 14:52:49 -05:00
|
|
|
if options.debug:
|
|
|
|
debug_command = options.debug_command.split()
|
2011-02-25 11:20:27 -05:00
|
|
|
if running_from_source_tree:
|
|
|
|
args += [os.path.join(top_dir, 'libtool'), '--mode=execute']
|
|
|
|
args += debug_command
|
2011-02-17 17:07:13 -05:00
|
|
|
|
2011-02-25 11:20:27 -05:00
|
|
|
args.append(os.path.join(self_dir, 'gnome-shell-real'))
|
2009-08-27 14:07:46 -04:00
|
|
|
if options.replace:
|
|
|
|
args.append('--replace')
|
2009-02-23 14:52:49 -05:00
|
|
|
if options.sync:
|
|
|
|
args.append('--sync')
|
|
|
|
return subprocess.Popen(args, env=env)
|
|
|
|
|
2012-04-12 13:43:08 -04:00
|
|
|
def run_shell():
|
2010-05-11 16:43:38 -04:00
|
|
|
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:
|
2012-04-12 13:43:08 -04:00
|
|
|
shell = start_shell()
|
2010-05-11 16:43:38 -04:00
|
|
|
|
|
|
|
# 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
|
|
|
|
|
2010-05-11 18:08:50 -04:00
|
|
|
|
2009-08-27 14:19:42 -04:00
|
|
|
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 == "":
|
2011-07-13 13:08:43 -04:00
|
|
|
return False
|
2009-08-27 14:19:42 -04:00
|
|
|
|
|
|
|
# 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()
|
2011-07-13 13:08:43 -04:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
# GNOME2 fallback
|
|
|
|
wm = launch_component("/desktop/gnome/session/required_components/windowmanager")
|
|
|
|
panel = launch_component("/desktop/gnome/session/required_components/panel")
|
2009-08-27 14:19:42 -04:00
|
|
|
|
2011-07-13 13:08:43 -04:00
|
|
|
if not wm and not panel: # Probably GNOME3
|
|
|
|
subprocess.Popen(['gnome-shell'])
|
2009-02-23 14:52:49 -05:00
|
|
|
|
|
|
|
# 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")
|
2010-03-17 13:02:24 -04:00
|
|
|
parser.add_option("", "--version", action="callback", callback=show_version,
|
|
|
|
help="Display version and exit")
|
2009-02-23 14:52:49 -05:00
|
|
|
|
|
|
|
options, args = parser.parse_args()
|
|
|
|
|
|
|
|
if args:
|
|
|
|
parser.print_usage()
|
|
|
|
sys.exit(1)
|
|
|
|
|
2010-04-29 10:30:58 -04:00
|
|
|
# Handle ssh logins
|
|
|
|
if 'DISPLAY' not in os.environ:
|
|
|
|
running_env = get_running_session_environs()
|
|
|
|
os.environ.update(running_env)
|
|
|
|
|
2009-02-23 14:52:49 -05:00
|
|
|
if options.debug_command:
|
|
|
|
options.debug = True
|
|
|
|
elif options.debug:
|
|
|
|
options.debug_command = "gdb --args"
|
|
|
|
|
Only respawn if gnome-shell exits abnormally
If Mutter exits with an exit status of 0, then that most likely
means that it was replaced by another window manager and we shoudln't
try to start the previous window manager and the panel.
(We don't actually know about the panel, but assume that if someone
is replacing us they know what they are doing.)
When Mutter exits with a signal, we know we want to restart.
When Mutter exits with a non-signal non-zero exit status, it's
ambiguous - we could be exiting because we lost the connection to
the X server, or because of a assertion failure in gnome-shell.
We assume the latter; if the X server is gone, all that will happen
is a bit of noise.
To know why Mutter exited accurately, we always wait() and
kill() the Mutter process, and then, if running in Xephyr, clean up
Xephyr afterwards. This has the nice side effect of exiting when
gnome-shell does and not forcing the user to close Xephyr manually.
http://bugzilla.gnome.org/show_bug.cgi?id=591171
2009-08-23 20:48:52 -04:00
|
|
|
# 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
|
|
|
|
|
2009-02-23 14:52:49 -05:00
|
|
|
try:
|
2012-04-12 13:43:08 -04:00
|
|
|
normal_exit = run_shell()
|
2009-02-23 14:52:49 -05:00
|
|
|
finally:
|
2012-04-12 13:43:08 -04:00
|
|
|
if options.replace and not normal_exit:
|
2009-08-27 14:19:42 -04:00
|
|
|
restore_gnome()
|
2011-03-10 19:17:14 -05:00
|
|
|
|
|
|
|
if normal_exit:
|
|
|
|
sys.exit(0)
|
|
|
|
else:
|
|
|
|
sys.exit(1)
|