~ubuntu-installer/ubiquity/trunk

215 by carranza
[project @ 488]
1
#!/usr/bin/python
190 by carranza
[project @ 360]
2
# -*- coding: utf-8 -*-
114 by juanje
[project @ 222]
3
830 by Colin Watson
copyright/licence notice, as best I can
4
# Copyright (C) 2005 Javier Carranza and others for Guadalinex
5
# Copyright (C) 2005, 2006 Canonical Ltd.
2108.1.30 by Mario Limonciello
Add support to disable/enable the bootloader installation
6
# Copyright (C) 2007 Mario Limonciello
830 by Colin Watson
copyright/licence notice, as best I can
7
#
8
# This program is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 2 of the License, or
11
# (at your option) any later version.
12
#
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
# GNU General Public License for more details.
17
#
18
# You should have received a copy of the GNU General Public License
19
# along with this program; if not, write to the Free Software
20
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
820 by Colin Watson
* Notice and exit on errors from the copy and configuration stages.
22
import sys
120 by juanje
[project @ 228]
23
import os
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
24
import platform
833 by Colin Watson
* Install language packs according to the selected language.
25
import errno
921 by Colin Watson
* Change permissions of log file in /target to 600 to guard against any
26
import stat
833 by Colin Watson
* Install language packs according to the selected language.
27
import re
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
28
import textwrap
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
29
import shutil
134 by juanje
[project @ 246]
30
import subprocess
315 by carranza
[project @ 665]
31
import time
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
32
import struct
33
import socket
34
import fcntl
1380 by Colin Watson
* Save tracebacks from install.py and slurp them back into the traceback
35
import traceback
1495 by Colin Watson
* Send all internal log messages to syslog rather than stderr.
36
import syslog
1698 by Colin Watson
* Save /target/var/lib/dpkg/status to
37
import gzip
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
38
import debconf
833 by Colin Watson
* Install language packs according to the selected language.
39
import apt_pkg
40
from apt.cache import Cache
41
from apt.progress import FetchProgress, InstallProgress
1395 by Colin Watson
fix sys.path for scripts/install.py
42
43
sys.path.insert(0, '/usr/lib/ubiquity')
44
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
45
from ubiquity import misc
2173 by Colin Watson
* Reimplement more of oem-config-udeb (ugh): disable the hwdb-client
46
from ubiquity import osextras
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
47
from ubiquity.components import language_apply, apt_setup, timezone_apply, \
1518.1.1 by Colin Watson
* Move from kbd-chooser to console-setup
48
                                clock_setup, console_setup_apply, \
1435.1.8 by Evan Dandrea
Merged with Ubiquity HEAD
49
                                usersetup_apply, hw_detect, check_kernels, \
1853 by Colin Watson
indentation, minor style nits
50
                                migrationassistant_apply
126 by juanje
[project @ 234]
51
833 by Colin Watson
* Install language packs according to the selected language.
52
class DebconfFetchProgress(FetchProgress):
53
    """An object that reports apt's fetching progress using debconf."""
54
975 by Colin Watson
* Avoid a long pause without any info message while updating apt's indices
55
    def __init__(self, db, title, info_starting, info):
833 by Colin Watson
* Install language packs according to the selected language.
56
        FetchProgress.__init__(self)
57
        self.db = db
58
        self.title = title
975 by Colin Watson
* Avoid a long pause without any info message while updating apt's indices
59
        self.info_starting = info_starting
833 by Colin Watson
* Install language packs according to the selected language.
60
        self.info = info
61
        self.old_capb = None
62
        self.eta = 0.0
63
64
    def start(self):
65
        self.db.progress('START', 0, 100, self.title)
975 by Colin Watson
* Avoid a long pause without any info message while updating apt's indices
66
        if self.info_starting is not None:
67
            self.db.progress('INFO', self.info_starting)
833 by Colin Watson
* Install language packs according to the selected language.
68
        self.old_capb = self.db.capb()
69
        capb_list = self.old_capb.split()
70
        capb_list.append('progresscancel')
71
        self.db.capb(' '.join(capb_list))
72
73
    # TODO cjwatson 2006-02-27: implement updateStatus
74
75
    def pulse(self):
76
        FetchProgress.pulse(self)
77
        try:
78
            self.db.progress('SET', int(self.percent))
79
        except debconf.DebconfError:
80
            return False
81
        if self.eta != 0.0:
82
            time_str = "%d:%02d" % divmod(int(self.eta), 60)
83
            self.db.subst(self.info, 'TIME', time_str)
84
            try:
85
                self.db.progress('INFO', self.info)
86
            except debconf.DebconfError:
87
                return False
88
        return True
89
90
    def stop(self):
91
        if self.old_capb is not None:
92
            self.db.capb(self.old_capb)
93
            self.old_capb = None
94
            self.db.progress('STOP')
95
96
class DebconfInstallProgress(InstallProgress):
97
    """An object that reports apt's installation progress using debconf."""
98
1416 by Colin Watson
* Silence apt errors while installing language packs, since we ignore them
99
    def __init__(self, db, title, info, error=None):
833 by Colin Watson
* Install language packs according to the selected language.
100
        InstallProgress.__init__(self)
101
        self.db = db
102
        self.title = title
103
        self.info = info
914 by Colin Watson
* Fix variable/method name clash in Install's apt error handling.
104
        self.error_template = error
833 by Colin Watson
* Install language packs according to the selected language.
105
        self.started = False
1415 by Colin Watson
* Turn DebconfInstallProgress upside-down so that the main process handles
106
        # InstallProgress uses a non-blocking status fd; our run()
107
        # implementation doesn't need that, and in fact we spin unless the
108
        # fd is blocking.
109
        flags = fcntl.fcntl(self.statusfd.fileno(), fcntl.F_GETFL)
110
        fcntl.fcntl(self.statusfd.fileno(), fcntl.F_SETFL,
111
                    flags & ~os.O_NONBLOCK)
833 by Colin Watson
* Install language packs according to the selected language.
112
113
    def startUpdate(self):
114
        self.db.progress('START', 0, 100, self.title)
115
        self.started = True
116
117
    def error(self, pkg, errormsg):
1416 by Colin Watson
* Silence apt errors while installing language packs, since we ignore them
118
        if self.error_template is not None:
119
            self.db.subst(self.error_template, 'PACKAGE', pkg)
120
            self.db.subst(self.error_template, 'MESSAGE', errormsg)
121
            self.db.input('critical', self.error_template)
122
            self.db.go()
833 by Colin Watson
* Install language packs according to the selected language.
123
124
    def statusChange(self, pkg, percent, status):
125
        self.percent = percent
126
        self.status = status
127
        self.db.progress('SET', int(percent))
128
        self.db.subst(self.info, 'DESCRIPTION', status)
129
        self.db.progress('INFO', self.info)
130
131
    def updateInterface(self):
132
        # TODO cjwatson 2006-02-28: InstallProgress.updateInterface doesn't
133
        # give us a handy way to spot when percentages/statuses change and
134
        # aren't pmerror/pmconffile, so we have to reimplement it here.
1415 by Colin Watson
* Turn DebconfInstallProgress upside-down so that the main process handles
135
        if self.statusfd is None:
136
            return False
137
        try:
138
            while not self.read.endswith("\n"):
139
                r = os.read(self.statusfd.fileno(),1)
140
                if not r:
141
                    return False
142
                self.read += r
143
        except OSError, (err,errstr):
144
            print errstr
145
        if self.read.endswith("\n"):
146
            s = self.read
147
            (status, pkg, percent, status_str) = s.split(":", 3)
148
            if status == "pmerror":
149
                self.error(pkg, status_str)
150
            elif status == "pmconffile":
151
                # we get a string like this:
152
                # 'current-conffile' 'new-conffile' useredited distedited
153
                match = re.compile("\s*\'(.*)\'\s*\'(.*)\'.*").match(status_str)
154
                if match:
155
                    self.conffile(match.group(1), match.group(2))
156
            else:
157
                self.statusChange(pkg, float(percent), status_str.strip())
158
            self.read = ""
159
        return True
833 by Colin Watson
* Install language packs according to the selected language.
160
841 by Colin Watson
* Make sure that stdout from language pack maintainer scripts doesn't
161
    def run(self, pm):
1415 by Colin Watson
* Turn DebconfInstallProgress upside-down so that the main process handles
162
        # Create a subprocess to deal with turning apt status messages into
163
        # debconf protocol messages.
164
        child_pid = self.fork()
165
        if child_pid == 0:
841 by Colin Watson
* Make sure that stdout from language pack maintainer scripts doesn't
166
            # child
1415 by Colin Watson
* Turn DebconfInstallProgress upside-down so that the main process handles
167
            os.close(self.writefd)
168
            try:
169
                while self.updateInterface():
170
                    pass
171
            except (KeyboardInterrupt, SystemExit):
172
                pass # we're going to exit anyway
173
            except:
1495 by Colin Watson
* Send all internal log messages to syslog rather than stderr.
174
                for line in traceback.format_exc().split('\n'):
1536 by Colin Watson
* syslog.LOG_WARN -> syslog.LOG_WARNING (closes: Malone #59257).
175
                    syslog.syslog(syslog.LOG_WARNING, line)
1415 by Colin Watson
* Turn DebconfInstallProgress upside-down so that the main process handles
176
            os._exit(0)
177
178
        self.statusfd.close()
179
2170 by Colin Watson
* Run apt's DoInstall() method with stdin redirected from /dev/null, to
180
        # Redirect stdin from /dev/null and stdout to stderr to avoid them
181
        # interfering with our debconf protocol stream.
182
        saved_stdin = os.dup(0)
183
        try:
184
            null = os.open('/dev/null', os.O_RDONLY)
185
            os.dup2(null, 0)
186
            os.close(null)
187
        except OSError:
188
            pass
1415 by Colin Watson
* Turn DebconfInstallProgress upside-down so that the main process handles
189
        saved_stdout = os.dup(1)
190
        os.dup2(2, 1)
191
192
        # Make sure all packages are installed non-interactively. We
193
        # don't have enough passthrough magic here to deal with any
194
        # debconf questions they might ask.
195
        saved_environ_keys = ('DEBIAN_FRONTEND', 'DEBIAN_HAS_FRONTEND',
196
                              'DEBCONF_USE_CDEBCONF')
197
        saved_environ = {}
198
        for key in saved_environ_keys:
199
            if key in os.environ:
200
                saved_environ[key] = os.environ[key]
201
        os.environ['DEBIAN_FRONTEND'] = 'noninteractive'
202
        if 'DEBIAN_HAS_FRONTEND' in os.environ:
203
            del os.environ['DEBIAN_HAS_FRONTEND']
204
        if 'DEBCONF_USE_CDEBCONF' in os.environ:
205
            # Probably not a good idea to use this in /target too ...
206
            del os.environ['DEBCONF_USE_CDEBCONF']
207
208
        res = pm.ResultFailed
209
        try:
841 by Colin Watson
* Make sure that stdout from language pack maintainer scripts doesn't
210
            res = pm.DoInstall(self.writefd)
1415 by Colin Watson
* Turn DebconfInstallProgress upside-down so that the main process handles
211
        finally:
212
            # Reap the status-to-debconf subprocess.
213
            os.close(self.writefd)
214
            while True:
215
                try:
216
                    (pid, status) = os.waitpid(child_pid, 0)
217
                    if pid != child_pid:
218
                        break
219
                    if os.WIFEXITED(status) or os.WIFSIGNALED(status):
220
                        break
221
                except OSError:
222
                    break
223
2170 by Colin Watson
* Run apt's DoInstall() method with stdin redirected from /dev/null, to
224
            # Put back stdin and stdout.
225
            os.dup2(saved_stdin, 0)
226
            os.close(saved_stdin)
1415 by Colin Watson
* Turn DebconfInstallProgress upside-down so that the main process handles
227
            os.dup2(saved_stdout, 1)
228
            os.close(saved_stdout)
229
230
            # Put back the environment.
231
            for key in saved_environ_keys:
232
                if key in saved_environ:
233
                    os.environ[key] = saved_environ[key]
234
                elif key in os.environ:
235
                    del os.environ[key]
236
841 by Colin Watson
* Make sure that stdout from language pack maintainer scripts doesn't
237
        return res
238
833 by Colin Watson
* Install language packs according to the selected language.
239
    def finishUpdate(self):
240
        if self.started:
241
            self.db.progress('STOP')
242
            self.started = False
243
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
244
class InstallStepError(Exception):
245
    """Raised when an install step fails.
246
247
    Attributes:
248
        message -- message returned with exception
249
250
    """
251
252
    def __init__(self, message):
1487 by Colin Watson
* Fix stringification of our derived exceptions (InstallStepError and
253
        Exception.__init__(self, message)
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
254
        self.message = message
255
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
256
class Install:
120 by juanje
[project @ 228]
257
795 by Colin Watson
* Rip all the /target unmounting code out of espresso.backend.copy and do
258
    def __init__(self):
715.1.1 by Tollef Fog Heen
fix severe python style issues
259
        """Initial attributes."""
361 by carranza
[project @ 749]
260
715.1.1 by Tollef Fog Heen
fix severe python style issues
261
        if os.path.isdir('/rofs'):
262
            self.source = '/rofs'
1376 by Colin Watson
/KNOPPIX -> /UNIONFS
263
        elif os.path.isdir('/UNIONFS'):
264
            # Klaus Knopper says this may not actually work very well
265
            # because it'll copy the WHOLE WORLD (~12GB).
266
            self.source = '/UNIONFS'
715.1.1 by Tollef Fog Heen
fix severe python style issues
267
        else:
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
268
            self.source = '/var/lib/ubiquity/source'
715.1.1 by Tollef Fog Heen
fix severe python style issues
269
        self.target = '/target'
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
270
        self.kernel_version = platform.release()
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
271
        self.db = debconf.Debconf()
126 by juanje
[project @ 234]
272
833 by Colin Watson
* Install language packs according to the selected language.
273
        apt_pkg.InitConfig()
274
        apt_pkg.Config.Set("Dir", "/target")
1417 by Colin Watson
* Point apt at /target/var/lib/dpkg/status rather than
275
        apt_pkg.Config.Set("Dir::State::status", "/target/var/lib/dpkg/status")
833 by Colin Watson
* Install language packs according to the selected language.
276
        apt_pkg.Config.Set("APT::GPGV::TrustedKeyring",
277
                           "/target/etc/apt/trusted.gpg")
1166 by Colin Watson
* Write out /target/etc/apt/apt.conf.d/00IgnoreTimeConflict for the
278
        apt_pkg.Config.Set("Acquire::gpgv::Options::",
279
                           "--ignore-time-conflict")
833 by Colin Watson
* Install language packs according to the selected language.
280
        apt_pkg.Config.Set("DPkg::Options::", "--root=/target")
281
        # We don't want apt-listchanges or dpkg-preconfigure, so just clear
282
        # out the list of pre-installation hooks.
283
        apt_pkg.Config.Clear("DPkg::Pre-Install-Pkgs")
284
        apt_pkg.InitSystem()
2212 by Evan Dandrea
* Temporary fix for the language packs no longer installing bug (LP:
285
        # TODO evand 2007-09-06: Temporary fix until I can get to the bottom of
286
        # the real cause of the cache being out of sync with respect to the
287
        # language packs (LP: #131294).
288
        apt_pkg.init()
833 by Colin Watson
* Install language packs according to the selected language.
289
1380 by Colin Watson
* Save tracebacks from install.py and slurp them back into the traceback
290
    def excepthook(self, exctype, excvalue, exctb):
291
        """Crash handler. Dump the traceback to a file so that it can be
292
        read by the caller."""
293
294
        if (issubclass(exctype, KeyboardInterrupt) or
295
            issubclass(exctype, SystemExit)):
296
            return
297
298
        tbtext = ''.join(traceback.format_exception(exctype, excvalue, exctb))
1503 by Colin Watson
syslog.LOG_ERROR -> syslog.LOG_ERR
299
        syslog.syslog(syslog.LOG_ERR, "Exception during installation:")
1495 by Colin Watson
* Send all internal log messages to syslog rather than stderr.
300
        for line in tbtext.split('\n'):
1503 by Colin Watson
syslog.LOG_ERROR -> syslog.LOG_ERR
301
            syslog.syslog(syslog.LOG_ERR, line)
1380 by Colin Watson
* Save tracebacks from install.py and slurp them back into the traceback
302
        tbfile = open('/var/lib/ubiquity/install.trace', 'w')
303
        print >>tbfile, tbtext
304
        tbfile.close()
305
306
        sys.exit(1)
307
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
308
    def run(self):
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
309
        """Run the install stage: copy everything to the target system, then
310
        configure it as necessary."""
715.1.1 by Tollef Fog Heen
fix severe python style issues
311
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
312
        self.db.progress('START', 0, 100, 'ubiquity/install/title')
313
        self.db.progress('INFO', 'ubiquity/install/mounting_source')
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
314
315
        try:
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
316
            if self.source == '/var/lib/ubiquity/source':
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
317
                self.mount_source()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
318
319
            self.db.progress('SET', 1)
1439 by Colin Watson
* Add 3% to the "Removing extra packages" stage of the installation
320
            self.db.progress('REGION', 1, 75)
1626 by Colin Watson
* Catch ENOENT, EIO, ENOTDIR, and EROFS while copying files, try to figure
321
            try:
322
                self.copy_all()
1685 by Colin Watson
* Handle EFAULT IOError exceptions while copying files (closes: Malone
323
            except EnvironmentError, e:
324
                if e.errno in (errno.ENOENT, errno.EIO, errno.EFAULT,
325
                               errno.ENOTDIR, errno.EROFS):
1626 by Colin Watson
* Catch ENOENT, EIO, ENOTDIR, and EROFS while copying files, try to figure
326
                    if e.filename is None:
327
                        error_template = 'cd_hd_fault'
328
                    elif e.filename.startswith('/target'):
329
                        error_template = 'hd_fault'
330
                    else:
331
                        error_template = 'cd_fault'
332
                    error_template = ('ubiquity/install/copying_error/%s' %
333
                                      error_template)
334
                    self.db.subst(error_template, 'ERROR', str(e))
335
                    self.db.input('critical', error_template)
336
                    self.db.go()
337
                    # Exit code 3 signals to the frontend that we have
338
                    # handled this error.
339
                    sys.exit(3)
340
                else:
341
                    raise
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
342
1439 by Colin Watson
* Add 3% to the "Removing extra packages" stage of the installation
343
            self.db.progress('SET', 75)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
344
            self.db.progress('REGION', 75, 76)
345
            self.db.progress('INFO', 'ubiquity/install/locales')
346
            self.configure_locales()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
347
1439 by Colin Watson
* Add 3% to the "Removing extra packages" stage of the installation
348
            self.db.progress('SET', 76)
349
            self.db.progress('REGION', 76, 77)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
350
            self.db.progress('INFO', 'ubiquity/install/user')
351
            self.configure_user()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
352
1439 by Colin Watson
* Add 3% to the "Removing extra packages" stage of the installation
353
            self.db.progress('SET', 77)
354
            self.db.progress('REGION', 77, 78)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
355
            self.run_target_config_hooks()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
356
1439 by Colin Watson
* Add 3% to the "Removing extra packages" stage of the installation
357
            self.db.progress('SET', 78)
358
            self.db.progress('REGION', 78, 79)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
359
            self.db.progress('INFO', 'ubiquity/install/network')
360
            self.configure_network()
1511 by Colin Watson
* Configure locales and the user account before running target-config
361
362
            self.db.progress('SET', 79)
363
            self.db.progress('REGION', 79, 80)
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
364
            self.db.progress('INFO', 'ubiquity/install/apt')
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
365
            self.configure_apt()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
366
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
367
            self.db.progress('SET', 80)
368
            self.db.progress('REGION', 80, 85)
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
369
            # Ignore failures from language pack installation.
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
370
            try:
371
                self.install_language_packs()
372
            except InstallStepError:
373
                pass
374
            except IOError:
375
                pass
376
            except SystemError:
377
                pass
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
378
1511 by Colin Watson
* Configure locales and the user account before running target-config
379
            self.db.progress('SET', 85)
380
            self.db.progress('REGION', 85, 86)
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
381
            self.db.progress('INFO', 'ubiquity/install/timezone')
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
382
            self.configure_timezone()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
383
1511 by Colin Watson
* Configure locales and the user account before running target-config
384
            self.db.progress('SET', 86)
1435.1.8 by Evan Dandrea
Merged with Ubiquity HEAD
385
            self.db.progress('REGION', 86, 87)
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
386
            self.db.progress('INFO', 'ubiquity/install/keyboard')
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
387
            self.configure_keyboard()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
388
1439 by Colin Watson
* Add 3% to the "Removing extra packages" stage of the installation
389
            self.db.progress('SET', 87)
390
            self.db.progress('REGION', 87, 88)
1435.1.5 by Evan Dandrea
migration-assistant works inside Ubiquity running after partitioning now.
391
            self.db.progress('INFO', 'ubiquity/install/migrationassistant')
392
            self.configure_ma()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
393
1439 by Colin Watson
* Add 3% to the "Removing extra packages" stage of the installation
394
            self.db.progress('SET', 88)
1674 by Colin Watson
* Remove kernels before running update-initramfs so that we update the
395
            self.db.progress('REGION', 88, 89)
396
            self.remove_unusable_kernels()
397
398
            self.db.progress('SET', 89)
399
            self.db.progress('REGION', 89, 93)
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
400
            self.db.progress('INFO', 'ubiquity/install/hardware')
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
401
            self.configure_hardware()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
402
1439 by Colin Watson
* Add 3% to the "Removing extra packages" stage of the installation
403
            self.db.progress('SET', 93)
404
            self.db.progress('REGION', 93, 94)
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
405
            self.db.progress('INFO', 'ubiquity/install/bootloader')
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
406
            self.configure_bootloader()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
407
1439 by Colin Watson
* Add 3% to the "Removing extra packages" stage of the installation
408
            self.db.progress('SET', 94)
2137 by Colin Watson
* Install packages passed to apt-install even if they aren't on the live
409
            self.db.progress('REGION', 94, 95)
410
            self.db.progress('INFO', 'ubiquity/install/installing')
411
            self.install_extras()
412
413
            self.db.progress('SET', 95)
414
            self.db.progress('REGION', 95, 99)
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
415
            self.db.progress('INFO', 'ubiquity/install/removing')
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
416
            self.remove_extras()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
417
418
            self.db.progress('SET', 99)
419
            self.db.progress('INFO', 'ubiquity/install/log_files')
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
420
            self.copy_logs()
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
421
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
422
            self.db.progress('SET', 100)
423
        finally:
1369 by Colin Watson
* Use try/finally in install.py to ensure that PROGRESS STOP is always
424
            self.cleanup()
425
            try:
426
                self.db.progress('STOP')
427
            except (KeyboardInterrupt, SystemExit):
428
                raise
429
            except:
430
                pass
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
431
432
433
    def copy_all(self):
715.1.1 by Tollef Fog Heen
fix severe python style issues
434
        """Core copy process. This is the most important step of this
435
        stage. It clones live filesystem into a local partition in the
436
        selected hard disk."""
437
438
        files = []
439
        total_size = 0
440
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
441
        self.db.progress('START', 0, 100, 'ubiquity/install/title')
442
        self.db.progress('INFO', 'ubiquity/install/scanning')
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
443
444
        # Obviously doing os.walk() twice is inefficient, but I'd rather not
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
445
        # suck the list into ubiquity's memory, and I'm guessing that the
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
446
        # kernel's dentry cache will avoid most of the slowness anyway.
447
        walklen = 0
448
        for entry in os.walk(self.source):
449
            walklen += 1
450
        walkpos = 0
451
        walkprogress = 0
452
715.1.1 by Tollef Fog Heen
fix severe python style issues
453
        for dirpath, dirnames, filenames in os.walk(self.source):
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
454
            walkpos += 1
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
455
            if int(float(walkpos) / walklen * 10) != walkprogress:
456
                walkprogress = int(float(walkpos) / walklen * 10)
457
                self.db.progress('SET', walkprogress)
715.1.1 by Tollef Fog Heen
fix severe python style issues
458
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
459
            sourcepath = dirpath[len(self.source) + 1:]
715.1.1 by Tollef Fog Heen
fix severe python style issues
460
461
            for name in dirnames + filenames:
462
                relpath = os.path.join(sourcepath, name)
463
                fqpath = os.path.join(self.source, dirpath, name)
464
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
465
                total_size += os.lstat(fqpath).st_size
466
                files.append(relpath)
715.1.1 by Tollef Fog Heen
fix severe python style issues
467
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
468
        self.db.progress('SET', 10)
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
469
        self.db.progress('INFO', 'ubiquity/install/copying')
715.1.1 by Tollef Fog Heen
fix severe python style issues
470
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
471
        # Progress bar handling:
472
        # We sample progress every half-second (assuming time.time() gives
473
        # us sufficiently good granularity) and use the average of progress
474
        # over the last minute or so to decide how much time remains. We
475
        # don't bother displaying any progress for the first ten seconds in
476
        # order to allow things to settle down, and we only update the "time
477
        # remaining" indicator at most every two seconds after that.
478
479
        copy_progress = 0
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
480
        copied_size, counter = 0, 0
980 by Colin Watson
apply timestamps to directories properly
481
        directory_times = []
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
482
        time_start = time.time()
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
483
        times = [(time_start, copied_size)]
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
484
        long_enough = False
485
        time_last_update = time_start
486
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
487
        old_umask = os.umask(0)
488
        for path in files:
489
            sourcepath = os.path.join(self.source, path)
490
            targetpath = os.path.join(self.target, path)
491
            st = os.lstat(sourcepath)
981 by Colin Watson
chmod after chown, to get setgid directories right
492
            mode = stat.S_IMODE(st.st_mode)
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
493
            if stat.S_ISLNK(st.st_mode):
1356 by Colin Watson
* When copying symlinks, check for existing dangling symlinks as well as
494
                if not os.path.lexists(targetpath):
998 by Colin Watson
* Don't copy regular files or symlinks if they already exist in the target
495
                    linkto = os.readlink(sourcepath)
496
                    os.symlink(linkto, targetpath)
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
497
            elif stat.S_ISDIR(st.st_mode):
981 by Colin Watson
chmod after chown, to get setgid directories right
498
                if not os.path.isdir(targetpath):
499
                    os.mkdir(targetpath, mode)
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
500
            elif stat.S_ISCHR(st.st_mode):
981 by Colin Watson
chmod after chown, to get setgid directories right
501
                os.mknod(targetpath, stat.S_IFCHR | mode, st.st_rdev)
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
502
            elif stat.S_ISBLK(st.st_mode):
981 by Colin Watson
chmod after chown, to get setgid directories right
503
                os.mknod(targetpath, stat.S_IFBLK | mode, st.st_rdev)
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
504
            elif stat.S_ISFIFO(st.st_mode):
981 by Colin Watson
chmod after chown, to get setgid directories right
505
                os.mknod(targetpath, stat.S_IFIFO | mode)
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
506
            elif stat.S_ISSOCK(st.st_mode):
981 by Colin Watson
chmod after chown, to get setgid directories right
507
                os.mknod(targetpath, stat.S_IFSOCK | mode)
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
508
            elif stat.S_ISREG(st.st_mode):
998 by Colin Watson
* Don't copy regular files or symlinks if they already exist in the target
509
                if not os.path.exists(targetpath):
2043 by Colin Watson
* Use code based on shutil.copyfileobj rather than shutil.copyfile to copy
510
                    sourcefh = None
511
                    targetfh = None
512
                    try:
513
                        sourcefh = open(sourcepath, 'rb')
514
                        targetfh = open(targetpath, 'wb')
515
                        shutil.copyfileobj(sourcefh, targetfh)
516
                    finally:
517
                        if targetfh:
518
                            targetfh.close()
519
                        if sourcefh:
520
                            sourcefh.close()
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
521
522
            copied_size += st.st_size
979 by Colin Watson
set ownership of symlinks too
523
            os.lchown(targetpath, st.st_uid, st.st_gid)
981 by Colin Watson
chmod after chown, to get setgid directories right
524
            if not stat.S_ISLNK(st.st_mode):
525
                os.chmod(targetpath, mode)
980 by Colin Watson
apply timestamps to directories properly
526
            if stat.S_ISDIR(st.st_mode):
527
                directory_times.append((targetpath, st.st_atime, st.st_mtime))
982 by Colin Watson
clarify utime comment for symlinks
528
            # os.utime() sets timestamp of target, not link
980 by Colin Watson
apply timestamps to directories properly
529
            elif not stat.S_ISLNK(st.st_mode):
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
530
                os.utime(targetpath, (st.st_atime, st.st_mtime))
531
532
            if int((copied_size * 90) / total_size) != copy_progress:
533
                copy_progress = int((copied_size * 90) / total_size)
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
534
                self.db.progress('SET', 10 + copy_progress)
535
536
            time_now = time.time()
537
            if (time_now - times[-1][0]) >= 0.5:
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
538
                times.append((time_now, copied_size))
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
539
                if not long_enough and time_now - times[0][0] >= 10:
540
                    long_enough = True
541
                if long_enough and time_now - time_last_update >= 2:
542
                    time_last_update = time_now
543
                    while (time_now - times[0][0] > 60 and
544
                           time_now - times[1][0] >= 60):
545
                        times.pop(0)
546
                    speed = ((times[-1][1] - times[0][1]) /
547
                             (times[-1][0] - times[0][0]))
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
548
                    time_remaining = int((total_size - copied_size) / speed)
1619 by Colin Watson
* Replace the rather jittery time-remaining message while copying files
549
                    if time_remaining < 60:
550
                        self.db.progress(
551
                            'INFO', 'ubiquity/install/copying_minute')
715.1.1 by Tollef Fog Heen
fix severe python style issues
552
980 by Colin Watson
apply timestamps to directories properly
553
        # Apply timestamps to all directories now that the items within them
554
        # have been copied.
555
        for dirtime in directory_times:
556
            (directory, atime, mtime) = dirtime
1554 by Colin Watson
* Ignore failures from os.utime on directories. I don't know why I get a
557
            try:
558
                os.utime(directory, (atime, mtime))
559
            except OSError:
560
                # I have no idea why I've been getting lots of bug reports
561
                # about this failing, but I really don't care. Ignore it.
562
                pass
980 by Colin Watson
apply timestamps to directories properly
563
972 by Colin Watson
* Fix estimated install copying time. We no longer use cpio to do the
564
        os.umask(old_umask)
715.1.1 by Tollef Fog Heen
fix severe python style issues
565
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
566
        self.db.progress('SET', 100)
567
        self.db.progress('STOP')
568
715.1.1 by Tollef Fog Heen
fix severe python style issues
569
570
    def copy_logs(self):
571
        """copy log files into installed system."""
572
1199 by Colin Watson
* Save /var/log/partman to installed system as /var/log/installer/partman.
573
        target_dir = os.path.join(self.target, 'var/log/installer')
574
        if not os.path.exists(target_dir):
575
            os.makedirs(target_dir)
576
1500 by Colin Watson
* Remove /var/log/installer/syslog from all error messages etc., as
577
        for log_file in ('/var/log/syslog', '/var/log/partman',
2093 by Colin Watson
* Save /var/log/casper.log to installed system (LP: #119993).
578
                         '/var/log/installer/version', '/var/log/casper.log'):
1199 by Colin Watson
* Save /var/log/partman to installed system as /var/log/installer/partman.
579
            target_log_file = os.path.join(target_dir,
580
                                           os.path.basename(log_file))
2066 by Colin Watson
more ex -> execute renaming
581
            if not misc.execute('cp', '-a', log_file, target_log_file):
1503 by Colin Watson
syslog.LOG_ERROR -> syslog.LOG_ERR
582
                syslog.syslog(syslog.LOG_ERR,
1495 by Colin Watson
* Send all internal log messages to syslog rather than stderr.
583
                              'Failed to copy installation log file')
1199 by Colin Watson
* Save /var/log/partman to installed system as /var/log/installer/partman.
584
            os.chmod(target_log_file, stat.S_IRUSR | stat.S_IWUSR)
1698 by Colin Watson
* Save /target/var/lib/dpkg/status to
585
        try:
586
            status = open(os.path.join(self.target, 'var/lib/dpkg/status'))
587
            status_gz = gzip.open(os.path.join(target_dir,
588
                                               'initial-status.gz'), 'w')
589
            while True:
590
                data = status.read(65536)
1711 by Colin Watson
* Fix infinite loop while creating initial-status log.
591
                if not data:
592
                    break
1698 by Colin Watson
* Save /target/var/lib/dpkg/status to
593
                status_gz.write(data)
594
            status_gz.close()
595
            status.close()
596
        except IOError:
597
            pass
2125 by Colin Watson
- Ask for a unique identifier for this batch of installations, and save
598
        try:
599
            if self.db.get('oem-config/enable') == 'true':
600
                oem_id = self.db.get('oem-config/id')
601
                oem_id_file = open(
602
                    os.path.join(self.target, 'var/log/installer/oem-id'))
603
                print >>oem_id_file, oem_id
604
                oem_id_file.close()
605
        except (debconf.DebconfError, IOError):
606
            pass
715.1.1 by Tollef Fog Heen
fix severe python style issues
607
608
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
609
    def mount_one_image(self, fsfile, mountpoint=None):
610
        if os.path.splitext(fsfile)[1] == '.cloop':
611
            blockdev_prefix = 'cloop'
612
        elif os.path.splitext(fsfile)[1] == '.squashfs':
613
            blockdev_prefix = 'loop'
614
615
        if blockdev_prefix == '':
616
            raise InstallStepError("No source device found for %s" % fsfile)
617
618
        dev = ''
619
        sysloops = filter(lambda x: x.startswith(blockdev_prefix),
620
                          os.listdir('/sys/block'))
621
        sysloops.sort()
622
        for sysloop in sysloops:
623
            try:
624
                sysloopf = open(os.path.join('/sys/block', sysloop, 'size'))
1527 by Colin Watson
fd leak
625
                sysloopsize = sysloopf.readline().strip()
626
                sysloopf.close()
1528 by Colin Watson
s/=/==/ in previous change, whoops
627
                if sysloopsize == '0':
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
628
                    devnull = open('/dev/null')
629
                    udevinfo = subprocess.Popen(
630
                        ['udevinfo', '-q', 'name',
631
                         '-p', os.path.join('/block', sysloop)],
632
                        stdout=subprocess.PIPE, stderr=devnull)
633
                    devbase = udevinfo.communicate()[0]
634
                    devnull.close()
1639 by Colin Watson
* Fix various bugs when mounting source filesystems natively rather than
635
                    if udevinfo.returncode != 0:
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
636
                        devbase = sysloop
637
                    dev = '/dev/%s' % devbase
638
                    break
639
            except:
640
                continue
641
642
        if dev == '':
643
            raise InstallStepError("No loop device available for %s" % fsfile)
644
2066 by Colin Watson
more ex -> execute renaming
645
        misc.execute('losetup', dev, fsfile)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
646
        if mountpoint is None:
647
            mountpoint = '/var/lib/ubiquity/%s' % sysloop
648
        if not os.path.isdir(mountpoint):
649
            os.mkdir(mountpoint)
2066 by Colin Watson
more ex -> execute renaming
650
        misc.execute('mount', dev, mountpoint)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
651
652
        return (dev, mountpoint)
653
715.1.1 by Tollef Fog Heen
fix severe python style issues
654
    def mount_source(self):
655
        """mounting loop system from cloop or squashfs system."""
656
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
657
        self.devs = []
658
        self.mountpoints = []
659
715.1.1 by Tollef Fog Heen
fix severe python style issues
660
        if not os.path.isdir(self.source):
1495 by Colin Watson
* Send all internal log messages to syslog rather than stderr.
661
            syslog.syslog('mkdir %s' % self.source)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
662
            os.mkdir(self.source)
663
664
        fs_preseed = self.db.get('ubiquity/install/filesystem-images')
665
666
        if fs_preseed == '':
667
            # Simple autodetection on unionfs systems
1778 by Colin Watson
fix filehandle leaks
668
            mounts = open('/proc/mounts')
669
            for line in mounts:
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
670
                (device, fstype) = line.split()[1:3]
671
                if fstype == 'squashfs' and os.path.exists(device):
2066 by Colin Watson
more ex -> execute renaming
672
                    misc.execute('mount', '--bind', device, self.source)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
673
                    self.mountpoints.append(self.source)
1778 by Colin Watson
fix filehandle leaks
674
                    mounts.close()
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
675
                    return
1778 by Colin Watson
fix filehandle leaks
676
            mounts.close()
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
677
678
            # Manual detection on non-unionfs systems
679
            fsfiles = ['/cdrom/casper/filesystem.cloop',
680
                       '/cdrom/casper/filesystem.squashfs',
681
                       '/cdrom/META/META.squashfs']
682
683
            for fsfile in fsfiles:
684
                if fsfile != '' and os.path.isfile(fsfile):
685
                    dev, mountpoint = self.mount_one_image(fsfile, self.source)
1639 by Colin Watson
* Fix various bugs when mounting source filesystems natively rather than
686
                    self.devs.append(dev)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
687
                    self.mountpoints.append(mountpoint)
688
689
        elif len(fs_preseed.split()) == 1:
690
            # Just one preseeded image.
691
            if not os.path.isfile(fs_preseed):
692
                raise InstallStepError(
693
                    "Preseeded filesystem image %s not found" % fs_preseed)
694
695
                dev, mountpoint = self.mount_one_image(fsfile, self.source)
1639 by Colin Watson
* Fix various bugs when mounting source filesystems natively rather than
696
                self.devs.append(dev)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
697
                self.mountpoints.append(mountpoint)
698
        else:
699
            # OK, so we need to mount multiple images and unionfs them
700
            # together.
701
            for fsfile in fs_preseed.split():
702
                if not os.path.isfile(fsfile):
703
                    raise InstallStepError(
704
                        "Preseeded filesystem image %s not found" % fsfile)
705
706
                dev, mountpoint = self.mount_one_image(fsfile)
707
                self.devs.append(dev)
708
                self.mountpoints.append(mountpoint)
709
710
            assert self.devs
711
            assert self.mountpoints
712
2066 by Colin Watson
more ex -> execute renaming
713
            misc.execute('mount', '-t', 'unionfs', '-o',
2094 by Colin Watson
* Fix typo in unionfs dirs= option generation (LP: #118742).
714
                         'dirs=' + ':'.join(map(lambda x: '%s=ro' % x,
715
                                                self.mountpoints)),
2066 by Colin Watson
more ex -> execute renaming
716
                         'unionfs', self.source)
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
717
            self.mountpoints.append(self.source)
715.1.1 by Tollef Fog Heen
fix severe python style issues
718
719
    def umount_source(self):
720
        """umounting loop system from cloop or squashfs system."""
721
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
722
        devs = self.devs
723
        devs.reverse()
724
        mountpoints = self.mountpoints
725
        mountpoints.reverse()
726
727
        for mountpoint in mountpoints:
2066 by Colin Watson
more ex -> execute renaming
728
            if not misc.execute('umount', mountpoint):
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
729
                raise InstallStepError("Failed to unmount %s" % mountpoint)
730
        for dev in devs:
2066 by Colin Watson
more ex -> execute renaming
731
            if dev != '' and not misc.execute('losetup', '-d', dev):
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
732
                raise InstallStepError(
733
                    "Failed to detach loopback device %s" % dev)
715.1.1 by Tollef Fog Heen
fix severe python style issues
734
735
1968 by Colin Watson
* Install dummy policy-rc.d and start-stop-daemon while doing anything
736
    def chroot_setup(self):
737
        """Set up /target for safe package management operations."""
738
        policy_rc_d = os.path.join(self.target, 'usr/sbin/policy-rc.d')
739
        f = open(policy_rc_d, 'w')
740
        print >>f, """\
741
#!/bin/sh
742
exit 101"""
743
        f.close()
744
        os.chmod(policy_rc_d, 0755)
745
746
        start_stop_daemon = os.path.join(self.target, 'sbin/start-stop-daemon')
747
        if os.path.exists(start_stop_daemon):
748
            os.rename(start_stop_daemon, '%s.REAL' % start_stop_daemon)
749
        f = open(start_stop_daemon, 'w')
750
        print >>f, """\
751
#!/bin/sh
752
echo 1>&2
753
echo 'Warning: Fake start-stop-daemon called, doing nothing.' 1>&2
754
exit 0"""
755
        f.close()
756
        os.chmod(start_stop_daemon, 0755)
757
758
        if not os.path.exists(os.path.join(self.target, 'proc/cmdline')):
759
            self.chrex('mount', '-t', 'proc', 'proc', '/proc')
760
        if not os.path.exists(os.path.join(self.target, 'sys/devices')):
761
            self.chrex('mount', '-t', 'sysfs', 'sysfs', '/sys')
762
763
    def chroot_cleanup(self):
764
        """Undo the work done by chroot_setup."""
765
        self.chrex('umount', '/sys')
766
        self.chrex('umount', '/proc')
767
768
        start_stop_daemon = os.path.join(self.target, 'sbin/start-stop-daemon')
769
        os.rename('%s.REAL' % start_stop_daemon, start_stop_daemon)
770
771
        policy_rc_d = os.path.join(self.target, 'usr/sbin/policy-rc.d')
772
        os.unlink(policy_rc_d)
773
774
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
775
    def run_target_config_hooks(self):
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
776
        """Run hook scripts from /usr/lib/ubiquity/target-config. This allows
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
777
        casper to hook into us and repeat bits of its configuration in the
778
        target system."""
779
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
780
        hookdir = '/usr/lib/ubiquity/target-config'
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
781
782
        if os.path.isdir(hookdir):
783
            # Exclude hooks containing '.', so that *.dpkg-* et al are avoided.
784
            hooks = filter(lambda entry: '.' not in entry, os.listdir(hookdir))
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
785
            self.db.progress('START', 0, len(hooks), 'ubiquity/install/title')
1940 by Colin Watson
* Use just one progress message for all target-config hooks, to avoid
786
            self.db.progress('INFO', 'ubiquity/install/target_hooks')
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
787
            for hookentry in hooks:
788
                hook = os.path.join(hookdir, hookentry)
789
                if not os.access(hook, os.X_OK):
790
                    self.db.progress('STEP', 1)
791
                    continue
792
                # Errors are ignored at present, although this may change.
1510 by Colin Watson
allow target-config hooks to get hold of stdout again, so that they can use debconf
793
                subprocess.call(['log-output', '-t', 'ubiquity',
794
                                 '--pass-stdout', hook])
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
795
                self.db.progress('STEP', 1)
796
            self.db.progress('STOP')
797
798
995 by Colin Watson
get_locales no longer needs to mess with the keymap; rename to configure_locales
799
    def configure_locales(self):
800
        """Apply locale settings to installed system."""
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
801
        dbfilter = language_apply.LanguageApply(None)
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
802
        ret = dbfilter.run_command(auto_process=True)
803
        if ret != 0:
1412 by Colin Watson
* Fix typoed variable name while raising InstallStepError.
804
            raise InstallStepError("LanguageApply failed with code %d" % ret)
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
805
1668 by Colin Watson
* Run fontconfig-voodoo to tweak fontconfig configuration for the selected
806
        # fontconfig configuration needs to be adjusted based on the
807
        # selected locale (from language-selector-common.postinst). Ignore
808
        # errors.
809
        self.chrex('fontconfig-voodoo', '--auto', '--quiet')
810
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
811
1036 by Colin Watson
* Use choose-mirror and apt-setup to set up a standard
812
    def configure_apt(self):
813
        """Configure /etc/apt/sources.list."""
1166 by Colin Watson
* Write out /target/etc/apt/apt.conf.d/00IgnoreTimeConflict for the
814
2134 by Colin Watson
* Replicate the apt configuration done by base-installer (trust CD-ROMs,
815
        # TODO cjwatson 2007-07-06: Much of the following is
816
        # cloned-and-hacked from base-installer/debian/postinst. Perhaps we
817
        # should come up with a way to avoid this.
818
819
        # Make apt trust CDs. This is not on by default (we think).
820
        # This will be left in place on the installed system.
821
        apt_conf_tc = open(os.path.join(
822
            self.target, 'etc/apt/apt.conf.d/00trustcdrom'), 'w')
823
        print >>apt_conf_tc, 'APT::Authentication::TrustCDROM "true";'
824
        apt_conf_tc.close()
825
1166 by Colin Watson
* Write out /target/etc/apt/apt.conf.d/00IgnoreTimeConflict for the
826
        # Avoid clock skew causing gpg verification issues.
827
        # This file will be left in place until the end of the install.
828
        apt_conf_itc = open(os.path.join(
829
            self.target, 'etc/apt/apt.conf.d/00IgnoreTimeConflict'), 'w')
2134 by Colin Watson
* Replicate the apt configuration done by base-installer (trust CD-ROMs,
830
        print >>apt_conf_itc, \
831
            'Acquire::gpgv::Options { "--ignore-time-conflict"; };'
1166 by Colin Watson
* Write out /target/etc/apt/apt.conf.d/00IgnoreTimeConflict for the
832
        apt_conf_itc.close()
833
2134 by Colin Watson
* Replicate the apt configuration done by base-installer (trust CD-ROMs,
834
        try:
835
            if self.db.get('debian-installer/allow_unauthenticated') == 'true':
836
                apt_conf_au = open(
837
                    os.path.join(self.target,
838
                                 'etc/apt/apt.conf.d/00AllowUnauthenticated'),
839
                    'w')
840
                print >>apt_conf_au, 'APT::Get::AllowUnauthenticated "true";'
841
                print >>apt_conf_au, \
842
                    'Aptitude::CmdLine::Ignore-Trust-Violations "true";'
843
                apt_conf_au.close()
844
        except debconf.DebconfError:
845
            pass
846
847
        # let apt inside the chroot see the cdrom
848
        target_cdrom = os.path.join(self.target, 'cdrom')
849
        misc.execute('umount', target_cdrom)
850
        if not os.path.exists(target_cdrom):
851
            os.mkdir(target_cdrom)
852
        misc.execute('mount', '--bind', '/cdrom', target_cdrom)
853
854
        # Make apt-cdrom and apt not unmount/mount CD-ROMs.
855
        # This file will be left in place until the end of the install.
856
        apt_conf_nmc = open(os.path.join(
857
            self.target, 'etc/apt/apt.conf.d/00NoMountCDROM'), 'w')
858
        print >>apt_conf_nmc, textwrap.dedent("""\
859
            APT::CDROM::NoMount "true";
860
            Acquire::cdrom {
861
              mount "/cdrom";
862
              "/cdrom/" {
863
                Mount  "true";
864
                UMount "true";
865
              };
866
            }""")
867
        apt_conf_nmc.close()
868
1036 by Colin Watson
* Use choose-mirror and apt-setup to set up a standard
869
        dbfilter = apt_setup.AptSetup(None)
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
870
        ret = dbfilter.run_command(auto_process=True)
871
        if ret != 0:
1412 by Colin Watson
* Fix typoed variable name while raising InstallStepError.
872
            raise InstallStepError("AptSetup failed with code %d" % ret)
1036 by Colin Watson
* Use choose-mirror and apt-setup to set up a standard
873
874
844 by Colin Watson
factor out broken has_key workaround to new get_cache_pkg method
875
    def get_cache_pkg(self, cache, pkg):
833 by Colin Watson
* Install language packs according to the selected language.
876
        # work around broken has_key in python-apt 0.6.16
877
        try:
844 by Colin Watson
factor out broken has_key workaround to new get_cache_pkg method
878
            return cache[pkg]
833 by Colin Watson
* Install language packs according to the selected language.
879
        except KeyError:
844 by Colin Watson
factor out broken has_key workaround to new get_cache_pkg method
880
            return None
881
882
847 by Colin Watson
make sure not to remove packages that were already installed and then dynamically marked for installation too
883
    def record_installed(self, pkgs):
884
        """Record which packages we've explicitly installed so that we don't
885
        try to remove them later."""
886
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
887
        record_file = "/var/lib/ubiquity/apt-installed"
847 by Colin Watson
make sure not to remove packages that were already installed and then dynamically marked for installation too
888
        if not os.path.exists(os.path.dirname(record_file)):
889
            os.makedirs(os.path.dirname(record_file))
890
        record = open(record_file, "a")
891
892
        for pkg in pkgs:
893
            print >>record, pkg
894
895
        record.close()
896
897
2135 by Colin Watson
factor out reading of apt-installed file
898
    def query_recorded_installed(self):
899
        apt_installed = set()
900
        if os.path.exists("/var/lib/ubiquity/apt-installed"):
901
            record_file = open("/var/lib/ubiquity/apt-installed")
902
            for line in record_file:
903
                apt_installed.add(line.strip())
904
            record_file.close()
905
        return apt_installed
906
907
844 by Colin Watson
factor out broken has_key workaround to new get_cache_pkg method
908
    def mark_install(self, cache, pkg):
909
        cachedpkg = self.get_cache_pkg(cache, pkg)
833 by Colin Watson
* Install language packs according to the selected language.
910
        if cachedpkg is not None and not cachedpkg.isInstalled:
911
            apt_error = False
912
            try:
913
                cachedpkg.markInstall()
914
            except SystemError:
915
                apt_error = True
916
            if cache._depcache.BrokenCount > 0 or apt_error:
1802 by Colin Watson
* Fix language pack installation to clean up any broken packages in the
917
                brokenpkgs = self.broken_packages(cache)
918
                while brokenpkgs:
919
                    for brokenpkg in brokenpkgs:
920
                        self.get_cache_pkg(cache, brokenpkg).markKeep()
921
                    new_brokenpkgs = self.broken_packages(cache)
922
                    if brokenpkgs == new_brokenpkgs:
923
                        break # we can do nothing more
924
                    brokenpkgs = new_brokenpkgs
833 by Colin Watson
* Install language packs according to the selected language.
925
                assert cache._depcache.BrokenCount == 0
926
927
928
    def install_language_packs(self):
929
        langpacks = []
930
        try:
1518 by Colin Watson
* Drop backward compatibility for base-config/language-packs preseeding
931
            langpack_db = self.db.get('pkgsel/language-packs')
833 by Colin Watson
* Install language packs according to the selected language.
932
            langpacks = langpack_db.replace(',', '').split()
933
        except debconf.DebconfError:
934
            pass
935
        if not langpacks:
936
            try:
937
                langpack_db = self.db.get('localechooser/supported-locales')
938
                langpack_set = set()
939
                for locale in langpack_db.replace(',', '').split():
940
                    langpack_set.add(locale.split('_')[0])
941
                langpacks = sorted(langpack_set)
942
            except debconf.DebconfError:
943
                pass
944
        if not langpacks:
945
            langpack_db = self.db.get('debian-installer/locale')
946
            langpacks = [langpack_db.split('_')[0]]
1495 by Colin Watson
* Send all internal log messages to syslog rather than stderr.
947
        syslog.syslog('keeping language packs for: %s' % ' '.join(langpacks))
833 by Colin Watson
* Install language packs according to the selected language.
948
949
        try:
950
            lppatterns = self.db.get('pkgsel/language-pack-patterns').split()
951
        except debconf.DebconfError:
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
952
            return
833 by Colin Watson
* Install language packs according to the selected language.
953
1116 by Colin Watson
* Make sure to keep language packs even if the apt update fails.
954
        to_install = []
955
        for lp in langpacks:
956
            # Basic language packs, required to get localisation working at
957
            # all. We install these almost unconditionally; if you want to
958
            # get rid of even these, you can preseed pkgsel/language-packs
959
            # to the empty string.
960
            to_install.append('language-pack-%s' % lp)
961
            # Other language packs, typically selected by preseeding.
962
            for pattern in lppatterns:
963
                to_install.append(pattern.replace('$LL', lp))
964
            # More extensive language support packages.
965
            to_install.append('language-support-%s' % lp)
2137 by Colin Watson
* Install packages passed to apt-install even if they aren't on the live
966
967
        self.do_install(to_install, langpacks=True)
833 by Colin Watson
* Install language packs according to the selected language.
968
969
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
970
    def configure_timezone(self):
971
        """Set timezone on installed system."""
972
973
        dbfilter = timezone_apply.TimezoneApply(None)
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
974
        ret = dbfilter.run_command(auto_process=True)
975
        if ret != 0:
1412 by Colin Watson
* Fix typoed variable name while raising InstallStepError.
976
            raise InstallStepError("TimezoneApply failed with code %d" % ret)
1280 by Colin Watson
* Add basic clock-setup integration; there's no UI at present, but it will
977
978
        dbfilter = clock_setup.ClockSetup(None)
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
979
        ret = dbfilter.run_command(auto_process=True)
980
        if ret != 0:
1412 by Colin Watson
* Fix typoed variable name while raising InstallStepError.
981
            raise InstallStepError("ClockSetup failed with code %d" % ret)
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
982
983
1024 by Colin Watson
* Propagate selected keymap to installed system (closes: Malone #37748).
984
    def configure_keyboard(self):
985
        """Set keyboard in installed system."""
986
1518.1.1 by Colin Watson
* Move from kbd-chooser to console-setup
987
        dbfilter = console_setup_apply.ConsoleSetupApply(None)
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
988
        ret = dbfilter.run_command(auto_process=True)
989
        if ret != 0:
1518.1.1 by Colin Watson
* Move from kbd-chooser to console-setup
990
            raise InstallStepError(
991
                "ConsoleSetupApply failed with code %d" % ret)
1024 by Colin Watson
* Propagate selected keymap to installed system (closes: Malone #37748).
992
993
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
994
    def configure_user(self):
995
        """create the user selected along the installation process
996
        into the installed system. Default user from live system is
997
        deleted and skel for this new user is copied to $HOME."""
998
999
        dbfilter = usersetup_apply.UserSetupApply(None)
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
1000
        ret = dbfilter.run_command(auto_process=True)
1001
        if ret != 0:
1412 by Colin Watson
* Fix typoed variable name while raising InstallStepError.
1002
            raise InstallStepError("UserSetupApply failed with code %d" % ret)
2109 by Colin Watson
* If oem-config/enable is true, then:
1003
1435.1.5 by Evan Dandrea
migration-assistant works inside Ubiquity running after partitioning now.
1004
    def configure_ma(self):
1005
        """import documents, settings, and users from previous operating
1006
        systems."""
1007
1435.2.7 by Evan Dandrea
When m-a is disabled, don't try to run its apply script.
1008
        if 'UBIQUITY_MIGRATION_ASSISTANT' in os.environ:
1009
            dbfilter = migrationassistant_apply.MigrationAssistantApply(None)
1010
            ret = dbfilter.run_command(auto_process=True)
1011
            if ret != 0:
1012
                raise InstallStepError("MigrationAssistantApply failed with code %d" % ret)
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1013
1014
1222 by Colin Watson
* Mimic base-installer's logic to configure initramfs-tools with an
1015
    def get_resume_partition(self):
1016
        biggest_size = 0
1017
        biggest_partition = None
1018
        swaps = open('/proc/swaps')
1019
        for line in swaps:
1020
            words = line.split()
1021
            if words[1] != 'partition':
1022
                continue
1023
            size = int(words[2])
1024
            if size > biggest_size:
1025
                biggest_size = size
1026
                biggest_partition = words[0]
1027
        swaps.close()
1028
        return biggest_partition
1029
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1030
    def configure_hardware(self):
1031
        """reconfiguring several packages which depends on the
1032
        hardware system in which has been installed on and need some
1033
        automatic configurations to get work."""
1034
1968 by Colin Watson
* Install dummy policy-rc.d and start-stop-daemon while doing anything
1035
        self.chroot_setup()
1036
        try:
1037
            dbfilter = hw_detect.HwDetect(None, self.db)
1038
            ret = dbfilter.run_command(auto_process=True)
1039
            if ret != 0:
1040
                raise InstallStepError("HwDetect failed with code %d" % ret)
1041
        finally:
1042
            self.chroot_cleanup()
1221 by Colin Watson
* Run hw-detect to set up /etc/modules properly (closes: Malone #40826).
1043
1330 by Colin Watson
* Set a slightly more meaningful progress info message while reconfiguring
1044
        self.db.progress('INFO', 'ubiquity/install/hardware')
1045
2066 by Colin Watson
more ex -> execute renaming
1046
        misc.execute('/usr/lib/ubiquity/debian-installer-utils'
1047
                     '/register-module.post-base-installer')
1221 by Colin Watson
* Run hw-detect to set up /etc/modules properly (closes: Malone #40826).
1048
1222 by Colin Watson
* Mimic base-installer's logic to configure initramfs-tools with an
1049
        resume = self.get_resume_partition()
1050
        if resume is not None:
1432 by Colin Watson
* Write out the resume partition as a UUID if possible.
1051
            resume_uuid = None
1052
            try:
1053
                resume_uuid = subprocess.Popen(
1054
                    ['vol_id', '-u', resume],
1055
                    stdout=subprocess.PIPE).communicate()[0].rstrip('\n')
1056
            except OSError:
1057
                pass
1058
            if resume_uuid:
1059
                resume = "UUID=%s" % resume_uuid
1418 by Colin Watson
* Handle new initramfs-tools configuration directory.
1060
            if os.path.exists(os.path.join(self.target,
1061
                                           'etc/initramfs-tools/conf.d')):
1062
                configdir = os.path.join(self.target,
1063
                                         'etc/initramfs-tools/conf.d')
1064
            elif os.path.exists(os.path.join(self.target,
1065
                                             'etc/mkinitramfs/conf.d')):
1066
                configdir = os.path.join(self.target,
1067
                                         'etc/mkinitramfs/conf.d')
1068
            else:
1069
                configdir = None
1070
            if configdir is not None:
1071
                configfile = open(os.path.join(configdir, 'resume'), 'w')
1072
                print >>configfile, "RESUME=%s" % resume
1073
                configfile.close()
1222 by Colin Watson
* Mimic base-installer's logic to configure initramfs-tools with an
1074
1539 by Colin Watson
* Reconfigure popularity-contest.
1075
        try:
1548 by Colin Watson
* Reconfigure usplash (closes: Malone #59320).
1076
            os.unlink('/target/etc/usplash.conf')
1077
        except OSError:
1078
            pass
1079
1080
        try:
1544 by Colin Watson
/etc/popularity-contest.conf -> /target/etc/popularity-contest.conf
1081
            os.unlink('/target/etc/popularity-contest.conf')
1539 by Colin Watson
* Reconfigure popularity-contest.
1082
        except OSError:
1083
            pass
1818 by Colin Watson
* Copy value of popularity-contest/participate into /target (LP: #78972).
1084
        try:
1085
            participate = self.db.get('popularity-contest/participate')
1086
            self.set_debconf('popularity-contest/participate', participate)
1841 by Colin Watson
* Fix crash due to underqualification of debconf.DebconfError (thanks,
1087
        except debconf.DebconfError:
1818 by Colin Watson
* Copy value of popularity-contest/participate into /target (LP: #78972).
1088
            pass
1539 by Colin Watson
* Reconfigure popularity-contest.
1089
2058 by Colin Watson
* Reconfigure /etc/papersize on installation (LP: #104160).
1090
        try:
1091
            os.unlink('/target/etc/papersize')
1092
        except OSError:
1093
            pass
2244 by Colin Watson
* Purge ucf's state for /etc/papersize so that it will be recreated
1094
        self.chrex('ucf', '--purge', '/etc/papersize')
2058 by Colin Watson
* Reconfigure /etc/papersize on installation (LP: #104160).
1095
        try:
1096
            self.set_debconf('libpaper/defaultpaper', '')
1097
        except debconf.DebconfError:
1098
            pass
1099
1968 by Colin Watson
* Install dummy policy-rc.d and start-stop-daemon while doing anything
1100
        self.chroot_setup()
1549 by Colin Watson
* Divert away update-initramfs while configuring hardware-specific
1101
        self.chrex('dpkg-divert', '--package', 'ubiquity', '--rename',
1102
                   '--quiet', '--add', '/usr/sbin/update-initramfs')
1103
        try:
1104
            os.symlink('/bin/true', '/target/usr/sbin/update-initramfs')
1105
        except OSError:
1106
            pass
1107
1157 by Colin Watson
* Reconfigure linux-restricted-modules-$KVER as well as linux-image-$KVER
1108
        packages = ['linux-image-' + self.kernel_version,
1539 by Colin Watson
* Reconfigure popularity-contest.
1109
                    'linux-restricted-modules-' + self.kernel_version,
1548 by Colin Watson
* Reconfigure usplash (closes: Malone #59320).
1110
                    'usplash',
2058 by Colin Watson
* Reconfigure /etc/papersize on installation (LP: #104160).
1111
                    'popularity-contest',
1112
                    'libpaper1']
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1113
1114
        try:
1115
            for package in packages:
1116
                self.reconfigure(package)
1117
        finally:
1549 by Colin Watson
* Divert away update-initramfs while configuring hardware-specific
1118
            try:
1119
                os.unlink('/target/usr/sbin/update-initramfs')
1120
            except OSError:
1121
                pass
1122
            self.chrex('dpkg-divert', '--package', 'ubiquity', '--rename',
1123
                       '--quiet', '--remove', '/usr/sbin/update-initramfs')
1678 by Colin Watson
* Use update-initramfs -c -k "$(uname -r)" rather than update-initramfs
1124
            self.chrex('update-initramfs', '-c', '-k', os.uname()[2])
1968 by Colin Watson
* Install dummy policy-rc.d and start-stop-daemon while doing anything
1125
            self.chroot_cleanup()
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1126
1674 by Colin Watson
* Remove kernels before running update-initramfs so that we update the
1127
        # Fix up kernel symlinks now that the initrd exists. Depending on
1128
        # the architecture, these may be in / or in /boot.
1129
        bootdir = os.path.join(self.target, 'boot')
1130
        if self.db.get('base-installer/kernel/linux/link_in_boot') == 'true':
1131
            linkdir = bootdir
1132
            linkprefix = ''
1133
        else:
1134
            linkdir = self.target
1135
            linkprefix = 'boot'
1136
1137
        # Remove old symlinks. We'll set them up from scratch.
1138
        re_symlink = re.compile('vmlinu[xz]|initrd.img$')
1139
        for entry in os.listdir(linkdir):
1140
            if re_symlink.match(entry) is not None:
1141
                filename = os.path.join(linkdir, entry)
1142
                if os.path.islink(filename):
1143
                    os.unlink(filename)
1144
        if linkdir != self.target:
1145
            # Remove symlinks in /target too, which may have been created on
1146
            # the live filesystem. This isn't necessary, but it may help
1147
            # avoid confusion.
1148
            for entry in os.listdir(self.target):
1149
                if re_symlink.match(entry) is not None:
1150
                    filename = os.path.join(self.target, entry)
1151
                    if os.path.islink(filename):
1152
                        os.unlink(filename)
1153
1154
        # Create symlinks. Prefer our current kernel version if possible,
1155
        # but if not (perhaps due to a customised live filesystem image),
1156
        # it's better to create some symlinks than none at all.
1157
        re_image = re.compile('(vmlinu[xz]|initrd.img)-')
1158
        for entry in os.listdir(bootdir):
1159
            match = re_image.match(entry)
1160
            if match is not None:
1161
                imagetype = match.group(1)
1162
                linksrc = os.path.join(linkprefix, entry)
1163
                linkdst = os.path.join(linkdir, imagetype)
1164
                if os.path.exists(linkdst):
1165
                    if entry.endswith('-' + self.kernel_version):
1166
                        os.unlink(linkdst)
1167
                    else:
1168
                        continue
1169
                os.symlink(linksrc, linkdst)
1170
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1171
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1172
    def get_all_interfaces(self):
1173
        """Get all non-local network interfaces."""
1174
        ifs = []
1175
        ifs_file = open('/proc/net/dev')
1176
        # eat header
1177
        ifs_file.readline()
1178
        ifs_file.readline()
1179
1180
        for line in ifs_file:
1181
            name = re.match('(.*?(?::\d+)?):', line.strip()).group(1)
1182
            if name == 'lo':
1183
                continue
1184
            ifs.append(name)
1185
1186
        ifs_file.close()
1187
        return ifs
1188
1189
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1190
    def configure_network(self):
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1191
        """Automatically configure the network.
2143 by Colin Watson
more trailing whitespace
1192
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1193
        At present, the only thing the user gets to tweak in the UI is the
1194
        hostname. Some other things will be copied from the live filesystem,
1195
        so changes made there will be reflected in the installed system.
2143 by Colin Watson
more trailing whitespace
1196
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1197
        Unfortunately, at present we have to duplicate a fair bit of netcfg
1198
        here, because it's hard to drive netcfg in a way that won't try to
1199
        bring interfaces up and down."""
1200
1201
        # TODO cjwatson 2006-03-30: just call netcfg instead of doing all
1202
        # this; requires a netcfg binary that doesn't bring interfaces up
1203
        # and down
1204
1205
        for path in ('/etc/network/interfaces', '/etc/resolv.conf'):
1206
            if os.path.exists(path):
1021 by Colin Watson
* Fix copying of /etc/network/interfaces and /etc/resolv.conf.
1207
                shutil.copy2(path, os.path.join(self.target, path[1:]))
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1208
1209
        try:
1210
            hostname = self.db.get('netcfg/get_hostname')
1211
        except debconf.DebconfError:
1212
            hostname = ''
1772 by Colin Watson
* Sync up hostname handling with netcfg; it now allows hostnames between 2
1213
        try:
1214
            domain = self.db.get('netcfg/get_domain')
1215
        except debconf.DebconfError:
1216
            domain = ''
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1217
        if hostname == '':
1218
            hostname = 'ubuntu'
1772 by Colin Watson
* Sync up hostname handling with netcfg; it now allows hostnames between 2
1219
996 by Colin Watson
reorder method definitions to match calling sequence
1220
        fp = open(os.path.join(self.target, 'etc/hostname'), 'w')
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1221
        print >>fp, hostname
996 by Colin Watson
reorder method definitions to match calling sequence
1222
        fp.close()
1223
1224
        hosts = open(os.path.join(self.target, 'etc/hosts'), 'w')
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1225
        print >>hosts, "127.0.0.1\tlocalhost"
1772 by Colin Watson
* Sync up hostname handling with netcfg; it now allows hostnames between 2
1226
        if domain:
1227
            print >>hosts, "127.0.1.1\t%s.%s\t%s" % (hostname, domain,
1228
                                                     hostname)
1229
        else:
1230
            print >>hosts, "127.0.1.1\t%s" % hostname
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1231
        print >>hosts, textwrap.dedent("""\
996 by Colin Watson
reorder method definitions to match calling sequence
1232
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1233
            # The following lines are desirable for IPv6 capable hosts
1234
            ::1     ip6-localhost ip6-loopback
1235
            fe00::0 ip6-localnet
1236
            ff00::0 ip6-mcastprefix
1237
            ff02::1 ip6-allnodes
1238
            ff02::2 ip6-allrouters
1239
            ff02::3 ip6-allhosts""")
996 by Colin Watson
reorder method definitions to match calling sequence
1240
        hosts.close()
1241
2100 by Colin Watson
* If /etc/udev/rules.d/70-persistent-net.rules exists, copy it rather than
1242
        persistent_net = '/etc/udev/rules.d/70-persistent-net.rules'
1243
        if os.path.exists(persistent_net):
1244
            shutil.copy2(persistent_net,
1245
                         os.path.join(self.target, persistent_net[1:]))
1246
        else:
1247
            # TODO cjwatson 2006-03-30: from <bits/ioctls.h>; ugh, but no
1248
            # binding available
1249
            SIOCGIFHWADDR = 0x8927
1250
            # <net/if_arp.h>
1251
            ARPHRD_ETHER = 1
1252
1253
            if_names = {}
1254
            sock = socket.socket(socket.SOCK_DGRAM)
1255
            interfaces = self.get_all_interfaces()
1256
            for i in range(len(interfaces)):
1257
                if_names[interfaces[i]] = struct.unpack('H6s',
1258
                    fcntl.ioctl(sock.fileno(), SIOCGIFHWADDR,
1259
                                struct.pack('256s', interfaces[i]))[16:24])
1260
            sock.close()
1261
1262
            iftab = open(os.path.join(self.target, 'etc/iftab'), 'w')
1263
1264
            print >>iftab, textwrap.dedent("""\
1265
                # This file assigns persistent names to network interfaces.
1266
                # See iftab(5) for syntax.
1267
                """)
1268
1269
            for i in range(len(interfaces)):
1270
                dup = False
1271
                with_arp = False
1272
1273
                if_name = if_names[interfaces[i]]
1274
                if if_name is None or if_name[0] != ARPHRD_ETHER:
1275
                    continue
1276
1277
                for j in range(len(interfaces)):
1278
                    if i == j or if_names[interfaces[j]] is None:
1279
                        continue
1280
                    if if_name[1] != if_names[interfaces[j]][1]:
1281
                        continue
1282
1283
                    if if_names[interfaces[j]][0] == ARPHRD_ETHER:
1284
                        dup = True
1285
1286
                if dup:
1287
                    continue
1288
1289
                line = (interfaces[i] + " mac " +
1290
                        ':'.join(['%02x' % ord(if_name[1][c])
1291
                                  for c in range(6)]))
1292
                line += " arp %d" % if_name[0]
1293
                print >>iftab, line
1294
1295
            iftab.close()
997 by Colin Watson
* Perform network configuration: we copy /etc/network/interfaces and
1296
996 by Colin Watson
reorder method definitions to match calling sequence
1297
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1298
    def configure_bootloader(self):
1299
        """configuring and installing boot loader into installed
1300
        hardware system."""
2108.1.30 by Mario Limonciello
Add support to disable/enable the bootloader installation
1301
        install_bootloader = self.db.get('ubiquity/install_bootloader')
1302
        if install_bootloader == "true":
1303
            misc.execute('mount', '--bind', '/proc', self.target + '/proc')
1304
            misc.execute('mount', '--bind', '/dev', self.target + '/dev')
1305
1306
            archdetect = subprocess.Popen(['archdetect'], stdout=subprocess.PIPE)
1307
            subarch = archdetect.communicate()[0].strip()
1308
1309
            try:
1310
                if subarch.startswith('amd64/') or subarch.startswith('i386/'):
1311
                    from ubiquity.components import grubinstaller
1312
                    dbfilter = grubinstaller.GrubInstaller(None)
1313
                    ret = dbfilter.run_command(auto_process=True)
1314
                    if ret != 0:
1315
                        raise InstallStepError(
1316
                            "GrubInstaller failed with code %d" % ret)
1317
                elif subarch == 'powerpc/ps3':
1318
                    from ubiquity.components import kbootinstaller
1319
                    dbfilter = kbootinstaller.KbootInstaller(None)
1320
                    ret = dbfilter.run_command(auto_process=True)
1321
                    if ret != 0:
1322
                        raise InstallStepError(
1323
                            "KbootInstaller failed with code %d" % ret)
1324
                elif subarch.startswith('powerpc/'):
1325
                    from ubiquity.components import yabootinstaller
1326
                    dbfilter = yabootinstaller.YabootInstaller(None)
1327
                    ret = dbfilter.run_command(auto_process=True)
1328
                    if ret != 0:
1329
                        raise InstallStepError(
1330
                            "YabootInstaller failed with code %d" % ret)
1331
                else:
1332
                    raise InstallStepError("No bootloader installer found")
1333
            except ImportError:
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
1334
                raise InstallStepError("No bootloader installer found")
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1335
2108.1.30 by Mario Limonciello
Add support to disable/enable the bootloader installation
1336
            misc.execute('umount', '-f', self.target + '/proc')
1337
            misc.execute('umount', '-f', self.target + '/dev')
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1338
1339
1671 by Colin Watson
attempt multiple broken package resolution passes while removing packages (more work on Malone #66406)
1340
    def broken_packages(self, cache):
1341
        brokenpkgs = set()
1342
        for pkg in cache.keys():
1709 by Colin Watson
* Work around a crash in broken_packages() due to inadequate python-apt
1343
            try:
1344
                if cache._depcache.IsInstBroken(cache._cache[pkg]):
1345
                    brokenpkgs.add(pkg)
1346
            except KeyError:
1347
                # Apparently sometimes the cache goes a bit bonkers ...
1684 by Colin Watson
* Defend a bit more against the apt cache going insane (closes: Malone
1348
                continue
1671 by Colin Watson
attempt multiple broken package resolution passes while removing packages (more work on Malone #66406)
1349
        return brokenpkgs
1350
2137 by Colin Watson
* Install packages passed to apt-install even if they aren't on the live
1351
    def do_install(self, to_install, langpacks=False):
1352
        if langpacks:
1353
            self.db.progress('START', 0, 10, 'ubiquity/langpacks/title')
1354
        else:
1355
            self.db.progress('START', 0, 10, 'ubiquity/install/title')
1356
        self.db.progress('INFO', 'ubiquity/install/find_installables')
1357
1358
        if langpacks:
1359
            # ... otherwise we're installing packages that have already been
1360
            # recorded
1361
            self.record_installed(to_install)
1362
1363
        self.db.progress('REGION', 0, 1)
1364
        fetchprogress = DebconfFetchProgress(
1365
            self.db, 'ubiquity/install/title',
1366
            'ubiquity/install/apt_indices_starting',
1367
            'ubiquity/install/apt_indices')
1368
        cache = Cache()
1369
1370
        if cache._depcache.BrokenCount > 0:
1371
            syslog.syslog(
1372
                'not installing additional packages, since there are broken '
1373
                'packages: %s' % ', '.join(self.broken_packages(cache)))
1374
            self.db.progress('STOP')
1375
            return
1376
1377
        for pkg in to_install:
1378
            self.mark_install(cache, pkg)
1379
1380
        self.db.progress('SET', 1)
1381
        self.db.progress('REGION', 1, 10)
1382
        if langpacks:
1383
            fetchprogress = DebconfFetchProgress(
1384
                self.db, 'ubiquity/langpacks/title', None,
1385
                'ubiquity/langpacks/packages')
1386
            installprogress = DebconfInstallProgress(
1387
                self.db, 'ubiquity/langpacks/title',
1388
                'ubiquity/install/apt_info')
1389
        else:
1390
            fetchprogress = DebconfFetchProgress(
1391
                self.db, 'ubiquity/install/title', None,
1392
                'ubiquity/install/fetch_remove')
1393
            installprogress = DebconfInstallProgress(
1394
                self.db, 'ubiquity/install/title',
1395
                'ubiquity/install/apt_info',
1396
                'ubiquity/install/apt_error_install')
1397
        self.chroot_setup()
1398
        commit_error = None
1399
        try:
1400
            try:
1401
                if not cache.commit(fetchprogress, installprogress):
1402
                    fetchprogress.stop()
1403
                    installprogress.finishUpdate()
1404
                    self.db.progress('STOP')
1405
                    return
1406
            except IOError, e:
1407
                for line in str(e).split('\n'):
1408
                    syslog.syslog(syslog.LOG_ERR, line)
1409
                commit_error = str(e)
1410
            except SystemError, e:
1411
                for line in str(e).split('\n'):
1412
                    syslog.syslog(syslog.LOG_ERR, line)
1413
                commit_error = str(e)
1414
        finally:
1415
            self.chroot_cleanup()
1416
        self.db.progress('SET', 10)
1417
1418
        cache.open(None)
1419
        if commit_error or cache._depcache.BrokenCount > 0:
1420
            if commit_error is None:
1421
                commit_error = ''
1422
            brokenpkgs = self.broken_packages(cache)
1423
            syslog.syslog('broken packages after installation: '
1424
                          '%s' % ', '.join(brokenpkgs))
1425
            self.db.subst('ubiquity/install/broken_install', 'ERROR',
1426
                          commit_error)
1427
            self.db.subst('ubiquity/install/broken_install', 'PACKAGES',
1428
                          ', '.join(brokenpkgs))
1429
            self.db.input('critical', 'ubiquity/install/broken_install')
1430
            self.db.go()
1431
1432
        self.db.progress('STOP')
1433
1434
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1435
    def do_remove(self, to_remove, recursive=False):
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
1436
        self.db.progress('START', 0, 5, 'ubiquity/install/title')
1437
        self.db.progress('INFO', 'ubiquity/install/find_removables')
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1438
1439
        fetchprogress = DebconfFetchProgress(
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
1440
            self.db, 'ubiquity/install/title',
1441
            'ubiquity/install/apt_indices_starting',
1442
            'ubiquity/install/apt_indices')
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1443
        cache = Cache()
1444
1989 by Colin Watson
* If installing or removing packages fails, then display a helpful error
1445
        if cache._depcache.BrokenCount > 0:
1446
            syslog.syslog(
1447
                'not processing removals, since there are broken packages: '
1448
                '%s' % ', '.join(self.broken_packages(cache)))
1449
            self.db.progress('STOP')
1450
            return
1451
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1452
        while True:
1453
            removed = set()
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1454
            for pkg in to_remove:
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1455
                cachedpkg = self.get_cache_pkg(cache, pkg)
1456
                if cachedpkg is not None and cachedpkg.isInstalled:
1457
                    apt_error = False
1458
                    try:
1064 by Colin Watson
* When removing packages that don't belong on the installed system, purge
1459
                        cachedpkg.markDelete(autoFix=False, purge=True)
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1460
                    except SystemError:
1461
                        apt_error = True
1462
                    if apt_error:
1463
                        cachedpkg.markKeep()
1464
                    elif cache._depcache.BrokenCount > 0:
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1465
                        # If we're recursively removing packages, or if all
1466
                        # of the broken packages are in the set of packages
1467
                        # to remove anyway, then go ahead and try to remove
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1468
                        # them too.
1671 by Colin Watson
attempt multiple broken package resolution passes while removing packages (more work on Malone #66406)
1469
                        brokenpkgs = self.broken_packages(cache)
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1470
                        broken_removed = set()
1671 by Colin Watson
attempt multiple broken package resolution passes while removing packages (more work on Malone #66406)
1471
                        while brokenpkgs and (recursive or
1472
                                              brokenpkgs <= to_remove):
1473
                            broken_removed_inner = set()
1666 by Colin Watson
* Fix incorrect loop variable reuse in do_remove (closes: Malone #66406).
1474
                            for pkg2 in brokenpkgs:
1475
                                cachedpkg2 = self.get_cache_pkg(cache, pkg2)
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1476
                                if cachedpkg2 is not None:
1671 by Colin Watson
attempt multiple broken package resolution passes while removing packages (more work on Malone #66406)
1477
                                    broken_removed_inner.add(pkg2)
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1478
                                    try:
1064 by Colin Watson
* When removing packages that don't belong on the installed system, purge
1479
                                        cachedpkg2.markDelete(autoFix=False,
1480
                                                              purge=True)
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1481
                                    except SystemError:
1482
                                        apt_error = True
1483
                                        break
1671 by Colin Watson
attempt multiple broken package resolution passes while removing packages (more work on Malone #66406)
1484
                            broken_removed |= broken_removed_inner
1485
                            if apt_error or not broken_removed_inner:
1486
                                break
1487
                            brokenpkgs = self.broken_packages(cache)
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1488
                        if apt_error or cache._depcache.BrokenCount > 0:
1489
                            # That didn't work. Revert all the removals we
1490
                            # just tried.
1666 by Colin Watson
* Fix incorrect loop variable reuse in do_remove (closes: Malone #66406).
1491
                            for pkg2 in broken_removed:
1492
                                self.get_cache_pkg(cache, pkg2).markKeep()
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1493
                            cachedpkg.markKeep()
1494
                        else:
1495
                            removed.add(pkg)
1496
                            removed |= broken_removed
1497
                    else:
1498
                        removed.add(pkg)
1499
                    assert cache._depcache.BrokenCount == 0
1671 by Colin Watson
attempt multiple broken package resolution passes while removing packages (more work on Malone #66406)
1500
            if not removed:
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1501
                break
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1502
            to_remove -= removed
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1503
1504
        self.db.progress('SET', 1)
1505
        self.db.progress('REGION', 1, 5)
1506
        fetchprogress = DebconfFetchProgress(
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
1507
            self.db, 'ubiquity/install/title', None,
1508
            'ubiquity/install/fetch_remove')
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1509
        installprogress = DebconfInstallProgress(
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
1510
            self.db, 'ubiquity/install/title', 'ubiquity/install/apt_info',
1511
            'ubiquity/install/apt_error_remove')
1968 by Colin Watson
* Install dummy policy-rc.d and start-stop-daemon while doing anything
1512
        self.chroot_setup()
1989 by Colin Watson
* If installing or removing packages fails, then display a helpful error
1513
        commit_error = None
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1514
        try:
1968 by Colin Watson
* Install dummy policy-rc.d and start-stop-daemon while doing anything
1515
            try:
1516
                if not cache.commit(fetchprogress, installprogress):
1517
                    fetchprogress.stop()
1518
                    installprogress.finishUpdate()
1519
                    self.db.progress('STOP')
1520
                    return
1521
            except SystemError, e:
1522
                for line in str(e).split('\n'):
1523
                    syslog.syslog(syslog.LOG_ERR, line)
1989 by Colin Watson
* If installing or removing packages fails, then display a helpful error
1524
                commit_error = str(e)
1968 by Colin Watson
* Install dummy policy-rc.d and start-stop-daemon while doing anything
1525
        finally:
1526
            self.chroot_cleanup()
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1527
        self.db.progress('SET', 5)
1528
1989 by Colin Watson
* If installing or removing packages fails, then display a helpful error
1529
        cache.open(None)
1530
        if commit_error or cache._depcache.BrokenCount > 0:
1531
            if commit_error is None:
1532
                commit_error = ''
1533
            brokenpkgs = self.broken_packages(cache)
1534
            syslog.syslog('broken packages after removal: '
1535
                          '%s' % ', '.join(brokenpkgs))
1536
            self.db.subst('ubiquity/install/broken_remove', 'ERROR',
1537
                          commit_error)
1538
            self.db.subst('ubiquity/install/broken_remove', 'PACKAGES',
1539
                          ', '.join(brokenpkgs))
1540
            self.db.input('critical', 'ubiquity/install/broken_remove')
1541
            self.db.go()
1542
846 by Colin Watson
* Remove packages specific to the live CD after installation (relies on
1543
        self.db.progress('STOP')
1544
1545
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1546
    def remove_unusable_kernels(self):
1547
        """Remove unusable kernels; keeping them may cause us to be unable
1548
        to boot."""
1549
1674 by Colin Watson
* Remove kernels before running update-initramfs so that we update the
1550
        self.db.progress('START', 0, 5, 'ubiquity/install/title')
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1551
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
1552
        self.db.progress('INFO', 'ubiquity/install/find_removables')
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1553
1554
        # Check for kernel packages to remove.
1555
        dbfilter = check_kernels.CheckKernels(None)
1556
        dbfilter.run_command(auto_process=True)
1557
1675 by Colin Watson
revert remove_kernels -> self.remove_kernels change, no longer necessary
1558
        remove_kernels = set()
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
1559
        if os.path.exists("/var/lib/ubiquity/remove-kernels"):
1778 by Colin Watson
fix filehandle leaks
1560
            remove_kernels_file = open("/var/lib/ubiquity/remove-kernels")
1561
            for line in remove_kernels_file:
1675 by Colin Watson
revert remove_kernels -> self.remove_kernels change, no longer necessary
1562
                remove_kernels.add(line.strip())
1778 by Colin Watson
fix filehandle leaks
1563
            remove_kernels_file.close()
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1564
1675 by Colin Watson
revert remove_kernels -> self.remove_kernels change, no longer necessary
1565
        if len(remove_kernels) == 0:
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1566
            self.db.progress('STOP')
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
1567
            return
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1568
1569
        self.db.progress('SET', 1)
1570
        self.db.progress('REGION', 1, 5)
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
1571
        try:
1675 by Colin Watson
revert remove_kernels -> self.remove_kernels change, no longer necessary
1572
            self.do_remove(remove_kernels, recursive=True)
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
1573
        except:
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1574
            self.db.progress('STOP')
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
1575
            raise
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1576
        self.db.progress('SET', 5)
1577
        self.db.progress('STOP')
1578
1579
2137 by Colin Watson
* Install packages passed to apt-install even if they aren't on the live
1580
    def install_extras(self):
1581
        """Try to install additional packages requested by installer
1582
        components."""
1583
1584
        # We only ever install these packages from the CD.
1585
        sources_list = os.path.join(self.target, 'etc/apt/sources.list')
1586
        os.rename(sources_list, "%s.apt-setup" % sources_list)
1587
        old_sources = open("%s.apt-setup" % sources_list)
1588
        new_sources = open(sources_list, 'w')
1589
        found_cdrom = False
1590
        for line in old_sources:
1591
            if 'cdrom:' in line:
1592
                print >>new_sources, line,
1593
                found_cdrom = True
1594
        new_sources.close()
1595
        old_sources.close()
1596
        if not found_cdrom:
1597
            os.rename("%s.apt-setup" % sources_list, sources_list)
1598
1599
        self.do_install(self.query_recorded_installed())
1600
1601
        if found_cdrom:
1602
            os.rename("%s.apt-setup" % sources_list, sources_list)
1603
2173 by Colin Watson
* Reimplement more of oem-config-udeb (ugh): disable the hwdb-client
1604
        # TODO cjwatson 2007-08-09: python reimplementation of
1605
        # oem-config/finish-install.d/07oem-config-user. This really needs
1606
        # to die in a great big chemical fire and call the same shell script
1607
        # instead.
2172 by Colin Watson
* Move oem-config post-user-creation hacks to the end of install_extras,
1608
        try:
1609
            if self.db.get('oem-config/enable') == 'true':
1610
                if os.path.isdir(os.path.join(self.target, 'home/oem')):
2173 by Colin Watson
* Reimplement more of oem-config-udeb (ugh): disable the hwdb-client
1611
                    open(os.path.join(self.target, 'home/oem/.hwdb'),
1612
                         'w').close()
1613
2172 by Colin Watson
* Move oem-config post-user-creation hacks to the end of install_extras,
1614
                    for desktop_file in (
1615
                        'usr/share/applications/oem-config-prepare-gtk.desktop',
1616
                        'usr/share/applications/kde/oem-config-prepare-kde.desktop'):
1617
                        if os.path.exists(os.path.join(self.target,
1618
                                                       desktop_file)):
1619
                            desktop_base = os.path.basename(desktop_file)
1620
                            self.chrex('install', '-D',
1621
                                       '-o', 'oem', '-g', 'oem',
1622
                                       '/%s' % desktop_file,
1623
                                       '/home/oem/Desktop/%s' % desktop_base)
1624
                            break
2173 by Colin Watson
* Reimplement more of oem-config-udeb (ugh): disable the hwdb-client
1625
1626
                # Some serious horribleness is needed here to handle absolute
1627
                # symlinks.
1628
                for name in ('gdm-cdd.conf', 'gdm.conf'):
1629
                    gdm_conf = osextras.realpath_root(
1630
                        self.target, os.path.join('/etc/gdm', name))
1631
                    if os.path.isfile(gdm_conf):
2326 by Colin Watson
* Shell out to sed for now rather than using flaky, complicated, and above
1632
                        self.chrex('sed', '-i.oem',
1633
                                   '-e', 's/^AutomaticLoginEnable=.*$/AutomaticLoginEnable=true/',
1634
                                   '-e', 's/^AutomaticLogin=.*$/AutomaticLogin=oem/',
1635
                                   '-e', 's/^TimedLoginEnable=.*$/TimedLoginEnable=true/',
1636
                                   '-e', 's/^TimedLogin=.*$/TimedLogin=oem/',
1637
                                   '-e', 's/^TimedLoginDelay=.*$/TimedLoginDelay=10/',
1638
                                   os.path.join('/etc/gdm', name));
2173 by Colin Watson
* Reimplement more of oem-config-udeb (ugh): disable the hwdb-client
1639
                        break
1640
1641
                kdmrc = os.path.join(self.target, 'etc/kde3/kdm/kdmrc')
1642
                if os.path.isfile(kdmrc):
2326 by Colin Watson
* Shell out to sed for now rather than using flaky, complicated, and above
1643
                    misc.execute('sed', '-i.oem', '-r',
1644
                                 '-e', 's/^#?AutoLoginEnable=.*$/AutoLoginEnable=true/',
1645
                                 '-e', 's/^#?AutoLoginUser=.*$/AutoLoginUser=oem/',
1646
                                 '-e', 's/^#?AutoReLogin=.*$/AutoReLogin=true/',
1647
                                 kdmrc)
2173 by Colin Watson
* Reimplement more of oem-config-udeb (ugh): disable the hwdb-client
1648
1649
                if osextras.find_on_path_root(self.target, 'kpersonalizer'):
1650
                    kpersonalizerrc = os.path.join(self.target,
1651
                                                   'etc/kde3/kpersonalizerrc')
1652
                    if not os.path.isfile(kpersonalizerrc):
1653
                        kpersonalizerrc_file = open(kpersonalizerrc, 'w')
1654
                        print >>kpersonalizerrc_file, '[General]'
1655
                        print >>kpersonalizerrc_file, 'FirstLogin=false'
1656
                        kpersonalizerrc_file.close()
1657
                        open('%s.created-by-oem', 'w').close()
2172 by Colin Watson
* Move oem-config post-user-creation hacks to the end of install_extras,
1658
        except debconf.DebconfError:
1659
            pass
1660
2137 by Colin Watson
* Install packages passed to apt-install even if they aren't on the live
1661
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1662
    def remove_extras(self):
1663
        """Try to remove packages that are needed on the live CD but not on
1664
        the installed system."""
1665
1666
        # Looking through files for packages to remove is pretty quick, so
1667
        # don't bother with a progress bar for that.
1668
1669
        # Check for packages specific to the live CD.
1670
        if (os.path.exists("/cdrom/casper/filesystem.manifest-desktop") and
1671
            os.path.exists("/cdrom/casper/filesystem.manifest")):
1672
            desktop_packages = set()
1778 by Colin Watson
fix filehandle leaks
1673
            manifest = open("/cdrom/casper/filesystem.manifest-desktop")
1674
            for line in manifest:
1173 by Colin Watson
* Ignore comments and blank lines in manifest files, so that we can put
1675
                if line.strip() != '' and not line.startswith('#'):
1676
                    desktop_packages.add(line.split()[0])
1778 by Colin Watson
fix filehandle leaks
1677
            manifest.close()
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1678
            live_packages = set()
1778 by Colin Watson
fix filehandle leaks
1679
            manifest = open("/cdrom/casper/filesystem.manifest")
1680
            for line in manifest:
1173 by Colin Watson
* Ignore comments and blank lines in manifest files, so that we can put
1681
                if line.strip() != '' and not line.startswith('#'):
1682
                    live_packages.add(line.split()[0])
1778 by Colin Watson
fix filehandle leaks
1683
            manifest.close()
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1684
            difference = live_packages - desktop_packages
1685
        else:
1686
            difference = set()
1687
1688
        # Keep packages we explicitly installed.
2135 by Colin Watson
factor out reading of apt-installed file
1689
        difference -= self.query_recorded_installed()
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1690
1691
        if len(difference) == 0:
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
1692
            return
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1693
1349 by Colin Watson
* Ignore failures while removing extra packages from installed system
1694
        # Don't worry about failures removing packages; it will be easier
1695
        # for the user to sort them out with a graphical package manager (or
1696
        # whatever) after installation than it will be to try to deal with
1697
        # them automatically here.
1698
        self.do_remove(difference)
1065 by Colin Watson
* Remove kernels that aren't appropriate for the current system, using
1699
1700
1166 by Colin Watson
* Write out /target/etc/apt/apt.conf.d/00IgnoreTimeConflict for the
1701
    def cleanup(self):
1702
        """Miscellaneous cleanup tasks."""
2134 by Colin Watson
* Replicate the apt configuration done by base-installer (trust CD-ROMs,
1703
1704
        misc.execute('umount', os.path.join(self.target, 'cdrom'))
1705
1706
        for apt_conf in ('00NoMountCDROM', '00IgnoreTimeConflict',
1707
                         '00AllowUnauthenticated'):
1708
            try:
1709
                os.unlink(os.path.join(
1710
                    self.target, 'etc/apt/apt.conf.d', apt_conf))
1711
            except:
1712
                pass
1713
1523 by Colin Watson
* Add a ubiquity/install/filesystem-images template, which can be
1714
        if self.source == '/var/lib/ubiquity/source':
1715
            self.umount_source()
1166 by Colin Watson
* Write out /target/etc/apt/apt.conf.d/00IgnoreTimeConflict for the
1716
1717
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1718
    def chrex(self, *args):
1719
        """executes commands on chroot system (provided by *args)."""
2066 by Colin Watson
more ex -> execute renaming
1720
        return misc.execute('chroot', self.target, *args)
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1721
1722
1723
    def copy_debconf(self, package):
1724
        """setting debconf database into installed system."""
1725
1726
        # TODO cjwatson 2006-02-25: unusable here now because we have a
1727
        # running debconf frontend that's locked the database; fortunately
1728
        # this isn't critical. We still need to think about how to handle
1729
        # preseeding in general, though.
1730
        targetdb = os.path.join(self.target, 'var/cache/debconf/config.dat')
1731
2066 by Colin Watson
more ex -> execute renaming
1732
        misc.execute('debconf-copydb', 'configdb', 'targetdb', '-p',
1733
                     '^%s/' % package, '--config=Name:targetdb',
1734
                     '--config=Driver:File','--config=Filename:' + targetdb)
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1735
1736
840 by Colin Watson
* Various pychecker-induced cleanups.
1737
    def set_debconf(self, question, value):
1499 by Colin Watson
use log-output to spawn external processes so that their output goes to syslog
1738
        dccomm = subprocess.Popen(['log-output', '-t', 'ubiquity',
1739
                                   '--pass-stdout',
1740
                                   'chroot', self.target,
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1741
                                   'debconf-communicate',
1121 by Colin Watson
* Rename from espresso to ubiquity, to better suggest an association with
1742
                                   '-fnoninteractive', 'ubiquity'],
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1743
                                  stdin=subprocess.PIPE,
1744
                                  stdout=subprocess.PIPE, close_fds=True)
2059 by Colin Watson
* When changing values of debconf questions in /target, make sure to shut
1745
        try:
1746
            dc = debconf.Debconf(read=dccomm.stdout, write=dccomm.stdin)
1747
            dc.set(question, value)
1748
            dc.fset(question, 'seen', 'true')
1749
        finally:
1750
            dccomm.stdin.close()
1751
            dccomm.wait()
828 by Colin Watson
* The Copy/Config split is more inconvenient than useful, so merge the two
1752
1753
1754
    def reconfigure(self, package):
1755
        """executes a dpkg-reconfigure into installed system to each
1756
        package which provided by args."""
1757
1758
        self.chrex('dpkg-reconfigure', '-fnoninteractive', package)
1759
1760
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
1761
if __name__ == '__main__':
1380 by Colin Watson
* Save tracebacks from install.py and slurp them back into the traceback
1762
    if not os.path.exists('/var/lib/ubiquity'):
1763
        os.makedirs('/var/lib/ubiquity')
1764
    if os.path.exists('/var/lib/ubiquity/install.trace'):
1765
        os.unlink('/var/lib/ubiquity/install.trace')
1766
1767
    install = Install()
1768
    sys.excepthook = install.excepthook
1398 by Colin Watson
* Use exceptions to communicate all failures in install.py, so that we get
1769
    install.run()
1770
    sys.exit(0)
801 by Colin Watson
* Rework copy and configuration progress bars using debconffilter. This
1771
715.1.1 by Tollef Fog Heen
fix severe python style issues
1772
# vim:ai:et:sts=4:tw=80:sw=4: