Package yum :: Module packages
[hide private]
[frames] | no frames]

Source Code for Module yum.packages

   1  #!/usr/bin/python -tt 
   2  # This program is free software; you can redistribute it and/or modify 
   3  # it under the terms of the GNU General Public License as published by 
   4  # the Free Software Foundation; either version 2 of the License, or 
   5  # (at your option) any later version. 
   6  # 
   7  # This program is distributed in the hope that it will be useful, 
   8  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
   9  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  10  # GNU Library General Public License for more details. 
  11  # 
  12  # You should have received a copy of the GNU General Public License 
  13  # along with this program; if not, write to the Free Software 
  14  # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
  15  # Copyright 2004 Duke University  
  16  # Written by Seth Vidal <skvidal at phy.duke.edu> 
  17   
  18  """ 
  19  Classes and functions dealing with rpm package representations. 
  20  """ 
  21   
  22  import rpm 
  23  import os 
  24  import os.path 
  25  import misc 
  26  import i18n 
  27  import re 
  28  import fnmatch 
  29  import stat 
  30  import warnings 
  31  from subprocess import Popen, PIPE 
  32  from rpmUtils import RpmUtilsError 
  33  import rpmUtils.miscutils 
  34  from rpmUtils.miscutils import flagToString, stringToVersion 
  35  import Errors 
  36  import errno 
  37  import struct 
  38  from constants import * 
  39   
  40  import urlparse 
  41  urlparse.uses_fragment.append("media") 
  42  from urlgrabber.grabber import URLGrabber, URLGrabError 
  43   
  44  # For verify 
  45  import pwd 
  46  import grp 
  47   
48 -def comparePoEVR(po1, po2):
49 """ 50 Compare two Package or PackageEVR objects. 51 """ 52 (e1, v1, r1) = (po1.epoch, po1.version, po1.release) 53 (e2, v2, r2) = (po2.epoch, po2.version, po2.release) 54 return rpmUtils.miscutils.compareEVR((e1, v1, r1), (e2, v2, r2))
55 -def comparePoEVREQ(po1, po2):
56 """ 57 Compare two Package or PackageEVR objects for equality. 58 """ 59 (e1, v1, r1) = (po1.epoch, po1.version, po1.release) 60 (e2, v2, r2) = (po2.epoch, po2.version, po2.release) 61 if r1 != r2: return False 62 if v1 != v2: return False 63 if e1 != e2: return False 64 return True
65
66 -def buildPkgRefDict(pkgs, casematch=True):
67 """take a list of pkg objects and return a dict the contains all the possible 68 naming conventions for them eg: for (name,i386,0,1,1) 69 dict[name] = (name, i386, 0, 1, 1) 70 dict[name.i386] = (name, i386, 0, 1, 1) 71 dict[name-1-1.i386] = (name, i386, 0, 1, 1) 72 dict[name-1] = (name, i386, 0, 1, 1) 73 dict[name-1-1] = (name, i386, 0, 1, 1) 74 dict[0:name-1-1.i386] = (name, i386, 0, 1, 1) 75 dict[name-0:1-1.i386] = (name, i386, 0, 1, 1) 76 """ 77 pkgdict = {} 78 for pkg in pkgs: 79 (n, a, e, v, r) = pkg.pkgtup 80 if not casematch: 81 n = n.lower() 82 a = a.lower() 83 e = e.lower() 84 v = v.lower() 85 r = r.lower() 86 name = n 87 nameArch = '%s.%s' % (n, a) 88 nameVerRelArch = '%s-%s-%s.%s' % (n, v, r, a) 89 nameVer = '%s-%s' % (n, v) 90 nameVerRel = '%s-%s-%s' % (n, v, r) 91 envra = '%s:%s-%s-%s.%s' % (e, n, v, r, a) 92 nevra = '%s-%s:%s-%s.%s' % (n, e, v, r, a) 93 for item in [name, nameArch, nameVerRelArch, nameVer, nameVerRel, envra, nevra]: 94 if not pkgdict.has_key(item): 95 pkgdict[item] = [] 96 pkgdict[item].append(pkg) 97 98 return pkgdict
99
100 -def parsePackages(pkgs, usercommands, casematch=0, 101 unique='repo-epoch-name-version-release-arch'):
102 """matches up the user request versus a pkg list: 103 for installs/updates available pkgs should be the 'others list' 104 for removes it should be the installed list of pkgs 105 takes an optional casematch option to determine if case should be matched 106 exactly. Defaults to not matching.""" 107 108 pkgdict = buildPkgRefDict(pkgs, bool(casematch)) 109 exactmatch = [] 110 matched = [] 111 unmatched = [] 112 for command in usercommands: 113 if not casematch: 114 command = command.lower() 115 if command in pkgdict: 116 exactmatch.extend(pkgdict[command]) 117 del pkgdict[command] 118 else: 119 # anything we couldn't find a match for 120 # could mean it's not there, could mean it's a wildcard 121 if misc.re_glob(command): 122 trylist = pkgdict.keys() 123 # command and pkgdict are already lowered if not casematch 124 # so case sensitive is always fine 125 restring = fnmatch.translate(command) 126 regex = re.compile(restring) 127 foundit = 0 128 for item in trylist: 129 if regex.match(item): 130 matched.extend(pkgdict[item]) 131 del pkgdict[item] 132 foundit = 1 133 134 if not foundit: 135 unmatched.append(command) 136 137 else: 138 unmatched.append(command) 139 140 unmatched = misc.unique(unmatched) 141 if unique == 'repo-epoch-name-version-release-arch': # pkg.__hash__ 142 matched = misc.unique(matched) 143 exactmatch = misc.unique(exactmatch) 144 elif unique == 'repo-pkgkey': # So we get all pkg entries from a repo 145 def pkgunique(pkgs): 146 u = {} 147 for pkg in pkgs: 148 mark = "%s%s" % (pkg.repo.id, pkg.pkgKey) 149 u[mark] = pkg 150 return u.values()
151 matched = pkgunique(matched) 152 exactmatch = pkgunique(exactmatch) 153 else: 154 raise ValueError, "Bad value for unique: %s" % unique 155 return exactmatch, matched, unmatched 156
157 -class FakeSack:
158 """ Fake PackageSack to use with FakeRepository"""
159 - def __init__(self):
160 pass # This is fake, so do nothing
161
162 - def delPackage(self, obj):
163 """delete a pkgobject, do nothing, but make localpackages work with --skip-broken""" 164 pass # This is fake, so do nothing
165
166 -class FakeRepository:
167 """Fake repository class for use in rpmsack package objects""" 168
169 - def _set_cleanup_repoid(self, repoid):
170 """ Set the repoid, but because it can be random ... clean it up. """ 171 172 # We don't want repoids to contain random bytes that can be 173 # in the FS directories. It's also nice if they aren't "huge". So 174 # just chop to the rpm name. 175 pathbased = False 176 if '/' in repoid: 177 repoid = os.path.basename(repoid) 178 pathbased = True 179 180 if repoid.endswith(".rpm"): 181 repoid = repoid[:-4] 182 pathbased = True 183 184 bytes = [] # Just in case someone uses mv to be evil: 185 if pathbased: 186 bytes.append('/') 187 188 for byte in repoid: 189 if ord(byte) >= 128: 190 byte = '?' 191 bytes.append(byte) 192 self.id = "".join(bytes)
193
194 - def __init__(self, repoid):
195 self._set_cleanup_repoid(repoid) 196 self.sack = FakeSack()
197
198 - def __cmp__(self, other):
199 if self.id > other.id: 200 return 1 201 elif self.id < other.id: 202 return -1 203 else: 204 return 0
205
206 - def __hash__(self):
207 return hash(self.id)
208
209 - def __str__(self):
210 return self.id
211 212 213 # goal for the below is to have a packageobject that can be used by generic 214 # functions independent of the type of package - ie: installed or available
215 -class PackageObject(object):
216 """Base Package Object - sets up the default storage dicts and the 217 most common returns""" 218
219 - def __init__(self):
220 self.name = None 221 self.version = None 222 self.release = None 223 self.epoch = None 224 self.arch = None 225 # self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release) 226 self._checksums = [] # (type, checksum, id(0,1)
227
228 - def _ui_envra(self):
229 if self.epoch == '0': 230 out = '%s-%s-%s.%s' % (self.name, 231 self.version, 232 self.release, 233 self.arch) 234 else: 235 out = '%s:%s-%s-%s.%s' % (self.epoch, 236 self.name, 237 self.version, 238 self.release, 239 self.arch) 240 return out
241 ui_envra = property(fget=lambda self: self._ui_envra()) 242
243 - def _ui_nevra(self):
244 if self.epoch == '0': 245 out = '%s-%s-%s.%s' % (self.name, 246 self.version, 247 self.release, 248 self.arch) 249 else: 250 out = '%s-%s:%s-%s.%s' % (self.name, 251 self.epoch, 252 self.version, 253 self.release, 254 self.arch) 255 return out
256 ui_nevra = property(fget=lambda self: self._ui_nevra()) 257
258 - def __str__(self):
259 return self.ui_envra
260
261 - def verCMP(self, other):
262 """ Compare package to another one, only rpm-version ordering. """ 263 if not other: 264 return 1 265 ret = cmp(self.name, other.name) 266 if ret == 0: 267 ret = comparePoEVR(self, other) 268 return ret
269
270 - def __cmp__(self, other):
271 """ Compare packages, this is just for UI/consistency. """ 272 ret = self.verCMP(other) 273 if ret == 0: 274 ret = cmp(self.arch, other.arch) 275 if ret == 0 and hasattr(self, 'repoid') and hasattr(other, 'repoid'): 276 ret = cmp(self.repoid, other.repoid) 277 return ret
278 - def __eq__(self, other):
279 """ Compare packages for yes/no equality, includes everything in the 280 UI package comparison. """ 281 if not other: 282 return False 283 if self.pkgtup != other.pkgtup: 284 return False 285 if self.repoid != other.repoid: 286 return False 287 return True
288 - def __ne__(self, other):
289 if not (self == other): 290 return True 291 return False
292
293 - def __getitem__(self, key):
294 return getattr(self, key)
295
296 - def verEQ(self, other):
297 """ Compare package to another one, only rpm-version equality. """ 298 if not other: 299 return False 300 ret = cmp(self.name, other.name) 301 if ret != 0: 302 return False 303 return comparePoEVREQ(self, other)
304 - def verLT(self, other):
305 """ Uses verCMP, tests if the other _rpm-version_ is < ours. """ 306 return self.verCMP(other) < 0
307 - def verLE(self, other):
308 """ Uses verCMP, tests if the other _rpm-version_ is <= ours. """ 309 return self.verCMP(other) <= 0
310 - def verGT(self, other):
311 """ Uses verCMP, tests if the other _rpm-version_ is > ours. """ 312 return self.verCMP(other) > 0
313 - def verGE(self, other):
314 """ Uses verCMP, tests if the other _rpm-version_ is >= ours. """ 315 return self.verCMP(other) >= 0
316
317 - def __repr__(self):
318 return "<%s : %s (%s)>" % (self.__class__.__name__, str(self),hex(id(self)))
319
320 - def returnSimple(self, varname):
321 warnings.warn("returnSimple() will go away in a future version of Yum.\n", 322 Errors.YumFutureDeprecationWarning, stacklevel=2) 323 return getattr(self, varname)
324
325 - def returnChecksums(self):
326 return self._checksums
327 328 checksums = property(fget=lambda self: self.returnChecksums()) 329
330 - def returnIdSum(self):
331 for (csumtype, csum, csumid) in self.checksums: 332 if csumid: 333 return (csumtype, csum)
334
335 -class RpmBase(object):
336 """return functions and storage for rpm-specific data""" 337
338 - def __init__(self):
339 self.prco = {} 340 self.prco['obsoletes'] = [] # (name, flag, (e,v,r)) 341 self.prco['conflicts'] = [] # (name, flag, (e,v,r)) 342 self.prco['requires'] = [] # (name, flag, (e,v,r)) 343 self.prco['provides'] = [] # (name, flag, (e,v,r)) 344 self.files = {} 345 self.files['file'] = [] 346 self.files['dir'] = [] 347 self.files['ghost'] = [] 348 self._changelog = [] # (ctime, cname, ctext) 349 self.licenses = [] 350 self._hash = None
351 352 # FIXME: This is identical to PackageObject.__eq__ and __ne__, should be 353 # remove (is .repoid fine here? ... we need it, maybe .repo.id).
354 - def __eq__(self, other):
355 if not other: # check if other not is a package object. 356 return False 357 if self.pkgtup != other.pkgtup: 358 return False 359 if self.repoid != other.repoid: 360 return False 361 return True
362 - def __ne__(self, other):
363 if not (self == other): 364 return True 365 return False
366
367 - def returnEVR(self):
368 return PackageEVR(self.epoch, self.version, self.release)
369
370 - def __hash__(self):
371 if self._hash is None: 372 mystr = '%s - %s:%s-%s-%s.%s' % (self.repo.id, self.epoch, self.name, 373 self.version, self.release, self.arch) 374 self._hash = hash(mystr) 375 return self._hash
376
377 - def returnPrco(self, prcotype, printable=False):
378 """return list of provides, requires, conflicts or obsoletes""" 379 380 prcos = [] 381 if self.prco.has_key(prcotype): 382 prcos = self.prco[prcotype] 383 384 if printable: 385 results = [] 386 for prco in prcos: 387 results.append(misc.prco_tuple_to_string(prco)) 388 return results 389 390 return prcos
391
392 - def checkPrco(self, prcotype, prcotuple):
393 """returns 1 or 0 if the pkg contains the requested tuple/tuple range""" 394 # get rid of simple cases - nothing 395 if prcotype not in self.prco: 396 return 0 397 398 # First try and exact match, then search 399 # Make it faster, if it's "big". 400 if len(self.prco[prcotype]) <= 8: 401 if prcotuple in self.prco[prcotype]: 402 return 1 403 else: 404 if not hasattr(self, '_prco_lookup'): 405 self._prco_lookup = {'obsoletes' : None, 'conflicts' : None, 406 'requires' : None, 'provides' : None} 407 408 if self._prco_lookup[prcotype] is None: 409 self._prco_lookup[prcotype] = set(self.prco[prcotype]) 410 411 if prcotuple in self._prco_lookup[prcotype]: 412 return 1 413 414 if True: # Keep indentation for patch smallness... 415 # make us look it up and compare 416 (reqn, reqf, (reqe, reqv ,reqr)) = prcotuple 417 if reqf is not None: 418 return self.inPrcoRange(prcotype, prcotuple) 419 else: 420 for (n, f, (e, v, r)) in self.returnPrco(prcotype): 421 if i18n.str_eq(reqn, n): 422 return 1 423 424 return 0
425
426 - def inPrcoRange(self, prcotype, reqtuple):
427 """returns true if the package has a the prco that satisfies 428 the reqtuple range, assume false. 429 Takes: prcotype, requested prco tuple""" 430 return bool(self.matchingPrcos(prcotype, reqtuple))
431
432 - def matchingPrcos(self, prcotype, reqtuple):
433 (reqn, reqf, (reqe, reqv, reqr)) = reqtuple 434 # find the named entry in pkgobj, do the comparsion 435 result = [] 436 for (n, f, (e, v, r)) in self.returnPrco(prcotype): 437 if not i18n.str_eq(reqn, n): 438 continue 439 440 if f == '=': 441 f = 'EQ' 442 if f != 'EQ' and prcotype == 'provides': 443 # isn't this odd, it's not 'EQ' and it is a provides 444 # - it really should be EQ 445 # use the pkgobj's evr for the comparison 446 if e is None: 447 e = self.epoch 448 if v is None: 449 v = self.ver 450 if r is None: 451 r = self.rel 452 #(e, v, r) = (self.epoch, self.ver, self.rel) 453 454 matched = rpmUtils.miscutils.rangeCompare( 455 reqtuple, (n, f, (e, v, r))) 456 if matched: 457 result.append((n, f, (e, v, r))) 458 459 return result
460 461 462
463 - def returnChangelog(self):
464 """return changelog entries""" 465 return self._changelog
466
467 - def returnFileEntries(self, ftype='file', primary_only=False):
468 """return list of files based on type, you can pass primary_only=True 469 to limit to those files in the primary repodata""" 470 if self.files: 471 if ftype in self.files: 472 if primary_only: 473 if ftype == 'dir': 474 match = misc.re_primary_dirname 475 else: 476 match = misc.re_primary_filename 477 return [fn for fn in self.files[ftype] if match(fn)] 478 return self.files[ftype] 479 return []
480
481 - def returnFileTypes(self):
482 """return list of types of files in the package""" 483 # maybe should die - use direct access to attribute 484 return self.files.keys()
485
486 - def returnPrcoNames(self, prcotype):
487 if not hasattr(self, '_cache_prco_names_' + prcotype): 488 data = [n for (n, f, v) in self.returnPrco(prcotype)] 489 setattr(self, '_cache_prco_names_' + prcotype, data) 490 return getattr(self, '_cache_prco_names_' + prcotype)
491
492 - def getProvidesNames(self):
493 warnings.warn('getProvidesNames() will go away in a future version of Yum.\n', 494 Errors.YumDeprecationWarning, stacklevel=2) 495 return self.provides_names
496
497 - def simpleFiles(self, ftype='files'):
498 warnings.warn('simpleFiles() will go away in a future version of Yum.' 499 'Use returnFileEntries(primary_only=True)\n', 500 Errors.YumDeprecationWarning, stacklevel=2) 501 if self.files and ftype in self.files: 502 return self.files[ftype] 503 return []
504 505 filelist = property(fget=lambda self: self.returnFileEntries(ftype='file')) 506 dirlist = property(fget=lambda self: self.returnFileEntries(ftype='dir')) 507 ghostlist = property(fget=lambda self: self.returnFileEntries(ftype='ghost')) 508 requires = property(fget=lambda self: self.returnPrco('requires')) 509 provides = property(fget=lambda self: self.returnPrco('provides')) 510 obsoletes = property(fget=lambda self: self.returnPrco('obsoletes')) 511 conflicts = property(fget=lambda self: self.returnPrco('conflicts')) 512 provides_names = property(fget=lambda self: self.returnPrcoNames('provides')) 513 requires_names = property(fget=lambda self: self.returnPrcoNames('requires')) 514 conflicts_names = property(fget=lambda self: self.returnPrcoNames('conflicts')) 515 obsoletes_names = property(fget=lambda self: self.returnPrcoNames('obsoletes')) 516 provides_print = property(fget=lambda self: self.returnPrco('provides', True)) 517 requires_print = property(fget=lambda self: self.returnPrco('requires', True)) 518 conflicts_print = property(fget=lambda self: self.returnPrco('conflicts', True)) 519 obsoletes_print = property(fget=lambda self: self.returnPrco('obsoletes', True)) 520 changelog = property(fget=lambda self: self.returnChangelog()) 521 EVR = property(fget=lambda self: self.returnEVR()) 522
523 - def _getBaseName(self):
524 """ Return the "base name" of the package, atm. we can only look at 525 the sourcerpm. """ 526 if hasattr(self, '_base_package_name_ret'): 527 return self._base_package_name_ret 528 529 if hasattr(self, 'sourcerpm') and self.sourcerpm: 530 (n, v, r, e, a) = rpmUtils.miscutils.splitFilename(self.sourcerpm) 531 if n != self.name: 532 self._base_package_name_ret = n 533 return n 534 535 # If there is no sourcerpm, or sourcerpm == us, use .name 536 self._base_package_name_ret = self.name 537 return self._base_package_name_ret
538 539 base_package_name = property(fget=lambda self: self._getBaseName())
540 541
542 -class PackageEVR:
543 544 """ 545 A comparable epoch, version, and release representation. 546 """ 547
548 - def __init__(self,e,v,r):
549 self.epoch = e 550 self.ver = v 551 self.version = v 552 self.rel = r 553 self.release = r
554
555 - def compare(self,other):
556 return rpmUtils.miscutils.compareEVR((self.epoch, self.ver, self.rel), (other.epoch, other.ver, other.rel))
557
558 - def __lt__(self, other):
559 if self.compare(other) < 0: 560 return True 561 return False
562 563
564 - def __gt__(self, other):
565 if self.compare(other) > 0: 566 return True 567 return False
568
569 - def __le__(self, other):
570 if self.compare(other) <= 0: 571 return True 572 return False
573
574 - def __ge__(self, other):
575 if self.compare(other) >= 0: 576 return True 577 return False
578
579 - def __eq__(self, other):
580 return comparePoEVREQ(self, other)
581
582 - def __ne__(self, other):
583 if not (self == other): 584 return True 585 return False
586 587 588
589 -class YumAvailablePackage(PackageObject, RpmBase):
590 """derived class for the packageobject and RpmBase packageobject yum 591 uses this for dealing with packages in a repository""" 592
593 - def __init__(self, repo, pkgdict = None):
594 PackageObject.__init__(self) 595 RpmBase.__init__(self) 596 597 self.repoid = repo.id 598 self.repo = repo 599 self.state = None 600 self._loadedfiles = False 601 self._verify_local_pkg_cache = None 602 603 if pkgdict != None: 604 self.importFromDict(pkgdict) 605 self.ver = self.version 606 self.rel = self.release 607 self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release)
608
609 - def _ui_from_repo(self):
610 """ This reports the repo the package is from, we integrate YUMDB info. 611 for RPM packages so a package from "fedora" that is installed has a 612 ui_from_repo of "@fedora". Note that, esp. with the --releasever 613 option, "fedora" or "rawhide" isn't authoritive. 614 So we also check against the current releasever and if it is 615 different we also print the YUMDB releasever. This means that 616 installing from F12 fedora, while running F12, would report as 617 "@fedora/13". """ 618 if self.repoid == 'installed' and 'from_repo' in self.yumdb_info: 619 end = '' 620 if (self.rpmdb.releasever is not None and 621 'releasever' in self.yumdb_info and 622 self.yumdb_info.releasever != self.rpmdb.releasever): 623 end = '/' + self.yumdb_info.releasever 624 return '@' + self.yumdb_info.from_repo + end 625 return self.repoid
626 ui_from_repo = property(fget=lambda self: self._ui_from_repo()) 627
628 - def exclude(self):
629 """remove self from package sack""" 630 self.repo.sack.delPackage(self)
631
632 - def printVer(self):
633 """returns a printable version string - including epoch, if it's set""" 634 if self.epoch != '0': 635 ver = '%s:%s-%s' % (self.epoch, self.version, self.release) 636 else: 637 ver = '%s-%s' % (self.version, self.release) 638 639 return ver
640
641 - def compactPrint(self):
642 ver = self.printVer() 643 return "%s.%s %s" % (self.name, self.arch, ver)
644
645 - def _size(self):
646 return self.packagesize
647
648 - def _remote_path(self):
649 return self.relativepath
650
651 - def _remote_url(self):
652 """returns a URL that can be used for downloading the package. 653 Note that if you're going to download the package in your tool, 654 you should use self.repo.getPackage.""" 655 base = self.basepath 656 if base: 657 # urljoin sucks in the reverse way that os.path.join sucks :) 658 if base[-1] != '/': 659 base = base + '/' 660 return urlparse.urljoin(base, self.remote_path) 661 return urlparse.urljoin(self.repo.urls[0], self.remote_path)
662 663 size = property(fget=lambda self: self._size()) 664 remote_path = property(_remote_path) 665 remote_url = property(_remote_url) 666
667 - def _committer(self):
668 "Returns the name of the last person to do a commit to the changelog." 669 670 if hasattr(self, '_committer_ret'): 671 return self._committer_ret 672 673 def _nf2ascii(x): 674 """ does .encode("ascii", "replace") but it never fails. """ 675 ret = [] 676 for val in x: 677 if ord(val) >= 128: 678 val = '?' 679 ret.append(val) 680 return "".join(ret)
681 682 if not len(self.changelog): # Empty changelog is _possible_ I guess 683 self._committer_ret = self.packager 684 return self._committer_ret 685 val = self.changelog[0][1] 686 # Chagnelog data is in multiple locale's, so we convert to ascii 687 # ignoring "bad" chars. 688 val = _nf2ascii(val) 689 # Hacky way to get rid of version numbers... 690 ix = val.find('> ') 691 if ix != -1: 692 val = val[0:ix+1] 693 self._committer_ret = val 694 return self._committer_ret
695 696 committer = property(_committer) 697
698 - def _committime(self):
699 "Returns the time of the last commit to the changelog." 700 701 if hasattr(self, '_committime_ret'): 702 return self._committime_ret 703 704 if not len(self.changelog): # Empty changelog is _possible_ I guess 705 self._committime_ret = self.buildtime 706 return self._committime_ret 707 708 self._committime_ret = self.changelog[0][0] 709 return self._committime_ret
710 711 committime = property(_committime) 712 713 # FIXME test this to see if it causes hell elsewhere
714 - def _checksum(self):
715 "Returns the 'default' checksum" 716 return self.checksums[0][1]
717 checksum = property(_checksum) 718
719 - def getDiscNum(self):
720 if self.basepath is None: 721 return None 722 (scheme, netloc, path, query, fragid) = urlparse.urlsplit(self.basepath) 723 if scheme == "media": 724 if len(fragid) == 0: 725 return 0 726 return int(fragid) 727 return None
728
729 - def returnHeaderFromPackage(self):
730 rpmfile = self.localPkg() 731 ts = rpmUtils.transaction.initReadOnlyTransaction() 732 hdr = rpmUtils.miscutils.hdrFromPackage(ts, rpmfile) 733 return hdr
734
735 - def returnLocalHeader(self):
736 """returns an rpm header object from the package object's local 737 header cache""" 738 739 if os.path.exists(self.localHdr()): 740 try: 741 hlist = rpm.readHeaderListFromFile(self.localHdr()) 742 hdr = hlist[0] 743 except (rpm.error, IndexError): 744 raise Errors.RepoError, 'Cannot open package header' 745 else: 746 raise Errors.RepoError, 'Package Header Not Available' 747 748 return hdr
749 750
751 - def localPkg(self):
752 """return path to local package (whether it is present there, or not)""" 753 if not hasattr(self, 'localpath'): 754 rpmfn = os.path.basename(self.remote_path) 755 self.localpath = self.repo.pkgdir + '/' + rpmfn 756 return self.localpath
757
758 - def localHdr(self):
759 """return path to local cached Header file downloaded from package 760 byte ranges""" 761 762 if not hasattr(self, 'hdrpath'): 763 pkgname = os.path.basename(self.remote_path) 764 hdrname = pkgname[:-4] + '.hdr' 765 self.hdrpath = self.repo.hdrdir + '/' + hdrname 766 767 return self.hdrpath
768
769 - def verifyLocalPkg(self):
770 """check the package checksum vs the localPkg 771 return True if pkg is good, False if not""" 772 773 # This is called a few times now, so we want some way to not have to 774 # read+checksum "large" datasets multiple times per. transaction. 775 try: 776 nst = os.stat(self.localPkg()) 777 except OSError, e: 778 return False 779 if (hasattr(self, '_verify_local_pkg_cache') and 780 self._verify_local_pkg_cache): 781 ost = self._verify_local_pkg_cache 782 if (ost.st_ino == nst.st_ino and 783 ost.st_dev == nst.st_dev and 784 ost.st_mtime == nst.st_mtime and 785 ost.st_size == nst.st_size): 786 return True 787 788 (csum_type, csum) = self.returnIdSum() 789 790 try: 791 filesum = misc.checksum(csum_type, self.localPkg(), 792 datasize=self.packagesize) 793 except Errors.MiscError: 794 return False 795 796 if filesum != csum: 797 return False 798 799 self._verify_local_pkg_cache = nst 800 801 return True
802
803 - def prcoPrintable(self, prcoTuple):
804 """convert the prco tuples into a nicer human string""" 805 warnings.warn('prcoPrintable() will go away in a future version of Yum.\n', 806 Errors.YumDeprecationWarning, stacklevel=2) 807 return misc.prco_tuple_to_string(prcoTuple)
808
809 - def requiresList(self):
810 """return a list of requires in normal rpm format""" 811 return self.requires_print
812
813 - def returnChecksums(self):
814 return [(self.checksum_type, self.pkgId, 1)]
815
816 - def importFromDict(self, pkgdict):
817 """handles an mdCache package dictionary item to populate out 818 the package information""" 819 820 # translates from the pkgdict, populating out the information for the 821 # packageObject 822 823 if hasattr(pkgdict, 'nevra'): 824 (n, e, v, r, a) = pkgdict.nevra 825 self.name = n 826 self.epoch = e 827 self.version = v 828 self.arch = a 829 self.release = r 830 831 if hasattr(pkgdict, 'time'): 832 self.buildtime = pkgdict.time['build'] 833 self.filetime = pkgdict.time['file'] 834 835 if hasattr(pkgdict, 'size'): 836 self.packagesize = pkgdict.size['package'] 837 self.archivesize = pkgdict.size['archive'] 838 self.installedsize = pkgdict.size['installed'] 839 840 if hasattr(pkgdict, 'location'): 841 if not pkgdict.location.has_key('base'): 842 url = None 843 elif pkgdict.location['base'] == '': 844 url = None 845 else: 846 url = pkgdict.location['base'] 847 848 self.basepath = url 849 self.relativepath = pkgdict.location['href'] 850 851 if hasattr(pkgdict, 'hdrange'): 852 self.hdrstart = pkgdict.hdrange['start'] 853 self.hdrend = pkgdict.hdrange['end'] 854 855 if hasattr(pkgdict, 'info'): 856 for item in ['summary', 'description', 'packager', 'group', 857 'buildhost', 'sourcerpm', 'url', 'vendor']: 858 setattr(self, item, pkgdict.info[item]) 859 self.summary = self.summary.replace('\n', '') 860 861 self.licenses.append(pkgdict.info['license']) 862 863 if hasattr(pkgdict, 'files'): 864 for fn in pkgdict.files: 865 ftype = pkgdict.files[fn] 866 if not self.files.has_key(ftype): 867 self.files[ftype] = [] 868 self.files[ftype].append(fn) 869 870 if hasattr(pkgdict, 'prco'): 871 for rtype in pkgdict.prco: 872 for rdict in pkgdict.prco[rtype]: 873 name = rdict['name'] 874 f = e = v = r = None 875 if rdict.has_key('flags'): f = rdict['flags'] 876 if rdict.has_key('epoch'): e = rdict['epoch'] 877 if rdict.has_key('ver'): v = rdict['ver'] 878 if rdict.has_key('rel'): r = rdict['rel'] 879 self.prco[rtype].append((name, f, (e,v,r))) 880 881 if hasattr(pkgdict, 'changelog'): 882 for cdict in pkgdict.changelog: 883 date = text = author = None 884 if cdict.has_key('date'): date = cdict['date'] 885 if cdict.has_key('value'): text = cdict['value'] 886 if cdict.has_key('author'): author = cdict['author'] 887 self._changelog.append((date, author, text)) 888 889 if hasattr(pkgdict, 'checksum'): 890 ctype = pkgdict.checksum['type'] 891 csum = pkgdict.checksum['value'] 892 csumid = pkgdict.checksum['pkgid'] 893 if csumid is None or csumid.upper() == 'NO': 894 csumid = 0 895 elif csumid.upper() == 'YES': 896 csumid = 1 897 else: 898 csumid = 0 899 self._checksums.append((ctype, csum, csumid))
900 901 # from here down this is for dumping a package object back out to metadata 902 903
904 - def _return_remote_location(self):
905 # break self.remote_url up into smaller pieces 906 base = os.path.dirname(self.remote_url) 907 href = os.path.basename(self.remote_url) 908 msg = """<location xml:base="%s" href="%s"/>\n""" % ( 909 misc.to_xml(base,attrib=True), misc.to_xml(href, attrib=True)) 910 return msg
911
912 - def _dump_base_items(self):
913 914 packager = url = '' 915 if self.packager: 916 packager = misc.to_unicode(misc.to_xml(self.packager)) 917 918 if self.url: 919 url = misc.to_unicode(misc.to_xml(self.url)) 920 (csum_type, csum, csumid) = self.checksums[0] 921 msg = """ 922 <name>%s</name> 923 <arch>%s</arch> 924 <version epoch="%s" ver="%s" rel="%s"/> 925 <checksum type="%s" pkgid="YES">%s</checksum> 926 <summary>%s</summary> 927 <description>%s</description> 928 <packager>%s</packager> 929 <url>%s</url> 930 <time file="%s" build="%s"/> 931 <size package="%s" installed="%s" archive="%s"/>\n""" % (self.name, 932 self.arch, self.epoch, self.ver, self.rel, csum_type, csum, 933 misc.to_unicode(misc.to_xml(self.summary)), 934 misc.to_unicode(misc.to_xml(self.description)), 935 packager, url, self.filetime, 936 self.buildtime, self.packagesize, self.size, self.archivesize) 937 938 msg += self._return_remote_location() 939 return msg
940
941 - def _dump_format_items(self):
942 msg = " <format>\n" 943 if self.license: 944 msg += """ <rpm:license>%s</rpm:license>\n""" % misc.to_xml(self.license) 945 else: 946 msg += """ <rpm:license/>\n""" 947 948 if self.vendor: 949 msg += """ <rpm:vendor>%s</rpm:vendor>\n""" % misc.to_xml(self.vendor) 950 else: 951 msg += """ <rpm:vendor/>\n""" 952 953 if self.group: 954 msg += """ <rpm:group>%s</rpm:group>\n""" % misc.to_xml(self.group) 955 else: 956 msg += """ <rpm:group/>\n""" 957 958 if self.buildhost: 959 msg += """ <rpm:buildhost>%s</rpm:buildhost>\n""" % misc.to_xml(self.buildhost) 960 else: 961 msg += """ <rpm:buildhost/>\n""" 962 963 if self.sourcerpm: 964 msg += """ <rpm:sourcerpm>%s</rpm:sourcerpm>\n""" % misc.to_xml(self.sourcerpm) 965 msg +=""" <rpm:header-range start="%s" end="%s"/>""" % (self.hdrstart, 966 self.hdrend) 967 msg += self._dump_pco('provides') 968 msg += self._dump_requires() 969 msg += self._dump_pco('conflicts') 970 msg += self._dump_pco('obsoletes') 971 msg += self._dump_files(True) 972 msg += """\n </format>""" 973 return msg
974
975 - def _dump_pco(self, pcotype):
976 977 msg = "" 978 mylist = getattr(self, pcotype) 979 if mylist: msg = "\n <rpm:%s>\n" % pcotype 980 for (name, flags, (e,v,r)) in mylist: 981 pcostring = ''' <rpm:entry name="%s"''' % misc.to_xml(name, attrib=True) 982 if flags: 983 pcostring += ''' flags="%s"''' % misc.to_xml(flags, attrib=True) 984 if e: 985 pcostring += ''' epoch="%s"''' % misc.to_xml(e, attrib=True) 986 if v: 987 pcostring += ''' ver="%s"''' % misc.to_xml(v, attrib=True) 988 if r: 989 pcostring += ''' rel="%s"''' % misc.to_xml(r, attrib=True) 990 991 pcostring += "/>\n" 992 msg += pcostring 993 994 if mylist: msg += " </rpm:%s>" % pcotype 995 return msg
996
997 - def _dump_files(self, primary=False):
998 msg ="" 999 if not primary: 1000 files = self.returnFileEntries('file') 1001 dirs = self.returnFileEntries('dir') 1002 ghosts = self.returnFileEntries('ghost') 1003 else: 1004 files = self.returnFileEntries('file', primary_only=True) 1005 dirs = self.returnFileEntries('dir', primary_only=True) 1006 ghosts = self.returnFileEntries('ghost', primary_only=True) 1007 1008 for fn in files: 1009 msg += """ <file>%s</file>\n""" % misc.to_xml(fn) 1010 for fn in dirs: 1011 msg += """ <file type="dir">%s</file>\n""" % misc.to_xml(fn) 1012 for fn in ghosts: 1013 msg += """ <file type="ghost">%s</file>\n""" % misc.to_xml(fn) 1014 1015 return msg
1016 1017
1018 - def _requires_with_pre(self):
1019 raise NotImplementedError()
1020
1021 - def _dump_requires(self):
1022 """returns deps in format""" 1023 mylist = self._requires_with_pre() 1024 1025 msg = "" 1026 1027 if mylist: msg = "\n <rpm:requires>\n" 1028 for (name, flags, (e,v,r),pre) in mylist: 1029 if name.startswith('rpmlib('): 1030 continue 1031 # this drops out requires that the pkg provides for itself. 1032 if name in self.provides_names or name in self.filelist + \ 1033 self.dirlist + self.ghostlist: 1034 if not flags: 1035 continue 1036 else: 1037 if self.checkPrco('provides', (name, flags, (e,v,r))): 1038 continue 1039 prcostring = ''' <rpm:entry name="%s"''' % misc.to_xml(name, attrib=True) 1040 if flags: 1041 prcostring += ''' flags="%s"''' % misc.to_xml(flags, attrib=True) 1042 if e: 1043 prcostring += ''' epoch="%s"''' % misc.to_xml(e, attrib=True) 1044 if v: 1045 prcostring += ''' ver="%s"''' % misc.to_xml(v, attrib=True) 1046 if r: 1047 prcostring += ''' rel="%s"''' % misc.to_xml(r, attrib=True) 1048 if pre: 1049 prcostring += ''' pre="%s"''' % pre 1050 1051 prcostring += "/>\n" 1052 msg += prcostring 1053 1054 if mylist: msg += " </rpm:requires>" 1055 return msg
1056
1057 - def _dump_changelog(self, clog_limit):
1058 if not self.changelog: 1059 return "" 1060 msg = "\n" 1061 # We need to output them "backwards", so the oldest is first 1062 if not clog_limit: 1063 clogs = self.changelog 1064 else: 1065 clogs = self.changelog[:clog_limit] 1066 last_ts = 0 1067 hack_ts = 0 1068 for (ts, author, content) in reversed(clogs): 1069 if ts != last_ts: 1070 hack_ts = 0 1071 else: 1072 hack_ts += 1 1073 last_ts = ts 1074 ts += hack_ts 1075 msg += """<changelog author="%s" date="%s">%s</changelog>\n""" % ( 1076 misc.to_xml(author, attrib=True), misc.to_xml(str(ts)), 1077 misc.to_xml(content)) 1078 return msg
1079
1080 - def xml_dump_primary_metadata(self):
1081 msg = """\n<package type="rpm">""" 1082 msg += misc.to_unicode(self._dump_base_items()) 1083 msg += misc.to_unicode(self._dump_format_items()) 1084 msg += """\n</package>""" 1085 return misc.to_utf8(msg)
1086
1087 - def xml_dump_filelists_metadata(self):
1088 msg = """\n<package pkgid="%s" name="%s" arch="%s"> 1089 <version epoch="%s" ver="%s" rel="%s"/>\n""" % (self.checksum, self.name, 1090 self.arch, self.epoch, self.ver, self.rel) 1091 msg += misc.to_unicode(self._dump_files()) 1092 msg += "</package>\n" 1093 return misc.to_utf8(msg)
1094
1095 - def xml_dump_other_metadata(self, clog_limit=0):
1096 msg = """\n<package pkgid="%s" name="%s" arch="%s"> 1097 <version epoch="%s" ver="%s" rel="%s"/>\n""" % (self.checksum, self.name, 1098 self.arch, self.epoch, self.ver, self.rel) 1099 msg += "%s\n</package>\n" % misc.to_unicode(self._dump_changelog(clog_limit)) 1100 return misc.to_utf8(msg)
1101 1102 1103 1104
1105 -class YumHeaderPackage(YumAvailablePackage):
1106 """Package object built from an rpm header"""
1107 - def __init__(self, repo, hdr):
1108 """hand in an rpm header, we'll assume it's installed and query from there""" 1109 1110 YumAvailablePackage.__init__(self, repo) 1111 1112 self.hdr = hdr 1113 self.name = misc.share_data(self.hdr['name']) 1114 this_a = self.hdr['arch'] 1115 if not this_a: # this should only happen on gpgkeys and other "odd" pkgs 1116 this_a = 'noarch' 1117 self.arch = misc.share_data(this_a) 1118 self.epoch = misc.share_data(self.doepoch()) 1119 self.version = misc.share_data(self.hdr['version']) 1120 self.release = misc.share_data(self.hdr['release']) 1121 self.ver = self.version 1122 self.rel = self.release 1123 self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release) 1124 # Summaries "can be" empty, which rpm return [], see BZ 473239, *sigh* 1125 self.summary = self.hdr['summary'] or '' 1126 self.summary = misc.share_data(self.summary.replace('\n', '')) 1127 self.description = self.hdr['description'] or '' 1128 self.description = misc.share_data(self.description) 1129 self.pkgid = self.hdr[rpm.RPMTAG_SHA1HEADER] 1130 if not self.pkgid: 1131 self.pkgid = "%s.%s" %(self.hdr['name'], self.hdr['buildtime']) 1132 self.packagesize = self.hdr['size'] 1133 self.__mode_cache = {} 1134 self.__prcoPopulated = False
1135
1136 - def __str__(self):
1137 if self.epoch == '0': 1138 val = '%s-%s-%s.%s' % (self.name, self.version, self.release, 1139 self.arch) 1140 else: 1141 val = '%s:%s-%s-%s.%s' % (self.epoch,self.name, self.version, 1142 self.release, self.arch) 1143 return val
1144
1145 - def returnPrco(self, prcotype, printable=False):
1146 if not self.__prcoPopulated: 1147 self._populatePrco() 1148 self.__prcoPopulated = True 1149 return YumAvailablePackage.returnPrco(self, prcotype, printable)
1150
1151 - def _get_hdr(self):
1152 return self.hdr
1153
1154 - def _populatePrco(self):
1155 "Populate the package object with the needed PRCO interface." 1156 1157 tag2prco = { "OBSOLETE": misc.share_data("obsoletes"), 1158 "CONFLICT": misc.share_data("conflicts"), 1159 "REQUIRE": misc.share_data("requires"), 1160 "PROVIDE": misc.share_data("provides") } 1161 hdr = self._get_hdr() 1162 for tag in tag2prco: 1163 name = hdr[getattr(rpm, 'RPMTAG_%sNAME' % tag)] 1164 name = map(misc.share_data, name) 1165 if name is None: 1166 continue 1167 1168 lst = hdr[getattr(rpm, 'RPMTAG_%sFLAGS' % tag)] 1169 flag = map(rpmUtils.miscutils.flagToString, lst) 1170 flag = map(misc.share_data, flag) 1171 1172 lst = hdr[getattr(rpm, 'RPMTAG_%sVERSION' % tag)] 1173 vers = map(rpmUtils.miscutils.stringToVersion, lst) 1174 vers = map(lambda x: (misc.share_data(x[0]), misc.share_data(x[1]), 1175 misc.share_data(x[2])), vers) 1176 1177 prcotype = tag2prco[tag] 1178 self.prco[prcotype] = map(misc.share_data, zip(name,flag,vers))
1179
1180 - def tagByName(self, tag):
1181 warnings.warn("tagByName() will go away in a furture version of Yum.\n", 1182 Errors.YumFutureDeprecationWarning, stacklevel=2) 1183 try: 1184 return getattr(self, tag) 1185 except AttributeError: 1186 raise Errors.MiscError, "Unknown header tag %s" % tag
1187
1188 - def __getattr__(self, thing):
1189 #FIXME - if an error - return AttributeError, not KeyError 1190 # ONLY FIX THIS AFTER THE API BREAK 1191 if thing.startswith('__') and thing.endswith('__'): 1192 # If these existed, then we wouldn't get here ... 1193 # So these are missing. 1194 raise AttributeError, "%s has no attribute %s" % (self, thing) 1195 try: 1196 return self.hdr[thing] 1197 except KeyError: 1198 # Note above, API break to fix this ... this at least is a nicer 1199 # msg. so we know what we accessed that is bad. 1200 raise KeyError, "%s has no attribute %s" % (self, thing)
1201
1202 - def doepoch(self):
1203 tmpepoch = self.hdr['epoch'] 1204 if tmpepoch is None: 1205 epoch = '0' 1206 else: 1207 epoch = str(tmpepoch) 1208 1209 return epoch
1210
1211 - def returnLocalHeader(self):
1212 return self.hdr
1213 1214
1215 - def _loadFiles(self):
1216 files = self.hdr['filenames'] 1217 fileflags = self.hdr['fileflags'] 1218 filemodes = self.hdr['filemodes'] 1219 filetuple = zip(files, filemodes, fileflags) 1220 if not self._loadedfiles: 1221 for (fn, mode, flag) in filetuple: 1222 #garbage checks 1223 if mode is None or mode == '': 1224 if not self.files.has_key('file'): 1225 self.files['file'] = [] 1226 self.files['file'].append(fn) 1227 continue 1228 if mode not in self.__mode_cache: 1229 self.__mode_cache[mode] = stat.S_ISDIR(mode) 1230 1231 fkey = 'file' 1232 if self.__mode_cache[mode]: 1233 fkey = 'dir' 1234 elif flag is not None and (flag & 64): 1235 fkey = 'ghost' 1236 self.files.setdefault(fkey, []).append(fn) 1237 1238 self._loadedfiles = True
1239
1240 - def returnFileEntries(self, ftype='file', primary_only=False):
1241 """return list of files based on type""" 1242 self._loadFiles() 1243 return YumAvailablePackage.returnFileEntries(self,ftype,primary_only)
1244
1245 - def returnChangelog(self):
1246 # note - if we think it is worth keeping changelogs in memory 1247 # then create a _loadChangelog() method to put them into the 1248 # self._changelog attr 1249 if len(self.hdr['changelogname']) > 0: 1250 return zip(misc.to_unicode(self.hdr['changelogtime'], errors='replace'), 1251 misc.to_unicode(self.hdr['changelogname'], errors='replace'), 1252 misc.to_unicode(self.hdr['changelogtext'], errors='replace')) 1253 return []
1254
1255 - def returnChecksums(self):
1256 raise NotImplementedError()
1257
1258 - def _size(self):
1259 return self.hdr['size']
1260
1261 - def _is_pre_req(self, flag):
1262 """check the flags for a requirement, return 1 or 0 whether or not requires 1263 is a pre-requires or a not""" 1264 # FIXME this should probably be put in rpmUtils.miscutils since 1265 # - that's what it is 1266 if flag is not None: 1267 # Note: RPMSENSE_PREREQ == 0 since rpm-4.4'ish 1268 if flag & (rpm.RPMSENSE_PREREQ | 1269 rpm.RPMSENSE_SCRIPT_PRE | 1270 rpm.RPMSENSE_SCRIPT_POST): 1271 return 1 1272 return 0
1273
1274 - def _requires_with_pre(self):
1275 """returns requires with pre-require bit""" 1276 name = self.hdr[rpm.RPMTAG_REQUIRENAME] 1277 lst = self.hdr[rpm.RPMTAG_REQUIREFLAGS] 1278 flag = map(flagToString, lst) 1279 pre = map(self._is_pre_req, lst) 1280 lst = self.hdr[rpm.RPMTAG_REQUIREVERSION] 1281 vers = map(stringToVersion, lst) 1282 if name is not None: 1283 lst = zip(name, flag, vers, pre) 1284 mylist = misc.unique(lst) 1285 return mylist
1286
1287 -class _CountedReadFile:
1288 """ Has just a read() method, and keeps a count so we can find out how much 1289 has been read. Implemented so we can get the real size of the file from 1290 prelink. """ 1291
1292 - def __init__(self, fp):
1293 self.fp = fp 1294 self.read_size = 0
1295
1296 - def read(self, size):
1297 ret = self.fp.read(size) 1298 self.read_size += len(ret) 1299 return ret
1300
1301 -class _PkgVerifyProb:
1302 """ Holder for each "problem" we find with a pkg.verify(). """ 1303
1304 - def __init__(self, type, msg, ftypes, fake=False):
1305 self.type = type 1306 self.message = msg 1307 self.database_value = None 1308 self.disk_value = None 1309 self.file_types = ftypes 1310 self.fake = fake
1311
1312 - def __cmp__(self, other):
1313 if other is None: 1314 return 1 1315 type2sort = {'type' : 1, 'symlink' : 2, 'checksum' : 3, 'size' : 4, 1316 'user' : 4, 'group' : 5, 'mode' : 6, 'genchecksum' : 7, 1317 'mtime' : 8, 'missing' : 9, 'permissions-missing' : 10, 1318 'state' : 11, 'missingok' : 12, 'ghost' : 13} 1319 ret = cmp(type2sort[self.type], type2sort[other.type]) 1320 if not ret: 1321 for attr in ['disk_value', 'database_value', 'file_types']: 1322 x = getattr(self, attr) 1323 y = getattr(other, attr) 1324 if x is None: 1325 assert y is None 1326 continue 1327 ret = cmp(x, y) 1328 if ret: 1329 break 1330 return ret
1331 1332 # From: lib/rpmvf.h ... not in rpm *sigh* 1333 _RPMVERIFY_DIGEST = (1 << 0) 1334 _RPMVERIFY_FILESIZE = (1 << 1) 1335 _RPMVERIFY_LINKTO = (1 << 2) 1336 _RPMVERIFY_USER = (1 << 3) 1337 _RPMVERIFY_GROUP = (1 << 4) 1338 _RPMVERIFY_MTIME = (1 << 5) 1339 _RPMVERIFY_MODE = (1 << 6) 1340 _RPMVERIFY_RDEV = (1 << 7) 1341 1342 _installed_repo = FakeRepository('installed') 1343 _installed_repo.cost = 0
1344 -class YumInstalledPackage(YumHeaderPackage):
1345 """super class for dealing with packages in the rpmdb"""
1346 - def __init__(self, hdr, yumdb=None):
1347 YumHeaderPackage.__init__(self, _installed_repo, hdr) 1348 if yumdb: 1349 self.yumdb_info = yumdb.get_package(self)
1350
1351 - def verify(self, patterns=[], deps=False, script=False, 1352 fake_problems=True, all=False, fast=False):
1353 """verify that the installed files match the packaged checksum 1354 optionally verify they match only if they are in the 'pattern' list 1355 returns a tuple """ 1356 def _ftype(mode): 1357 """ Given a "mode" return the name of the type of file. """ 1358 if stat.S_ISREG(mode): return "file" 1359 if stat.S_ISDIR(mode): return "directory" 1360 if stat.S_ISLNK(mode): return "symlink" 1361 if stat.S_ISFIFO(mode): return "fifo" 1362 if stat.S_ISCHR(mode): return "character device" 1363 if stat.S_ISBLK(mode): return "block device" 1364 return "<unknown>"
1365 1366 statemap = {rpm.RPMFILE_STATE_REPLACED : 'replaced', 1367 rpm.RPMFILE_STATE_NOTINSTALLED : 'not installed', 1368 rpm.RPMFILE_STATE_WRONGCOLOR : 'wrong color', 1369 rpm.RPMFILE_STATE_NETSHARED : 'netshared'} 1370 1371 fi = self.hdr.fiFromHeader() 1372 results = {} # fn = problem_obj? 1373 1374 # Use prelink_undo_cmd macro? 1375 prelink_cmd = "/usr/sbin/prelink" 1376 have_prelink = os.path.exists(prelink_cmd) 1377 1378 # determine what checksum algo to use: 1379 csum_type = 'md5' # default for legacy 1380 if hasattr(rpm, 'RPMTAG_FILEDIGESTALGO'): 1381 csum_num = self.hdr[rpm.RPMTAG_FILEDIGESTALGO] 1382 if csum_num: 1383 if csum_num in RPM_CHECKSUM_TYPES: 1384 csum_type = RPM_CHECKSUM_TYPES[csum_num] 1385 # maybe an else with an error code here? or even a verify issue? 1386 1387 for filetuple in fi: 1388 #tuple is: (filename, fsize, mode, mtime, flags, frdev?, inode, link, 1389 # state, vflags?, user, group, checksum(or none for dirs) 1390 (fn, size, mode, mtime, flags, dev, inode, link, state, vflags, 1391 user, group, csum) = filetuple 1392 if patterns: 1393 matched = False 1394 for p in patterns: 1395 if fnmatch.fnmatch(fn, p): 1396 matched = True 1397 break 1398 if not matched: 1399 continue 1400 1401 ftypes = [] 1402 if flags & rpm.RPMFILE_CONFIG: 1403 ftypes.append('configuration') 1404 if flags & rpm.RPMFILE_DOC: 1405 ftypes.append('documentation') 1406 if flags & rpm.RPMFILE_GHOST: 1407 ftypes.append('ghost') 1408 if flags & rpm.RPMFILE_LICENSE: 1409 ftypes.append('license') 1410 if flags & rpm.RPMFILE_PUBKEY: 1411 ftypes.append('public key') 1412 if flags & rpm.RPMFILE_README: 1413 ftypes.append('README') 1414 if flags & rpm.RPMFILE_MISSINGOK: 1415 ftypes.append('missing ok') 1416 # not in python rpm bindings yet 1417 # elif flags & rpm.RPMFILE_POLICY: 1418 # ftypes.append('policy') 1419 1420 if all: 1421 vflags = -1 1422 1423 if state != rpm.RPMFILE_STATE_NORMAL: 1424 if state in statemap: 1425 ftypes.append("state=" + statemap[state]) 1426 else: 1427 ftypes.append("state=<unknown>") 1428 if fake_problems: 1429 results[fn] = [_PkgVerifyProb('state', 1430 'state is not normal', 1431 ftypes, fake=True)] 1432 continue 1433 1434 if flags & rpm.RPMFILE_MISSINGOK and fake_problems: 1435 results[fn] = [_PkgVerifyProb('missingok', 'missing but ok', 1436 ftypes, fake=True)] 1437 if flags & rpm.RPMFILE_MISSINGOK and not all: 1438 continue # rpm just skips missing ok, so we do too 1439 1440 if flags & rpm.RPMFILE_GHOST and fake_problems: 1441 results[fn] = [_PkgVerifyProb('ghost', 'ghost file', ftypes, 1442 fake=True)] 1443 if flags & rpm.RPMFILE_GHOST and not all: 1444 continue 1445 1446 # do check of file status on system 1447 problems = [] 1448 if os.path.lexists(fn): 1449 # stat 1450 my_st = os.lstat(fn) 1451 my_st_size = my_st.st_size 1452 try: 1453 my_user = pwd.getpwuid(my_st[stat.ST_UID])[0] 1454 except KeyError, e: 1455 my_user = 'uid %s not found' % my_st[stat.ST_UID] 1456 try: 1457 my_group = grp.getgrgid(my_st[stat.ST_GID])[0] 1458 except KeyError, e: 1459 my_group = 'gid %s not found' % my_st[stat.ST_GID] 1460 1461 if mode < 0: 1462 # Stupid rpm, should be unsigned value but is signed ... 1463 # so we "fix" it via. this hack 1464 mode = (mode & 0xFFFF) 1465 1466 ftype = _ftype(mode) 1467 my_ftype = _ftype(my_st.st_mode) 1468 1469 if vflags & _RPMVERIFY_RDEV and ftype != my_ftype: 1470 prob = _PkgVerifyProb('type', 'file type does not match', 1471 ftypes) 1472 prob.database_value = ftype 1473 prob.disk_value = my_ftype 1474 problems.append(prob) 1475 1476 if (ftype == "symlink" and my_ftype == "symlink" and 1477 vflags & _RPMVERIFY_LINKTO): 1478 fnl = fi.FLink() # fi.foo is magic, don't think about it 1479 my_fnl = os.readlink(fn) 1480 if my_fnl != fnl: 1481 prob = _PkgVerifyProb('symlink', 1482 'symlink does not match', ftypes) 1483 prob.database_value = fnl 1484 prob.disk_value = my_fnl 1485 problems.append(prob) 1486 1487 check_content = True 1488 if 'ghost' in ftypes: 1489 check_content = False 1490 if my_ftype == "symlink" and ftype == "file": 1491 # Don't let things hide behind symlinks 1492 my_st_size = os.stat(fn).st_size 1493 elif my_ftype != "file": 1494 check_content = False 1495 check_perms = True 1496 if my_ftype == "symlink": 1497 check_perms = False 1498 1499 if (check_content and vflags & _RPMVERIFY_MTIME and 1500 my_st.st_mtime != mtime): 1501 prob = _PkgVerifyProb('mtime', 'mtime does not match', 1502 ftypes) 1503 prob.database_value = mtime 1504 prob.disk_value = my_st.st_mtime 1505 problems.append(prob) 1506 1507 if check_perms and vflags & _RPMVERIFY_USER and my_user != user: 1508 prob = _PkgVerifyProb('user', 'user does not match', ftypes) 1509 prob.database_value = user 1510 prob.disk_value = my_user 1511 problems.append(prob) 1512 if (check_perms and vflags & _RPMVERIFY_GROUP and 1513 my_group != group): 1514 prob = _PkgVerifyProb('group', 'group does not match', 1515 ftypes) 1516 prob.database_value = group 1517 prob.disk_value = my_group 1518 problems.append(prob) 1519 1520 my_mode = my_st.st_mode 1521 if 'ghost' in ftypes: # This is what rpm does 1522 my_mode &= 0777 1523 mode &= 0777 1524 if check_perms and vflags & _RPMVERIFY_MODE and my_mode != mode: 1525 prob = _PkgVerifyProb('mode', 'mode does not match', ftypes) 1526 prob.database_value = mode 1527 prob.disk_value = my_st.st_mode 1528 problems.append(prob) 1529 1530 if fast and not problems and (my_st_size == size): 1531 vflags &= ~_RPMVERIFY_DIGEST 1532 1533 # Note that because we might get the _size_ from prelink, 1534 # we need to do the checksum, even if we just throw it away, 1535 # just so we get the size correct. 1536 if (check_content and 1537 ((have_prelink and (vflags & _RPMVERIFY_FILESIZE) and 1538 (my_st_size != size)) or 1539 (csum and vflags & _RPMVERIFY_DIGEST))): 1540 try: 1541 my_csum = misc.checksum(csum_type, fn) 1542 gen_csum = True 1543 except Errors.MiscError: 1544 # Don't have permission? 1545 gen_csum = False 1546 1547 if csum and vflags & _RPMVERIFY_DIGEST and not gen_csum: 1548 prob = _PkgVerifyProb('genchecksum', 1549 'checksum not available', ftypes) 1550 prob.database_value = csum 1551 prob.disk_value = None 1552 problems.append(prob) 1553 1554 if gen_csum and my_csum != csum and have_prelink: 1555 # This is how rpm -V works, try and if that fails try 1556 # again with prelink. 1557 p = Popen([prelink_cmd, "-y", fn], 1558 bufsize=-1, stdin=PIPE, 1559 stdout=PIPE, stderr=PIPE, close_fds=True) 1560 (ig, fp, er) = (p.stdin, p.stdout, p.stderr) 1561 # er.read(1024 * 1024) # Try and get most of the stderr 1562 fp = _CountedReadFile(fp) 1563 tcsum = misc.checksum(csum_type, fp) 1564 if fp.read_size: # If prelink worked 1565 my_csum = tcsum 1566 my_st_size = fp.read_size 1567 1568 if (csum and vflags & _RPMVERIFY_DIGEST and gen_csum and 1569 my_csum != csum): 1570 prob = _PkgVerifyProb('checksum', 1571 'checksum does not match', ftypes) 1572 prob.database_value = csum 1573 prob.disk_value = my_csum 1574 problems.append(prob) 1575 1576 # Size might be got from prelink ... *sigh*. 1577 if (check_content and vflags & _RPMVERIFY_FILESIZE and 1578 my_st_size != size): 1579 prob = _PkgVerifyProb('size', 'size does not match', ftypes) 1580 prob.database_value = size 1581 prob.disk_value = my_st_size 1582 problems.append(prob) 1583 1584 else: 1585 try: 1586 os.stat(fn) 1587 perms_ok = True # Shouldn't happen 1588 except OSError, e: 1589 perms_ok = True 1590 if e.errno == errno.EACCES: 1591 perms_ok = False 1592 1593 if perms_ok: 1594 prob = _PkgVerifyProb('missing', 'file is missing', ftypes) 1595 else: 1596 prob = _PkgVerifyProb('permissions-missing', 1597 'file is missing (Permission denied)', 1598 ftypes) 1599 problems.append(prob) 1600 1601 if problems: 1602 results[fn] = problems 1603 1604 return results
1605 1606
1607 -class YumLocalPackage(YumHeaderPackage):
1608 """Class to handle an arbitrary package from a file path 1609 this inherits most things from YumInstalledPackage because 1610 installed packages and an arbitrary package on disk act very 1611 much alike. init takes a ts instance and a filename/path 1612 to the package.""" 1613
1614 - def __init__(self, ts=None, filename=None):
1615 if ts is None: 1616 raise Errors.MiscError, \ 1617 'No Transaction Set Instance for YumLocalPackage instance creation' 1618 if filename is None: 1619 raise Errors.MiscError, \ 1620 'No Filename specified for YumLocalPackage instance creation' 1621 1622 self.pkgtype = 'local' 1623 self.localpath = filename 1624 self._checksum = None 1625 1626 1627 try: 1628 hdr = rpmUtils.miscutils.hdrFromPackage(ts, self.localpath) 1629 except RpmUtilsError, e: 1630 raise Errors.MiscError, \ 1631 'Could not open local rpm file: %s: %s' % (self.localpath, e) 1632 1633 fakerepo = FakeRepository(filename) 1634 fakerepo.cost = 0 1635 YumHeaderPackage.__init__(self, fakerepo, hdr) 1636 self.id = self.pkgid 1637 self._stat = os.stat(self.localpath) 1638 self.filetime = str(self._stat[-2]) 1639 self.packagesize = str(self._stat[6]) 1640 self.arch = self.isSrpm() 1641 self.pkgtup = (self.name, self.arch, self.epoch, self.ver, self.rel) 1642 self._hdrstart = None 1643 self._hdrend = None 1644 self.checksum_type = misc._default_checksums[0] 1645 1646 # these can be set by callers that need these features (ex: createrepo) 1647 self._reldir = None 1648 self._baseurl = ""
1649 # self._packagenumber will be needed when we do sqlite creation here 1650 1651
1652 - def isSrpm(self):
1653 if self.tagByName('sourcepackage') == 1 or not self.tagByName('sourcerpm'): 1654 return 'src' 1655 else: 1656 return self.tagByName('arch')
1657
1658 - def localPkg(self):
1659 return self.localpath
1660
1661 - def _do_checksum(self, checksum_type=None):
1662 if checksum_type is None: 1663 checksum_type = misc._default_checksums[0] 1664 if not self._checksum: 1665 self._checksum = misc.checksum(checksum_type, self.localpath) 1666 self._checksums = [(checksum_type, self._checksum, 1)] 1667 1668 return self._checksum
1669 1670 checksum = property(fget=lambda self: self._do_checksum()) 1671
1672 - def returnChecksums(self):
1673 self._do_checksum() 1674 return self._checksums
1675
1676 - def verifyLocalPkg(self):
1677 """ don't bother "checking" the package matches itself. """ 1678 return True
1679
1680 - def _get_header_byte_range(self):
1681 """takes an rpm file or fileobject and returns byteranges for location of the header""" 1682 if self._hdrstart and self._hdrend: 1683 return (self._hdrstart, self._hdrend) 1684 1685 1686 fo = open(self.localpath, 'r') 1687 #read in past lead and first 8 bytes of sig header 1688 fo.seek(104) 1689 # 104 bytes in 1690 binindex = fo.read(4) 1691 # 108 bytes in 1692 (sigindex, ) = struct.unpack('>I', binindex) 1693 bindata = fo.read(4) 1694 # 112 bytes in 1695 (sigdata, ) = struct.unpack('>I', bindata) 1696 # each index is 4 32bit segments - so each is 16 bytes 1697 sigindexsize = sigindex * 16 1698 sigsize = sigdata + sigindexsize 1699 # we have to round off to the next 8 byte boundary 1700 disttoboundary = (sigsize % 8) 1701 if disttoboundary != 0: 1702 disttoboundary = 8 - disttoboundary 1703 # 112 bytes - 96 == lead, 8 = magic and reserved, 8 == sig header data 1704 hdrstart = 112 + sigsize + disttoboundary 1705 1706 fo.seek(hdrstart) # go to the start of the header 1707 fo.seek(8,1) # read past the magic number and reserved bytes 1708 1709 binindex = fo.read(4) 1710 (hdrindex, ) = struct.unpack('>I', binindex) 1711 bindata = fo.read(4) 1712 (hdrdata, ) = struct.unpack('>I', bindata) 1713 1714 # each index is 4 32bit segments - so each is 16 bytes 1715 hdrindexsize = hdrindex * 16 1716 # add 16 to the hdrsize to account for the 16 bytes of misc data b/t the 1717 # end of the sig and the header. 1718 hdrsize = hdrdata + hdrindexsize + 16 1719 1720 # header end is hdrstart + hdrsize 1721 hdrend = hdrstart + hdrsize 1722 fo.close() 1723 self._hdrstart = hdrstart 1724 self._hdrend = hdrend 1725 1726 return (hdrstart, hdrend)
1727 1728 hdrend = property(fget=lambda self: self._get_header_byte_range()[1]) 1729 hdrstart = property(fget=lambda self: self._get_header_byte_range()[0]) 1730
1731 - def _return_remote_location(self):
1732 1733 # if we start seeing fullpaths in the location tag - this is the culprit 1734 if self._reldir and self.localpath.startswith(self._reldir): 1735 relpath = self.localpath.replace(self._reldir, '') 1736 if relpath[0] == '/': relpath = relpath[1:] 1737 else: 1738 relpath = self.localpath 1739 1740 if self._baseurl: 1741 msg = """<location xml:base="%s" href="%s"/>\n""" % ( 1742 misc.to_xml(self._baseurl, attrib=True), 1743 misc.to_xml(relpath, attrib=True)) 1744 else: 1745 msg = """<location href="%s"/>\n""" % misc.to_xml(relpath, attrib=True) 1746 1747 return msg
1748 1749
1750 -class YumUrlPackage(YumLocalPackage):
1751 """Class to handle an arbitrary package from a URL 1752 this inherits most things from YumLocalPackage, but will download a 1753 remote package to make it local. 1754 init takes a YumBase, a ts instance and a url to the package.""" 1755
1756 - def __init__(self, yb=None, ts=None, url=None, ua=None):
1757 if url.lower().startswith("file:"): 1758 result = url[len("file:"):] 1759 elif not misc.re_remote_url(url): 1760 result = url 1761 else: 1762 cb = None 1763 pd = {} 1764 for repo in yb.repos.listEnabled(): 1765 cb = repo.callback # Hacky, but these are "always" the same 1766 if (repo.proxy == yb.conf.proxy and 1767 repo.proxy_username == yb.conf.proxy_username and 1768 repo.proxy_password == yb.conf.proxy_password): 1769 # Even more hacky... 1770 pd = repo.proxy_dict 1771 break 1772 fname = os.path.basename(url) 1773 local = misc.getCacheDir() 1774 if local is None: # bugger... 1775 local = "%s/../" % repo.cachedir 1776 local = "%s/%s" % (local, fname) 1777 try: 1778 ug = URLGrabber(bandwidth = yb.conf.bandwidth, 1779 retry = yb.conf.retries, 1780 throttle = yb.conf.throttle, 1781 progress_obj = cb, 1782 proxies=pd) 1783 if ua is not None: 1784 ug.opts.user_agent = ua 1785 result = ug.urlgrab(url, local, text=fname) 1786 except URLGrabError, e: 1787 raise Errors.MiscError("Cannot download %s: %s" % (url, e)) 1788 YumLocalPackage.__init__(self, ts, result)
1789