~mvo/update-manager/not-automatic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# DistUpgradeQuirks.py 
#  
#  Copyright (c) 2004-2008 Canonical
#  
#  Author: Michael Vogt <michael.vogt@ubuntu.com>
# 
#  This program is free software; you can redistribute it and/or 
#  modify it under the terms of the GNU General Public License as 
#  published by the Free Software Foundation; either version 2 of the
#  License, or (at your option) any later version.
# 
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
# 
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#  USA

import logging
import re
import os
import os.path
import shutil
import sys
import subprocess
from subprocess import PIPE, Popen

from DistUpgradeGettext import gettext as _
from DistUpgradeGettext import ngettext
import gettext

class DistUpgradeQuirks(object):
    """
    This class collects the various quirks handlers that can
    be hooked into to fix/work around issues that the individual
    releases have
    """
    
    def __init__(self, controller, config):
        self.controller = controller
        self._view = controller._view
        self.config = config
        self.uname = Popen(["uname","-r"],stdout=PIPE).communicate()[0].strip()

    # the quirk function have the name:
    #  $Name (e.g. PostUpgrade)
    #  $todist$Name (e.g. intrepidPostUpgrade)
    #  $from_$fromdist$Name (e.g. from_dapperPostUpgrade)
    def run(self, quirksName):
        """
        Run the specific quirks handler, the follow handlers are supported:
        - PostInitialUpdate: run *before* the sources.list is rewritten but
                             after a initial apt-get update
        - PostDistUpgradeCache: run *after* the dist-upgrade was calculated
                                in the cache
        - PostUpgrade: run *after* the upgrade is finished successfully and 
                       packages got installed
        - PostCleanup: run *after* the cleanup (orphaned etc) is finished
        """
        # run the handler that is common to all dists
        funcname = "%s" % quirksName
        func = getattr(self, funcname, None)
        if func is not None:
            logging.debug("quirks: running %s" % funcname)
            func()

        # run the quirksHandler to-dist
        funcname = "%s%s" % (self.config.get("Sources","To"), quirksName)
        func = getattr(self, funcname, None)
        if func is not None:
            logging.debug("quirks: running %s" % funcname)
            func()

        # now run the quirksHandler from_${FROM-DIST}Quirks
        funcname = "from_%s%s" % (self.config.get("Sources","From"), quirksName)
        func = getattr(self, funcname, None)
        if func is not None:
            logging.debug("quirks: running %s" % funcname)
            func()

    def _supportInModaliases(self, xorgdrivername, modaliasesdir="./modaliases", lspci=None):
        """ 
        Check if xorgdriver will work on this hardware

        This helper will check with the modaliasesdir if the given 
        xorgdriver will work on this hardware (or the hardware given
        via the lspci argument)
        """
        if not lspci:
            lspci = set()
            p = subprocess.Popen(["lspci","-n"],stdout=subprocess.PIPE)
            for line in p.communicate()[0].split("\n"):
                if line:
                    lspci.add(line.split()[2])
        for filename in os.listdir(modaliasesdir):
            for line in open(os.path.join(modaliasesdir,filename)):
                line = line.strip()
                if line == "" or line.startswith("#"):
                    continue
                (key, pciid, xorgdriver, pkgname) = line.split()
                if xorgdriver != xorgdrivername:
                    continue
                m = re.match("pci:v0000(.+)d0000(.+)sv.*", pciid)
                if m:
                    matchid = "%s:%s" % (m.group(1), m.group(2))
                    if matchid.lower() in lspci:
                        logging.debug("found system pciid '%s' in modaliases" % matchid)
                        return True
        logging.debug("checking for %s support in modaliases but none found" % xorgdriver)
        return False
                    

    # individual quirks handler when the dpkg run is finished ---------
    def PostCleanup(self):
        " run after cleanup " 
        logging.debug("running Quirks.PostCleanup")
        self.controller.cache.releaseLock()
        res = subprocess.call(["dpkg","--forget-old-unavail"])
        logging.debug("dpkg --forget-old-unavail returned %s" % res)
        # FIXME: we should call --clear-avail as well? 
        self.controller.cache.getLock()

    def from_dapperPostUpgrade(self):
        " this works around quirks for dapper->hardy upgrades "
        logging.debug("running Controller.from_dapperQuirks handler")
        self._rewriteFstab()
        self._checkAdminGroup()
        
    def intrepidPostUpgrade(self):
        " this applies rules for the hardy->intrepid upgrade "
	logging.debug("running Controller.intrepidQuirks handler")
        self._addRelatimeToFstab()

    def gutsyPostUpgrade(self):
        """ this function works around quirks in the feisty->gutsy upgrade """
        logging.debug("running Controller.gutsyQuirks handler")

    def feistyPostUpgrade(self):
        """ this function works around quirks in the edgy->feisty upgrade """
        logging.debug("running Controller.feistyQuirks handler")
        self._rewriteFstab()
        self._checkAdminGroup()

    # quirks when run when the initial apt-get update was run -------

    # fglrx is broken in intrepid (no support for xserver 1.5)
    def jauntyPostInitialUpdate(self):
        " quirks that are run before the upgrade to jaunty "
        logging.debug("running %s" %  sys._getframe().f_code.co_name
)
        # this is to deal with the fact that support for some of the cards
        # that fglrx used to support got dropped
        if (self._checkVideoDriver("fglrx") and 
            not self._supportInModaliases("fglrx")):
             res = self._view.askYesNoQuestion(_("Upgrading may reduce desktop "
                                         "effects, and performance in games "
                                         "and other graphically intensive "
                                         "programs."),
                                       _("This computer is currently using "
                                         "the AMD 'fglrx' graphics driver. "
                                         "No version of this driver is "
                                         "available that works with your "
                                         "hardware in Ubuntu "
                                         "9.04.\n\nDo you want to continue?"))
             if res == False:
                 self.controller.abort()
             # if the user wants to continue we remove the fglrx driver
             # here because its no use (no support for this card)
             self.controller.cache.markRemove("xorg-driver-fglrx",
                                              "no support in new fglrx for the card")
             self.controller.cache.markRemove("xorg-driver-fglrx-envy",
                                              "no support in new fglrx for the card")


    # quirks when the cache upgrade calculation is finished
    def from_dapperPostDistUpgradeCache(self):
        self.hardyPostDistUpgradeCache()
        self.gutsyPostDistUpgradeCache()
        self.feistyPostDistUpgradeCache()
        self.edgyPostDistUpgradeCache()

    def jauntyPostDistUpgradeCache(self):
        """ 
        this function works around quirks in the 
        intrepid->jaunty upgrade calculation
        """
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
        if ("lilo" in self.controller.cache and
            self.controller.cache["lilo"].isInstalled and
            "grub" in self.controller.cache and
            self.controller.cache["grub"].isInstalled):
            logging.debug("both grub and lilo installed")
            if not os.path.exists("/etc/lilo.conf"):
                self.controller.cache.markRemove("lilo",
                                                 "both grub and lilo installed "
                                                 "(#314004)")
            else:
                logging.warning("lilo and grub installed but lilo.conf exists")
       
        
    def intrepidPostDistUpgradeCache(self):
        """ 
        this function works around quirks in the 
        hardy->intrepid upgrade 
        """
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
        # language-support-* changed its dependencies from "recommends"
        # to "suggests" for language-pack-* - this means that apt will
        # think they are now auto-removalable if they got installed
        # as a dep of language-support-* - we fix this here
        for pkg in self.controller.cache:
            if (pkg.name.startswith("language-pack-") and 
                not pkg.name.endswith("-base") and
                self.controller.cache._depcache.IsAutoInstalled(pkg._pkg) and
                pkg.isInstalled):
                logging.debug("setting '%s' to manual installed" % pkg.name)
                pkg.markKeep()
                pkg.markInstall()
        # for kde we need to switch from 
        # kubuntu-desktop-kde4 
        # to
        # kubuntu-desktop
        frompkg = "kubuntu-kde4-desktop"
        topkg = "kubuntu-desktop"
        if (self.controller.cache.has_key(frompkg) and
            self.controller.cache[frompkg].isInstalled):
            logging.debug("transitioning %s to %s" % (frompkg, topkg))
            self.controller.cache[topkg].markInstall()
        # next check if a key depends of kubuntu-kde4-desktop is installed
        # and transition in this case as well
        deps_found = True
        for pkg in self.config.getlist(frompkg,"KeyDependencies"):
            deps_found &= (self.controller.cache.has_key(pkg) and
                           self.controller.cache[pkg].isInstalled)
        if deps_found:
            logging.debug("transitioning %s to %s (via key depends)" % (frompkg, topkg))
            self.controller.cache[topkg].markInstall()
        # landscape-client (in desktop mode) goes away (was a stub
        # anyway)
        name = "landscape-client"
        ver = "0.1"
        if not self.controller.serverMode:
            if (self.controller.cache.has_key(name) and
                self.controller.cache[name].installedVersion == ver):
                self.controller.cache.markRemove(name, 
                                "custom landscape stub removal rule")
                if self.controller.cache.has_key("landscape-common"):
                    self.controller.cache["landscape-common"].markKeep()
                    self.controller.cache.markInstall("landscape-common", 
                                "custom landscape-common stub install rule (to ensure its nor marked for auto-remove)")
        # now check for nvidia and show a warning if needed
        cache = self.controller.cache
        for pkgname in ["nvidia-glx-71","nvidia-glx-96"]:
            if (cache.has_key(pkgname) and 
                cache[pkgname].markedInstall and
                self._checkVideoDriver("nvidia")):
                logging.debug("found %s video driver" % pkgname)
                res = self._view.askYesNoQuestion(_("Upgrading may reduce desktop "
                                        "effects, and performance in games "
                                        "and other graphically intensive "
                                        "programs."),
                                      _("This computer is currently using "
                                        "the NVIDIA 'nvidia' "
                                        "graphics driver. "
                                        "No version of this driver is "
                                        "available that works with your "
                                        "video card in Ubuntu "
                                        "8.10.\n\nDo you want to continue?"))
                if res == False:
                    self.controller.abort()
                # if the user continue, do not install the broken driver
                # so that we can transiton him to the free "nv" one after
                # the upgrade
                self.controller.cache[pkgname].markKeep()
        # check if we have sse
        for pkgname in ["nvidia-glx-173","nvidia-glx-177"]:
            if (cache.has_key(pkgname) and 
                cache[pkgname].markedInstall and
                self._checkVideoDriver("nvidia")):
                logging.debug("found %s video driver" % pkgname)
                if not self._cpuHasSSESupport():
                    logging.warning("nvidia driver that needs SSE but cpu has no SSE support")
                    res = self._view.askYesNoQuestion(_("Upgrading may reduce desktop "
                                        "effects, and performance in games "
                                        "and other graphically intensive "
                                        "programs."),
                                      _("This computer is currently using "
                                        "the NVIDIA 'nvidia' "
                                        "graphics driver. "
                                        "No version of this driver is "
                                        "available that works with your "
                                        "video card in Ubuntu "
                                        "8.10.\n\nDo you want to continue?"))
                    if res == False:
                        self.controller.abort()
                    # if the user continue, do not install the broken driver
                    # so that we can transiton him to the free "nv" one after
                    # the upgrade
                    self.controller.cache[pkgname].markKeep()
        # kdelibs4-dev is unhappy (#279621)
        fromp = "kdelibs4-dev"
        to = "kdelibs5-dev"
        if (self.controller.cache.has_key(fromp) and 
            self.controller.cache[fromp].isInstalled and
            self.controller.cache.has_key(to)):
            self.controller.cache.markInstall(to, "kdelibs4-dev -> kdelibs5-dev transition")

    def hardyPostDistUpgradeCache(self):
        """ 
        this function works around quirks in the 
        {dapper,gutsy}->hardy upgrade 
        """
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
        # deal with gnome-translator and help apt with the breaks
        if (self.controller.cache.has_key("nautilus") and
            self.controller.cache["nautilus"].isInstalled and
            not self.controller.cache["nautilus"].markedUpgrade):
            # uninstallable and gutsy apt is unhappy about this
            # breaks because it wants to upgrade it and gives up
            # if it can't
            for broken in ("link-monitor-applet"):
                if self.controller.cache.has_key(broken) and self.controller.cache[broken].isInstalled:
                    self.controller.cache[broken].markDelete()
            self.controller.cache["nautilus"].markInstall()
        # evms gives problems, remove it if it is not in use
        self._checkAndRemoveEvms()
        # give the language-support-* packages a extra kick
        # (if we have network, otherwise this will not work)
        if self.config.get("Options","withNetwork") == "True":
            for pkg in self.controller.cache:
                if (pkg.name.startswith("language-support-") and
                    pkg.isInstalled and
                    not pkg.markedUpgrade):
                    self.controller.cache.markInstall(pkg.name,"extra language-support- kick")

    def gutsyPostDistUpgradeCache(self):
        """ this function works around quirks in the feisty->gutsy upgrade """
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
        # lowlatency kernel flavour vanished from feisty->gutsy
        try:
            (version, build, flavour) = self.uname.split("-")
            if (flavour == 'lowlatency' or 
                flavour == '686' or
                flavour == 'k7'):
                kernel = "linux-image-generic"
                if not (self.controller.cache[kernel].isInstalled or self.controller.cache[kernel].markedInstall):
                    logging.debug("Selecting new kernel '%s'" % kernel)
                    self.controller.cache[kernel].markInstall()
        except Exception, e:
            logging.warning("problem while transitioning lowlatency kernel (%s)" % e)
        # fix feisty->gutsy utils-linux -> nfs-common transition (LP: #141559)
        try:
            for line in map(string.strip, open("/proc/mounts")):
                if line == '' or line.startswith("#"):
                    continue
                try:
                    (device, mount_point, fstype, options, a, b) = line.split()
                except Exception, e:
                    logging.error("can't parse line '%s'" % line)
                    continue
                if "nfs" in fstype:
                    logging.debug("found nfs mount in line '%s', marking nfs-common for install " % line)
                    self.controller.cache["nfs-common"].markInstall()
                    break
        except Exception, e:
            logging.warning("problem while transitioning util-linux -> nfs-common (%s)" % e)

    def feistyPostDistUpgradeCache(self):
        """ this function works around quirks in the edgy->feisty upgrade """
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
        # ndiswrapper changed again *sigh*
        for (fr, to) in [("ndiswrapper-utils-1.8","ndiswrapper-utils-1.9")]:
            if self.controller.cache.has_key(fr) and self.controller.cache.has_key(to):
                if self.controller.cache[fr].isInstalled and not self.controller.cache[to].markedInstall:
                    try:
                        self.controller.cache.markInstall(to,"%s->%s quirk upgrade rule" % (fr, to))
                    except SystemError, e:
                        logging.warning("Failed to apply %s->%s install (%s)" % (fr, to, e))
            

    def edgyPostDistUpgradeCache(self):
        """ this function works around quirks in the dapper->edgy upgrade """
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
        for pkg in self.controller.cache:
            # deal with the python2.4-$foo -> python-$foo transition
            if (pkg.name.startswith("python2.4-") and
                pkg.isInstalled and
                not pkg.markedUpgrade):
                basepkg = "python-"+pkg.name[len("python2.4-"):]
                if (self.controller.cache.has_key(basepkg) and 
                    self.controller.cache[basepkg].candidateDownloadable and
                    not self.controller.cache[basepkg].markedInstall):
                    try:
                        self.controller.cache.markInstall(basepkg,
                                         "python2.4->python upgrade rule")
                    except SystemError, e:
                        logging.debug("Failed to apply python2.4->python install: %s (%s)" % (basepkg, e))
            # xserver-xorg-input-$foo gives us trouble during the upgrade too
            if (pkg.name.startswith("xserver-xorg-input-") and
                pkg.isInstalled and
                not pkg.markedUpgrade):
                try:
                    self.controller.cache.markInstall(pkg.name, "xserver-xorg-input fixup rule")
                except SystemError, e:
                    logging.debug("Failed to apply fixup: %s (%s)" % (pkg.name, e))
            
        # deal with held-backs that are unneeded
        for pkgname in ["hpijs", "bzr", "tomboy"]:
            if (self.controller.cache.has_key(pkgname) and self.controller.cache[pkgname].isInstalled and
                self.controller.cache[pkgname].isUpgradable and not self.controller.cache[pkgname].markedUpgrade):
                try:
                    self.controller.cache.markInstall(pkgname,"%s quirk upgrade rule" % pkgname)
                except SystemError, e:
                    logging.debug("Failed to apply %s install (%s)" % (pkgname,e))
        # libgl1-mesa-dri from xgl.compiz.info (and friends) breaks the
	# upgrade, work around this here by downgrading the package
        if self.controller.cache.has_key("libgl1-mesa-dri"):
            pkg = self.controller.cache["libgl1-mesa-dri"]
            # the version from the compiz repo has a "6.5.1+cvs20060824" ver
            if (pkg.candidateVersion == pkg.installedVersion and
                "+cvs2006" in pkg.candidateVersion):
                for ver in pkg._pkg.VersionList:
                    # the "official" edgy version has "6.5.1~20060817-0ubuntu3"
                    if "~2006" in ver.VerStr:
			# ensure that it is from a trusted repo
			for (VerFileIter, index) in ver.FileList:
				indexfile = self.controller.cache._list.FindIndex(VerFileIter)
				if indexfile and indexfile.IsTrusted:
					logging.info("Forcing downgrade of libgl1-mesa-dri for xgl.compz.info installs")
		                        self.controller.cache._depcache.SetCandidateVer(pkg._pkg, ver)
					break
                                    
        # deal with general if $foo is installed, install $bar
        for (fr, to) in [("xserver-xorg-driver-all","xserver-xorg-video-all")]:
            if self.controller.cache.has_key(fr) and self.controller.cache.has_key(to):
                if self.controller.cache[fr].isInstalled and not self.controller.cache[to].markedInstall:
                    try:
                        self.controller.cache.markInstall(to,"%s->%s quirk upgrade rule" % (fr, to))
                    except SystemError, e:
                        logging.debug("Failed to apply %s->%s install (%s)" % (fr, to, e))
                    
    def dapperPostDistUpgradeCache(self):
        """ this function works around quirks in the breezy->dapper upgrade """
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
        if (self.controller.cache.has_key("nvidia-glx") and self.controller.cache["nvidia-glx"].isInstalled and
            self.controller.cache.has_key("nvidia-settings") and self.controller.cache["nvidia-settings"].isInstalled):
            logging.debug("nvidia-settings and nvidia-glx is installed")
            self.controller.cache.markRemove("nvidia-settings")
            self.controller.cache.markInstall("nvidia-glx")

    def from_hardyPostDistUpgradeCache(self):
        """ this function works around quirks in upgrades from hardy """
        logging.debug("running %s" %  sys._getframe().f_code.co_name)
        # evms got removed after hardy, warn and abort
        if self._usesEvmsInMounts():
            logging.error("evms in use in /etc/fstab")
            self._view.error(_("evms in use"),
                             _("Your system uses the 'evms' volume manager "
                               "in /proc/mounts. "
                               "The 'evms' software is no longer supported, "
                               "please switch it off and run the upgrade "
                               "again when this is done."))
            self.controller.abort()
            

    # helpers
    def _cpuHasSSESupport(self, cpuinfo="/proc/cpuinfo"):
        " helper that checks if the given cpu has sse support "
        if not os.path.exists(cpuinfo):
            return False
        for line in open(cpuinfo):
            if line.startswith("flags") and not " sse" in line:
                return False
        return True

    def _usesEvmsInMounts(self):
        " check if evms is used in /proc/mounts "
        logging.debug("running _usesEvmsInMounts")
        for line in open("/proc/mounts"):
            line = line.strip()
            if line == '' or line.startswith("#"):
                continue
            try:
                (device, mount_point, fstype, options, a, b) = line.split()
            except Exception, e:
                logging.error("can't parse line '%s'" % line)
                continue
            if "evms" in device:
                logging.debug("found evms device in line '%s', skipping " % line)
                return True
        return False

    def _checkAndRemoveEvms(self):
        " check if evms is in use and if not, remove it "
        logging.debug("running _checkAndRemoveEvms")
        if self._usesEvmsInMounts():
            return False
        # if not in use, nuke it
        for pkg in ["evms","libevms-2.5","libevms-dev",
                    "evms-ncurses", "evms-ha",
                    "evms-bootdebug",
                    "evms-gui", "evms-cli",
                    "linux-patch-evms"]:
            if self.controller.cache.has_key(pkg) and self.controller.cache[pkg].isInstalled:
                self.controller.cache[pkg].markDelete()
        return True

    def _addRelatimeToFstab(self):
        " add the relatime option to ext2/ext3 filesystems on upgrade "
        logging.debug("_addRelatime")
        replaced = False
        lines = []
        for line in open("/etc/fstab"):
            line = line.strip()
            if line == '' or line.startswith("#"):
                lines.append(line)
                continue
            try:
                (device, mount_point, fstype, options, a, b) = line.split()
            except Exception, e:
                logging.error("can't parse line '%s'" % line)
                lines.append(line)
                continue
            if (("ext2" in fstype or
                 "ext3" in fstype) and 
                (not "noatime" in options) and
                (not "relatime" in options) ):
                logging.debug("adding 'relatime' to line '%s' " % line)
                line = line.replace(options,"%s,relatime" % options)
                logging.debug("replaced line is '%s' " % line)
                replaced=True
            lines.append(line)
        # we have converted a line
        if replaced:
            logging.debug("writing new /etc/fstab")
            f=open("/etc/fstab.intrepid","w")
            f.write("\n".join(lines))
            # add final newline (see LP: #279093)
            f.write("\n")
            f.close()
            os.rename("/etc/fstab.intrepid","/etc/fstab")
        return True
        

    def _rewriteFstab(self):
        " convert /dev/{hd?,scd0} to /dev/cdrom for the feisty upgrade "
        logging.debug("_rewriteFstab()")
        replaced = 0
        lines = []
        # we have one cdrom to convert
        for line in open("/etc/fstab"):
            line = line.strip()
            if line == '' or line.startswith("#"):
                lines.append(line)
                continue
            try:
                (device, mount_point, fstype, options, a, b) = line.split()
            except Exception, e:
                logging.error("can't parse line '%s'" % line)
                lines.append(line)
                continue
            # edgy kernel has /dev/cdrom -> /dev/hd?
            # feisty kernel (for a lot of chipsets) /dev/cdrom -> /dev/scd0
            # this breaks static mounting (LP#86424)
            #
            # we convert here to /dev/cdrom only if current /dev/cdrom
            # points to the device in /etc/fstab already. this ensures
            # that we don't break anything or that we get it wrong
            # for systems with two (or more) cdroms. this is ok, because
            # we convert under the old kernel
            if ("iso9660" in fstype and
                device != "/dev/cdrom" and
                os.path.exists("/dev/cdrom") and
                os.path.realpath("/dev/cdrom") == device
                ):
                logging.debug("replacing '%s' " % line)
                line = line.replace(device,"/dev/cdrom")
                logging.debug("replaced line is '%s' " % line)
                replaced += 1
            lines.append(line)
        # we have converted a line (otherwise we would have exited already)
        if replaced > 0:
            logging.debug("writing new /etc/fstab")
            shutil.copy("/etc/fstab","/etc/fstab.edgy")
            f=open("/etc/fstab","w")
            f.write("\n".join(lines))
            # add final newline (see LP: #279093)
            f.write("\n")
            f.close()
        return True

    def _checkAdminGroup(self):
        " check if the current sudo user is in the admin group "
        logging.debug("_checkAdminGroup")
        import grp
        try:
            admin_group = grp.getgrnam("admin").gr_mem
        except KeyError, e:
            logging.warning("System has no admin group (%s)" % e)
            subprocess.call(["addgroup","--system","admin"])
        # double paranoia
        try:
            admin_group = grp.getgrnam("admin").gr_mem
        except KeyError, e:
            logging.warning("adding the admin group failed (%s)" % e)
            return
        # if the current SUDO_USER is not in the admin group
        # we add him - this is no security issue because
        # the user is already root so adding him to the admin group
        # does not change anything
        if (os.environ.has_key("SUDO_USER") and
            not os.environ["SUDO_USER"] in admin_group):
            admin_user = os.environ["SUDO_USER"]
            logging.info("SUDO_USER=%s is not in admin group" % admin_user)
            cmd = ["usermod","-a","-G","admin",admin_user]
            res = subprocess.call(cmd)
            logging.debug("cmd: %s returned %i" % (cmd, res))

    def _checkVideoDriver(self, name):
        " check if the given driver is in use in xorg.conf "
        XORG="/etc/X11/xorg.conf"
        if not os.path.exists(XORG):
            return False
        for line in open(XORG):
            s=line.split("#")[0].strip()
            # check for fglrx driver entry
            if (s.lower().startswith("driver") and
                s.endswith('"%s"' % name)):
                return True
        return False