1 from portage.cache.cache_errors import CacheError
2 from portage.const import REPO_NAME_LOC
3 from portage.data import portage_gid, secpass
4 from portage.dbapi import dbapi
5 from portage.dep import use_reduce, paren_reduce, dep_getslot, dep_getkey, \
6 match_from_list, match_to_list
7 from portage.exception import OperationNotPermitted, PortageException, \
8 UntrustedSignature, SecurityViolation, InvalidSignature, MissingSignature, \
10 from portage.manifest import Manifest
11 from portage.output import red
12 from portage.util import ensure_dirs, writemsg, apply_recursive_permissions
13 from portage.versions import pkgsplit, catpkgsplit, best
15 import portage.gpg, portage.checksum
17 from portage import eclass_cache, auxdbkeys, auxdbkeylen, doebuild, flatten, \
18 listdir, dep_expand, eapi_is_supported, key_expand, dep_check, config
23 class portdbapi(dbapi):
24 """this tree will scan a portage directory located at root (passed to init)"""
25 portdbapi_instances = []
27 def __init__(self, porttree_root, mysettings=None):
28 portdbapi.portdbapi_instances.append(self)
31 self.mysettings = mysettings
33 from portage import settings
34 self.mysettings = config(clone=settings)
35 self._categories = set(self.mysettings.categories)
36 # This is strictly for use in aux_get() doebuild calls when metadata
37 # is generated by the depend phase. It's safest to use a clone for
38 # this purpose because doebuild makes many changes to the config
39 # instance that is passed in.
40 self.doebuild_settings = config(clone=self.mysettings)
42 self.manifestVerifyLevel = None
43 self.manifestVerifier = None
44 self.manifestCache = {} # {location: [stat, md5]}
45 self.manifestMissingCache = []
47 if "gpg" in self.mysettings.features:
48 self.manifestVerifyLevel = portage.gpg.EXISTS
49 if "strict" in self.mysettings.features:
50 self.manifestVerifyLevel = portage.gpg.MARGINAL
51 self.manifestVerifier = portage.gpg.FileChecker(self.mysettings["PORTAGE_GPG_DIR"], "gentoo.gpg", minimumTrust=self.manifestVerifyLevel)
52 elif "severe" in self.mysettings.features:
53 self.manifestVerifyLevel = portage.gpg.TRUSTED
54 self.manifestVerifier = portage.gpg.FileChecker(self.mysettings["PORTAGE_GPG_DIR"], "gentoo.gpg", requireSignedRing=True, minimumTrust=self.manifestVerifyLevel)
56 self.manifestVerifier = portage.gpg.FileChecker(self.mysettings["PORTAGE_GPG_DIR"], "gentoo.gpg", minimumTrust=self.manifestVerifyLevel)
58 #self.root=settings["PORTDIR"]
59 self.porttree_root = os.path.realpath(porttree_root)
61 self.depcachedir = self.mysettings.depcachedir[:]
63 self.tmpfs = self.mysettings["PORTAGE_TMPFS"]
64 if self.tmpfs and not os.path.exists(self.tmpfs):
66 if self.tmpfs and not os.access(self.tmpfs, os.W_OK):
68 if self.tmpfs and not os.access(self.tmpfs, os.R_OK):
71 self.eclassdb = eclass_cache.cache(self.porttree_root,
72 overlays=self.mysettings["PORTDIR_OVERLAY"].split())
74 self.metadbmodule = self.mysettings.load_best_module("portdbapi.metadbmodule")
76 #if the portdbapi is "frozen", then we assume that we can cache everything (that no updates to it are happening)
80 self.porttrees = [self.porttree_root] + \
81 [os.path.realpath(t) for t in self.mysettings["PORTDIR_OVERLAY"].split()]
83 for path in self.porttrees:
84 repo_name_path = os.path.join(path, REPO_NAME_LOC)
86 repo_name = open(repo_name_path, 'r').readline().strip()
87 self.treemap[repo_name] = path
88 except (OSError,IOError):
91 self.auxdbmodule = self.mysettings.load_best_module("portdbapi.auxdbmodule")
93 self._init_cache_dirs()
94 # XXX: REMOVE THIS ONCE UNUSED_0 IS YANKED FROM auxdbkeys
96 filtered_auxdbkeys = filter(lambda x: not x.startswith("UNUSED_0"), auxdbkeys)
98 from portage.cache import metadata_overlay, volatile
99 for x in self.porttrees:
100 db_ro = self.auxdbmodule(self.depcachedir, x,
101 filtered_auxdbkeys, gid=portage_gid, readonly=True)
102 self.auxdb[x] = metadata_overlay.database(
103 self.depcachedir, x, filtered_auxdbkeys,
104 gid=portage_gid, db_rw=volatile.database,
107 for x in self.porttrees:
108 # location, label, auxdbkeys
109 self.auxdb[x] = self.auxdbmodule(
110 self.depcachedir, x, filtered_auxdbkeys, gid=portage_gid)
111 # Selectively cache metadata in order to optimize dep matching.
112 self._aux_cache_keys = set(["EAPI", "KEYWORDS", "SLOT"])
115 def _init_cache_dirs(self):
116 """Create /var/cache/edb/dep and adjust permissions for the portage
124 for mydir in (self.depcachedir,):
125 if ensure_dirs(mydir, gid=portage_gid, mode=dirmode, mask=modemask):
126 writemsg("Adjusting permissions recursively: '%s'\n" % mydir,
129 raise # bail out on the first error that occurs during recursion
130 if not apply_recursive_permissions(mydir,
131 gid=portage_gid, dirmode=dirmode, dirmask=modemask,
132 filemode=filemode, filemask=modemask, onerror=onerror):
133 raise OperationNotPermitted(
134 "Failed to apply recursive permissions for the portage group.")
135 except PortageException, e:
138 def close_caches(self):
139 for x in self.auxdb.keys():
143 def flush_cache(self):
144 for x in self.auxdb.values():
147 def finddigest(self,mycpv):
149 mydig = self.findname2(mycpv)[0]
152 mydigs = mydig.split("/")[:-1]
153 mydig = "/".join(mydigs)
154 mysplit = mycpv.split("/")
157 return mydig+"/files/digest-"+mysplit[-1]
159 def findname(self,mycpv):
160 return self.findname2(mycpv)[0]
162 def getRepositoryPath(self, repository_id):
164 This function is required for GLEP 42 compliance; given a valid repository ID
165 it must return a path to the repository
166 TreeMap = { id:path }
168 if repository_id in self.treemap:
169 return self.treemap[repository_id]
172 def getRepositories(self):
174 This function is required for GLEP 42 compliance; it will return a list of
178 return [k for k in self.treemap.keys() if k]
180 def findname2(self, mycpv, mytree=None):
182 Returns the location of the CPV, and what overlay it was in.
183 Searches overlays first, then PORTDIR; this allows us to return the first
184 matching file. As opposed to starting in portdir and then doing overlays
185 second, we would have to exhaustively search the overlays until we found
190 mysplit = mycpv.split("/")
191 psplit = pkgsplit(mysplit[1])
196 mytrees = self.porttrees[:]
200 file=x+"/"+mysplit[0]+"/"+psplit[0]+"/"+mysplit[1]+".ebuild"
201 if os.access(file, os.R_OK):
205 def aux_get(self, mycpv, mylist, mytree=None):
206 "stub code for returning auxilliary db information, such as SLOT, DEPEND, etc."
207 'input: "sys-apps/foo-1.0",["SLOT","DEPEND","HOMEPAGE"]'
208 'return: ["0",">=sys-libs/bar-1.0","http://www.foo.com"] or raise KeyError if error'
210 if not mytree and not set(mylist).difference(self._aux_cache_keys):
211 aux_cache = self._aux_cache.get(mycpv)
212 if aux_cache is not None:
213 return [aux_cache[x] for x in mylist]
215 global auxdbkeys, auxdbkeylen
216 cat,pkg = mycpv.split("/", 1)
218 myebuild, mylocation = self.findname2(mycpv, mytree)
221 writemsg("!!! aux_get(): ebuild path for '%(cpv)s' not specified:\n" % {"cpv":mycpv},
223 writemsg("!!! %s\n" % myebuild, noiselevel=1)
224 raise KeyError(mycpv)
226 myManifestPath = "/".join(myebuild.split("/")[:-1])+"/Manifest"
227 if "gpg" in self.mysettings.features:
229 mys = portage.gpg.fileStats(myManifestPath)
230 if (myManifestPath in self.manifestCache) and \
231 (self.manifestCache[myManifestPath] == mys):
233 elif self.manifestVerifier:
234 if not self.manifestVerifier.verify(myManifestPath):
235 # Verification failed the desired level.
236 raise UntrustedSignature, "Untrusted Manifest: %(manifest)s" % {"manifest":myManifestPath}
238 if ("severe" in self.mysettings.features) and \
239 (mys != portage.gpg.fileStats(myManifestPath)):
240 raise SecurityViolation, "Manifest changed: %(manifest)s" % {"manifest":myManifestPath}
242 except InvalidSignature, e:
243 if ("strict" in self.mysettings.features) or \
244 ("severe" in self.mysettings.features):
246 writemsg("!!! INVALID MANIFEST SIGNATURE DETECTED: %(manifest)s\n" % {"manifest":myManifestPath})
247 except MissingSignature, e:
248 if ("severe" in self.mysettings.features):
250 if ("strict" in self.mysettings.features):
251 if myManifestPath not in self.manifestMissingCache:
252 writemsg("!!! WARNING: Missing signature in: %(manifest)s\n" % {"manifest":myManifestPath})
253 self.manifestMissingCache.insert(0,myManifestPath)
254 except (OSError, FileNotFound), e:
255 if ("strict" in self.mysettings.features) or \
256 ("severe" in self.mysettings.features):
257 raise SecurityViolation, "Error in verification of signatures: %(errormsg)s" % {"errormsg":str(e)}
258 writemsg("!!! Manifest is missing or inaccessable: %(manifest)s\n" % {"manifest":myManifestPath},
262 if os.access(myebuild, os.R_OK):
263 emtime = os.stat(myebuild)[stat.ST_MTIME]
265 writemsg("!!! aux_get(): ebuild for '%(cpv)s' does not exist at:\n" % {"cpv":mycpv},
267 writemsg("!!! %s\n" % myebuild,
272 mydata = self.auxdb[mylocation][mycpv]
273 if emtime != long(mydata.get("_mtime_", 0)):
275 elif len(mydata.get("_eclasses_", [])) > 0:
276 doregen = not self.eclassdb.is_eclass_data_valid(mydata["_eclasses_"])
285 del self.auxdb[mylocation][mycpv]
289 writemsg("auxdb is valid: "+str(not doregen)+" "+str(pkg)+"\n", 2)
292 writemsg("doregen: %s %s\n" % (doregen, mycpv), 2)
293 writemsg("Generating cache entry(0) for: "+str(myebuild)+"\n", 1)
295 self.doebuild_settings.reset()
297 myret = doebuild(myebuild, "depend",
298 self.doebuild_settings["ROOT"], self.doebuild_settings,
299 dbkey=mydata, tree="porttree", mydbapi=self)
300 if myret != os.EX_OK:
301 raise KeyError(mycpv)
303 if "EAPI" not in mydata or not mydata["EAPI"].strip():
306 if not eapi_is_supported(mydata["EAPI"]):
307 # if newer version, wipe everything and negate eapi
308 eapi = mydata["EAPI"]
310 map(lambda x: mydata.setdefault(x, ""), auxdbkeys)
311 mydata["EAPI"] = "-"+eapi
313 if mydata.get("INHERITED", False):
314 mydata["_eclasses_"] = self.eclassdb.get_eclass_data(mydata["INHERITED"].split())
316 mydata["_eclasses_"] = {}
318 del mydata["INHERITED"]
320 mydata["_mtime_"] = emtime
322 self.auxdb[mylocation][mycpv] = mydata
324 if not mydata.setdefault("EAPI", "0"):
327 #finally, we look at our internal cache entry and return the requested data.
331 returnme.append(' '.join(mydata.get("_eclasses_", {}).keys()))
333 returnme.append(mydata.get(x,""))
337 for x in self._aux_cache_keys:
338 aux_cache[x] = mydata.get(x, "")
339 self._aux_cache[mycpv] = aux_cache
343 def getfetchlist(self, mypkg, useflags=None, mysettings=None, all=0, mytree=None):
344 if mysettings is None:
345 mysettings = self.mysettings
347 myuris = self.aux_get(mypkg, ["SRC_URI"], mytree=mytree)[0]
349 print red("getfetchlist():")+" aux_get() error reading "+mypkg+"; aborting."
353 useflags = mysettings["USE"].split()
355 myurilist = paren_reduce(myuris)
356 myurilist = use_reduce(myurilist, uselist=useflags, matchall=all)
357 newuris = flatten(myurilist)
361 mya = os.path.basename(x)
362 if not mya in myfiles:
364 return [newuris, myfiles]
366 def getfetchsizes(self, mypkg, useflags=None, debug=0):
367 # returns a filename:size dictionnary of remaining downloads
368 myebuild = self.findname(mypkg)
369 pkgdir = os.path.dirname(myebuild)
370 mf = Manifest(pkgdir, self.mysettings["DISTDIR"])
371 checksums = mf.getDigests()
374 print "[empty/missing/bad digest]: "+mypkg
378 myuris, myfiles = self.getfetchlist(mypkg,all=1)
380 myuris, myfiles = self.getfetchlist(mypkg,useflags=useflags)
381 #XXX: maybe this should be improved: take partial downloads
382 # into account? check checksums?
383 for myfile in myfiles:
384 if myfile not in checksums:
386 writemsg("[bad digest]: missing %s for %s\n" % (myfile, mypkg))
388 file_path = os.path.join(self.mysettings["DISTDIR"], myfile)
391 mystat = os.stat(file_path)
397 existing_size = mystat.st_size
398 remaining_size = int(checksums[myfile]["size"]) - existing_size
399 if remaining_size > 0:
400 # Assume the download is resumable.
401 filesdict[myfile] = remaining_size
402 elif remaining_size < 0:
403 # The existing file is too large and therefore corrupt.
404 filesdict[myfile] = int(checksums[myfile]["size"])
407 def fetch_check(self, mypkg, useflags=None, mysettings=None, all=False):
410 useflags = mysettings["USE"].split()
411 myuri, myfiles = self.getfetchlist(mypkg, useflags=useflags, mysettings=mysettings, all=all)
412 myebuild = self.findname(mypkg)
413 pkgdir = os.path.dirname(myebuild)
414 mf = Manifest(pkgdir, self.mysettings["DISTDIR"])
415 mysums = mf.getDigests()
419 if not mysums or x not in mysums:
421 reason = "digest missing"
424 ok, reason = portage.checksum.verify_all(
425 os.path.join(self.mysettings["DISTDIR"], x), mysums[x])
426 except FileNotFound, e:
428 reason = "File Not Found: '%s'" % str(e)
435 def getsize(self, mypkg, useflags=None, debug=0):
436 # returns the total size of remaining downloads
438 # we use getfetchsizes() now, so this function would be obsoleted
440 filesdict = self.getfetchsizes(mypkg, useflags=useflags, debug=debug)
441 if filesdict is None:
442 return "[empty/missing/bad digest]"
444 for myfile in filesdict.keys():
445 mysum += filesdict[myfile]
448 def cpv_exists(self, mykey):
449 "Tells us whether an actual ebuild exists on disk (no masking)"
450 cps2 = mykey.split("/")
451 cps = catpkgsplit(mykey, silent=0)
455 if self.findname(cps[0] + "/" + cps2[1]):
461 "returns a list of all keys in our tree"
463 for x in self.mysettings.categories:
464 for oroot in self.porttrees:
465 for y in listdir(oroot+"/"+x, EmptyOnError=1, ignorecvs=1, dirsonly=1):
471 def p_list(self,mycp):
473 for oroot in self.porttrees:
474 for x in listdir(oroot+"/"+mycp,EmptyOnError=1,ignorecvs=1):
475 if x[-7:]==".ebuild":
479 def cp_list(self, mycp, use_cache=1, mytree=None):
480 mysplit = mycp.split("/")
481 invalid_category = mysplit[0] not in self._categories
486 mytrees = self.porttrees
487 for oroot in mytrees:
488 for x in listdir(oroot+"/"+mycp, EmptyOnError=1, ignorecvs=1):
489 if x.endswith(".ebuild"):
493 writemsg("\nInvalid ebuild name: %s\n" % \
494 os.path.join(oroot, mycp, x), noiselevel=-1)
496 d[mysplit[0]+"/"+pf] = None
497 if invalid_category and d:
498 writemsg(("\n!!! '%s' has a category that is not listed in " + \
499 "/etc/portage/categories\n") % mycp, noiselevel=-1)
504 for x in ["list-visible", "bestmatch-visible", "match-visible", "match-all"]:
512 def xmatch(self,level,origdep,mydep=None,mykey=None,mylist=None):
513 "caching match function; very trick stuff"
514 #if no updates are being made to the tree, we can consult our xcache...
517 return self.xcache[level][origdep][:]
522 #this stuff only runs on first call of xmatch()
523 #create mydep, mykey from origdep
524 mydep = dep_expand(origdep, mydb=self, settings=self.mysettings)
525 mykey = dep_getkey(mydep)
527 if level == "list-visible":
528 #a list of all visible packages, not called directly (just by xmatch())
529 #myval = self.visible(self.cp_list(mykey))
531 myval = self.gvisible(self.visible(self.cp_list(mykey)))
532 elif level == "bestmatch-visible":
533 #dep match -- best match of all visible packages
534 #get all visible matches (from xmatch()), then choose the best one
536 myval = best(self.xmatch("match-visible", None, mydep=mydep, mykey=mykey))
537 elif level == "bestmatch-list":
538 #dep match -- find best match but restrict search to sublist
539 #no point in calling xmatch again since we're not caching list deps
541 myval = best(match_from_list(mydep, mylist))
542 elif level == "match-list":
543 #dep match -- find all matches but restrict search to sublist (used in 2nd half of visible())
545 myval = match_from_list(mydep, mylist)
546 elif level == "match-visible":
547 #dep match -- find all visible matches
548 #get all visible packages, then get the matching ones
550 myval = match_from_list(mydep,
551 self.xmatch("list-visible", mykey, mydep=mykey, mykey=mykey))
552 elif level == "match-all":
553 #match *all* visible *and* masked packages
555 myval = match_from_list(mydep, self.cp_list(mykey))
557 print "ERROR: xmatch doesn't handle", level, "query!"
559 myslot = dep_getslot(mydep)
560 if myslot is not None:
564 if self.aux_get(cpv, ["SLOT"])[0] == myslot:
565 slotmatches.append(cpv)
567 pass # ebuild masked by corruption
569 if self.frozen and (level not in ["match-list", "bestmatch-list"]):
570 self.xcache[level][mydep] = myval
571 if origdep and origdep != mydep:
572 self.xcache[level][origdep] = myval
575 def match(self, mydep, use_cache=1):
576 return self.xmatch("match-visible", mydep)
578 def visible(self, mylist):
579 """two functions in one. Accepts a list of cpv values and uses the package.mask *and*
580 packages file to remove invisible entries, returning remaining items. This function assumes
581 that all entries in mylist have the same category and package name."""
582 if (mylist is None) or (len(mylist) == 0):
585 #first, we mask out packages in the package.mask file
587 cpv = catpkgsplit(mykey)
590 print "visible(): invalid cat/pkg-v:", mykey
592 mycp = cpv[0] + "/" + cpv[1]
593 maskdict = self.mysettings.pmaskdict
594 unmaskdict = self.mysettings.punmaskdict
595 if maskdict.has_key(mycp):
596 for x in maskdict[mycp]:
597 mymatches = self.xmatch("match-all", x)
598 if mymatches is None:
599 #error in package.mask file; print warning and continue:
600 print "visible(): package.mask entry \"" + x + "\" is invalid, ignoring..."
604 if unmaskdict.has_key(mycp):
605 for z in unmaskdict[mycp]:
606 mymatches_unmask = self.xmatch("match-all",z)
607 if y in mymatches_unmask:
616 revmaskdict = self.mysettings.prevmaskdict
617 if revmaskdict.has_key(mycp):
618 for x in revmaskdict[mycp]:
619 #important: only match against the still-unmasked entries...
620 #notice how we pass "newlist" to the xmatch() call below....
621 #Without this, ~ deps in the packages files are broken.
622 mymatches = self.xmatch("match-list", x, mylist=newlist)
623 if mymatches is None:
624 #error in packages file; print warning and continue:
625 print "emerge: visible(): profile packages entry \""+x+"\" is invalid, ignoring..."
628 while pos < len(newlist):
629 if newlist[pos] not in mymatches:
635 def gvisible(self,mylist):
636 "strip out group-masked (not in current group) entries"
642 accept_keywords = self.mysettings["ACCEPT_KEYWORDS"].split()
643 pkgdict = self.mysettings.pkeywordsdict
646 keys, eapi = self.aux_get(mycpv, ["KEYWORDS", "EAPI"])
649 except PortageException, e:
650 writemsg("!!! Error: aux_get('%s', ['KEYWORDS', 'EAPI'])\n" % \
651 mycpv, noiselevel=-1)
652 writemsg("!!! %s\n" % str(e), noiselevel=-1)
655 mygroups = keys.split()
656 # Repoman may modify this attribute as necessary.
657 pgroups = accept_keywords[:]
659 cp = dep_getkey(mycpv)
660 if pkgdict.has_key(cp):
661 matches = match_to_list(mycpv, pkgdict[cp].keys())
663 pgroups.extend(pkgdict[cp][atom])
667 # The -* special case should be removed once the tree
668 # is clean of KEYWORDS=-* crap
669 if x != "-*" and x.startswith("-"):
671 inc_pgroups.remove(x[1:])
674 if x not in inc_pgroups:
675 inc_pgroups.append(x)
676 pgroups = inc_pgroups
682 writemsg("--- WARNING: Package '%s' uses '*' keyword.\n" % mycpv,
693 if not match and ((hastesting and "~*" in pgroups) or (hasstable and "*" in pgroups) or "**" in pgroups):
695 if match and eapi_is_supported(eapi):
696 newlist.append(mycpv)
700 def close_portdbapi_caches():
701 for i in portdbapi.portdbapi_instances:
705 class portagetree(object):
706 def __init__(self, root="/", virtual=None, clone=None, settings=None):
708 Constructor for a PortageTree
710 @param root: ${ROOT}, defaults to '/', see make.conf(5)
711 @type root: String/Path
712 @param virtual: UNUSED
713 @type virtual: No Idea
714 @param clone: Set this if you want a copy of Clone
715 @type clone: Existing portagetree Instance
716 @param settings: Portage Configuration object (portage.settings)
717 @type settings: Instance of portage.config
721 self.root = clone.root
722 self.portroot = clone.portroot
723 self.pkglines = clone.pkglines
727 from portage import settings
728 self.settings = settings
729 self.portroot = settings["PORTDIR"]
730 self.virtual = virtual
731 self.dbapi = portdbapi(
732 settings["PORTDIR"], mysettings=settings)
734 def dep_bestmatch(self,mydep):
735 "compatibility method"
736 mymatch = self.dbapi.xmatch("bestmatch-visible",mydep)
741 def dep_match(self,mydep):
742 "compatibility method"
743 mymatch = self.dbapi.xmatch("match-visible",mydep)
748 def exists_specific(self,cpv):
749 return self.dbapi.cpv_exists(cpv)
751 def getallnodes(self):
752 """new behavior: these are all *unmasked* nodes. There may or may not be available
753 masked package for nodes in this nodes list."""
754 return self.dbapi.cp_all()
756 def getname(self, pkgname):
757 "returns file location for this particular package (DEPRECATED)"
760 mysplit = pkgname.split("/")
761 psplit = pkgsplit(mysplit[1])
762 return "/".join([self.portroot, mysplit[0], psplit[0], mysplit[1]])+".ebuild"
764 def resolve_specific(self, myspec):
765 cps = catpkgsplit(myspec)
768 mykey = key_expand(cps[0]+"/"+cps[1], mydb=self.dbapi,
769 settings=self.settings)
770 mykey = mykey + "-" + cps[2]
772 mykey = mykey + "-" + cps[3]
775 def depcheck(self, mycheck, use="yes", myusesplit=None):
776 return dep_check(mycheck, self.dbapi, use=use, myuse=myusesplit)
778 def getslot(self,mycatpkg):
779 "Get a slot for a catpkg; assume it exists."
782 myslot = self.dbapi.aux_get(mycatpkg, ["SLOT"])[0]
783 except SystemExit, e: