source: ubiquity/trunk/fuentes/bin/ubiquity-dm @ 3069

Last change on this file since 3069 was 3069, checked in by kbut, 3 years ago

Update from upstream

  • Property svn:executable set to *
File size: 32.7 KB
Line 
1#!/usr/bin/python3
2
3from __future__ import print_function
4
5import errno
6import getpass
7import grp
8import imp
9import os
10import pwd
11import signal
12import subprocess
13import sys
14import sysconfig
15import traceback
16import time
17
18import debconf
19import PAM
20
21sys.path.insert(0, '/usr/lib/ubiquity')
22
23from ubiquity import gsettings, osextras
24from ubiquity.casper import get_casper
25from ubiquity.debconfcommunicator import DebconfCommunicator
26import ubiquity.frontend
27from ubiquity.misc import create_bool, utf8
28
29
30logfile = None
31
32
33def log(msg):
34    print('ubiquity-dm: ' + msg, file=logfile, flush=True)
35
36
37def _pam_conv(auth, query_list, userData):
38    resp = []
39    for query, type in query_list:
40        if type == PAM.PAM_PROMPT_ECHO_ON:
41            val = input(query)
42            resp.append((val, 0))
43        elif type == PAM.PAM_PROMPT_ECHO_OFF:
44            val = getpass.getpass(query)
45            resp.append((val, 0))
46        elif type in (PAM.PAM_PROMPT_ERROR_MSG, PAM.PAM_PROMPT_TEXT_INFO):
47            print(query)
48            resp.append(('', 0))
49        else:
50            return None
51    return resp
52
53
54def set_locale():
55    db = DebconfCommunicator('ubiquity', cloexec=True)
56    locale = ''
57    try:
58        locale = db.get('debian-installer/locale')
59    except debconf.DebconfError:
60        pass
61    db.shutdown()
62
63    if not locale:
64        return
65
66    with open('/etc/default/locale', 'w') as default_locale:
67        print('LANG="%s"' % locale, file=default_locale)
68
69    with open('/etc/environment') as environment:
70        environment_lines = environment.readlines()
71    with open('/etc/environment', 'w') as environment:
72        seen_lang = False
73        for line in environment_lines:
74            if line.startswith('LANG='):
75                print('LANG="%s"' % locale, file=environment)
76                seen_lang = True
77            else:
78                print(line.rstrip('\n'), file=environment)
79        if not seen_lang:
80            print('LANG="%s"' % locale, file=environment)
81
82    with open('/etc/locale.gen', 'w') as locale_gen:
83        print('%s UTF-8' % locale, file=locale_gen)
84
85    subprocess.call(['/usr/sbin/locale-gen', locale],
86                    stdout=logfile, stderr=logfile)
87
88
89def add_ubiquity_kdedir():
90    os.environ['KDEDIRS'] = '/usr/share/ubiquity/qt:' + \
91        os.environ.get('KDEDIRS', '')
92
93
94class XStartupError(EnvironmentError):
95    pass
96
97
98class MissingProgramError(EnvironmentError):
99    pass
100
101
102class SignalWatcher:
103    def __init__(self, owner, program,
104                 interface, object_path,
105                 signal, expected):
106        self.owner = owner
107        self.program = program
108        self.connection = None
109        self.interface = interface
110        self.object_path = object_path
111        self.signal = signal
112        self.expected = expected
113        self.processes = []
114
115        from gi.repository import GLib, Gio
116        owner.drop_privileges()
117        self.loop = GLib.MainLoop()
118        Gio.bus_get(Gio.BusType.SESSION, None,
119                    self.on_got_bus, None)
120
121    def signal_timeout(self, user_data):
122        log("SignalWatcher: signal timed out, continuing with ubiquity-dm")
123        self.loop.quit()
124
125    def on_got_bus(self, source, result, user_data):
126        try:
127            from gi.repository import GLib, Gio
128            self.connection = Gio.bus_get_finish(result)
129            self.connection.signal_subscribe(None, self.interface,
130                                             self.signal,
131                                             self.object_path, None,
132                                             Gio.DBusSignalFlags.NONE,
133                                             self.on_signal, None)
134            self.processes.append(subprocess.Popen(
135                                  [self.program],
136                                  stdin=None, stdout=logfile, stderr=logfile))
137            self.owner.regain_privileges()
138            GLib.timeout_add_seconds(5, self.signal_timeout, None)
139        except Exception:
140            log("failed to ensure xsettings plugin was started:")
141            log(traceback.format_exc())
142            self.loop.quit()
143
144    def on_signal(self, connection, sender, path, interface, signal, params,
145                  user_data):
146        (plugin, ) = params
147        # log ('on_signal: got %s' % plugin)
148        if plugin == "xsettings":
149                self.loop.quit()
150
151    def run(self):
152        self.loop.run()
153        return self.processes
154
155
156class DM:
157    def __init__(self, vt, display, default_username):
158        self.auth = PAM.pam()
159        self.vt = vt
160        self.display = display
161        self.server_started = False
162
163        self.username = get_casper('USERNAME', default_username)
164        try:
165            self.uid, self.gid = pwd.getpwnam(self.username)[2:4]
166        except KeyError:
167            import syslog
168            syslog.syslog('Could not find %s, falling back to root.' %
169                          self.username)
170            self.username = 'root'
171            self.uid, self.gid = 0, 0
172        self.homedir = pwd.getpwnam(self.username)[5]
173        self.uid = int(self.uid)
174        self.gid = int(self.gid)
175        self.groups = []
176        for g in grp.getgrall():
177            if self.username in g[3] or g[0] == self.username:
178                self.groups.append(g[2])
179
180        # Look for a frontend module; we won't actually use it (yet), but
181        # this lets us find out which window manager etc. to launch. Be
182        # careful that importing this here will cause the underlying library
183        # to try to talk to the X server, which won't go well.
184        frontend_names = ['gtk_ui', 'kde_ui']
185        self.frontend = None
186        for f in frontend_names:
187            try:
188                imp.find_module(f, ubiquity.frontend.__path__)
189                self.frontend = f
190                break
191            except ImportError:
192                pass
193        else:
194            raise AttributeError('No frontend available; tried %s' %
195                                 ', '.join(frontend_names))
196
197        db = DebconfCommunicator('ubiquity', cloexec=True)
198        try:
199            self.force_failsafe = create_bool(
200                db.get('ubiquity/force_failsafe_graphics'))
201        except debconf.DebconfError:
202            self.force_failsafe = False
203        db.shutdown()
204
205    def sigusr1_handler(self, signum, frame):
206        self.server_started = True
207
208    def active_vt(self):
209        import fcntl
210        import array
211
212        console = os.open('/dev/tty0', os.O_RDONLY | os.O_NOCTTY)
213        try:
214            VT_GETSTATE = 0x5603
215            vt_stat = array.array('H', [0, 0, 0])
216            fcntl.ioctl(console, VT_GETSTATE, vt_stat)
217            return vt_stat[0]
218        finally:
219            os.close(console)
220
221    def drop_privileges(self):
222        os.setgroups(self.groups)
223        os.setresgid(self.gid, self.gid, 0)
224        os.setresuid(self.uid, self.uid, 0)
225
226    def regain_privileges(self):
227        os.setresuid(0, 0, 0)
228        os.setresgid(0, 0, 0)
229        os.setgroups([])
230
231    def server_preexec(self):
232        signal.signal(signal.SIGUSR1, signal.SIG_IGN)
233
234    def run_hooks(self, hookdir):
235        if os.path.isdir(hookdir):
236            # Exclude hooks containing '.', so that *.dpkg-* et al are avoided.
237            hooks = [entry for entry in os.listdir(hookdir)
238                     if '.' not in entry]
239            for hookentry in hooks:
240                hook = os.path.join(hookdir, hookentry)
241                subprocess.call(
242                    hook, stdout=logfile, stderr=logfile,
243                    preexec_fn=self.drop_privileges)
244
245    def pam_open_session(self):
246            self.auth.start('su')
247            self.auth.set_item(PAM.PAM_USER, self.username)
248            self.auth.set_item(PAM.PAM_CONV, _pam_conv)
249            self.auth.putenv('XDG_SESSION_CLASS=greeter')
250            self.auth.putenv('XDG_SEAT=seat0')
251            # at the time pam_open_session is called self.vt is the
252            # correct vt: either the one originally passed as cmd line
253            # arg or as determined by active_vt()
254            #
255            # self.vt is of the form str("vt10")
256            self.auth.putenv('XDG_VTNR=%s' % self.vt[2:])
257            self.auth.authenticate()
258            self.auth.open_session()
259            os.environ.update(
260                [i.split('=', 1) for i in self.auth.getenvlist()])
261
262    def pam_close_session(self):
263        if self.auth:
264            self.auth.close_session()
265            self.auth = None
266
267    def run(self, *program):
268        # Extract the program basename to see if we are in oem-config or
269        # ubiquity.
270        program_basename = os.path.basename(program[0])
271
272        extras = []
273        null = open('/dev/null', 'w')
274        log('starting')
275
276        signal.signal(signal.SIGUSR1, self.sigusr1_handler)
277        signal.signal(signal.SIGTTIN, signal.SIG_IGN)
278        signal.signal(signal.SIGTTOU, signal.SIG_IGN)
279
280        servercommand = ['X', '-br', '-ac', '-noreset', '-nolisten', 'tcp']
281
282        log('plymouth')
283        try:
284            plymouth_running = subprocess.call(['plymouth', '--ping']) == 0
285        except OSError:
286            plymouth_running = False
287        if plymouth_running:
288            subprocess.call(['plymouth', 'deactivate'])
289            if subprocess.call(['plymouth', '--has-active-vt']) == 0:
290                self.vt = 'vt%d' % self.active_vt()
291                servercommand.extend(['-background', 'none'])
292            else:
293                subprocess.call(['plymouth', 'quit'])
294                plymouth_running = False
295
296        servercommand.extend([self.vt, self.display])
297
298        log('start X {}'.format(servercommand))
299        for attempt in ('main', 'fbdev', 'vesa'):
300            command = list(servercommand)
301            if attempt == 'main' and self.force_failsafe:
302                continue
303            elif attempt != 'main':
304                # TODO cjwatson 2010-02-11: This is a bodge.  The
305                # duplication is nasty, but fortunately bullet-proof X
306                # actually turns out not to be very complicated nowadays.
307                # Most of the complexity is in the fallback session, which I
308                # haven't attempted to integrate here, so you won't get
309                # things like interactive reconfiguration.  I believe Evan
310                # is working on doing that, but is blocked on a couple of
311                # Upstart bugs; once all that's resolved, we should back
312                # this out.
313                if attempt == 'fbdev' and not os.path.exists('/dev/fb0'):
314                    continue
315                xorg_conf_failsafe = '/etc/X11/xorg.conf.failsafe'
316                command.extend(['-config', xorg_conf_failsafe])
317                command.extend(['-logfile', '/var/log/Xorg.%s.log' % attempt])
318
319                with open(xorg_conf_failsafe, 'w') as xorg_conf_failsafe_file:
320                    print('''\
321Section "Device"
322\tIdentifier    "Configured Video Device"
323\tDriver                "%s"
324EndSection
325
326Section "Monitor"
327\tIdentifier    "Configured Monitor"
328EndSection
329
330Section "Screen"
331\tIdentifier    "Default Screen"
332\tMonitor               "Configured Monitor"
333\tDevice                "Configured Video Device"
334EndSection
335''' % attempt, file=xorg_conf_failsafe_file)
336
337            server = subprocess.Popen(
338                command, stdin=null, stdout=logfile, stderr=logfile,
339                preexec_fn=self.server_preexec)
340
341            # Really we should select on a pipe or something, but it's not
342            # worth the effort for now.
343            try:
344                timeout = 60
345                while not self.server_started:
346                    status = server.poll()
347                    if type(status) is int and status != 0:
348                        if plymouth_running:
349                            subprocess.call(['plymouth', 'quit'])
350                        raise XStartupError('X server exited with return '
351                                            'code ' + str(status))
352                    if timeout == 0:
353                        if plymouth_running:
354                            subprocess.call(['plymouth', 'quit'])
355                        raise XStartupError('X server failed to start after 60'
356                                            ' seconds')
357                    time.sleep(1)
358                    timeout -= 1
359                if plymouth_running:
360                    subprocess.call(['plymouth', 'quit', '--retain-splash'])
361            except XStartupError:
362                if attempt == 'vesa':
363                    raise
364
365            if self.server_started:
366                break
367
368        log('set vars')
369        os.environ['DISPLAY'] = self.display
370        os.environ['HOME'] = self.homedir
371        # Give ubiquity a UID and GID that it can drop privileges to.
372        os.environ['PKEXEC_UID'] = str(self.uid)
373        os.environ['GVFS_DISABLE_FUSE'] = '1'
374        # Overlay scrollbars are now a gtk module
375        os.environ['GTK_MODULES'] = 'overlay-scrollbar'
376
377        log('pam_open_session')
378        self.pam_open_session()
379
380        # run simple, custom scripts during install time
381        if program_basename == 'ubiquity':
382            log('dm-scripts')
383            self.run_hooks('/usr/lib/ubiquity/dm-scripts/install')
384
385        # run simple, custom scripts during  oem-config
386        if program_basename == 'oem-config-wrapper':
387            log('oem dm-scripts')
388            self.run_hooks('/usr/lib/ubiquity/dm-scripts/oem')
389
390        # Session bus, apparently needed by most interfaces now
391        if ('DBUS_SESSION_BUS_ADDRESS' not in os.environ and
392                osextras.find_on_path('dbus-launch')):
393            log('dbus')
394            dbus_subp = subprocess.Popen(
395                ['dbus-launch', '--exit-with-session'],
396                stdin=null, stdout=subprocess.PIPE, stderr=logfile,
397                preexec_fn=self.drop_privileges, universal_newlines=True)
398            for line in dbus_subp.stdout:
399                try:
400                    name, value = line.rstrip('\n').split('=', 1)
401                    os.environ[name] = value
402                except ValueError:
403                    pass
404            dbus_subp.stdout.close()
405            dbus_subp.wait()
406
407        # dconf writer
408        if os.path.exists("/usr/lib/dconf/dconf-service"):
409            log('dconf-service')
410            extras.append(subprocess.Popen(
411                ['/usr/lib/dconf/dconf-service'],
412                stdin=null, stdout=logfile, stderr=logfile,
413                preexec_fn=self.drop_privileges))
414
415        # Accessibility infrastructure
416        proc_cmdline = []
417        with open('/proc/cmdline', 'r') as fp:
418            proc_cmdline = fp.readline().split()
419
420        log('start frontend {}'.format(self.frontend))
421        if self.frontend == 'gtk_ui':
422            # Set a desktop wallpaper.
423            visual_a11y = 'access=v' in proc_cmdline
424
425            background_image = None
426            for background in (
427                    '/usr/share/xfce4/backdrops/xubuntu-wallpaper.png',
428                    '/usr/share/backgrounds/'
429                    'ubuntustudio/ubuntustudio-default.png',
430                    '/usr/share/lubuntu/wallpapers/'
431                    'lubuntu-default-wallpaper.png'):
432                exists = os.access(background, os.R_OK)
433                if exists:
434                    background_image = background
435                    break
436
437            accessibility = False
438            if gsettings._gsettings_exists():
439                accessibility = gsettings.get(
440                    'org.gnome.desktop.interface', 'toolkit-accessibility',
441                    self.username)
442
443                # Set gsettings keys
444                gsettings_keys = [
445                    ('org.gnome.desktop.lockdown', 'disable-lock-screen',
446                     'true'),
447                    ('org.gnome.desktop.lockdown', 'disable-user-switching',
448                     'true'),
449                    ('org.gnome.settings-daemon.plugins.background', 'active',
450                     'true'),
451                    ('org.gnome.desktop.background', 'draw-background',
452                     'true'),
453                    ('org.gnome.desktop.background', 'show-desktop-icons',
454                     'false'),
455                    ('org.gnome.metacity', 'compositing-manager',
456                     'true'),
457                    ('org.gnome.desktop.wm.preferences', 'num-workspaces',
458                     '1'),
459                ]
460
461                # Setting a wallpaper image, or solid color.
462                if visual_a11y:
463                    gsettings_keys.append(
464                        ('org.gnome.desktop.background', 'picture-options',
465                         'none'))
466                    gsettings_keys.append(
467                        ('org.gnome.desktop.background', 'picture-uri',
468                         "''"))
469
470                if osextras.find_on_path('marco'):
471                    gsettings_keys = [
472                        ('org.mate.lockdown', 'disable-lock-screen',
473                         'true'),
474                        ('org.mate.lockdown', 'disable-user-switching',
475                         'true'),
476                        ('org.mate.SettingsDaemon.plugins.background',
477                         'active', 'true'),
478                        ('org.mate.background', 'draw-background',
479                         'true'),
480                        ('org.mate.background', 'show-desktop-icons',
481                         'false'),
482                        ('org.mate.Marco.general', 'compositing-manager',
483                         'true'),
484                        ('org.mate.Marco.general', 'num-workspaces',
485                         '1'),
486                    ]
487
488                    # Setting a wallpaper image, or solid color.
489                    if visual_a11y:
490                        gsettings_keys.append(
491                            ('org.mate.background', 'picture-options',
492                             'none'))
493                        gsettings_keys.append(
494                            ('org.mate.background', 'picture-filename',
495                             "''"))
496
497                if osextras.find_on_path('gnome-shell'):
498                    gsettings_keys.append(
499                        ('org.gnome.settings-daemon.plugins.background',
500                         'active', 'false'))
501                    gsettings_keys.remove(
502                        ('org.gnome.desktop.wm.preferences', 'num-workspaces',
503                         '1'))
504
505                for gs_schema, gs_key, gs_value in gsettings_keys:
506                    subprocess.call(
507                        ['gsettings', 'set', gs_schema, gs_key, gs_value],
508                        stdin=null, stdout=logfile, stderr=logfile,
509                        preexec_fn=self.drop_privileges)
510
511                usd = '/usr/lib/unity-settings-daemon/unity-settings-daemon'
512                gsd = '/usr/lib/gnome-settings-daemon/gnome-settings-daemon'
513                msd = '/usr/bin/mate-settings-daemon'
514
515                if osextras.find_on_path(usd):
516                    xsettings = SignalWatcher(self, usd,
517                                              "org.gnome.SettingsDaemon",
518                                              "/org/gnome/SettingsDaemon",
519                                              "PluginActivated",
520                                              "xsettings")
521                    # the SignalWatcher will run until the signal is seen...
522                    extras.extend(xsettings.run())
523                    # At this point we're sure the usd xsettings plugin is
524                    # available, we can continue setting up the session.
525
526                elif osextras.find_on_path(gsd):
527                    extras.append(subprocess.Popen(
528                        [gsd], stdin=null, stdout=logfile, stderr=logfile,
529                        preexec_fn=self.drop_privileges))
530
531                elif osextras.find_on_path(msd):
532                    extras.append(subprocess.Popen(
533                        [msd], stdin=null, stdout=logfile, stderr=logfile,
534                        preexec_fn=self.drop_privileges))
535
536                elif background_image and osextras.find_on_path('feh'):
537                    subprocess.call(
538                        ['feh', '--bg-fill', background_image],
539                        stdin=null, stdout=logfile, stderr=logfile,
540                        preexec_fn=self.drop_privileges)
541
542            if (accessibility or 'maybe-ubiquity' in proc_cmdline or
543                    'only-ubiquity' in proc_cmdline or
544                    program_basename == 'oem-config-wrapper'):
545                launcher = '/usr/lib/at-spi2-core/at-spi-bus-launcher'
546                if os.path.exists(launcher):
547                    extras.append(subprocess.Popen(
548                        [launcher, '--launch-immediately'],
549                        stdin=null, stdout=logfile, stderr=logfile,
550                        preexec_fn=self.drop_privileges))
551                    os.environ['GTK_MODULES'] += os.pathsep + 'gail'
552
553            if osextras.find_on_path('gnome-shell'):
554                wm_cmd = ['gnome-shell', '--sm-disable', '--mode=ubiquity']
555            elif osextras.find_on_path('marco'):
556                wm_cmd = ['marco', '--sm-disable']
557            elif osextras.find_on_path('metacity'):
558                wm_cmd = ['metacity', '--sm-disable']
559            elif osextras.find_on_path('xfwm4'):
560                wm_cmd = ['xfwm4', '--compositor=off', '--sm-client-disable']
561            elif osextras.find_on_path('matchbox-window-manager'):
562                wm_cmd = ['matchbox-window-manager']
563            elif osextras.find_on_path('openbox-lubuntu'):
564                wm_cmd = ['openbox-lubuntu']
565            elif osextras.find_on_path('openbox'):
566                wm_cmd = ['openbox']
567            elif osextras.find_on_path('compiz'):
568                wm_cmd = ['compiz', '--sm-disable', 'decor', 'resize', 'place',
569                          'move']
570            else:
571                raise MissingProgramError(
572                    'No window manager found (tried '
573                    'marco, metacity, xfwm4, matchbox-window-manager, '
574                    'openbox-lubuntu, openbox, compiz)')
575
576            wm = subprocess.Popen(
577                wm_cmd, stdin=null, stdout=logfile, stderr=logfile,
578                preexec_fn=self.drop_privileges)
579
580            if osextras.find_on_path('xfsettingsd'):
581                extras.append(subprocess.Popen(
582                    ['xprop', '-root', '-format', '_NET_NUMBER_OF_DESKTOPS',
583                     '32c', '-set', '_NET_NUMBER_OF_DESKTOPS', '1'],
584                    stdin=null, stdout=logfile, stderr=logfile,
585                    preexec_fn=self.drop_privileges))
586                extras.append(subprocess.Popen(
587                    ['xfsettingsd', '--sm-client-disable'],
588                    stdin=null, stdout=logfile, stderr=logfile,
589                    preexec_fn=self.drop_privileges))
590
591            if osextras.find_on_path('lxsession'):
592                extras.append(subprocess.Popen(
593                    ['lxsession', '-s', 'Lubuntu', '-e', 'LXDE', '-a'],
594                    stdin=null, stdout=logfile, stderr=logfile,
595                    preexec_fn=self.drop_privileges))
596
597            if os.path.exists('/usr/lib/ubiquity/panel'):
598                if ("openbox-lubuntu" not in wm_cmd and
599                        "openbox" not in wm_cmd and
600                        "gnome-shell" not in wm_cmd):
601                    multiarchdir = os.path.split(
602                        sysconfig.get_config_var('multiarchsubdir'))[-1]
603                    indicators = list(filter(os.path.isfile, [
604                        os.path.join('/usr/lib', multiarchdir, i) for i in (
605                            'indicator-application/'
606                            'indicator-application-service',
607                            'indicator-session/indicator-session-service',
608                            'indicator-sound/indicator-sound-service',
609                            'indicator-bluetooth/indicator-bluetooth-service',
610                            'indicator-keyboard/indicator-keyboard-service',
611                            'indicator-keyboard-service',
612                            'indicator-power/indicator-power-service',
613                        )]))
614                    extras.append(subprocess.Popen(
615                        ['/usr/lib/ubiquity/panel'],
616                        stdin=null, stdout=logfile, stderr=logfile,
617                        preexec_fn=self.drop_privileges))
618                    for indicator in indicators:
619                        extras.append(subprocess.Popen(
620                            [indicator],
621                            stdin=null, stdout=logfile, stderr=logfile,
622                            preexec_fn=self.drop_privileges))
623
624            if (osextras.find_on_path('nm-applet') and
625                    "gnome-shell" not in wm_cmd):
626                extras.append(subprocess.Popen(
627                    ['nm-applet'],
628                    stdin=null, stdout=logfile, stderr=logfile,
629                    preexec_fn=self.drop_privileges))
630
631            if osextras.find_on_path('ibus-daemon'):
632                extras.append(subprocess.Popen(
633                    ['ibus-daemon'],
634                    stdin=null, stdout=logfile, stderr=logfile,
635                    preexec_fn=self.drop_privileges))
636
637            # Simply start bluetooth-applet, ubiquity-bluetooth-agent will
638            # override it from casper to make sure it also covers the regular
639            # live session
640            if osextras.find_on_path('bluetooth-applet'):
641                extras.append(subprocess.Popen(
642                    ['bluetooth-applet'],
643                    stdin=null, stdout=logfile, stderr=logfile,
644                    preexec_fn=self.drop_privileges))
645
646            # Accessibility tools
647            if accessibility:
648                # FIXME: launch onboard, when touch screen detected
649                if 'access=m2' in proc_cmdline:
650                    if osextras.find_on_path('onboard'):
651                        extras.append(subprocess.Popen(
652                            ['onboard'],
653                            stdin=null, stdout=logfile, stderr=logfile,
654                            preexec_fn=self.drop_privileges))
655                else:
656                    if osextras.find_on_path('orca'):
657                        time.sleep(15)
658                        extras.append(subprocess.Popen(
659                            ['orca'],
660                            stdin=null, stdout=logfile, stderr=logfile,
661                            preexec_fn=self.drop_privileges))
662        elif self.frontend == 'kde_ui':
663            # Force Qt5 KDE theming to load for kwin and friends.
664            os.environ["QT_QPA_PLATFORMTHEME"] = "kde"
665            if 'access=v1' not in proc_cmdline:
666                log('paint background')
667                path = \
668                    '/usr/share/wallpapers/Next/contents/images/2560x1600.png'
669                extras.append(subprocess.Popen(
670                    ['ubiquity-qtsetbg', path],
671                    stdin=null, stdout=logfile, stderr=logfile,
672                    preexec_fn=self.drop_privileges))
673
674            log("add_ubiquity_kdedir")
675            add_ubiquity_kdedir()
676            log('start kde4breeze')
677            if osextras.find_on_path('kf5-config'):
678                output = subprocess.check_output(
679                    ['kf5-config', '--path', 'lib'],
680                    preexec_fn=self.drop_privileges)
681                output = output.decode()
682                output = output.replace('\n', '')
683                output = output.split(':')[1]
684                breeze = subprocess.Popen(
685                    [output + '/kconf_update_bin/kde4breeze'],
686                    stdin=null, stdout=logfile, stderr=logfile,
687                    preexec_fn=self.drop_privileges)
688                ret = breeze.wait()
689                log('kde4breeze exited with code {}'.format(ret))
690                if ret != 0:
691                    raise
692            else:
693                raise
694            log('start kwin')
695            if osextras.find_on_path('kwin'):
696                wm_cmd = ['kwin']
697            elif osextras.find_on_path('kwin_x11'):
698                wm_cmd = ['kwin_x11']
699            wm = subprocess.Popen(
700                wm_cmd, stdin=null, stdout=logfile, stderr=logfile,
701                preexec_fn=self.drop_privileges)
702
703        log('start greeter')
704        greeter = subprocess.Popen(
705            program, stdin=null, stdout=logfile, stderr=logfile)
706        ret = greeter.wait()
707        log('greeter exited with code {}'.format(ret))
708
709        reboot = False
710        if ret != 0:
711            db = DebconfCommunicator('ubiquity', cloexec=True)
712            try:
713                error_cmd = db.get('ubiquity/failure_command')
714                if error_cmd:
715                    subprocess.call(['sh', '-c', error_cmd])
716            except debconf.DebconfError:
717                pass
718
719            reboot = False
720            try:
721                if '--automatic' in program:
722                    reboot = db.get('ubiquity/reboot_on_failure') == 'true'
723            except debconf.DebconfError:
724                pass
725
726            if reboot:
727                question = 'ubiquity/install_failed_reboot'
728            else:
729                question = 'ubiquity/install_failed'
730            title = ''
731            message = ''
732            try:
733                title = utf8(db.metaget(question, 'description'),
734                             errors='replace')
735                message = utf8(db.metaget(question, 'extended_description'),
736                               errors='replace')
737            except debconf.DebconfError:
738                pass
739            db.shutdown()
740
741            if title and message:
742                if self.frontend == 'gtk_ui':
743                    cmd = ['zenity', '--error', '--title=%s' % title,
744                           '--text=%s' % message]
745                    subprocess.call(cmd)
746                elif self.frontend == 'kde_ui':
747                    cmd = ['kdialog', '--title=%s' % title,
748                           '--msgbox=%s' % message]
749                    subprocess.call(cmd)
750                else:
751                    # Not ideal, but if we cannot let the user know what's
752                    # going on, it's best to drop them into a desktop and let
753                    # them figure it out.
754                    reboot = False
755
756        # Revert gnome-settings to default, for dropping to desktop
757        if self.frontend == 'gtk_ui' and gsettings._gsettings_exists():
758            for gs_schema, gs_key, gs_value in gsettings_keys:
759                subprocess.call(
760                    ['gsettings', 'reset', gs_schema, gs_key],
761                    stdin=null, stdout=logfile, stderr=logfile,
762                    preexec_fn=self.drop_privileges)
763
764        def kill_if_exists(pid, signum):
765            try:
766                os.kill(pid, signum)
767            except OSError as e:
768                if e.errno != errno.ESRCH:
769                    raise
770
771        def sigalrm_handler(signum, frame):
772            kill_if_exists(wm.pid, signal.SIGKILL)
773            for extra in extras:
774                kill_if_exists(extra.pid, signal.SIGKILL)
775
776        kill_if_exists(wm.pid, signal.SIGTERM)
777        for extra in extras:
778            kill_if_exists(extra.pid, signal.SIGTERM)
779        signal.signal(signal.SIGALRM, sigalrm_handler)
780        signal.alarm(1)  # low patience with WMs failing to exit on demand
781        processes = set(extras)
782        processes.add(wm)
783        while processes:
784            done = set()
785            for process in processes:
786                try:
787                    process.wait()
788                    done.add(process)
789                except OSError as e:
790                    if e.errno == errno.EINTR:
791                        continue
792                    raise
793            processes -= done
794        signal.alarm(0)
795
796        # Clear the console so we don't see boot-time messages on switch
797        try:
798            with open('/dev/tty' + self.vt[2:], 'r+') as vthandle:
799                subprocess.call(['clear'], stdin=vthandle, stdout=vthandle)
800        except IOError:
801            pass
802
803        kill_if_exists(server.pid, signal.SIGTERM)
804        server.wait()
805
806        null.close()
807
808        if reboot:
809            subprocess.Popen(['reboot'])
810        if ret is not None and ret >= 0:
811            return ret
812        else:
813            return 1
814
815
816def run(vt, display, username):
817    try:
818        dm = DM(vt, display, username)
819    except XStartupError:
820        log("XStartupError")
821        return 1
822    ret = dm.run(*sys.argv[4:])
823    if ret == 0:
824        log("set_locale")
825        set_locale()
826    dm.pam_close_session()
827    return ret
828
829
830def main():
831    global logfile
832
833    if len(sys.argv) < 4:
834        sys.stderr.write('Usage: %s <vt[1-N]> <:[0-N]> <username> <program> '
835                         '[<arguments>]\n' % sys.argv[0])
836        return 1
837    vt, display, username = sys.argv[1:4]
838
839    try:
840        os.makedirs('/var/log/installer')
841    except OSError as e:
842        # be happy if someone already created the path
843        if e.errno != errno.EEXIST:
844            raise
845    logfile = open('/var/log/installer/dm', 'w')
846    try:
847        ret = run(vt, display, username)
848        log('Exiting with code {}'.format(ret))
849    except Exception:
850        log('Failed with an exception:')
851        log(traceback.format_exc())
852        return 1
853
854if __name__ == '__main__':
855    sys.exit(main())
Note: See TracBrowser for help on using the repository browser.