885 lines
34 KiB
Python
Executable File
885 lines
34 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
import errno
|
|
import getpass
|
|
import grp
|
|
import os
|
|
import pwd
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
import sysconfig
|
|
import traceback
|
|
import time
|
|
|
|
import debconf
|
|
import PAM
|
|
|
|
sys.path.insert(0, '/usr/lib/ubiquity')
|
|
|
|
from ubiquity import gsettings, osextras
|
|
from ubiquity.casper import get_casper
|
|
from ubiquity.debconfcommunicator import DebconfCommunicator
|
|
from ubiquity.misc import create_bool, utf8
|
|
|
|
|
|
logfile = None
|
|
|
|
|
|
def log(msg):
|
|
print('ubiquity-dm: ' + msg, file=logfile, flush=True)
|
|
|
|
|
|
def _pam_conv(auth, query_list, userData):
|
|
resp = []
|
|
for query, type in query_list:
|
|
if type == PAM.PAM_PROMPT_ECHO_ON:
|
|
val = input(query)
|
|
resp.append((val, 0))
|
|
elif type == PAM.PAM_PROMPT_ECHO_OFF:
|
|
val = getpass.getpass(query)
|
|
resp.append((val, 0))
|
|
elif type in (PAM.PAM_PROMPT_ERROR_MSG, PAM.PAM_PROMPT_TEXT_INFO):
|
|
print(query)
|
|
resp.append(('', 0))
|
|
else:
|
|
return None
|
|
return resp
|
|
|
|
|
|
def set_locale():
|
|
db = DebconfCommunicator('ubiquity', cloexec=True)
|
|
locale = ''
|
|
try:
|
|
locale = db.get('debian-installer/locale')
|
|
except debconf.DebconfError:
|
|
pass
|
|
db.shutdown()
|
|
|
|
if not locale:
|
|
return
|
|
|
|
with open('/etc/default/locale', 'w') as default_locale:
|
|
print('LANG="%s"' % locale, file=default_locale)
|
|
|
|
with open('/etc/environment') as environment:
|
|
environment_lines = environment.readlines()
|
|
with open('/etc/environment', 'w') as environment:
|
|
seen_lang = False
|
|
for line in environment_lines:
|
|
if line.startswith('LANG='):
|
|
print('LANG="%s"' % locale, file=environment)
|
|
seen_lang = True
|
|
else:
|
|
print(line.rstrip('\n'), file=environment)
|
|
if not seen_lang:
|
|
print('LANG="%s"' % locale, file=environment)
|
|
|
|
with open('/etc/locale.gen', 'w') as locale_gen:
|
|
print('%s UTF-8' % locale, file=locale_gen)
|
|
|
|
subprocess.call(['/usr/sbin/locale-gen', locale],
|
|
stdout=logfile, stderr=logfile)
|
|
|
|
|
|
def add_ubiquity_kdedir():
|
|
os.environ['KDEDIRS'] = '/usr/share/ubiquity/qt:' + \
|
|
os.environ.get('KDEDIRS', '')
|
|
|
|
|
|
class XStartupError(EnvironmentError):
|
|
pass
|
|
|
|
|
|
class MissingProgramError(EnvironmentError):
|
|
pass
|
|
|
|
|
|
class SignalWatcher:
|
|
def __init__(self, owner, program,
|
|
interface, object_path,
|
|
signal, expected):
|
|
self.owner = owner
|
|
self.program = program
|
|
self.connection = None
|
|
self.interface = interface
|
|
self.object_path = object_path
|
|
self.signal = signal
|
|
self.expected = expected
|
|
self.processes = []
|
|
|
|
from gi.repository import GLib, Gio
|
|
owner.drop_privileges()
|
|
self.loop = GLib.MainLoop()
|
|
Gio.bus_get(Gio.BusType.SESSION, None,
|
|
self.on_got_bus, None)
|
|
|
|
def signal_timeout(self, user_data):
|
|
log("SignalWatcher: signal timed out, continuing with ubiquity-dm")
|
|
self.loop.quit()
|
|
|
|
def on_got_bus(self, source, result, user_data):
|
|
try:
|
|
from gi.repository import GLib, Gio
|
|
self.connection = Gio.bus_get_finish(result)
|
|
self.connection.signal_subscribe(None, self.interface,
|
|
self.signal,
|
|
self.object_path, None,
|
|
Gio.DBusSignalFlags.NONE,
|
|
self.on_signal, None)
|
|
self.processes.append(subprocess.Popen(
|
|
[self.program],
|
|
stdin=None, stdout=logfile, stderr=logfile))
|
|
self.owner.regain_privileges()
|
|
GLib.timeout_add_seconds(5, self.signal_timeout, None)
|
|
except Exception:
|
|
log("failed to ensure xsettings plugin was started:")
|
|
log(traceback.format_exc())
|
|
self.loop.quit()
|
|
|
|
def on_signal(self, connection, sender, path, interface, signal, params,
|
|
user_data):
|
|
(plugin, ) = params
|
|
# log ('on_signal: got %s' % plugin)
|
|
if plugin == "xsettings":
|
|
self.loop.quit()
|
|
|
|
def run(self):
|
|
self.loop.run()
|
|
return self.processes
|
|
|
|
|
|
class DM:
|
|
def __init__(self, vt, display, default_username):
|
|
self.auth = PAM.pam()
|
|
self.vt = vt
|
|
self.display = display
|
|
self.server_started = False
|
|
|
|
self.username = get_casper('USERNAME', default_username)
|
|
try:
|
|
self.uid, self.gid = pwd.getpwnam(self.username)[2:4]
|
|
except KeyError:
|
|
import syslog
|
|
syslog.syslog('Could not find %s, falling back to root.' %
|
|
self.username)
|
|
self.username = 'root'
|
|
self.uid, self.gid = 0, 0
|
|
self.homedir = pwd.getpwnam(self.username)[5]
|
|
self.uid = int(self.uid)
|
|
self.gid = int(self.gid)
|
|
self.groups = []
|
|
for g in grp.getgrall():
|
|
if self.username in g[3] or g[0] == self.username:
|
|
self.groups.append(g[2])
|
|
|
|
# Look for a frontend module; we won't actually use it (yet), but
|
|
# this lets us find out which window manager etc. to launch. Be
|
|
# careful that importing this here will cause the underlying library
|
|
# to try to talk to the X server, which won't go well.
|
|
from importlib import util
|
|
frontend_names = ['gtk_ui', 'kde_ui']
|
|
self.frontend = None
|
|
for f in frontend_names:
|
|
if util.find_spec('ubiquity.frontend.%s' % f) is not None:
|
|
self.frontend = f
|
|
break
|
|
if self.frontend is None:
|
|
raise AttributeError('No frontend available; tried %s' %
|
|
', '.join(frontend_names))
|
|
|
|
db = DebconfCommunicator('ubiquity', cloexec=True)
|
|
try:
|
|
self.force_failsafe = create_bool(
|
|
db.get('ubiquity/force_failsafe_graphics'))
|
|
except debconf.DebconfError:
|
|
self.force_failsafe = False
|
|
db.shutdown()
|
|
|
|
def sigusr1_handler(self, signum, frame):
|
|
self.server_started = True
|
|
|
|
def active_vt(self):
|
|
import fcntl
|
|
import array
|
|
|
|
console = os.open('/dev/tty0', os.O_RDONLY | os.O_NOCTTY)
|
|
try:
|
|
VT_GETSTATE = 0x5603
|
|
vt_stat = array.array('H', [0, 0, 0])
|
|
fcntl.ioctl(console, VT_GETSTATE, vt_stat)
|
|
return vt_stat[0]
|
|
finally:
|
|
os.close(console)
|
|
|
|
def drop_privileges(self):
|
|
os.setgroups(self.groups)
|
|
os.setresgid(self.gid, self.gid, 0)
|
|
os.setresuid(self.uid, self.uid, 0)
|
|
|
|
def regain_privileges(self):
|
|
os.setresuid(0, 0, 0)
|
|
os.setresgid(0, 0, 0)
|
|
os.setgroups([])
|
|
|
|
def server_preexec(self):
|
|
signal.signal(signal.SIGUSR1, signal.SIG_IGN)
|
|
|
|
def run_hooks(self, hookdir):
|
|
if os.path.isdir(hookdir):
|
|
# Exclude hooks containing '.', so that *.dpkg-* et al are avoided.
|
|
hooks = [entry for entry in os.listdir(hookdir)
|
|
if '.' not in entry]
|
|
for hookentry in hooks:
|
|
hook = os.path.join(hookdir, hookentry)
|
|
subprocess.call(
|
|
hook, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges)
|
|
|
|
def pam_open_session(self):
|
|
self.auth.start('su')
|
|
self.auth.set_item(PAM.PAM_USER, self.username)
|
|
self.auth.set_item(PAM.PAM_CONV, _pam_conv)
|
|
self.auth.putenv('XDG_SESSION_CLASS=greeter')
|
|
self.auth.putenv('XDG_SEAT=seat0')
|
|
# at the time pam_open_session is called self.vt is the
|
|
# correct vt: either the one originally passed as cmd line
|
|
# arg or as determined by active_vt()
|
|
#
|
|
# self.vt is of the form str("vt10")
|
|
self.auth.putenv('XDG_VTNR=%s' % self.vt[2:])
|
|
self.auth.authenticate()
|
|
self.auth.open_session()
|
|
os.environ.update(
|
|
[i.split('=', 1) for i in self.auth.getenvlist()])
|
|
|
|
def pam_close_session(self):
|
|
if self.auth:
|
|
self.auth.close_session()
|
|
self.auth = None
|
|
|
|
def run(self, *program):
|
|
# Extract the program basename to see if we are in oem-config or
|
|
# ubiquity.
|
|
program_basename = os.path.basename(program[0])
|
|
|
|
extras = []
|
|
null = open('/dev/null', 'w')
|
|
log('starting')
|
|
|
|
signal.signal(signal.SIGUSR1, self.sigusr1_handler)
|
|
signal.signal(signal.SIGTTIN, signal.SIG_IGN)
|
|
signal.signal(signal.SIGTTOU, signal.SIG_IGN)
|
|
|
|
servercommand = ['X', '-br', '-ac', '-noreset', '-nolisten', 'tcp']
|
|
|
|
log('plymouth')
|
|
try:
|
|
plymouth_running = subprocess.call(['plymouth', '--ping']) == 0
|
|
except OSError:
|
|
plymouth_running = False
|
|
if plymouth_running:
|
|
subprocess.call(['plymouth', 'deactivate'])
|
|
if subprocess.call(['plymouth', '--has-active-vt']) == 0:
|
|
self.vt = 'vt%d' % self.active_vt()
|
|
servercommand.extend(['-background', 'none'])
|
|
else:
|
|
subprocess.call(['plymouth', 'quit'])
|
|
plymouth_running = False
|
|
|
|
servercommand.extend([self.vt, self.display])
|
|
|
|
log('start X {}'.format(servercommand))
|
|
for attempt in ('main', 'fbdev', 'vesa'):
|
|
command = list(servercommand)
|
|
if attempt == 'main' and self.force_failsafe:
|
|
continue
|
|
elif attempt != 'main':
|
|
# TODO cjwatson 2010-02-11: This is a bodge. The
|
|
# duplication is nasty, but fortunately bullet-proof X
|
|
# actually turns out not to be very complicated nowadays.
|
|
# Most of the complexity is in the fallback session, which I
|
|
# haven't attempted to integrate here, so you won't get
|
|
# things like interactive reconfiguration. I believe Evan
|
|
# is working on doing that, but is blocked on a couple of
|
|
# Upstart bugs; once all that's resolved, we should back
|
|
# this out.
|
|
if attempt == 'fbdev' and not os.path.exists('/dev/fb0'):
|
|
continue
|
|
xorg_conf_failsafe = '/etc/X11/xorg.conf.failsafe'
|
|
command.extend(['-config', xorg_conf_failsafe])
|
|
command.extend(['-logfile', '/var/log/Xorg.%s.log' % attempt])
|
|
|
|
with open(xorg_conf_failsafe, 'w') as xorg_conf_failsafe_file:
|
|
print('''\
|
|
Section "Device"
|
|
\tIdentifier "Configured Video Device"
|
|
\tDriver "%s"
|
|
EndSection
|
|
|
|
Section "Monitor"
|
|
\tIdentifier "Configured Monitor"
|
|
EndSection
|
|
|
|
Section "Screen"
|
|
\tIdentifier "Default Screen"
|
|
\tMonitor "Configured Monitor"
|
|
\tDevice "Configured Video Device"
|
|
EndSection
|
|
''' % attempt, file=xorg_conf_failsafe_file)
|
|
|
|
server = subprocess.Popen(
|
|
command, stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.server_preexec)
|
|
|
|
# Really we should select on a pipe or something, but it's not
|
|
# worth the effort for now.
|
|
try:
|
|
timeout = 60
|
|
while not self.server_started:
|
|
status = server.poll()
|
|
if type(status) is int and status != 0:
|
|
if plymouth_running:
|
|
subprocess.call(['plymouth', 'quit'])
|
|
raise XStartupError('X server exited with return '
|
|
'code ' + str(status))
|
|
if timeout == 0:
|
|
if plymouth_running:
|
|
subprocess.call(['plymouth', 'quit'])
|
|
raise XStartupError('X server failed to start after 60'
|
|
' seconds')
|
|
time.sleep(1)
|
|
timeout -= 1
|
|
if plymouth_running:
|
|
subprocess.call(['plymouth', 'quit', '--retain-splash'])
|
|
except XStartupError:
|
|
if attempt == 'vesa':
|
|
raise
|
|
|
|
if self.server_started:
|
|
break
|
|
|
|
log('set vars')
|
|
os.environ['DISPLAY'] = self.display
|
|
os.environ['HOME'] = self.homedir
|
|
# Give ubiquity a UID and GID that it can drop privileges to.
|
|
os.environ['PKEXEC_UID'] = str(self.uid)
|
|
os.environ['GVFS_DISABLE_FUSE'] = '1'
|
|
|
|
log('pam_open_session')
|
|
self.pam_open_session()
|
|
|
|
# run simple, custom scripts during install time
|
|
if program_basename == 'ubiquity':
|
|
log('dm-scripts')
|
|
self.run_hooks('/usr/lib/ubiquity/dm-scripts/install')
|
|
|
|
# run simple, custom scripts during oem-config
|
|
if program_basename == 'oem-config-wrapper':
|
|
log('oem dm-scripts')
|
|
self.run_hooks('/usr/lib/ubiquity/dm-scripts/oem')
|
|
|
|
# Session bus, apparently needed by most interfaces now
|
|
if ('DBUS_SESSION_BUS_ADDRESS' not in os.environ and
|
|
osextras.find_on_path('dbus-launch')):
|
|
log('dbus')
|
|
dbus_subp = subprocess.Popen(
|
|
['dbus-launch', '--exit-with-session'],
|
|
stdin=null, stdout=subprocess.PIPE, stderr=logfile,
|
|
preexec_fn=self.drop_privileges, universal_newlines=True)
|
|
for line in dbus_subp.stdout:
|
|
try:
|
|
name, value = line.rstrip('\n').split('=', 1)
|
|
os.environ[name] = value
|
|
except ValueError:
|
|
pass
|
|
dbus_subp.stdout.close()
|
|
dbus_subp.wait()
|
|
|
|
# dconf writer
|
|
if os.path.exists("/usr/lib/dconf/dconf-service"):
|
|
log('dconf-service')
|
|
extras.append(subprocess.Popen(
|
|
['/usr/lib/dconf/dconf-service'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
if osextras.find_on_path("dbus-update-activation-environment"):
|
|
subprocess.Popen(
|
|
[
|
|
"dbus-update-activation-environment",
|
|
"--verbose",
|
|
"--systemd",
|
|
"DISPLAY",
|
|
],
|
|
stdin=null,
|
|
stdout=logfile,
|
|
stderr=logfile,
|
|
preexec_fn=self.drop_privileges,
|
|
)
|
|
|
|
# Accessibility infrastructure
|
|
proc_cmdline = []
|
|
with open('/proc/cmdline', 'r') as fp:
|
|
proc_cmdline = fp.readline().split()
|
|
|
|
log('start frontend {}'.format(self.frontend))
|
|
if self.frontend == 'gtk_ui':
|
|
# Set a desktop wallpaper.
|
|
visual_a11y = 'access=v' in proc_cmdline
|
|
|
|
background_image = None
|
|
for background in (
|
|
'/usr/share/xfce4/backdrops/xubuntu-wallpaper.png',
|
|
'/usr/share/backgrounds/'
|
|
'ubuntustudio/ubuntustudio-default.png',
|
|
'/usr/share/lubuntu/wallpapers/'
|
|
'lubuntu-default-wallpaper.png'):
|
|
exists = os.access(background, os.R_OK)
|
|
if exists:
|
|
background_image = background
|
|
break
|
|
|
|
accessibility = False
|
|
if gsettings._gsettings_exists():
|
|
accessibility = gsettings.get(
|
|
'org.gnome.desktop.interface', 'toolkit-accessibility',
|
|
self.username)
|
|
|
|
# Set gsettings keys
|
|
gsettings_keys = [
|
|
('org.gnome.desktop.lockdown', 'disable-lock-screen',
|
|
'true'),
|
|
('org.gnome.desktop.lockdown', 'disable-user-switching',
|
|
'true'),
|
|
('org.gnome.settings-daemon.plugins.background', 'active',
|
|
'true'),
|
|
('org.gnome.desktop.background', 'draw-background',
|
|
'true'),
|
|
('org.gnome.desktop.background', 'show-desktop-icons',
|
|
'false'),
|
|
('org.gnome.metacity', 'compositing-manager',
|
|
'true'),
|
|
('org.gnome.desktop.wm.preferences', 'num-workspaces',
|
|
'1'),
|
|
]
|
|
|
|
# Setting a wallpaper image, or solid color.
|
|
if visual_a11y:
|
|
gsettings_keys.append(
|
|
('org.gnome.desktop.background', 'picture-options',
|
|
'none'))
|
|
gsettings_keys.append(
|
|
('org.gnome.desktop.background', 'picture-uri',
|
|
"''"))
|
|
|
|
if osextras.find_on_path('marco'):
|
|
gsettings_keys = [
|
|
('org.mate.lockdown', 'disable-lock-screen',
|
|
'true'),
|
|
('org.mate.lockdown', 'disable-user-switching',
|
|
'true'),
|
|
('org.mate.SettingsDaemon.plugins.background',
|
|
'active', 'true'),
|
|
('org.mate.background', 'draw-background',
|
|
'true'),
|
|
('org.mate.background', 'show-desktop-icons',
|
|
'false'),
|
|
('org.mate.Marco.general', 'compositing-manager',
|
|
'true'),
|
|
('org.mate.Marco.general', 'num-workspaces',
|
|
'1'),
|
|
]
|
|
|
|
# Setting a wallpaper image, or solid color.
|
|
if visual_a11y:
|
|
gsettings_keys.append(
|
|
('org.mate.background', 'picture-options',
|
|
'none'))
|
|
gsettings_keys.append(
|
|
('org.mate.background', 'picture-filename',
|
|
"''"))
|
|
|
|
if (osextras.find_on_path('gnome-shell') or
|
|
osextras.find_on_path('budgie-wm')):
|
|
gsettings_keys.append(
|
|
('org.gnome.settings-daemon.plugins.background',
|
|
'active', 'false'))
|
|
gsettings_keys.remove(
|
|
('org.gnome.desktop.wm.preferences', 'num-workspaces',
|
|
'1'))
|
|
os.environ['XDG_SESSION_TYPE'] = 'x11'
|
|
|
|
if (osextras.find_on_path('gnome-shell')):
|
|
# set this environment variable since we have
|
|
# session-specific GSettings overrides for Ubuntu/Shell,
|
|
# like the theme.
|
|
os.environ['XDG_CURRENT_DESKTOP'] = 'ubuntu:GNOME'
|
|
if (osextras.find_on_path(
|
|
'dbus-update-activation-environment')):
|
|
subprocess.Popen(
|
|
['dbus-update-activation-environment',
|
|
'--verbose', '--systemd', 'XDG_CURRENT_DESKTOP'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges)
|
|
|
|
if (osextras.find_on_path('budgie-wm')):
|
|
gsettings_keys.append(
|
|
('org.gnome.desktop.interface', 'icon-theme',
|
|
"'ubuntu-mono-dark'"))
|
|
os.environ['XDG_CURRENT_DESKTOP'] = 'Budgie'
|
|
|
|
for gs_schema, gs_key, gs_value in gsettings_keys:
|
|
subprocess.call(
|
|
['gsettings', 'set', gs_schema, gs_key, gs_value],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges)
|
|
|
|
msd = '/usr/bin/mate-settings-daemon'
|
|
usd = '/usr/lib/unity-settings-daemon/unity-settings-daemon'
|
|
gsd = ['/usr/libexec/gsd-a11y-keyboard',
|
|
'/usr/libexec/gsd-a11y-settings',
|
|
'/usr/libexec/gsd-clipboard',
|
|
'/usr/libexec/gsd-keyboard',
|
|
'/usr/libexec/gsd-media-keys',
|
|
'/usr/libexec/gsd-power',
|
|
'/usr/libexec/gsd-xsettings']
|
|
|
|
if osextras.find_on_path(msd):
|
|
extras.append(subprocess.Popen(
|
|
[msd], stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
elif (osextras.find_on_path(usd)):
|
|
# Wait until xsettings plugin is activated
|
|
xsettings = SignalWatcher(self, usd,
|
|
"org.gnome.SettingsDaemon",
|
|
"/org/gnome/SettingsDaemon",
|
|
"PluginActivated",
|
|
"xsettings")
|
|
# the SignalWatcher will run until the signal is seen...
|
|
extras.extend(xsettings.run())
|
|
# At this point we're sure the usd xsettings plugin is
|
|
# available, we can continue setting up the session.
|
|
|
|
elif (osextras.find_on_path('gnome-shell') or
|
|
osextras.find_on_path('budgie-wm')):
|
|
for gsdbinary in gsd:
|
|
if os.path.exists(gsdbinary):
|
|
extras.append(subprocess.Popen(
|
|
[gsdbinary],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
elif background_image and osextras.find_on_path('feh'):
|
|
subprocess.call(
|
|
['feh', '--bg-fill', background_image],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges)
|
|
|
|
if (accessibility or 'maybe-ubiquity' in proc_cmdline or
|
|
'only-ubiquity' in proc_cmdline or
|
|
program_basename == 'oem-config-wrapper'):
|
|
launcher = '/usr/lib/at-spi2-core/at-spi-bus-launcher'
|
|
if os.path.exists(launcher):
|
|
extras.append(subprocess.Popen(
|
|
[launcher, '--launch-immediately'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
try:
|
|
os.environ['GTK_MODULES'] += os.pathsep + 'gail'
|
|
except KeyError:
|
|
os.environ['GTK_MODULES'] = 'gail'
|
|
|
|
if osextras.find_on_path('gnome-shell'):
|
|
wm_cmd = ['gnome-shell', '--sm-disable', '--mode=ubiquity']
|
|
elif osextras.find_on_path('budgie-wm'):
|
|
wm_cmd = ['budgie-wm', '--sm-disable']
|
|
elif osextras.find_on_path('marco'):
|
|
wm_cmd = ['marco', '--sm-disable']
|
|
elif osextras.find_on_path('metacity'):
|
|
wm_cmd = ['metacity', '--sm-disable']
|
|
elif osextras.find_on_path('xfwm4'):
|
|
wm_cmd = ['xfwm4', '--compositor=off', '--sm-client-disable']
|
|
elif osextras.find_on_path('matchbox-window-manager'):
|
|
wm_cmd = ['matchbox-window-manager']
|
|
elif osextras.find_on_path('openbox-lubuntu'):
|
|
wm_cmd = ['openbox-lubuntu']
|
|
elif osextras.find_on_path('openbox'):
|
|
wm_cmd = ['openbox']
|
|
elif osextras.find_on_path('compiz'):
|
|
wm_cmd = ['compiz', '--sm-disable', 'decor', 'resize', 'place',
|
|
'move']
|
|
else:
|
|
raise MissingProgramError(
|
|
'No window manager found (tried '
|
|
'gnome-shell, budgie-wm, marco, metacity, xfwm4, '
|
|
'matchbox-window-manager, openbox-lubuntu, '
|
|
'openbox, compiz)')
|
|
|
|
wm = subprocess.Popen(
|
|
wm_cmd, stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges)
|
|
|
|
if osextras.find_on_path('xfsettingsd'):
|
|
extras.append(subprocess.Popen(
|
|
['xprop', '-root', '-format', '_NET_NUMBER_OF_DESKTOPS',
|
|
'32c', '-set', '_NET_NUMBER_OF_DESKTOPS', '1'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
extras.append(subprocess.Popen(
|
|
['xfsettingsd', '--sm-client-disable'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
if osextras.find_on_path('lxsession'):
|
|
extras.append(subprocess.Popen(
|
|
['lxsession', '-s', 'Lubuntu', '-e', 'LXDE', '-a'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
if os.path.exists('/usr/lib/ubiquity/panel'):
|
|
if ("openbox-lubuntu" not in wm_cmd and
|
|
"openbox" not in wm_cmd and
|
|
"gnome-shell" not in wm_cmd and
|
|
"xfwm4" not in wm_cmd):
|
|
multiarchdir = os.path.split(
|
|
sysconfig.get_config_var('multiarchsubdir'))[-1]
|
|
indicators = list(filter(os.path.isfile, [
|
|
os.path.join('/usr/lib', multiarchdir, i) for i in (
|
|
'indicator-application/'
|
|
'indicator-application-service',
|
|
'indicator-session/indicator-session-service',
|
|
'indicator-sound/indicator-sound-service',
|
|
'indicator-bluetooth/indicator-bluetooth-service',
|
|
'indicator-keyboard/indicator-keyboard-service',
|
|
'indicator-keyboard-service',
|
|
'indicator-power/indicator-power-service',
|
|
)]))
|
|
extras.append(subprocess.Popen(
|
|
['/usr/lib/ubiquity/panel'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
for indicator in indicators:
|
|
extras.append(subprocess.Popen(
|
|
[indicator],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
if (osextras.find_on_path('nm-applet') and
|
|
"gnome-shell" not in wm_cmd):
|
|
extras.append(subprocess.Popen(
|
|
['nm-applet'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
if (osextras.find_on_path('ibus-daemon') and
|
|
"gnome-shell" not in wm_cmd):
|
|
extras.append(subprocess.Popen(
|
|
['ibus-daemon'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
# Simply start bluetooth-applet, ubiquity-bluetooth-agent will
|
|
# override it from casper to make sure it also covers the regular
|
|
# live session
|
|
if osextras.find_on_path('bluetooth-applet'):
|
|
extras.append(subprocess.Popen(
|
|
['bluetooth-applet'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
# Accessibility tools
|
|
if accessibility:
|
|
# FIXME: launch onboard, when touch screen detected
|
|
if 'access=m2' in proc_cmdline:
|
|
if osextras.find_on_path('onboard'):
|
|
extras.append(subprocess.Popen(
|
|
['onboard'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
else:
|
|
if osextras.find_on_path('orca'):
|
|
time.sleep(15)
|
|
extras.append(subprocess.Popen(
|
|
['orca'],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
elif self.frontend == 'kde_ui':
|
|
# Force Qt5 KDE theming to load for kwin and friends.
|
|
os.environ["QT_QPA_PLATFORMTHEME"] = "kde"
|
|
if 'access=v1' not in proc_cmdline:
|
|
log('paint background')
|
|
path = \
|
|
'/usr/share/wallpapers/Next/contents/images/2560x1600.jpg'
|
|
extras.append(subprocess.Popen(
|
|
['ubiquity-qtsetbg', path],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges))
|
|
|
|
log("add_ubiquity_kdedir")
|
|
add_ubiquity_kdedir()
|
|
log('start kwin')
|
|
if osextras.find_on_path('kwin'):
|
|
wm_cmd = ['kwin']
|
|
elif osextras.find_on_path('kwin_x11'):
|
|
wm_cmd = ['kwin_x11']
|
|
wm = subprocess.Popen(
|
|
wm_cmd, stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges)
|
|
|
|
log('start greeter')
|
|
greeter = subprocess.Popen(
|
|
program, stdin=null, stdout=logfile, stderr=logfile)
|
|
ret = greeter.wait()
|
|
log('greeter exited with code {}'.format(ret))
|
|
|
|
reboot = False
|
|
if ret != 0:
|
|
db = DebconfCommunicator('ubiquity', cloexec=True)
|
|
try:
|
|
error_cmd = db.get('ubiquity/failure_command')
|
|
if error_cmd:
|
|
subprocess.call(['sh', '-c', error_cmd])
|
|
except debconf.DebconfError:
|
|
pass
|
|
|
|
reboot = False
|
|
try:
|
|
if '--automatic' in program:
|
|
reboot = db.get('ubiquity/reboot_on_failure') == 'true'
|
|
except debconf.DebconfError:
|
|
pass
|
|
|
|
if reboot:
|
|
question = 'ubiquity/install_failed_reboot'
|
|
else:
|
|
question = 'ubiquity/install_failed'
|
|
title = ''
|
|
message = ''
|
|
try:
|
|
title = utf8(db.metaget(question, 'description'),
|
|
errors='replace')
|
|
message = utf8(db.metaget(question, 'extended_description'),
|
|
errors='replace')
|
|
except debconf.DebconfError:
|
|
pass
|
|
db.shutdown()
|
|
|
|
if title and message:
|
|
if self.frontend == 'gtk_ui':
|
|
cmd = ['zenity', '--error', '--title=%s' % title,
|
|
'--text=%s' % message]
|
|
subprocess.call(cmd)
|
|
elif self.frontend == 'kde_ui':
|
|
cmd = ['kdialog', '--title=%s' % title,
|
|
'--msgbox=%s' % message]
|
|
subprocess.call(cmd)
|
|
else:
|
|
# Not ideal, but if we cannot let the user know what's
|
|
# going on, it's best to drop them into a desktop and let
|
|
# them figure it out.
|
|
reboot = False
|
|
|
|
# Revert gnome-settings to default, for dropping to desktop
|
|
if self.frontend == 'gtk_ui' and gsettings._gsettings_exists():
|
|
for gs_schema, gs_key, gs_value in gsettings_keys:
|
|
subprocess.call(
|
|
['gsettings', 'reset', gs_schema, gs_key],
|
|
stdin=null, stdout=logfile, stderr=logfile,
|
|
preexec_fn=self.drop_privileges)
|
|
|
|
def kill_if_exists(pid, signum):
|
|
try:
|
|
os.kill(pid, signum)
|
|
except OSError as e:
|
|
if e.errno != errno.ESRCH:
|
|
raise
|
|
|
|
def sigalrm_handler(signum, frame):
|
|
kill_if_exists(wm.pid, signal.SIGKILL)
|
|
for extra in extras:
|
|
kill_if_exists(extra.pid, signal.SIGKILL)
|
|
|
|
kill_if_exists(wm.pid, signal.SIGTERM)
|
|
for extra in extras:
|
|
kill_if_exists(extra.pid, signal.SIGTERM)
|
|
signal.signal(signal.SIGALRM, sigalrm_handler)
|
|
signal.alarm(1) # low patience with WMs failing to exit on demand
|
|
processes = set(extras)
|
|
processes.add(wm)
|
|
while processes:
|
|
done = set()
|
|
for process in processes:
|
|
try:
|
|
process.wait()
|
|
done.add(process)
|
|
except OSError as e:
|
|
if e.errno == errno.EINTR:
|
|
continue
|
|
raise
|
|
processes -= done
|
|
signal.alarm(0)
|
|
|
|
# Clear the console so we don't see boot-time messages on switch
|
|
try:
|
|
with open('/dev/tty' + self.vt[2:], 'r+') as vthandle:
|
|
subprocess.call(['clear'], stdin=vthandle, stdout=vthandle)
|
|
except IOError:
|
|
pass
|
|
|
|
kill_if_exists(server.pid, signal.SIGTERM)
|
|
server.wait()
|
|
|
|
null.close()
|
|
|
|
if reboot:
|
|
subprocess.Popen(['reboot'])
|
|
if ret is not None and ret >= 0:
|
|
return ret
|
|
else:
|
|
return 1
|
|
|
|
|
|
def run(vt, display, username):
|
|
try:
|
|
dm = DM(vt, display, username)
|
|
except XStartupError:
|
|
log("XStartupError")
|
|
return 1
|
|
ret = dm.run(*sys.argv[4:])
|
|
if ret == 0:
|
|
log("set_locale")
|
|
set_locale()
|
|
dm.pam_close_session()
|
|
return ret
|
|
|
|
|
|
def main():
|
|
global logfile
|
|
|
|
if len(sys.argv) < 4:
|
|
sys.stderr.write('Usage: %s <vt[1-N]> <:[0-N]> <username> <program> '
|
|
'[<arguments>]\n' % sys.argv[0])
|
|
return 1
|
|
vt, display, username = sys.argv[1:4]
|
|
|
|
try:
|
|
os.makedirs('/var/log/installer')
|
|
except OSError as e:
|
|
# be happy if someone already created the path
|
|
if e.errno != errno.EEXIST:
|
|
raise
|
|
logfile = open('/var/log/installer/dm', 'w')
|
|
try:
|
|
ret = run(vt, display, username)
|
|
log('Exiting with code {}'.format(ret))
|
|
except Exception:
|
|
log('Failed with an exception:')
|
|
log(traceback.format_exc())
|
|
return 1
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|