From 315956fae84b5c56cfc7d46481a53dfa9beb8ccf Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Tue, 18 Jul 2006 23:40:59 +0000 Subject: [PATCH] Do file locking on PORTAGE_BUILDDIR and use a finally block to ensure that the lock is properly released. This fixes bug #140971. svn path=/main/trunk/; revision=3920 --- pym/portage.py | 419 ++++++++++++++++++++++++++----------------------- 1 file changed, 225 insertions(+), 194 deletions(-) diff --git a/pym/portage.py b/pym/portage.py index 786f89cd6..7fcee29c8 100644 --- a/pym/portage.py +++ b/pym/portage.py @@ -2734,17 +2734,20 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, return 1 logfile=None - # Build directory creation isn't required for any of these. - if mydo not in ["fetch","digest","manifest"]: - mystatus = prepare_build_dirs(myroot, mysettings, cleanup) - if mystatus: - return mystatus - + builddir_lock = None + try: + # Build directory creation isn't required for any of these. + if mydo not in ["fetch","digest","manifest"]: + builddir_lock = portage_locks.lockdir( + mysettings["PORTAGE_BUILDDIR"]) + mystatus = prepare_build_dirs(myroot, mysettings, cleanup) + if mystatus: + return mystatus if mydo == "unmerge": return unmerge(mysettings["CATEGORY"], mysettings["PF"], myroot, mysettings, vartree=vartree) - if "PORT_LOGDIR" in mysettings: + if "PORT_LOGDIR" in mysettings and "PORTAGE_BUILDDIR" in mysettings: logid_path = os.path.join(mysettings["PORTAGE_BUILDDIR"], ".logid") if not os.path.exists(logid_path): f = open(logid_path, "w") @@ -2752,212 +2755,240 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, del f logid_time = time.strftime("%Y%m%d-%H%M%S", time.gmtime(os.stat(logid_path).st_mtime)) - logfile = os.path.join(mysettings["PORT_LOGDIR"], "%s:%s:%s.log" %\ + logfile = os.path.join( + mysettings["PORT_LOGDIR"], "%s:%s:%s.log" % \ (mysettings["CATEGORY"], mysettings["PF"], logid_time)) mysettings["PORTAGE_LOG_FILE"] = logfile del logid_path, logid_time - # if any of these are being called, handle them -- running them out of the sandbox -- and stop now. - if mydo in ["clean","cleanrm"]: - return spawn(EBUILD_SH_BINARY+" clean",mysettings,debug=debug,free=1,logfile=None) - elif mydo in ["help","setup"]: - return spawn(EBUILD_SH_BINARY+" "+mydo,mysettings,debug=debug,free=1,logfile=logfile) - elif mydo == "preinst": - mysettings.load_infodir(mysettings["O"]) - if mysettings.has_key("EMERGE_FROM") and "binary" == mysettings["EMERGE_FROM"]: - mysettings["IMAGE"] = os.path.join(mysettings["PKG_TMPDIR"], mysettings["PF"], "bin") + # if any of these are being called, handle them -- running them out of + # the sandbox -- and stop now. + if mydo in ["clean","cleanrm"]: + return spawn(EBUILD_SH_BINARY + " clean", mysettings, + debug=debug, free=1, logfile=None) + elif mydo in ["help","setup"]: + return spawn(EBUILD_SH_BINARY + " " + mydo, mysettings, + debug=debug, free=1, logfile=logfile) + elif mydo == "preinst": + mysettings.load_infodir(mysettings["O"]) + if mysettings.get("EMERGE_FROM", None) == "binary": + mysettings["IMAGE"] = os.path.join( + mysettings["PKG_TMPDIR"], mysettings["PF"], "bin") + else: + mysettings["IMAGE"] = mysettings["D"] + phase_retval = spawn(" ".join((EBUILD_SH_BINARY, mydo)), + mysettings, debug=debug, free=1, logfile=logfile) + if phase_retval == os.EX_OK: + # Post phase logic and tasks that have been factored out of + # ebuild.sh. + myargs = [MISC_SH_BINARY, "preinst_mask", "preinst_sfperms", + "preinst_selinux_labels", "preinst_suid_scan"] + phase_retval = spawn(" ".join(myargs), + mysettings, debug=debug, free=1, logfile=logfile) + if phase_retval != os.EX_OK: + writemsg("!!! post preinst failed; exiting.\n", + noiselevel=-1) + del mysettings["IMAGE"] + return phase_retval + elif mydo in ["prerm","postrm","postinst","config"]: + mysettings.load_infodir(mysettings["O"]) + return spawn(EBUILD_SH_BINARY + " " + mydo, + mysettings, debug=debug, free=1, logfile=logfile) + + mycpv = "/".join((mysettings["CATEGORY"], mysettings["PF"])) + + newuris, alist = mydbapi.getfetchlist(mycpv, mysettings=mysettings) + alluris, aalist = mydbapi.getfetchlist( + mycpv, mysettings=mysettings, all=True) + mysettings["A"] = " ".join(alist) + mysettings["AA"] = " ".join(aalist) + if ("mirror" in features) or fetchall: + fetchme = alluris[:] + checkme = aalist[:] + elif mydo == "digest": + fetchme = alluris[:] + checkme = aalist[:] + # Skip files that we already have digests for. + mf = Manifest(mysettings["O"], mysettings["DISTDIR"]) + mydigests = mf.getTypeDigests("DIST") + for filename, hashes in mydigests.iteritems(): + if len(hashes) == len(mf.hashes): + while True: + try: + i = checkme.index(filename) # raises ValueError + del fetchme[i] + del checkme[i] + except ValueError: + break + del filename, hashes else: - mysettings["IMAGE"] = mysettings["D"] - phase_retval = spawn(" ".join((EBUILD_SH_BINARY, mydo)), mysettings, debug=debug, free=1, logfile=logfile) - if phase_retval == os.EX_OK: - # Post phase logic and tasks that have been factored out of ebuild.sh. - myargs = [MISC_SH_BINARY, "preinst_mask", "preinst_sfperms", - "preinst_selinux_labels", "preinst_suid_scan"] - phase_retval = spawn(" ".join(myargs), mysettings, debug=debug, free=1, logfile=logfile) - if phase_retval != os.EX_OK: - writemsg("!!! post preinst failed; exiting.\n", noiselevel=-1) - del mysettings["IMAGE"] - return phase_retval - elif mydo in ["prerm","postrm","postinst","config"]: - mysettings.load_infodir(mysettings["O"]) - return spawn(EBUILD_SH_BINARY+" "+mydo,mysettings,debug=debug,free=1,logfile=logfile) - - mycpv = "/".join((mysettings["CATEGORY"], mysettings["PF"])) - - newuris, alist = mydbapi.getfetchlist(mycpv, mysettings=mysettings) - alluris, aalist = mydbapi.getfetchlist( - mycpv, mysettings=mysettings, all=True) - mysettings["A"]=string.join(alist," ") - mysettings["AA"]=string.join(aalist," ") - if ("mirror" in features) or fetchall: - fetchme=alluris[:] - checkme=aalist[:] - elif mydo == "digest": - fetchme = alluris[:] - checkme = aalist[:] - # Skip files that we already have digests for. - mf = Manifest(mysettings["O"], mysettings["DISTDIR"]) - mydigests = mf.getTypeDigests("DIST") - for filename, hashes in mydigests.iteritems(): - if len(hashes) == len(mf.hashes): - while filename in checkme: - i = checkme.index(filename) - del fetchme[i] - del checkme[i] - del filename, hashes - else: - fetchme=newuris[:] - checkme=alist[:] - - # Only try and fetch the files if we are going to need them ... otherwise, - # if user has FEATURES=noauto and they run `ebuild clean unpack compile install`, - # we will try and fetch 4 times :/ - need_distfiles = (mydo in ("digest", "fetch", "unpack") or - mydo != "manifest" and "noauto" not in features) - if need_distfiles and not fetch(fetchme, mysettings, listonly=listonly, fetchonly=fetchonly): - return 1 + fetchme = newuris[:] + checkme = alist[:] + + # Only try and fetch the files if we are going to need them ... + # otherwise, if user has FEATURES=noauto and they run `ebuild clean + # unpack compile install`, we will try and fetch 4 times :/ + need_distfiles = (mydo in ("digest", "fetch", "unpack") or \ + mydo != "manifest" and "noauto" not in features) + if need_distfiles and not fetch( + fetchme, mysettings, listonly=listonly, fetchonly=fetchonly): + return 1 - if mydo=="fetch" and listonly: - return 0 + if mydo == "fetch" and listonly: + return 0 - try: - if mydo == "manifest": - return not digestgen(aalist, mysettings, overwrite=1, - manifestonly=1, myportdb=mydbapi) - elif mydo == "digest": - return not digestgen(aalist, mysettings, overwrite=1, - myportdb=mydbapi) - elif "digest" in mysettings.features: - digestgen(aalist, mysettings, overwrite=0, myportdb=mydbapi) - except portage_exception.PermissionDenied, e: - writemsg("!!! %s\n" % str(e), noiselevel=-1) - if mydo in ("digest", "manifest"): - return 1 + try: + if mydo == "manifest": + return not digestgen(aalist, mysettings, overwrite=1, + manifestonly=1, myportdb=mydbapi) + elif mydo == "digest": + return not digestgen(aalist, mysettings, overwrite=1, + myportdb=mydbapi) + elif "digest" in mysettings.features: + digestgen(aalist, mysettings, overwrite=0, myportdb=mydbapi) + except portage_exception.PermissionDenied, e: + writemsg("!!! %s\n" % str(e), noiselevel=-1) + if mydo in ("digest", "manifest"): + return 1 - # See above comment about fetching only when needed - if not digestcheck(checkme, mysettings, ("strict" in features), - (mydo not in ["digest","fetch","unpack"] and - mysettings["PORTAGE_CALLER"] == "ebuild" and "noauto" in features)): - return 1 + # See above comment about fetching only when needed + if not digestcheck(checkme, mysettings, ("strict" in features), + (mydo not in ["digest","fetch","unpack"] and \ + mysettings.get("PORTAGE_CALLER", None) == "ebuild" and \ + "noauto" in features)): + return 1 - if mydo=="fetch": - return 0 + if mydo == "fetch": + return 0 - # inefficient. improve this logic via making actionmap easily searchable to see if we're in the chain of what - # will be executed, either that or forced N doebuild calls instead of a single set of phase calls. - if (mydo not in ("setup", "clean", "postinst", "preinst", "prerm", "fetch", "digest", "manifest") and - "noauto" not in features) or mydo == "unpack": # remove PORTAGE_ACTUAL_DISTDIR once cvs/svn is supported via SRC_URI - mysettings["PORTAGE_ACTUAL_DISTDIR"] = orig_distdir = mysettings["DISTDIR"] - edpath = mysettings["DISTDIR"] = os.path.join(mysettings["PORTAGE_BUILDDIR"], "distdir") - if os.path.exists(edpath): + if (mydo != "setup" and "noauto" not in features) or mydo == "unpack": + orig_distdir = mysettings["DISTDIR"] + mysettings["PORTAGE_ACTUAL_DISTDIR"] = orig_distdir + edpath = mysettings["DISTDIR"] = \ + os.path.join(mysettings["PORTAGE_BUILDDIR"], "distdir") + if os.path.exists(edpath): + try: + if os.path.isdir(edpath) and not os.path.islink(edpath): + shutil.rmtree(edpath) + else: + os.unlink(edpath) + except OSError: + print "!!! Failed reseting ebuild distdir path, " + edpath + raise + os.mkdir(edpath) + apply_secpass_permissions(edpath, gid=portage_gid, mode=0775) try: - if os.path.isdir(edpath) and not os.path.islink(edpath): - shutil.rmtree(edpath) - else: - os.unlink(edpath) + for file in aalist: + os.symlink(os.path.join(orig_distdir, file), + os.path.join(edpath, file)) except OSError: - print "!!! Failed reseting ebuild distdir path, " + edpath + print "!!! Failed symlinking in '%s' to ebuild distdir" % file raise - os.mkdir(edpath) - apply_secpass_permissions(edpath, gid=portage_gid, mode=0775) - try: - for file in aalist: - os.symlink(os.path.join(orig_distdir, file), os.path.join(edpath, file)) - except OSError: - print "!!! Failed symlinking in '%s' to ebuild distdir" % file - raise - #initial dep checks complete; time to process main commands - - nosandbox=(("userpriv" in features) and ("usersandbox" not in features) and \ - ("userpriv" not in mysettings["RESTRICT"]) and ("nouserpriv" not in mysettings["RESTRICT"])) - if nosandbox and ("userpriv" not in features or "userpriv" in mysettings["RESTRICT"] or \ - "nouserpriv" in mysettings["RESTRICT"]): - nosandbox = ("sandbox" not in features and "usersandbox" not in features) - - sesandbox = mysettings.selinux_enabled() and "sesandbox" in mysettings.features - ebuild_sh = EBUILD_SH_BINARY + " %s" - misc_sh = MISC_SH_BINARY + " dyn_%s" - - # args are for the to spawn function - actionmap = { - "depend": {"cmd":ebuild_sh, "args":{"droppriv":1, "free":0, "sesandbox":0}}, - "setup": {"cmd":ebuild_sh, "args":{"droppriv":0, "free":1, "sesandbox":0}}, - "unpack": {"cmd":ebuild_sh, "args":{"droppriv":1, "free":0, "sesandbox":sesandbox}}, - "compile":{"cmd":ebuild_sh, "args":{"droppriv":1, "free":nosandbox, "sesandbox":sesandbox}}, - "test": {"cmd":ebuild_sh, "args":{"droppriv":1, "free":nosandbox, "sesandbox":sesandbox}}, - "install":{"cmd":ebuild_sh, "args":{"droppriv":0, "free":0, "sesandbox":sesandbox}}, - "rpm": {"cmd":misc_sh, "args":{"droppriv":0, "free":0, "sesandbox":0}}, - "package":{"cmd":misc_sh, "args":{"droppriv":0, "free":0, "sesandbox":0}}, - } - - # merge the deps in so we have again a 'full' actionmap - # be glad when this can die. - for x in actionmap.keys(): - if len(actionmap_deps.get(x, [])): - actionmap[x]["dep"] = ' '.join(actionmap_deps[x]) - - if mydo in actionmap.keys(): - if mydo=="package": - for x in ["","/"+mysettings["CATEGORY"],"/All"]: - if not os.path.exists(mysettings["PKGDIR"]+x): - os.makedirs(mysettings["PKGDIR"]+x) - # REBUILD CODE FOR TBZ2 --- XXXX - retval = spawnebuild(mydo, actionmap, mysettings, debug, logfile=logfile) - elif mydo=="qmerge": - #check to ensure install was run. this *only* pops up when users forget it and are using ebuild - if not os.path.exists(mysettings["PORTAGE_BUILDDIR"]+"/.installed"): - print "!!! mydo=qmerge, but install phase hasn't been ran" - sys.exit(1) - # qmerge is a special phase that implies noclean. - if "noclean" not in mysettings.features: - mysettings.features.append("noclean") - #qmerge is specifically not supposed to do a runtime dep check - retval = merge(mysettings["CATEGORY"], mysettings["PF"], mysettings["D"], - os.path.join(mysettings["PORTAGE_BUILDDIR"], "build-info"), myroot, - mysettings, myebuild=mysettings["EBUILD"], mytree=tree, - mydbapi=mydbapi, vartree=vartree, prev_mtimes=prev_mtimes) - elif mydo=="merge": - retval = spawnebuild("install", actionmap, mysettings, debug, - alwaysdep=1, logfile=logfile) - if retval == os.EX_OK: - retval = merge(mysettings["CATEGORY"], mysettings["PF"], - mysettings["D"], os.path.join(mysettings["PORTAGE_BUILDDIR"], - "build-info"), myroot, mysettings, - myebuild=mysettings["EBUILD"], mytree=tree, mydbapi=mydbapi, - vartree=vartree, prev_mtimes=prev_mtimes) - else: - print "!!! Unknown mydo:",mydo - sys.exit(1) + #initial dep checks complete; time to process main commands + + nosandbox = (("userpriv" in features) and \ + ("usersandbox" not in features) and \ + ("userpriv" not in mysettings["RESTRICT"]) and \ + ("nouserpriv" not in mysettings["RESTRICT"])) + if nosandbox and ("userpriv" not in features or \ + "userpriv" in mysettings["RESTRICT"] or \ + "nouserpriv" in mysettings["RESTRICT"]): + nosandbox = ("sandbox" not in features and \ + "usersandbox" not in features) + + sesandbox = mysettings.selinux_enabled() and \ + "sesandbox" in mysettings.features + ebuild_sh = EBUILD_SH_BINARY + " %s" + misc_sh = MISC_SH_BINARY + " dyn_%s" + + # args are for the to spawn function + actionmap = { +"depend": {"cmd":ebuild_sh, "args":{"droppriv":1, "free":0, "sesandbox":0}}, +"setup": {"cmd":ebuild_sh, "args":{"droppriv":0, "free":1, "sesandbox":0}}, +"unpack": {"cmd":ebuild_sh, "args":{"droppriv":1, "free":0, "sesandbox":sesandbox}}, +"compile":{"cmd":ebuild_sh, "args":{"droppriv":1, "free":nosandbox, "sesandbox":sesandbox}}, +"test": {"cmd":ebuild_sh, "args":{"droppriv":1, "free":nosandbox, "sesandbox":sesandbox}}, +"install":{"cmd":ebuild_sh, "args":{"droppriv":0, "free":0, "sesandbox":sesandbox}}, +"rpm": {"cmd":misc_sh, "args":{"droppriv":0, "free":0, "sesandbox":0}}, +"package":{"cmd":misc_sh, "args":{"droppriv":0, "free":0, "sesandbox":0}}, + } - # Make sure that DISTDIR is restored to it's normal value before we return! - if "PORTAGE_ACTUAL_DISTDIR" in mysettings: - mysettings["DISTDIR"] = mysettings["PORTAGE_ACTUAL_DISTDIR"] - del mysettings["PORTAGE_ACTUAL_DISTDIR"] + # merge the deps in so we have again a 'full' actionmap + # be glad when this can die. + for x in actionmap.keys(): + if len(actionmap_deps.get(x, [])): + actionmap[x]["dep"] = ' '.join(actionmap_deps[x]) + + if mydo in actionmap.keys(): + if mydo=="package": + portage_util.ensure_dirs( + os.path.join(mysettings["PKGDIR"], mysettings["CATEGORY"])) + portage_util.ensure_dirs( + os.path.join(mysettings["PKGDIR"], "All")) + retval = spawnebuild(mydo, + actionmap, mysettings, debug, logfile=logfile) + elif mydo=="qmerge": + # check to ensure install was run. this *only* pops up when users + # forget it and are using ebuild + if not os.path.exists( + os.path.join(mysettings["PORTAGE_BUILDDIR"], ".installed")): + writemsg("!!! mydo=qmerge, but install phase hasn't been ran\n", + noiselevel=-1) + return 1 + # qmerge is a special phase that implies noclean. + if "noclean" not in mysettings.features: + mysettings.features.append("noclean") + #qmerge is specifically not supposed to do a runtime dep check + retval = merge( + mysettings["CATEGORY"], mysettings["PF"], mysettings["D"], + os.path.join(mysettings["PORTAGE_BUILDDIR"], "build-info"), + myroot, mysettings, myebuild=mysettings["EBUILD"], mytree=tree, + mydbapi=mydbapi, vartree=vartree, prev_mtimes=prev_mtimes) + elif mydo=="merge": + retval = spawnebuild("install", actionmap, mysettings, debug, + alwaysdep=1, logfile=logfile) + if retval == os.EX_OK: + retval = merge(mysettings["CATEGORY"], mysettings["PF"], + mysettings["D"], os.path.join(mysettings["PORTAGE_BUILDDIR"], + "build-info"), myroot, mysettings, + myebuild=mysettings["EBUILD"], mytree=tree, mydbapi=mydbapi, + vartree=vartree, prev_mtimes=prev_mtimes) + else: + print "!!! Unknown mydo:",mydo + return 1 - if logfile: - try: - if os.stat(logfile).st_size == 0: - os.unlink(logfile) - except OSError: - pass + if retval != os.EX_OK and tree == "porttree": + for i in xrange(len(mydbapi.porttrees)-1): + t = mydbapi.porttrees[i+1] + if myebuild.startswith(t): + # Display the non-cannonical path, in case it's different, to + # prevent confusion. + overlays = mysettings["PORTDIR_OVERLAY"].split() + try: + writemsg("!!! This ebuild is from an overlay: '%s'\n" % \ + overlays[i], noiselevel=-1) + except IndexError: + pass + break + return retval - if retval != os.EX_OK and tree == "porttree": - for i in xrange(len(mydbapi.porttrees)-1): - t = mydbapi.porttrees[i+1] - if myebuild.startswith(t): - # Display the non-cannonical path, in case it's different, to - # prevent confusion. - overlays = mysettings["PORTDIR_OVERLAY"].split() - try: - writemsg("!!! This ebuild is from an overlay: '%s'\n" % \ - overlays[i], noiselevel=-1) - except IndexError: - pass - break + finally: + if builddir_lock: + portage_locks.unlockdir(builddir_lock) - return retval + # Make sure that DISTDIR is restored to it's normal value before we return! + if "PORTAGE_ACTUAL_DISTDIR" in mysettings: + mysettings["DISTDIR"] = mysettings["PORTAGE_ACTUAL_DISTDIR"] + del mysettings["PORTAGE_ACTUAL_DISTDIR"] + + if logfile: + try: + if os.stat(logfile).st_size == 0: + os.unlink(logfile) + except OSError: + pass expandcache={} -- 2.26.2