-
Notifications
You must be signed in to change notification settings - Fork 85
/
rpmsack.py
1987 lines (1633 loc) · 70 KB
/
rpmsack.py
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
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/python -tt
# 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 Library 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 rpm
import types
import warnings
import glob
import os
import os.path
from rpmUtils import miscutils
from rpmUtils import arch
from rpmUtils.transaction import initReadOnlyTransaction
import misc
import Errors
from packages import YumInstalledPackage, parsePackages
from packageSack import PackageSackBase, PackageSackVersion
# For returnPackages(patterns=)
import fnmatch
import re
from yum.i18n import to_unicode, _
import constants
import yum.depsolve
def _open_no_umask(*args):
""" Annoying people like to set umask's for root, which screws everything
up for user readable stuff. """
oumask = os.umask(022)
try:
ret = open(*args)
finally:
os.umask(oumask)
return ret
def _makedirs_no_umask(*args):
""" Annoying people like to set umask's for root, which screws everything
up for user readable stuff. """
oumask = os.umask(022)
try:
ret = os.makedirs(*args)
finally:
os.umask(oumask)
return ret
def _iopen(*args):
""" IOError wrapper BS for open, stupid exceptions. """
try:
ret = open(*args)
except IOError, e:
return None, e
return ret, None
class RPMInstalledPackage(YumInstalledPackage):
def __init__(self, rpmhdr, index, rpmdb):
self._has_hdr = True
YumInstalledPackage.__init__(self, rpmhdr, yumdb=rpmdb.yumdb)
self.idx = index
self.rpmdb = rpmdb
self._has_hdr = False
del self.hdr
def _get_hdr(self):
# Note that we can't use hasattr(self, 'hdr') or we'll recurse
if self._has_hdr:
return self.hdr
ts = self.rpmdb.readOnlyTS()
mi = ts.dbMatch(0, self.idx)
try:
return mi.next()
except StopIteration:
raise Errors.PackageSackError, 'Rpmdb changed underneath us'
def __getattr__(self, varname):
# If these existed, then we wouldn't get here...
# Prevent access of __foo__, _cached_foo etc from loading the header
if varname.startswith('_'):
raise AttributeError, "%s has no attribute %s" % (self, varname)
if varname != 'hdr': # Don't cache the hdr, unless explicitly requested
# Note that we don't even cache the .blah value, but looking up the
# header is _really_ fast so it's not obvious any of it is worth it.
# This is different to prco etc. data, which is loaded separately.
val = self._get_hdr()
else:
self.hdr = val = self._get_hdr()
self._has_hdr = True
if varname != 'hdr': # This is unusual, for anything that happens
val = val[varname] # a lot we should preload at __init__.
# Also note that pkg.no_value raises KeyError.
return val
def requiring_packages(self):
"""return list of installed pkgs requiring this package"""
pkgset = set()
for (reqn, reqf, reqevr) in self.provides:
for pkg in self.rpmdb.getRequires(reqn,reqf,reqevr):
if pkg != self:
pkgset.add(pkg)
for fn in self.filelist + self.dirlist:
for pkg in self.rpmdb.getRequires(fn, None, (None, None, None)):
if pkg != self:
pkgset.add(pkg)
return list(pkgset)
def required_packages(self):
pkgset = set()
for (reqn, reqf, reqevr) in self.strong_requires:
for pkg in self.rpmdb.getProvides(reqn, reqf, reqevr):
if pkg != self:
pkgset.add(pkg)
return list(pkgset)
class RPMDBProblem:
'''
Represents a problem in the rpmdb, from the check_*() functions.
'''
def __init__(self, pkg, problem, **kwargs):
self.pkg = pkg
self.problem = problem
for kwarg in kwargs:
setattr(self, kwarg, kwargs[kwarg])
def __cmp__(self, other):
if other is None:
return 1
return cmp(self.pkg, other.pkg) or cmp(self.problem, other.problem)
class RPMDBProblemDependency(RPMDBProblem):
def __str__(self):
if self.problem == 'requires':
return "%s %s %s" % (self.pkg, _('has missing requires of'),
self.missing)
return "%s %s %s: %s" % (self.pkg, _('has installed conflicts'),
self.found,', '.join(map(str, self.conflicts)))
class RPMDBProblemDuplicate(RPMDBProblem):
def __init__(self, pkg, **kwargs):
RPMDBProblem.__init__(self, pkg, "duplicate", **kwargs)
def __str__(self):
return _("%s is a duplicate with %s") % (self.pkg, self.duplicate)
class RPMDBProblemObsoleted(RPMDBProblem):
def __init__(self, pkg, **kwargs):
RPMDBProblem.__init__(self, pkg, "obsoleted", **kwargs)
def __str__(self):
return _("%s is obsoleted by %s") % (self.pkg, self.obsoleter)
class RPMDBProblemProvides(RPMDBProblem):
def __init__(self, pkg, **kwargs):
RPMDBProblem.__init__(self, pkg, "provides", **kwargs)
def __str__(self):
return _("%s provides %s but it cannot be found") % (self.pkg,
self.provide)
class RPMDBPackageSack(PackageSackBase):
'''
Represent rpmdb as a packagesack
'''
DEP_TABLE = {
'requires' : (rpm.RPMTAG_REQUIRENAME,
rpm.RPMTAG_REQUIREVERSION,
rpm.RPMTAG_REQUIREFLAGS),
'provides' : (rpm.RPMTAG_PROVIDENAME,
rpm.RPMTAG_PROVIDEVERSION,
rpm.RPMTAG_PROVIDEFLAGS),
'conflicts' : (rpm.RPMTAG_CONFLICTNAME,
rpm.RPMTAG_CONFLICTVERSION,
rpm.RPMTAG_CONFLICTFLAGS),
'obsoletes' : (rpm.RPMTAG_OBSOLETENAME,
rpm.RPMTAG_OBSOLETEVERSION,
rpm.RPMTAG_OBSOLETEFLAGS)
}
# Do we want to cache rpmdb data in a file, for later use?
__cache_rpmdb__ = True
def __init__(self, root='/', releasever=None, cachedir=None,
persistdir='/var/lib/yum'):
self.root = root
self._idx2pkg = {}
self._name2pkg = {}
self._pkgnames_loaded = set()
self._tup2pkg = {}
self._completely_loaded = False
self._pkgname_fails = set()
self._pkgmatch_fails = set()
self._provmatch_fails = set()
self._simple_pkgtup_list = []
self._get_pro_cache = {}
self._get_req_cache = {}
self._loaded_gpg_keys = False
if cachedir is None:
cachedir = persistdir + "/rpmdb-indexes"
self.setCacheDir(cachedir)
if not os.path.normpath(persistdir).startswith(self.root):
self._persistdir = root + '/' + persistdir
else:
self._persistdir = persistdir
if hasattr(rpm, 'expandMacro'):
dbpath = rpm.expandMacro('%_dbpath')
else:
dbpath = '/var/lib/rpm'
self._rpmdbpath = os.path.normpath(root + '/' + dbpath)
self._have_cached_rpmdbv_data = None
self._cached_conflicts_data = None
# Store the result of what happens, if a transaction completes.
self._trans_cache_store = {}
self.ts = None
self.releasever = releasever
self.auto_close = False # this forces a self.ts.close() after
# most operations so it doesn't leave
# any lingering locks.
self._cached_rpmdb_mtime = None
self._cache = {
'provides' : { },
'requires' : { },
'conflicts' : { },
'obsoletes' : { },
}
addldb_path = os.path.normpath(self._persistdir + '/yumdb')
version_path = os.path.normpath(cachedir + '/version')
self.yumdb = RPMDBAdditionalData(db_path=addldb_path,
version_path=version_path)
def _get_pkglist(self):
'''Getter for the pkglist property.
Returns a list of package tuples.
'''
if not self._simple_pkgtup_list:
csumpkgtups = self.preloadPackageChecksums(load_packages=False)
if csumpkgtups is not None:
self._simple_pkgtup_list = csumpkgtups.keys()
if not self._simple_pkgtup_list:
for (hdr, mi) in self._get_packages():
self._simple_pkgtup_list.append(self._hdr2pkgTuple(hdr))
return self._simple_pkgtup_list
pkglist = property(_get_pkglist, None)
def dropCachedData(self):
""" Drop all cached data, this is a big perf. hit if we need to load
the data back in again. Also note that if we ever call this while
a transaction is ongoing we'll have multiple copies of packages
which is _bad_. """
self._idx2pkg = {}
self._name2pkg = {}
self._pkgnames_loaded = set()
self._tup2pkg = {}
self._completely_loaded = False
self._pkgmatch_fails = set()
self._pkgname_fails = set()
self._provmatch_fails = set()
self._simple_pkgtup_list = []
self._get_pro_cache = {}
self._get_req_cache = {}
# We can be called on python shutdown (due to yb.__del__), at which
# point other modules might not be available.
if misc is not None:
misc.unshare_data()
self._cache = {
'provides' : { },
'requires' : { },
'conflicts' : { },
'obsoletes' : { },
}
self._have_cached_rpmdbv_data = None
self._cached_conflicts_data = None
self.transactionReset() # Should do nothing, but meh...
self._cached_rpmdb_mtime = None
def dropCachedDataPostTransaction(self, txmbrs):
""" Drop cached data that is assocciated with the given transaction,
this tries to keep as much data as possible and even does a
"preload" on the checksums. This should be called once, when a
transaction is complete. """
# -- Below -- self._idx2pkg = {}
# -- Below -- self._name2pkg = {}
# -- Below -- self._pkgnames_loaded = set()
# -- Below -- self._tup2pkg = {}
self._completely_loaded = False
self._pkgmatch_fails = set()
# -- Below -- self._pkgname_fails = set()
self._provmatch_fails = set()
self._simple_pkgtup_list = []
self._get_pro_cache = {}
self._get_req_cache = {}
# We can be called on python shutdown (due to yb.__del__), at which
# point other modules might not be available.
if misc is not None:
misc.unshare_data()
self._cache = {
'provides' : { },
'requires' : { },
'conflicts' : { },
'obsoletes' : { },
}
self._have_cached_rpmdbv_data = None
self._cached_conflicts_data = None
self.transactionReset() # Should do nothing, but meh...
# We are keeping some data from before, and sometimes (Eg. remove only)
# we never open the rpmdb again ... so get the mtime now.
rpmdbfname = self._rpmdbpath + "/Packages"
self._cached_rpmdb_mtime = os.path.getmtime(rpmdbfname)
def _safe_del(x, y):
""" Make sure we never traceback here, because it screws our yumdb
if we do. """
# Maybe use x.pop(y, None) ?
if y in x:
del x[y]
precache = []
for txmbr in txmbrs:
self._pkgnames_loaded.discard(txmbr.name)
_safe_del(self._name2pkg, txmbr.name)
if txmbr.output_state in constants.TS_INSTALL_STATES:
self._pkgname_fails.discard(txmbr.name)
precache.append(txmbr)
if txmbr.reinstall:
# For reinstall packages we have:
#
# 1. one txmbr: the new install.
# 2. two rpmdb entries: the new; the old;
#
# ...so we need to remove the old one, given only the new
# one.
ipo = self._tup2pkg[txmbr.pkgtup]
_safe_del(self._idx2pkg, ipo.idx)
_safe_del(self._tup2pkg, txmbr.pkgtup)
if txmbr.output_state in constants.TS_REMOVE_STATES:
_safe_del(self._idx2pkg, txmbr.po.idx)
_safe_del(self._tup2pkg, txmbr.pkgtup)
for txmbr in precache:
(n, a, e, v, r) = txmbr.pkgtup
pkg = self.searchNevra(n, e, v, r, a)
if not pkg:
# Wibble?
self._deal_with_bad_rpmdbcache("dCDPT(pkg checksums): %s" %
txmbr)
continue
pkg = pkg[0]
csum = txmbr.po.returnIdSum()
if csum is None:
continue
(T, D) = (str(csum[0]), str(csum[1]))
if ('checksum_type' in pkg.yumdb_info._read_cached_data or
'checksum_data' in pkg.yumdb_info._read_cached_data):
continue
pkg.yumdb_info._read_cached_data['checksum_type'] = T
pkg.yumdb_info._read_cached_data['checksum_data'] = D
def setCacheDir(self, cachedir):
""" Sets the internal cachedir value for the rpmdb, to be the
"rpmdb-indexes" directory in the persisent yum storage. """
if not os.path.normpath(cachedir).startswith(self.root):
self._cachedir = self.root + '/' + cachedir
else:
self._cachedir = '/' + cachedir
if hasattr(self, 'yumdb'): # Need to keep this up to date, after init.
version_path = os.path.normpath(self._cachedir + '/version')
self.yumdb.conf.version_path = version_path
def readOnlyTS(self):
if not self.ts:
self.ts = initReadOnlyTransaction(root=self.root)
if not self.ts.open:
self.ts = initReadOnlyTransaction(root=self.root)
return self.ts
def buildIndexes(self):
# Not used here
return
def _checkIndexes(self, failure='error'):
# Not used here
return
def delPackage(self, obj):
# Not supported with this sack type
pass
def searchAll(self, name, query_type='like'):
result = {}
# check provides
tag = self.DEP_TABLE['provides'][0]
mi = self._get_packages(patterns=[(tag, rpm.RPMMIRE_GLOB, name)])
for hdr, idx in mi:
pkg = self._makePackageObject(hdr, idx)
result.setdefault(pkg.pkgid, pkg)
fileresults = self.searchFiles(name)
for pkg in fileresults:
result.setdefault(pkg.pkgid, pkg)
return result.values()
def searchFiles(self, name):
"""search the filelists in the rpms for anything matching name"""
result = {}
name = os.path.normpath(name)
# Note that globs can't be done. As of 4.8.1:
# mi.pattern('basenames', rpm.RPMMIRE_GLOB, name)
# ...produces no results.
for hdr, idx in self._get_packages('basenames', name):
pkg = self._makePackageObject(hdr, idx)
result.setdefault(pkg.pkgid, pkg)
return result.values()
def searchPrco(self, name, prcotype):
result = self._cache[prcotype].get(name)
if result is not None:
return result
(n,f,(e,v,r)) = misc.string_to_prco_tuple(name)
glob = False
if misc.re_glob(n):
glob = True
result = {}
tag = self.DEP_TABLE[prcotype][0]
for hdr, idx in self._get_packages(tag, misc.to_utf8(n)):
po = self._makePackageObject(hdr, idx)
if not glob:
if po.checkPrco(prcotype, (n, f, (e,v,r))):
result[po.pkgid] = po
else:
result[po.pkgid] = po
# If it's not a provides or filename, we are done
if prcotype == 'provides' and name[0] == '/':
fileresults = self.searchFiles(name)
for pkg in fileresults:
result[pkg.pkgid] = pkg
result = result.values()
self._cache[prcotype][name] = result
return result
def searchProvides(self, name):
if name in self._provmatch_fails:
return []
ret = self.searchPrco(name, 'provides')
if not ret:
self._provmatch_fails.add(name)
return ret
def searchRequires(self, name):
return self.searchPrco(name, 'requires')
def searchObsoletes(self, name):
return self.searchPrco(name, 'obsoletes')
def searchConflicts(self, name):
return self.searchPrco(name, 'conflicts')
def simplePkgList(self):
return self.pkglist
installed = PackageSackBase.contains
def returnNewestByNameArch(self, naTup=None, patterns=None):
#FIXME - should this (or any packagesack) be returning tuples?
if not naTup:
return
(name, arch) = naTup
allpkg = self._search(name=name, arch=arch)
if not allpkg:
raise Errors.PackageSackError, 'No Package Matching %s' % name
return [ po.pkgtup for po in misc.newestInList(allpkg) ]
def returnNewestByName(self, name=None):
if not name:
return
allpkgs = self._search(name=name)
if not allpkgs:
raise Errors.PackageSackError, 'No Package Matching %s' % name
return misc.newestInList(allpkgs)
@staticmethod
def _compile_patterns(patterns, ignore_case=False):
if not patterns or len(patterns) > constants.PATTERNS_MAX:
return None
ret = []
for pat in patterns:
if not pat:
continue
qpat = pat[0]
if qpat in ('?', '*', '['):
qpat = None
elif ignore_case:
qpat = qpat.lower()
ret.append((qpat, misc.compile_pattern(pat, ignore_case)))
return ret
@staticmethod
def _match_repattern(repatterns, hdr, ignore_case):
""" This is basically parsePackages() but for rpm hdr objects. """
if repatterns is None:
return True
for qpat, repat in repatterns:
epoch = hdr['epoch']
if epoch is None:
epoch = '0'
else:
epoch = str(epoch)
qname = hdr['name'][0]
if ignore_case:
qname = qname.lower()
if qpat is not None and qpat != qname and qpat != epoch[0]:
continue
if repat(hdr['name']):
return True
if repat("%(name)s-%(version)s-%(release)s.%(arch)s" % hdr):
return True
if repat("%(name)s.%(arch)s" % hdr):
return True
if repat("%(name)s-%(version)s" % hdr):
return True
if repat("%(name)s-%(version)s-%(release)s" % hdr):
return True
if repat(epoch + ":%(name)s-%(version)s-%(release)s.%(arch)s"
% hdr):
return True
if repat(("%(name)s-" + epoch + ":%(version)s-%(release)s.%(arch)s")
% hdr):
return True
return False
def returnPackages(self, repoid=None, patterns=None, ignore_case=False):
"""Returns a list of packages. Note that the packages are
always filtered to those matching the patterns/case. repoid is
ignored, and is just here for compatibility with non-rpmdb sacks. """
# See if we can load the "patterns" via. dbMatch('name', ...) because
# that's basically instant and walking the entire rpmdb isn't.
# We assume that if we get "Yum" and _something_ matches, that we have
# _all_ the matches. IOW there can be either Yum or yum, but not BOTH.
if not self._completely_loaded and patterns:
ret = []
for pat in patterns:
# We aren't wasting anything here, because the next bit
# will pick up any loads :)
pkgs = self.searchNames([pat])
if not pkgs:
# We need to do a big search for 'pkg*'
if misc.re_glob(pat):
break
# We need to do a big search for 'pkg-1.2'
if '-' in pat:
break
# We need to do a big search for 'pkg.noarch'
if '.' in pat:
break
# We don't need to do a big search for '0:pkg', because
# <en> isn't possible ... and envra matches the above.
# if ':' in pat:
# break
# At this point we have just found something that doesn't
# match, like "yum list zzuf" ... we don't want this to take
# the much slower path.
ret.extend(pkgs)
else:
return ret
ret = []
if patterns and not ignore_case:
tpats = []
for pat in patterns:
if pat in self._pkgmatch_fails:
continue
if pat in self._pkgnames_loaded:
ret.extend(self._name2pkg[pat])
continue
tpats.append(pat)
patterns = tpats
if not patterns:
return ret
if not self._completely_loaded:
rpats = self._compile_patterns(patterns, ignore_case)
for hdr, idx in self._get_packages():
if self._match_repattern(rpats, hdr, ignore_case):
self._makePackageObject(hdr, idx)
self._completely_loaded = rpats is None
pkgobjlist = self._idx2pkg.values()
# Remove gpg-pubkeys, as no sane callers expects/likes them...
if self._loaded_gpg_keys:
pkgobjlist = [pkg for pkg in pkgobjlist if pkg.name != 'gpg-pubkey']
if patterns:
pkgobjlist = parsePackages(pkgobjlist, patterns, not ignore_case)
self._pkgmatch_fails.update(pkgobjlist[2])
if ret:
pkgobjlist = pkgobjlist[0] + pkgobjlist[1] + ret
else:
pkgobjlist = pkgobjlist[0] + pkgobjlist[1]
for pkg in pkgobjlist:
for pat in patterns:
if pkg.name == pat:
self._pkgnames_loaded.add(pkg.name)
return pkgobjlist
def _uncached_returnConflictPackages(self):
""" Load the packages which have conflicts from the rpmdb, newer
versions of rpm have an index here so this is as fast as
cached (we test rpm version at cache write time). """
if self._cached_conflicts_data is None:
result = {}
for hdr, idx in self._get_packages('conflictname'):
if not hdr[rpm.RPMTAG_CONFLICTNAME]:
# Pre. rpm-4.9.x the above dbMatch() does nothing.
continue
po = self._makePackageObject(hdr, idx)
result[po.pkgid] = po
if po._has_hdr:
continue # Unlikely, but, meh...
po.hdr = hdr
po._has_hdr = True
po.conflicts
po._has_hdr = False
del po.hdr
self._cached_conflicts_data = result.values()
return self._cached_conflicts_data
def _write_conflicts_new(self, pkgs, rpmdbv):
if not os.access(self._cachedir, os.W_OK):
return
conflicts_fname = self._cachedir + '/conflicts'
fo = _open_no_umask(conflicts_fname + '.tmp', 'w')
fo.write("%s\n" % rpmdbv)
fo.write("%u\n" % len(pkgs))
for pkg in sorted(pkgs):
for var in pkg.pkgtup:
fo.write("%s\n" % var)
fo.close()
os.rename(conflicts_fname + '.tmp', conflicts_fname)
def _write_conflicts(self, pkgs):
rpmdbv = self.simpleVersion(main_only=True)[0]
self._write_conflicts_new(pkgs, rpmdbv)
def _uncached_returnObsoletePackages(self):
""" Load the packages which have obsoletes from the rpmdb, this is
needed because newer rpm's have obsoletes imply conflicts. """
if self._cached_obsoletes_data is None:
result = {}
for hdr, idx in self._get_packages('obsoletename'):
if not hdr[rpm.RPMTAG_OBSOLETENAME]:
# Pre. rpm-4.9.x the above dbMatch() does nothing.
continue
po = self._makePackageObject(hdr, idx)
result[po.pkgid] = po
if po._has_hdr:
continue # Unlikely, but, meh...
po.hdr = hdr
po._has_hdr = True
po.obsoletes
po._has_hdr = False
del po.hdr
self._cached_obsoletes_data = result.values()
return self._cached_obsoletes_data
def _write_obsoletes_new(self, pkgs, rpmdbv):
if not os.access(self._cachedir, os.W_OK):
return
obsoletes_fname = self._cachedir + '/obsoletes'
fo = _open_no_umask(obsoletes_fname + '.tmp', 'w')
fo.write("%s\n" % rpmdbv)
fo.write("%u\n" % len(pkgs))
for pkg in sorted(pkgs):
for var in pkg.pkgtup:
fo.write("%s\n" % var)
fo.close()
os.rename(obsoletes_fname + '.tmp', obsoletes_fname)
def _write_obsoletes(self, pkgs):
rpmdbv = self.simpleVersion(main_only=True)[0]
self._write_obsoletes_new(pkgs, rpmdbv)
def _deal_with_bad_rpmdbcache(self, caller):
""" This shouldn't be called, but people are hitting weird stuff so
we want to deal with it so it doesn't stay broken "forever". """
misc.unlink_f(self._cachedir + "/version")
misc.unlink_f(self._cachedir + '/conflicts')
misc.unlink_f(self._cachedir + '/obsoletes')
misc.unlink_f(self._cachedir + '/file-requires')
misc.unlink_f(self._cachedir + '/pkgtups-checksums')
# We have a couple of options here, we can:
#
# . Ignore it and continue - least invasive, least likely to get any
# bugs fixed.
#
# . Ignore it and continue, when not in debug mode - Helps users doing
# weird things (and we won't know), but normal bugs will be seen by
# anyone not running directly from a package.
#
# . Always throw - but at least it shouldn't happen again.
#
if __debug__:
raise Errors.PackageSackError, 'Rpmdb checksum is invalid: %s' % caller
def _read_pkglist(self, fname):
if not self.__cache_rpmdb__:
return None
def _read_str(fo):
return fo.readline()[:-1]
full_fname = self._cachedir + '/' + fname
fo, e = _iopen(full_fname)
if fo is None:
return None
frpmdbv = fo.readline()
rpmdbv = self.simpleVersion(main_only=True)[0]
if not frpmdbv or rpmdbv != frpmdbv[:-1]:
return None
ret = []
try:
# Read the pkgs...
pkgtups_num = int(_read_str(fo))
while pkgtups_num > 0:
pkgtups_num -= 1
# n, a, e, v, r
pkgtup = (_read_str(fo), _read_str(fo),
_read_str(fo), _read_str(fo), _read_str(fo))
int(pkgtup[2]) # Check epoch is valid
ret.extend(self.searchPkgTuple(pkgtup))
if fo.readline() != '': # Should be EOF
return None
except ValueError:
self._deal_with_bad_rpmdbcache(fname)
return None
return ret
def _read_conflicts(self):
self._cached_conflicts_data = self._read_pkglist("conflicts")
return self._cached_conflicts_data
def transactionCacheConflictPackages(self, pkgs):
if self.__cache_rpmdb__:
self._trans_cache_store['conflicts'] = pkgs
def returnConflictPackages(self):
""" Return a list of packages that have conflicts. """
pkgs = self._read_conflicts()
if pkgs is None:
pkgs = self._uncached_returnConflictPackages()
if self.__cache_rpmdb__:
self._write_conflicts(pkgs)
return pkgs
def transactionCacheObsoletePackages(self, pkgs):
if self.__cache_rpmdb__:
self._trans_cache_store['obsoletes'] = pkgs
def _read_obsoletes(self):
self._cached_obsoletes_data = self._read_pkglist("obsoletes")
return self._cached_obsoletes_data
def returnObsoletePackages(self):
""" Return a list of packages that have obsoletes. """
pkgs = self._read_obsoletes()
if pkgs is None:
pkgs = self._uncached_returnObsoletePackages()
if self.__cache_rpmdb__:
self._write_obsoletes(pkgs)
return pkgs
def transactionResultVersion(self, rpmdbv):
""" We are going to do a transaction, and the parameter will be the
rpmdb version when we finish. The idea being we can update all
our rpmdb caches for that rpmdb version. """
if not self.__cache_rpmdb__:
self._trans_cache_store = {}
return
if 'conflicts' in self._trans_cache_store:
pkgs = self._trans_cache_store['conflicts']
self._write_conflicts_new(pkgs, rpmdbv)
if 'obsoletes' in self._trans_cache_store:
pkgs = self._trans_cache_store['obsoletes']
self._write_obsoletes_new(pkgs, rpmdbv)
if 'file-requires' in self._trans_cache_store:
data = self._trans_cache_store['file-requires']
self._write_file_requires(rpmdbv, data)
if 'pkgtups-checksums' in self._trans_cache_store:
data = self._trans_cache_store['pkgtups-checksums']
self._write_package_checksums(rpmdbv, data)
self._trans_cache_store = {}
def transactionReset(self):
""" We are going to reset the transaction, because the data we've added
already might now be invalid (Eg. skip-broken, or splitting a
transaction). """
self._trans_cache_store = {}
def returnGPGPubkeyPackages(self):
""" Return packages of the gpg-pubkeys ... hacky. """
ts = self.readOnlyTS()
mi = ts.dbMatch('name', 'gpg-pubkey')
ret = []
for hdr in mi:
self._loaded_gpg_keys = True
ret.append(self._makePackageObject(hdr, mi.instance()))
return ret
def _read_file_requires(self):
def _read_str(fo):
return fo.readline()[:-1]
assert self.__cache_rpmdb__
fo, e = _iopen(self._cachedir + '/file-requires')
if fo is None:
return None, None
rpmdbv = self.simpleVersion(main_only=True)[0]
frpmdbv = fo.readline()
if not frpmdbv or rpmdbv != frpmdbv[:-1]:
return None, None
iFR = {}
iFP = {}
try:
# Read the requires...
pkgtups_num = int(_read_str(fo))
while pkgtups_num > 0:
pkgtups_num -= 1
# n, a, e, v, r
pkgtup = (_read_str(fo), _read_str(fo),
_read_str(fo), _read_str(fo), _read_str(fo))
int(pkgtup[2]) # Check epoch is valid
files_num = int(_read_str(fo))
while files_num > 0:
files_num -= 1
fname = _read_str(fo)
iFR.setdefault(pkgtup, []).append(fname)
# Read the provides...
files_num = int(_read_str(fo))
while files_num > 0:
files_num -= 1
fname = _read_str(fo)
pkgtups_num = int(_read_str(fo))
while pkgtups_num > 0:
pkgtups_num -= 1
# n, a, e, v, r
pkgtup = (_read_str(fo), _read_str(fo),
_read_str(fo), _read_str(fo), _read_str(fo))
int(pkgtup[2]) # Check epoch is valid
iFP.setdefault(fname, []).append(pkgtup)
if fo.readline() != '': # Should be EOF
return None, None
except ValueError:
self._deal_with_bad_rpmdbcache("file requires")
return None, None
return iFR, iFP
def fileRequiresData(self):
""" Get a cached copy of the fileRequiresData for
depsolving/checkFileRequires, note the giant comment in that
function about how we don't keep this perfect for the providers of
the requires. """
if self.__cache_rpmdb__:
iFR, iFP = self._read_file_requires()
if iFR is not None:
return iFR, set(), iFP
installedFileRequires = {}
installedUnresolvedFileRequires = set()
resolved = set()
for pkg in self.returnPackages():
for name, flag, evr in pkg.requires:
if not name.startswith('/'):
continue
installedFileRequires.setdefault(pkg.pkgtup, []).append(name)
if name not in resolved:
dep = self.getProvides(name, flag, evr)
resolved.add(name)
if not dep:
installedUnresolvedFileRequires.add(name)
fileRequires = set()
for fnames in installedFileRequires.itervalues():
fileRequires.update(fnames)
installedFileProviders = {}
for fname in fileRequires:
pkgtups = [pkg.pkgtup for pkg in self.getProvides(fname)]
installedFileProviders[fname] = pkgtups
ret = (installedFileRequires, installedUnresolvedFileRequires,
installedFileProviders)
if self.__cache_rpmdb__:
rpmdbv = self.simpleVersion(main_only=True)[0]
self._write_file_requires(rpmdbv, ret)
return ret
def transactionCacheFileRequires(self, installedFileRequires,
installedUnresolvedFileRequires,
installedFileProvides,
problems):
if not self.__cache_rpmdb__: