~brian-murray/ubuntu/natty/apport/rr-if-idontknow

« back to all changes in this revision

Viewing changes to backends/packaging-apt-dpkg.py

  • Committer: Martin Pitt
  • Date: 2011-02-04 14:41:46 UTC
  • mfrom: (1369.34.52 trunk)
  • Revision ID: martin.pitt@canonical.com-20110204144146-o0fq0kh4shzsutr3
new upstream release 1.17.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
warnings.filterwarnings('ignore', 'apt API not stable yet', FutureWarning)
19
19
import apt
20
20
 
 
21
import apport
21
22
from apport.packaging import PackageInfo
22
23
 
23
24
class __AptDpkgPackageInfo(PackageInfo):
28
29
        self._apt_cache = None
29
30
        self._contents_dir = None
30
31
        self._mirror = None
31
 
        # Checking whether we should use the API from python-apt 0.7.9
32
 
        self.apt_pre_079 = not hasattr(apt.Package, 'installed')
33
32
 
34
33
        self.configuration = '/etc/default/apport'
35
34
 
63
62
        try:
64
63
            return self._cache()[package]
65
64
        except KeyError:
66
 
            raise ValueError, 'package does not exist'
 
65
            raise ValueError('package does not exist')
67
66
 
68
67
    def get_version(self, package):
69
68
        '''Return the installed version of a package.'''
70
69
 
71
70
        pkg = self._apt_pkg(package)
72
 
        if self.apt_pre_079:
73
 
            if not pkg.isInstalled:
74
 
                raise ValueError, 'package does not exist'
75
 
            return pkg.installedVersion
76
 
        else:
77
 
            inst = pkg.installed
78
 
            if not inst:
79
 
                raise ValueError, 'package does not exist'
80
 
            return inst.version
 
71
        inst = pkg.installed
 
72
        if not inst:
 
73
            raise ValueError('package does not exist')
 
74
        return inst.version
81
75
 
82
76
    def get_available_version(self, package):
83
77
        '''Return the latest available version of a package.'''
84
78
 
85
 
        if self.apt_pre_079:
86
 
            return self._apt_pkg(package).candidateVersion
87
 
        else:
88
 
            return self._apt_pkg(package).candidate.version
 
79
        return self._apt_pkg(package).candidate.version
89
80
 
90
81
    def get_dependencies(self, package):
91
82
        '''Return a list of packages a package depends on.'''
100
91
    def get_source(self, package):
101
92
        '''Return the source package name for a package.'''
102
93
 
103
 
        if self.apt_pre_079:
104
 
            return self._apt_pkg(package).sourcePackageName
 
94
        if self._apt_pkg(package).installed:
 
95
            return self._apt_pkg(package).installed.source_name
 
96
        elif self._apt_pkg(package).candidate:
 
97
            return self._apt_pkg(package).candidate.source_name
105
98
        else:
106
 
            if self._apt_pkg(package).installed:
107
 
                return self._apt_pkg(package).installed.source_name
108
 
            elif self._apt_pkg(package).candidate:
109
 
                return self._apt_pkg(package).candidate.source_name
110
 
            else:
111
 
                raise ValueError, 'package %s does not exist' % package
 
99
            raise ValueError('package %s does not exist' % package)
112
100
 
113
101
    def is_distro_package(self, package):
114
102
        '''Check if a package is a genuine distro package (True) or comes from
121
109
 
122
110
        pkg = self._apt_pkg(package)
123
111
        # some PPA packages have installed version None, see LP#252734
124
 
        if self.apt_pre_079:
125
 
            if pkg.isInstalled and pkg.installedVersion is None:
126
 
                return False
127
 
        else:
128
 
            if pkg.installed and pkg.installed.version is None:
129
 
                return False
 
112
        if pkg.installed and pkg.installed.version is None:
 
113
            return False
130
114
 
131
115
        native_origins = [this_os]
132
116
        try:
139
123
            pass
140
124
 
141
125
        origins = None
142
 
        if self.apt_pre_079:
143
 
            origins = pkg.candidateOrigin
144
 
        else:
145
 
            origins = pkg.candidate.origins
 
126
        origins = pkg.candidate.origins
146
127
        if origins: # might be None
147
128
            for o in origins:
148
129
                if o.origin in native_origins:
155
136
        This might differ on multiarch architectures (e. g.  an i386 Firefox
156
137
        package on a x86_64 system)'''
157
138
 
158
 
        if self.apt_pre_079:
159
 
            return self._apt_pkg(package).architecture or 'unknown'
 
139
        if self._apt_pkg(package).installed:
 
140
            return self._apt_pkg(package).installed.architecture or 'unknown'
 
141
        elif self._apt_pkg(package).candidate:
 
142
            return self._apt_pkg(package).candidate.architecture or 'unknown'
160
143
        else:
161
 
            if self._apt_pkg(package).installed:
162
 
                return self._apt_pkg(package).installed.architecture or 'unknown'
163
 
            elif self._apt_pkg(package).candidate:
164
 
                return self._apt_pkg(package).candidate.architecture or 'unknown'
165
 
            else:
166
 
                raise ValueError, 'package %s does not exist' % package
 
144
            raise ValueError('package %s does not exist' % package)
167
145
 
168
146
    def get_files(self, package):
169
147
        '''Return list of files shipped by a package.'''
197
175
            try:
198
176
                # ignore lines with NUL bytes (happens, LP#96050)
199
177
                if '\0' in line:
200
 
                    print >> sys.stderr, 'WARNING:', sumfile, 'contains NUL character, ignoring line'
 
178
                    apport.warning('%s contains NUL character, ignoring line', sumfile)
201
179
                    continue
202
180
                words  = line.split()
203
181
                if not words:
204
 
                    print >> sys.stderr, 'WARNING:', sumfile, 'contains empty line, ignoring line'
 
182
                    apport.warning('%s contains empty line, ignoring line', sumfile)
205
183
                    continue
206
184
                s = os.stat('/' + words[-1])
207
185
                if max(s.st_mtime, s.st_ctime) <= max_time:
364
342
        debug_pkgname = 'linux-image-debug-%s' % kver
365
343
        c = self._cache()
366
344
        if c.has_key(debug_pkgname) and c[debug_pkgname].isInstalled:
367
 
            #print 'kernel ddeb already installed'
 
345
            #print('kernel ddeb already installed')
368
346
            return (installed, outdated)
369
347
        target_dir = apt_pkg.Config.FindDir('Dir::Cache::archives')+'/partial'
370
348
        deb = '%s_%s_%s.ddeb' % (debug_pkgname, ver, arch)
414
392
            else:
415
393
                c.update()
416
394
            c.open(apt.progress.OpProgress())
417
 
        except SystemError, e:
 
395
        except SystemError as e:
418
396
            if 'Hash Sum mismatch' in str(e):
419
397
                # temporary archive inconsistency
420
 
                print >> sys.stderr, str(e), 'aborting'
 
398
                apport.error('%s, aborting' % str(e))
421
399
                sys.exit(99) # signal crash digger about transient error
422
400
            else:
423
401
                raise
424
402
        except apt.cache.LockFailedException:
425
403
            if os.geteuid() != 0:
426
 
                print >> sys.stderr, 'WARNING: Could not update apt, you need to be root'
 
404
                apport.error('Could not update apt, you need to be root')
427
405
            else:
428
406
                raise
429
407
 
439
417
            try:
440
418
                (pkg, version) = l.split()[:2]
441
419
            except ValueError:
442
 
                print >> sys.stderr, 'WARNING: invalid Package/Dependencies line: ', l
 
420
                apport.warning('invalid Package/Dependencies line: %s', l)
443
421
                # invalid line, ignore
444
422
                continue
445
423
            dependency_versions[pkg] = version
448
426
                    dependency_versions[pkg+'-dbg'] = dependency_versions[pkg]
449
427
                    dependency_versions[pkg+'-dbgsym'] = dependency_versions[pkg]
450
428
            except ValueError:
451
 
                print >> sys.stderr, 'WARNING: package %s not known to package cache' % pkg
 
429
                apport.warning('package %s not known to package cache', pkg)
452
430
 
453
431
        for pkg, ver in dependency_versions.iteritems():
454
432
            if not c.has_key(pkg):
455
 
                print >> sys.stderr, 'WARNING: package %s not available' % pkg
 
433
                apport.warning('package %s not available', pkg)
456
434
                continue
457
435
 
458
436
            # ignore packages which are already installed in the right version
459
 
            if self.apt_pre_079:
460
 
                if (ver and c[pkg].isInstalled and c[pkg].installedVersion ==\
461
 
                    ver) or (not ver and c[pkg].isInstalled):
462
 
                    continue
463
 
            else:
464
 
                if (ver and c[pkg].installed and c[pkg].installed.version ==\
465
 
                    ver) or (not ver and c[pkg].installed):
466
 
                    continue
 
437
            if (ver and c[pkg].installed and c[pkg].installed.version ==\
 
438
                ver) or (not ver and c[pkg].installed):
 
439
                continue
467
440
 
468
441
            candidate_version = None
469
 
            if self.apt_pre_079:
470
 
                candidate_version = c[pkg].candidateVersion
471
 
            else:
472
 
                candidate_version = c[pkg].candidate.version
 
442
            candidate_version = c[pkg].candidate.version
473
443
            if ver and candidate_version != ver:
474
444
                if not pkg.endswith('-dbgsym'):
475
445
                    outdated += '%s: installed version %s, latest version: %s\n' % (
476
446
                        pkg, ver, candidate_version)
477
 
                print >> sys.stderr, 'WARNING: %s version %s required, but %s is available' % (
478
 
                    pkg, ver, candidate_version)
 
447
                apport.warning('%s version %s required, but %s is available',
 
448
                        pkg, ver, candidate_version)
479
449
                if not unpack_only:
480
450
                    uninstallable.append (c[pkg].name)
481
451
                    continue
502
472
                    try:
503
473
                        c.commit(fetchProgress, installProgress)
504
474
                    except SystemError:
505
 
                        print >> sys.stderr, 'Error: Could not install all archives. If you use this tool on a production system, it is recommended to use the -u option. See --help for details.'
506
 
                        sys.exit(1)
 
475
                        apport.fatal('Could not install all archives. If you use this tool on a production system, it is recommended to use the -u option. See --help for details.')
507
476
 
508
477
                # after commit(), the Cache object does not empty the pending
509
478
                # changes, so we need to reinitialize it to avoid applying the same
510
479
                # changes again below
511
480
                installed = [p.name for p in c.getChanges()]
512
481
                c = apt.Cache()
513
 
        except IOError, e:
 
482
        except IOError as e:
514
483
            pass # we will complain to the user later
515
484
 
516
485
        # package hooks might reassign Package:, check that we have the originally crashing binary
518
487
            if path in report and not os.path.exists(report[path]):
519
488
                pkg = self.get_file_package(report[path], True)
520
489
                if pkg:
521
 
                    print 'Installing extra package', pkg, 'to get', path
 
490
                    print('Installing extra package %s to get %s' % (pkg, path))
522
491
                    c[pkg].markInstall(False)
523
492
                else:
524
493
                    err = 'current version of package %s does not contain the program %s any more' % (report['Package'], path)
525
494
                    outdated += err + '\n'
526
 
                    print >> sys.stderr, 'WARNING:', err
 
495
                    apport.warning(err)
527
496
 
528
497
        # check list of libraries that the crashed process referenced at
529
498
        # runtime and warn about those which are not available
546
515
            if pkg:
547
516
                if not os.path.exists(l):
548
517
                    if pkg in uninstallable:
549
 
                        print >> sys.stderr, 'WARNING: %s cannot be installed (incompatible version)' % pkg
 
518
                        apport.warning('%s cannot be installed (incompatible version)', pkg)
550
519
                        continue
551
520
                    if c.has_key(pkg):
552
521
                        c[pkg].markInstall(False)
553
522
                    else:
554
 
                        print >> sys.stderr, 'WARNING: %s was loaded at runtime, but its package %s is not available' % (l, pkg)
 
523
                        apport.warning('%s was loaded at runtime, but its package %s is not available', l, pkg)
555
524
 
556
525
                if c.has_key(pkg+'-dbgsym') and pkg+'-dbgsym' not in uninstallable :
557
526
                    c[pkg+'-dbgsym'].markInstall(False)
558
527
                else:
559
 
                    print >> sys.stderr, 'WARNING: %s-dbgsym is not available or is incompatible' % pkg
 
528
                    apport.warning('%s-dbgsym is not available or is incompatible', pkg)
560
529
            else:
561
 
                    print >> sys.stderr, 'WARNING: %s is needed, but cannot be mapped to a package' % l
 
530
                    apport.warning('%s is needed, but cannot be mapped to a package', l)
562
531
 
563
532
        try:
564
533
            if c.getChanges():
568
537
                else:
569
538
                    c.commit(fetchProgress, installProgress)
570
539
            installed += [p.name for p in c.getChanges()]
571
 
        except (SystemError, IOError), e:
572
 
            print >> sys.stderr, 'WARNING: could not install missing packages:', e
 
540
        except (SystemError, IOError) as e:
 
541
            apport.warning('could not install missing packages: %s', str(e))
573
542
            if os.geteuid() != 0:
574
 
                print >> sys.stderr, 'You either need to call this program as root or install these packages manually:'
 
543
                apport.error('You either need to call this program as root or install these packages manually:')
575
544
            for p in c.getChanges():
576
 
                if self.apt_pre_079:
577
 
                    print >> sys.stderr, '  %s %s' % (p.name,
578
 
                                                      p.candidateVersion)
579
 
                else:
580
 
                    print >> sys.stderr, '  %s %s' % (p.name,
581
 
                                                      p.candidate.version)
 
545
                apport.error('  %s %s', p.name, p.candidate.version)
582
546
 
583
547
        return (installed, outdated)
584
548
 
613
577
        if dpkg.returncode == 0:
614
578
            return out
615
579
        else:
616
 
            raise ValueError, 'package does not exist'
 
580
            raise ValueError('package does not exist')
617
581
 
618
582
    def _check_files_md5(self, sumfile):
619
583
        '''Internal function for calling md5sum.
656
620
                    self._mirror = fields[1]
657
621
                    break
658
622
            else:
659
 
                raise SystemError, 'cannot determine default mirror: /etc/apt/sources.list does not contain a valid deb line'
 
623
                raise SystemError('cannot determine default mirror: /etc/apt/sources.list does not contain a valid deb line')
660
624
 
661
625
        return self._mirror
662
626
 
795
759
        pm = apt.apt_pkg.GetPackageManager(cache._depcache)
796
760
        try:
797
761
            res = cache._fetchArchives(fetcher, pm)
798
 
        except IOError, e:
799
 
            print >> sys.stderr, 'ERROR: could not fetch all archives:', e
 
762
        except IOError as e:
 
763
            apport.error('could not fetch all archives: %s', str(e))
800
764
 
801
765
        # extract
802
766
        if verbosity:
806
770
        if no_dpkg:
807
771
            for i in fetcher.Items:
808
772
                if verbosity:
809
 
                    print 'Extracting', i.DestFile
 
773
                    print('Extracting ' + i.DestFile)
810
774
                if subprocess.call(['dpkg', '-x', i.DestFile, '/'], stdout=so,
811
775
                    stderr=subprocess.STDOUT) != 0:
812
 
                    print >> sys.stderr, 'WARNING: %s failed to extract' % i.DestFile
 
776
                    apport.warning('%s failed to extract', i.DestFile)
813
777
        else:
814
778
            res = subprocess.call(['dpkg', '--force-depends', '--force-overwrite', '--unpack'] + 
815
779
                [klass.deb_without_preinst(i.DestFile) for i in fetcher.Items], stdout=so)
816
780
            if res != 0:
817
 
                raise IOError, 'dpkg failed to unpack archives'
 
781
                raise IOError('dpkg failed to unpack archives')
818
782
 
819
783
        # remove other maintainer scripts
820
784
        for c in cache.getChanges():
875
839
        def test_get_version(self):
876
840
            '''get_version().'''
877
841
 
878
 
            self.assert_(impl.get_version('libc6').startswith('2'))
 
842
            self.assertTrue(impl.get_version('libc6').startswith('2'))
879
843
            self.assertRaises(ValueError, impl.get_version, 'nonexisting')
880
844
            self.assertRaises(ValueError, impl.get_version, 'wukrainian')
881
845
 
882
846
        def test_get_available_version(self):
883
847
            '''get_available_version().'''
884
848
 
885
 
            self.assert_(impl.get_available_version('libc6').startswith('2'))
 
849
            self.assertTrue(impl.get_available_version('libc6').startswith('2'))
886
850
            self.assertRaises(ValueError, impl.get_available_version, 'nonexisting')
887
851
 
888
852
        def test_get_dependencies(self):
890
854
 
891
855
            # package with both Depends: and Pre-Depends:
892
856
            d  = impl.get_dependencies('bash')
893
 
            self.assert_(len(d) > 2)
894
 
            self.assert_('libc6' in d)
 
857
            self.assertTrue(len(d) > 2)
 
858
            self.assertTrue('libc6' in d)
895
859
            for dep in d:
896
 
                self.assert_(impl.get_version(dep))
 
860
                self.assertTrue(impl.get_version(dep))
897
861
 
898
862
            # Pre-Depends: only
899
863
            d  = impl.get_dependencies('coreutils')
900
 
            self.assert_(len(d) >= 1)
901
 
            self.assert_('libc6' in d)
 
864
            self.assertTrue(len(d) >= 1)
 
865
            self.assertTrue('libc6' in d)
902
866
            for dep in d:
903
 
                self.assert_(impl.get_version(dep))
 
867
                self.assertTrue(impl.get_version(dep))
904
868
 
905
869
            # Depends: only
906
870
            d  = impl.get_dependencies('libc6')
907
 
            self.assert_(len(d) >= 1)
 
871
            self.assertTrue(len(d) >= 1)
908
872
            for dep in d:
909
 
                self.assert_(impl.get_version(dep))
 
873
                self.assertTrue(impl.get_version(dep))
910
874
 
911
875
        def test_get_source(self):
912
876
            '''get_source().'''
913
877
 
914
878
            self.assertRaises(ValueError, impl.get_source, 'nonexisting')
915
879
            self.assertEqual(impl.get_source('bash'), 'bash')
916
 
            self.assert_('glibc' in impl.get_source('libc6'))
 
880
            self.assertTrue('glibc' in impl.get_source('libc6'))
917
881
 
918
882
        def test_is_distro_package(self):
919
883
            '''is_distro_package().'''
920
884
 
921
885
            self.assertRaises(ValueError, impl.is_distro_package, 'nonexisting')
922
 
            self.assert_(impl.is_distro_package('bash'))
 
886
            self.assertTrue(impl.is_distro_package('bash'))
923
887
            # no False test here, hard to come up with a generic one
924
888
 
925
889
        def test_get_architecture(self):
937
901
            '''get_files().'''
938
902
 
939
903
            self.assertRaises(ValueError, impl.get_files, 'nonexisting')
940
 
            self.assert_('/bin/bash' in impl.get_files('bash'))
 
904
            self.assertTrue('/bin/bash' in impl.get_files('bash'))
941
905
 
942
906
        def test_get_file_package(self):
943
907
            '''get_file_package() on installed files.'''
960
924
            try:
961
925
                mapdir = os.path.join(basedir, 'dists', release_name)
962
926
                os.makedirs(mapdir)
963
 
                print >> gzip.open(os.path.join(mapdir, 'Contents-%s.gz' %
964
 
                    impl.get_system_architecture()), 'w'), '''
 
927
                f = gzip.open(os.path.join(mapdir, 'Contents-%s.gz' %
 
928
                    impl.get_system_architecture()), 'w')
 
929
                f.write('''
965
930
 foo header
966
931
FILE                                                    LOCATION
967
932
usr/bin/frobnicate                                      foo/frob
968
933
usr/bin/frob                                            foo/frob-utils
969
934
bo/gu/s                                                 na/mypackage
970
 
'''
 
935
''')
 
936
                f.close()
971
937
 
972
938
                self.assertEqual(impl.get_file_package('usr/bin/frob', False, mapdir), None)
973
939
                # must not match frob (same file name prefix)
988
954
                os.mkdir(cache_dir)
989
955
                self.assertEqual(impl.get_file_package('usr/bin/frob', True, cache_dir), 'frob-utils')
990
956
                self.assertEqual(len(os.listdir(cache_dir)), 1)
991
 
                self.assert_(os.listdir(cache_dir)[0].startswith('Contents-'))
 
957
                self.assertTrue(os.listdir(cache_dir)[0].startswith('Contents-'))
992
958
                self.assertEqual(impl.get_file_package('/bo/gu/s', True, cache_dir), 'mypackage')
993
959
            finally:
994
960
                shutil.rmtree(basedir)
1014
980
            arch = impl.get_system_architecture()
1015
981
            # must be nonempty without line breaks
1016
982
            self.assertNotEqual(arch, '')
1017
 
            self.assert_('\n' not in arch)
 
983
            self.assertTrue('\n' not in arch)
1018
984
 
1019
985
        def test_compare_versions(self):
1020
986
            '''compare_versions.'''
1056
1022
        def test_get_kernel_pacakge(self):
1057
1023
            '''get_kernel_package().'''
1058
1024
 
1059
 
            self.assert_('linux' in impl.get_kernel_package())
 
1025
            self.assertTrue('linux' in impl.get_kernel_package())
1060
1026
 
1061
1027
        def test_package_name_glob(self):
1062
1028
            '''package_name_glob().'''
1063
1029
 
1064
 
            self.assert_(len(impl.package_name_glob('a*')) > 5)
1065
 
            self.assert_('bash' in impl.package_name_glob('ba*h'))
 
1030
            self.assertTrue(len(impl.package_name_glob('a*')) > 5)
 
1031
            self.assertTrue('bash' in impl.package_name_glob('ba*h'))
1066
1032
            self.assertEqual(impl.package_name_glob('bash'), ['bash'])
1067
1033
            self.assertEqual(impl.package_name_glob('xzywef*'), [])
1068
1034