From 896eadb0ba4fa60a8904c45f206211def762009d Mon Sep 17 00:00:00 2001 From: Kito Date: Wed, 15 Mar 2006 21:52:05 +0000 Subject: [PATCH] merge with trunk rev 2854:2896 svn path=/main/branches/prefix/; revision=2897 --- bin/dopython | 23 -- bin/emerge | 93 +++--- bin/misc-functions.sh | 4 +- pym/cache/util.py | 2 +- pym/portage.py | 618 ++++++++++++++++++--------------------- pym/portage_data.py | 14 +- pym/portage_exception.py | 2 + pym/portage_util.py | 105 +++++-- 8 files changed, 444 insertions(+), 417 deletions(-) delete mode 100755 bin/dopython diff --git a/bin/dopython b/bin/dopython deleted file mode 100755 index cb6b5112e..000000000 --- a/bin/dopython +++ /dev/null @@ -1,23 +0,0 @@ -#!@PYTHON@ -# Copyright 1999-2006 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/dopython,v 1.8 2004/10/04 13:56:50 vapier Exp $ - -import sys -sys.path = ["@PORTAGE_BASE@/pym"]+sys.path - -from sys import * -import portage -import types -mycommand=argv[1]+"(" -x=2 -while (x 0: - myprint=myprint+(" "*(newlp-nc_len(myprint))) - myprint=myprint+"["+darkblue(xs[1]+xs[2])+"] " - if (oldlp-nc_len(myprint)) > 0: - myprint=myprint+" "*(oldlp-nc_len(myprint)) - myprint=myprint+myoldbest - myprint=myprint+darkgreen(" to "+x[1])+" "+verboseadd + if "--quiet" in myopts: + myprint=addl+" "+indent+darkgreen(xs[0]) + myprint=myprint+darkblue(xs[1]+xs[2])+" " + myprint=myprint+myoldbest + myprint=myprint+darkgreen(" to "+x[1]) + else: + myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(xs[0]) + if (newlp-nc_len(myprint)) > 0: + myprint=myprint+(" "*(newlp-nc_len(myprint))) + myprint=myprint+"["+darkblue(xs[1]+xs[2])+"] " + if (oldlp-nc_len(myprint)) > 0: + myprint=myprint+" "*(oldlp-nc_len(myprint)) + myprint=myprint+myoldbest + myprint=myprint+darkgreen(" to "+x[1])+" "+verboseadd else: myprint="["+x[0]+" "+addl+"] "+darkgreen(x[2])+" "+myoldbest+" "+darkgreen("to "+x[1])+" "+verboseadd else: if "--columns" in myopts: - myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(xs[0]) - if (newlp-nc_len(myprint)) > 0: - myprint=myprint+(" "*(newlp-nc_len(myprint))) - myprint=myprint+green(" ["+xs[1]+xs[2]+"] ") - if (oldlp-nc_len(myprint)) > 0: - myprint=myprint+(" "*(oldlp-nc_len(myprint))) - myprint=myprint+myoldbest+" "+verboseadd + if "--quiet" in myopts: + myprint=addl+" "+indent+darkgreen(xs[0]) + myprint=myprint+" "+green(xs[1]+xs[2]) + myprint=myprint+myoldbest + else: + myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(xs[0]) + if (newlp-nc_len(myprint)) > 0: + myprint=myprint+(" "*(newlp-nc_len(myprint))) + myprint=myprint+green(" ["+xs[1]+xs[2]+"] ") + if (oldlp-nc_len(myprint)) > 0: + myprint=myprint+(" "*(oldlp-nc_len(myprint))) + myprint=myprint+myoldbest+" "+verboseadd else: if x[3]=="nomerge": myprint=darkblue("[nomerge ] "+indent+x[2]+" "+myoldbest+" ")+verboseadd @@ -2975,7 +2987,7 @@ elif "info"==myaction: if "--verbose" in myopts: myvars=portage.settings.keys() else: - myvars = ['GENTOO_MIRRORS', 'CONFIG_PROTECT', 'CONFIG_PROTECT_MASK', + myvars = ['DEFAULT_PATH', 'GENTOO_MIRRORS', 'CONFIG_PROTECT', 'CONFIG_PROTECT_MASK', 'PORTDIR', 'DISTDIR', 'PKGDIR', 'PORTAGE_TMPDIR', 'PORTDIR_OVERLAY', 'PREFIX', 'USE', 'CHOST', 'CFLAGS', 'CXXFLAGS', 'ACCEPT_KEYWORDS', 'SYNC', 'FEATURES', 'EMERGE_DEFAULT_OPTS'] @@ -3159,7 +3171,7 @@ else: portage.commit_mtimedb() myresumeopts=portage.mtimedb["resume"]["myopts"][:] - for opt in ("--skipfirst", "--ask", "--verbose"): + for opt in ("--skipfirst", "--ask", "--tree"): while opt in myresumeopts: myresumeopts.remove(opt) @@ -3264,6 +3276,7 @@ else: mydepgraph.merge(portage.mtimedb["resume"]["mergelist"]) else: if "resume" in portage.mtimedb and \ + "mergelist" in portage.mtimedb["resume"] and \ len(portage.mtimedb["resume"]["mergelist"]) > 1: portage.mtimedb["resume_backup"] = portage.mtimedb["resume"] del portage.mtimedb["resume"] diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh index 9cd614491..d3a6e048b 100644 --- a/bin/misc-functions.sh +++ b/bin/misc-functions.sh @@ -120,7 +120,7 @@ install_qa_check() { # are supported at the moment. Keep this list in sync with # http://hardened.gentoo.org/gnu-stack.xml (Arch Status) case ${CTARGET:-${CHOST}} in - i?86*|ia64*|m68k*|powerpc64*|s390*|x86_64*) + i?86*|ia64*|m68k*|s390*|x86_64*) # Allow devs to mark things as ignorable ... e.g. things # that are binary-only and upstream isn't cooperating ... # we allow ebuild authors to set QA_EXECSTACK_arch and @@ -290,7 +290,7 @@ install_mask() { rm -Rf ${root}/${no_inst} >&/dev/null # we also need to handle globs (*.a, *.h, etc) - find "${root}" -name ${no_inst} -exec rm -fR {} \; >/dev/null + find "${root}" -path ${no_inst} -exec rm -fR {} \; >/dev/null done # set everything back the way we found it set +o noglob diff --git a/pym/cache/util.py b/pym/cache/util.py index 26d917af3..7e5e1f771 100644 --- a/pym/cache/util.py +++ b/pym/cache/util.py @@ -16,7 +16,7 @@ def mirror_cache(valid_nodes_iterable, src_cache, trg_cache, eclass_cache=None, noise=verbose_instance dead_nodes = {} - dead_nodes.fromkeys(trg_cache.keys()) + dead_nodes = dict.fromkeys(trg_cache.keys()) count=0 if not trg_cache.autocommits: diff --git a/pym/portage.py b/pym/portage.py index 2196941de..cc9ddf248 100644 --- a/pym/portage.py +++ b/pym/portage.py @@ -12,11 +12,9 @@ VERSION="$Rev$"[6:-2] + "-svn" try: import sys -except SystemExit, e: - raise -except: +except ImportError: print "Failed to import sys! Something is _VERY_ wrong with python." - raise SystemExit, 127 + raise try: import os,string,types,signal,fcntl,errno @@ -33,9 +31,7 @@ try: from time import sleep from random import shuffle from cache.cache_errors import CacheError -except SystemExit, e: - raise -except Exception, e: +except ImportError, e: sys.stderr.write("\n\n") sys.stderr.write("!!! Failed to complete python imports. There are internal modules for\n") sys.stderr.write("!!! python and failure here indicates that you have a problem with python\n") @@ -43,27 +39,14 @@ except Exception, e: sys.stderr.write("!!! You might consider starting python with verbose flags to see what has\n") sys.stderr.write("!!! gone wrong. Here is the information we got for this exception:\n") - sys.stderr.write(" "+str(e)+"\n\n"); - sys.exit(127) -except: - sys.stderr.write("\n\n") - sys.stderr.write("!!! Failed to complete python imports. There are internal modules for\n") - sys.stderr.write("!!! python and failure here indicates that you have a problem with python\n") - sys.stderr.write("!!! itself and thus portage is not able to continue processing.\n\n") - - sys.stderr.write("!!! You might consider starting python with verbose flags to see what has\n") - sys.stderr.write("!!! gone wrong. The exception was non-standard and we were unable to catch it.\n\n") - sys.exit(127) + raise try: # XXX: This should get renamed to bsd_chflags, I think. import chflags bsd_chflags = chflags -except SystemExit, e: - raise -except: - # XXX: This should get renamed to bsd_chflags, I think. +except ImportError: bsd_chflags = None try: @@ -92,8 +75,8 @@ try: portage_uid, portage_gid import portage_util - from portage_util import atomic_ofstream, dump_traceback, getconfig, grabdict, \ - grabdict_package, grabfile, grabfile_package, \ + from portage_util import atomic_ofstream, apply_secpass_permissions, \ + dump_traceback, getconfig, grabdict, grabdict_package, grabfile, grabfile_package, \ map_dictlist_vals, pickle_read, pickle_write, stack_dictlist, stack_dicts, stack_lists, \ unique_array, varexpand, writedict, writemsg, writemsg_stdout, write_atomic import portage_exception @@ -111,9 +94,7 @@ try: # Need these functions directly in portage namespace to not break every external tool in existence from portage_versions import ververify,vercmp,catsplit,catpkgsplit,pkgsplit,pkgcmp -except SystemExit, e: - raise -except Exception, e: +except ImportError, e: sys.stderr.write("\n\n") sys.stderr.write("!!! Failed to complete portage imports. There are internal modules for\n") sys.stderr.write("!!! portage and failure here indicates that you have a problem with your\n") @@ -121,9 +102,8 @@ except Exception, e: sys.stderr.write("!!! portage tree under '/usr/portage/sys-apps/portage/files/' (default).\n") sys.stderr.write("!!! There is a README.RESCUE file that details the steps required to perform\n") sys.stderr.write("!!! a recovery of portage.\n") - sys.stderr.write(" "+str(e)+"\n\n") - sys.exit(127) + raise # =========================================================================== @@ -234,10 +214,8 @@ def cacheddir(my_original_path, ignorecvs, ignorelist, EmptyOnError, followSymli if stat.S_ISDIR(pathstat[stat.ST_MODE]): mtime = pathstat[stat.ST_MTIME] else: - raise Exception - except SystemExit, e: - raise - except: + raise portage_exception.PortageException + except (IOError,OSError,portage_exception.PortageException): if EmptyOnError: return [], [] return None, None @@ -262,9 +240,7 @@ def cacheddir(my_original_path, ignorecvs, ignorelist, EmptyOnError, followSymli ftype.append(2) else: ftype.append(3) - except SystemExit, e: - raise - except: + except (IOError, OSError): ftype.append(3) dircache[mypath] = mtime, list, ftype @@ -1839,25 +1815,22 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks", can_fetch=False else: def distdir_perms(filename): + all_applied = True try: - portage_util.apply_permissions(filename, gid=portage_gid, mode=0775) - except OSError, oe: - if oe.errno == errno.EPERM: - writemsg("!!! Unable to apply group permissions to '%s'. Non-root users may experience issues.\n" - % filename) - else: - raise oe + all_applied = portage_util.apply_secpass_permissions(filename, gid=portage_gid, mode=0775) + except portage_exception.OperationNotPermitted: + all_applied = False + if not all_applied: + writemsg(("!!! Unable to apply group permissions to '%s'." \ + + " Non-root users may experience issues.\n") % filename) distdir_perms(mysettings["DISTDIR"]) if use_locks and locks_in_subdir: distlocks_subdir = os.path.join(mysettings["DISTDIR"], locks_in_subdir) try: distdir_perms(distlocks_subdir) - except OSError, oe: - if oe.errno == errno.ENOENT: - os.mkdir(distlocks_subdir) - distdir_perms(distlocks_subdir) - else: - raise oe + except portage_exception.FileNotFound: + os.mkdir(distlocks_subdir) + distdir_perms(distlocks_subdir) if not os.access(distlocks_subdir, os.W_OK): writemsg("!!! No write access to write to %s. Aborting.\n" % distlocks_subdir) return 0 @@ -2371,22 +2344,12 @@ def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None): retval=spawnebuild(actionmap[mydo]["dep"],actionmap,mysettings,debug,alwaysdep=alwaysdep,logfile=logfile) if retval: return retval - # spawn ebuild.sh or misc-functions.sh as appropriate - if mydo in ["package","rpm"]: - mycommand = MISC_SH_BINARY + " dyn_" + mydo - else: - mycommand = EBUILD_SH_BINARY + " " + mydo - phase_retval = spawn(mycommand, mysettings, debug=debug, - droppriv=actionmap[mydo]["args"][0], - free=actionmap[mydo]["args"][1], - sesandbox=actionmap[mydo]["args"][2], logfile=logfile) + kwargs = actionmap[mydo]["args"] + phase_retval = spawn(actionmap[mydo]["cmd"] % mydo, mysettings, debug=debug, logfile=logfile, **kwargs) if phase_retval == os.EX_OK: if mydo == "install": mycommand = " ".join([MISC_SH_BINARY, "install_qa_check"]) - return spawn(mycommand, mysettings, debug=debug, - droppriv=actionmap[mydo]["args"][0], - free=actionmap[mydo]["args"][1], - sesandbox=actionmap[mydo]["args"][2], logfile=logfile) + return spawn(mycommand, mysettings, debug=debug, logfile=logfile, **kwargs) return phase_retval # chunked out deps for each phase, so that ebuild binary can use it @@ -2406,13 +2369,7 @@ actionmap_deps={ def eapi_is_supported(eapi): return str(eapi).strip() == str(portage_const.EAPI).strip() - -def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,cleanup=0,dbkey=None,use_cache=1,fetchall=0,tree=None): - global db, actionmap_deps - - if not tree: - dump_traceback("Warning: tree not specified to doebuild") - tree = "porttree" +def doebuild_environment(myebuild, mydo, myroot, mysettings, debug, use_cache, tree): ebuild_path = os.path.abspath(myebuild) pkg_dir = os.path.dirname(ebuild_path) @@ -2420,40 +2377,19 @@ def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,clea if mysettings.configdict["pkg"].has_key("CATEGORY"): cat = mysettings.configdict["pkg"]["CATEGORY"] else: - cat = os.path.basename(os.path.normpath(pkg_dir+"/..")) - mypv = os.path.basename(ebuild_path)[:-7] - mycpv = cat+"/"+mypv - + cat = os.path.basename(os.path.normpath(pkg_dir+"/..")) + mypv = os.path.basename(ebuild_path)[:-7] + mycpv = cat+"/"+mypv mysplit=pkgsplit(mypv,silent=0) if mysplit==None: writemsg("!!! Error: PF is null '%s'; exiting.\n" % mypv) return 1 - if mydo != "depend": # XXX: We're doing a little hack here to curtain the gvisible locking # XXX: that creates a deadlock... Really need to isolate that. mysettings.reset(use_cache=use_cache) mysettings.setcpv(mycpv,use_cache=use_cache) - validcommands = ["help","clean","prerm","postrm","preinst","postinst", - "config","setup","depend","fetch","digest", - "unpack","compile","test","install","rpm","qmerge","merge", - "package","unmerge", "manifest"] - - if mydo not in validcommands: - validcommands.sort() - writemsg("!!! doebuild: '%s' is not one of the following valid commands:" % mydo) - for vcount in range(len(validcommands)): - if vcount%6 == 0: - writemsg("\n!!! ") - writemsg(string.ljust(validcommands[vcount], 11)) - writemsg("\n") - return 1 - - if not os.path.exists(myebuild): - writemsg("!!! doebuild: "+str(myebuild)+" not found for "+str(mydo)+"\n") - return 1 - if debug: # Otherwise it overrides emerge's settings. # We have no other way to set debug... debug can't be passed in # due to how it's coded... Don't overwrite this so we can use it. @@ -2513,7 +2449,13 @@ def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,clea mysettings["BUILD_PREFIX"] = mysettings["PORTAGE_TMPDIR"]+"/portage" mysettings["HOME"] = mysettings["BUILD_PREFIX"]+"/homedir" mysettings["PKG_TMPDIR"] = mysettings["PORTAGE_TMPDIR"]+"/binpkgs" - mysettings["PORTAGE_BUILDDIR"] = mysettings["BUILD_PREFIX"]+"/"+mysettings["PF"] + + # Package {pre,post}inst and {pre,post}rm may overlap, so they must have separate + # locations in order to prevent interference. + if mydo in ("unmerge", "prerm", "postrm", "cleanrm"): + mysettings["PORTAGE_BUILDDIR"] = os.path.join(mysettings["PKG_TMPDIR"], mysettings["PF"]) + else: + mysettings["PORTAGE_BUILDDIR"] = os.path.join(mysettings["BUILD_PREFIX"], mysettings["PF"]) mysettings["PORTAGE_BASHRC"] = EBUILD_SH_ENV_FILE @@ -2530,6 +2472,216 @@ def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,clea myso=os.uname()[2] mysettings["KVERS"]=myso[1] +def prepare_build_dirs(myroot, mysettings, cleanup): + + if not os.path.exists(mysettings["BUILD_PREFIX"]): + os.makedirs(mysettings["BUILD_PREFIX"]) + apply_secpass_permissions(mysettings["BUILD_PREFIX"], + uid=portage_uid, gid=portage_gid, mode=00775) + + # Should be ok again to set $T, as sandbox does not depend on it + # XXX Bug. no way in hell this is valid for clean handling. + mysettings["T"]=mysettings["PORTAGE_BUILDDIR"]+"/temp" + if cleanup: + if os.path.exists(mysettings["T"]): + shutil.rmtree(mysettings["T"]) + if not os.path.exists(mysettings["T"]): + os.makedirs(mysettings["T"]) + apply_secpass_permissions(mysettings["T"], + uid=portage_uid, gid=portage_gid, mode=02770) + + logdir = mysettings["T"]+"/logging" + if not os.path.exists(logdir): + os.makedirs(logdir) + apply_secpass_permissions(logdir, + uid=portage_uid, gid=portage_gid, mode=0770) + + try: # XXX: negative RESTRICT + if not (("nouserpriv" in string.split(mysettings["PORTAGE_RESTRICT"])) or \ + ("userpriv" in string.split(mysettings["PORTAGE_RESTRICT"]))): + if ("userpriv" in features) and (portage_uid and portage_gid): + if (secpass==2): + if os.path.exists(mysettings["HOME"]): + # XXX: Potentially bad, but held down by HOME replacement above. + spawn("rm -Rf "+mysettings["HOME"],mysettings, free=1) + if not os.path.exists(mysettings["HOME"]): + os.makedirs(mysettings["HOME"]) + elif ("userpriv" in features): + print "!!! Disabling userpriv from features... Portage UID/GID not valid." + del features[features.index("userpriv")] + except SystemExit, e: + raise + except Exception, e: + print "!!! Couldn't empty HOME:",mysettings["HOME"] + print "!!!",e + + try: + # no reason to check for depend since depend returns above. + for myvar in ("BUILD_PREFIX", "PORTAGE_BUILDDIR"): + if not os.path.exists(mysettings[myvar]): + os.makedirs(mysettings[myvar]) + apply_secpass_permissions(mysettings[myvar], + uid=portage_uid, gid=portage_gid) + except OSError, e: + print "!!! File system problem. (ReadOnly? Out of space?)" + print "!!! Perhaps: rm -Rf",mysettings["BUILD_PREFIX"] + print "!!!",str(e) + return 1 + + try: + if not os.path.exists(mysettings["HOME"]): + os.makedirs(mysettings["HOME"]) + apply_secpass_permissions(mysettings["HOME"], + uid=portage_uid, gid=portage_gid, mode=02770) + except OSError, e: + print "!!! File system problem. (ReadOnly? Out of space?)" + print "!!! Failed to create fake home directory in PORTAGE_BUILDDIR" + print "!!!",str(e) + return 1 + + try: + if ("ccache" in features): + if (not mysettings.has_key("CCACHE_DIR")) or (mysettings["CCACHE_DIR"]==""): + mysettings["CCACHE_DIR"]=mysettings["PORTAGE_TMPDIR"]+"/ccache" + if not os.path.exists(mysettings["CCACHE_DIR"]): + os.makedirs(mysettings["CCACHE_DIR"]) + mystat = os.stat(mysettings["CCACHE_DIR"]) + if ("userpriv" in features): + if mystat[stat.ST_UID] != portage_uid or ((mystat[stat.ST_MODE]&02070)!=02070): + writemsg("* Adjusting permissions on ccache in %s\n" % mysettings["CCACHE_DIR"]) + spawn("chgrp -R "+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) + spawn("chown "+str(portage_uid)+":"+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) + spawn("chmod -R ug+rw "+mysettings["CCACHE_DIR"], mysettings, free=1) + spawn("find "+mysettings["CCACHE_DIR"]+" -type d -exec chmod g+xs \{\} \;", mysettings, free=1) + else: + if mystat[stat.ST_UID] != 0 or ((mystat[stat.ST_MODE]&02070)!=02070): + writemsg("* Adjusting permissions on ccache in %s\n" % mysettings["CCACHE_DIR"]) + spawn("chgrp -R "+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) + spawn("chown 0:"+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) + spawn("chmod -R ug+rw "+mysettings["CCACHE_DIR"], mysettings, free=1) + spawn("find "+mysettings["CCACHE_DIR"]+" -type d -exec chmod g+xs \{\} \;", mysettings, free=1) + except OSError, e: + print "!!! File system problem. (ReadOnly? Out of space?)" + print "!!! Perhaps: rm -Rf",mysettings["BUILD_PREFIX"] + print "!!!",str(e) + return 1 + try: + if "confcache" in features: + if not mysettings.has_key("CONFCACHE_DIR"): + mysettings["CONFCACHE_DIR"] = os.path.join(mysettings["PORTAGE_TMPDIR"], "confcache") + if not os.path.exists(mysettings["CONFCACHE_DIR"]): + if not os.getuid() == 0: + # we're boned. + features.remove("confcache") + mysettings["FEATURES"] = " ".join(features) + else: + os.makedirs(mysettings["CONFCACHE_DIR"], mode=0775) + apply_secpass_permissions(mysettings["CONFCACHE_DIR"], + gid=portage_gid, mode=0775) + else: + apply_secpass_permissions(mysettings["CONFCACHE_DIR"], + gid=portage_gid, mode=0775) + + # check again, since it may have been disabled. + if "confcache" in features: + for x in listdir(mysettings["CONFCACHE_DIR"]): + p = os.path.join(mysettings["CONFCACHE_DIR"], x) + apply_secpass_permissions(p, gid=portage_gid, mode=0660, mask=07000) + except OSError, e: + print "!!! Failed resetting perms on confcachedir %s" % mysettings["CONFCACHE_DIR"] + return 1 + #try: + # mystat=os.stat(mysettings["CCACHE_DIR"]) + # if (mystat[stat.ST_GID]!=portage_gid) or ((mystat[stat.ST_MODE]&02070)!=02070): + # print "*** Adjusting ccache permissions for portage user..." + # os.chown(mysettings["CCACHE_DIR"],portage_uid,portage_gid) + # os.chmod(mysettings["CCACHE_DIR"],02770) + # spawn("chown -R "+str(portage_uid)+":"+str(portage_gid)+" "+mysettings["CCACHE_DIR"],mysettings, free=1) + # spawn("chmod -R g+rw "+mysettings["CCACHE_DIR"],mysettings, free=1) + #except SystemExit, e: + # raise + #except: + # pass + + if "distcc" in features: + try: + if (not mysettings.has_key("DISTCC_DIR")) or (mysettings["DISTCC_DIR"]==""): + mysettings["DISTCC_DIR"]=mysettings["PORTAGE_TMPDIR"]+"/portage/.distcc" + if not os.path.exists(mysettings["DISTCC_DIR"]): + os.makedirs(mysettings["DISTCC_DIR"]) + apply_secpass_permissions(mysettings["DISTCC_DIR"], + uid=portage_uid, gid=portage_gid, mode=02775) + for x in ("/lock", "/state"): + if not os.path.exists(mysettings["DISTCC_DIR"]+x): + os.mkdir(mysettings["DISTCC_DIR"]+x) + apply_secpass_permissions(mysettings["DISTCC_DIR"]+x, + uid=portage_uid, gid=portage_gid, mode=02775) + except OSError, e: + writemsg("\n!!! File system problem when setting DISTCC_DIR directory permissions.\n") + writemsg( "!!! DISTCC_DIR="+str(mysettings["DISTCC_DIR"]+"\n")) + writemsg( "!!! "+str(e)+"\n\n") + time.sleep(5) + features.remove("distcc") + mysettings["DISTCC_DIR"]="" + + mysettings["WORKDIR"]=mysettings["PORTAGE_BUILDDIR"]+"/work" + mysettings["DEST"]=mysettings["PORTAGE_BUILDDIR"]+"/image/" + mysettings["D"]=os.path.join(mysettings["DEST"],portage_const.PREFIX) + + if mysettings.has_key("PORT_LOGDIR"): + if not os.access(mysettings["PORT_LOGDIR"],os.F_OK): + try: + os.mkdir(mysettings["PORT_LOGDIR"]) + except OSError, e: + print "!!! Unable to create PORT_LOGDIR" + print "!!!",e + if os.access(mysettings["PORT_LOGDIR"]+"/",os.W_OK): + try: + apply_secpass_permissions(mysettings["PORT_LOGDIR"], + uid=portage_uid, gid=portage_gid, mode=02770) + if not mysettings.has_key("LOG_PF") or (mysettings["LOG_PF"] != mysettings["PF"]): + mysettings["LOG_PF"]=mysettings["PF"] + mysettings["LOG_COUNTER"]=str(db[myroot]["vartree"].dbapi.get_counter_tick_core("/")) + logfile="%s/%s-%s.log" % (mysettings["PORT_LOGDIR"],mysettings["LOG_COUNTER"],mysettings["LOG_PF"]) + except OSError, e: + mysettings["PORT_LOGDIR"]="" + print "!!! Unable to chown/chmod PORT_LOGDIR. Disabling logging." + print "!!!",e + else: + print "!!! Cannot create log... No write access / Does not exist" + print "!!! PORT_LOGDIR:",mysettings["PORT_LOGDIR"] + mysettings["PORT_LOGDIR"]="" + + +def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,cleanup=0,dbkey=None,use_cache=1,fetchall=0,tree=None): + global db, actionmap_deps + + if not tree: + dump_traceback("Warning: tree not specified to doebuild") + tree = "porttree" + + validcommands = ["help","clean","prerm","postrm","cleanrm","preinst","postinst", + "config","setup","depend","fetch","digest", + "unpack","compile","test","install","rpm","qmerge","merge", + "package","unmerge", "manifest"] + + if mydo not in validcommands: + validcommands.sort() + writemsg("!!! doebuild: '%s' is not one of the following valid commands:" % mydo) + for vcount in range(len(validcommands)): + if vcount%6 == 0: + writemsg("\n!!! ") + writemsg(string.ljust(validcommands[vcount], 11)) + writemsg("\n") + return 1 + + if not os.path.exists(myebuild): + writemsg("!!! doebuild: "+str(myebuild)+" not found for "+str(mydo)+"\n") + return 1 + + mystatus = doebuild_environment(myebuild, mydo, myroot, mysettings, debug, use_cache, tree) + if mystatus: + return mystatus # get possible slot information from the deps file if mydo=="depend": @@ -2550,211 +2702,21 @@ def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,clea # Build directory creation isn't required for any of these. if mydo not in ["fetch","digest","manifest"]: - if not os.path.exists(mysettings["BUILD_PREFIX"]): - os.makedirs(mysettings["BUILD_PREFIX"]) - if (os.getuid() == 0): - os.chown(mysettings["BUILD_PREFIX"],portage_uid,portage_gid) - os.chmod(mysettings["BUILD_PREFIX"],00775) - - # Should be ok again to set $T, as sandbox does not depend on it - # XXX Bug. no way in hell this is valid for clean handling. - mysettings["T"]=mysettings["PORTAGE_BUILDDIR"]+"/temp" - if cleanup: - if os.path.exists(mysettings["T"]): - shutil.rmtree(mysettings["T"]) - if not os.path.exists(mysettings["T"]): - os.makedirs(mysettings["T"]) - if (os.getuid() == 0): - os.chown(mysettings["T"],portage_uid,portage_gid) - os.chmod(mysettings["T"],02770) - - logdir = mysettings["T"]+"/logging" - if not os.path.exists(logdir): - os.makedirs(logdir) - if secpass == 2: - os.chown(logdir, portage_uid, portage_gid) - os.chmod(logdir, 0770) - - try: # XXX: negative RESTRICT - if not (("nouserpriv" in string.split(mysettings["PORTAGE_RESTRICT"])) or \ - ("userpriv" in string.split(mysettings["PORTAGE_RESTRICT"]))): - if ("userpriv" in features) and (portage_uid and portage_gid): - if (secpass==2): - if os.path.exists(mysettings["HOME"]): - # XXX: Potentially bad, but held down by HOME replacement above. - spawn("rm -Rf "+mysettings["HOME"],mysettings, free=1) - if not os.path.exists(mysettings["HOME"]): - os.makedirs(mysettings["HOME"]) - elif ("userpriv" in features): - print "!!! Disabling userpriv from features... Portage UID/GID not valid." - del features[features.index("userpriv")] - except SystemExit, e: - raise - except Exception, e: - print "!!! Couldn't empty HOME:",mysettings["HOME"] - print "!!!",e - - try: - # no reason to check for depend since depend returns above. - if not os.path.exists(mysettings["BUILD_PREFIX"]): - os.makedirs(mysettings["BUILD_PREFIX"]) - if (os.getuid() == 0): - os.chown(mysettings["BUILD_PREFIX"],portage_uid,portage_gid) - if not os.path.exists(mysettings["PORTAGE_BUILDDIR"]): - os.makedirs(mysettings["PORTAGE_BUILDDIR"]) - if (os.getuid() == 0): - os.chown(mysettings["PORTAGE_BUILDDIR"],portage_uid,portage_gid) - except OSError, e: - print "!!! File system problem. (ReadOnly? Out of space?)" - print "!!! Perhaps: rm -Rf",mysettings["BUILD_PREFIX"] - print "!!!",str(e) - return 1 - - try: - if not os.path.exists(mysettings["HOME"]): - os.makedirs(mysettings["HOME"]) - if (os.getuid() == 0): - os.chown(mysettings["HOME"],portage_uid,portage_gid) - os.chmod(mysettings["HOME"],02770) - except OSError, e: - print "!!! File system problem. (ReadOnly? Out of space?)" - print "!!! Failed to create fake home directory in PORTAGE_BUILDDIR" - print "!!!",str(e) - return 1 - - try: - if ("ccache" in features): - if (not mysettings.has_key("CCACHE_DIR")) or (mysettings["CCACHE_DIR"]==""): - mysettings["CCACHE_DIR"]=mysettings["PORTAGE_TMPDIR"]+"/ccache" - if not os.path.exists(mysettings["CCACHE_DIR"]): - os.makedirs(mysettings["CCACHE_DIR"]) - mystat = os.stat(mysettings["CCACHE_DIR"]) - if ("userpriv" in features): - if mystat[stat.ST_UID] != portage_uid or ((mystat[stat.ST_MODE]&02070)!=02070): - writemsg("* Adjusting permissions on ccache in %s\n" % mysettings["CCACHE_DIR"]) - spawn("chgrp -R "+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) - spawn("chown "+str(portage_uid)+":"+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) - spawn("chmod -R ug+rw "+mysettings["CCACHE_DIR"], mysettings, free=1) - spawn("find "+mysettings["CCACHE_DIR"]+" -type d -exec chmod g+xs \{\} \;", mysettings, free=1) - else: - if mystat[stat.ST_UID] != 0 or ((mystat[stat.ST_MODE]&02070)!=02070): - writemsg("* Adjusting permissions on ccache in %s\n" % mysettings["CCACHE_DIR"]) - spawn("chgrp -R "+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) - spawn("chown 0:"+str(portage_gid)+" "+mysettings["CCACHE_DIR"], mysettings, free=1) - spawn("chmod -R ug+rw "+mysettings["CCACHE_DIR"], mysettings, free=1) - spawn("find "+mysettings["CCACHE_DIR"]+" -type d -exec chmod g+xs \{\} \;", mysettings, free=1) - except OSError, e: - print "!!! File system problem. (ReadOnly? Out of space?)" - print "!!! Perhaps: rm -Rf",mysettings["BUILD_PREFIX"] - print "!!!",str(e) - return 1 - try: - if "confcache" in features: - if not mysettings.has_key("CONFCACHE_DIR"): - mysettings["CONFCACHE_DIR"] = os.path.join(mysettings["PORTAGE_TMPDIR"], "confcache") - if not os.path.exists(mysettings["CONFCACHE_DIR"]): - if not os.getuid() == 0: - # we're boned. - features.remove("confcache") - mysettings["FEATURES"] = " ".join(features) - else: - os.makedirs(mysettings["CONFCACHE_DIR"], mode=0775) - os.chown(mysettings["CONFCACHE_DIR"], -1, portage_gid) - else: - st = os.stat(mysettings["CONFCACHE_DIR"]) - if not (st.st_mode & 07777) == 0775: - os.chmod(mysettings["CONFCACHE_DIR"], 0775) - if not st.st_gid == portage_gid: - os.chown(mysettings["CONFCACHE_DIR"], -1, portage_gid) - - # check again, since it may have been disabled. - if "confcache" in features: - for x in listdir(mysettings["CONFCACHE_DIR"]): - p = os.path.join(mysettings["CONFCACHE_DIR"], x) - st = os.stat(p) - if not (st.st_mode & 07777) & 07660 == 0660: - os.chmod(p, (st.st_mode & 0777) | 0660) - if not st.st_gid == portage_gid: - os.chown(p, -1, portage_gid) - - except OSError, e: - print "!!! Failed resetting perms on confcachedir %s" % mysettings["CONFCACHE_DIR"] - return 1 - #try: - # mystat=os.stat(mysettings["CCACHE_DIR"]) - # if (mystat[stat.ST_GID]!=portage_gid) or ((mystat[stat.ST_MODE]&02070)!=02070): - # print "*** Adjusting ccache permissions for portage user..." - # os.chown(mysettings["CCACHE_DIR"],portage_uid,portage_gid) - # os.chmod(mysettings["CCACHE_DIR"],02770) - # spawn("chown -R "+str(portage_uid)+":"+str(portage_gid)+" "+mysettings["CCACHE_DIR"],mysettings, free=1) - # spawn("chmod -R g+rw "+mysettings["CCACHE_DIR"],mysettings, free=1) - #except SystemExit, e: - # raise - #except: - # pass - - if "distcc" in features: - try: - if (not mysettings.has_key("DISTCC_DIR")) or (mysettings["DISTCC_DIR"]==""): - mysettings["DISTCC_DIR"]=mysettings["PORTAGE_TMPDIR"]+"/portage/.distcc" - if not os.path.exists(mysettings["DISTCC_DIR"]): - os.makedirs(mysettings["DISTCC_DIR"]) - os.chown(mysettings["DISTCC_DIR"],portage_uid,portage_gid) - os.chmod(mysettings["DISTCC_DIR"],02775) - for x in ("/lock", "/state"): - if not os.path.exists(mysettings["DISTCC_DIR"]+x): - os.mkdir(mysettings["DISTCC_DIR"]+x) - os.chown(mysettings["DISTCC_DIR"]+x,portage_uid,portage_gid) - os.chmod(mysettings["DISTCC_DIR"]+x,02775) - except OSError, e: - writemsg("\n!!! File system problem when setting DISTCC_DIR directory permissions.\n") - writemsg( "!!! DISTCC_DIR="+str(mysettings["DISTCC_DIR"]+"\n")) - writemsg( "!!! "+str(e)+"\n\n") - time.sleep(5) - features.remove("distcc") - mysettings["DISTCC_DIR"]="" - - mysettings["WORKDIR"]=mysettings["PORTAGE_BUILDDIR"]+"/work" - mysettings["DEST"]=os.path.join(mysettings["PORTAGE_BUILDDIR"],"image") - mysettings["D"]=mysettings["PORTAGE_BUILDDIR"]+"/image"+portage_const.PREFIX - - if mysettings.has_key("PORT_LOGDIR"): - if not os.access(mysettings["PORT_LOGDIR"],os.F_OK): - try: - os.mkdir(mysettings["PORT_LOGDIR"]) - except OSError, e: - print "!!! Unable to create PORT_LOGDIR" - print "!!!",e - if os.access(mysettings["PORT_LOGDIR"]+"/",os.W_OK): - try: - perms = os.stat(mysettings["PORT_LOGDIR"]) - if perms[stat.ST_UID] != portage_uid or perms[stat.ST_GID] != portage_gid: - os.chown(mysettings["PORT_LOGDIR"],portage_uid,portage_gid) - if stat.S_IMODE(perms[stat.ST_MODE]) != 02770: - os.chmod(mysettings["PORT_LOGDIR"],02770) - if not mysettings.has_key("LOG_PF") or (mysettings["LOG_PF"] != mysettings["PF"]): - mysettings["LOG_PF"]=mysettings["PF"] - mysettings["LOG_COUNTER"]=str(db[myroot]["vartree"].dbapi.get_counter_tick_core("/")) - logfile="%s/%s-%s.log" % (mysettings["PORT_LOGDIR"],mysettings["LOG_COUNTER"],mysettings["LOG_PF"]) - except OSError, e: - mysettings["PORT_LOGDIR"]="" - print "!!! Unable to chown/chmod PORT_LOGDIR. Disabling logging." - print "!!!",e - else: - print "!!! Cannot create log... No write access / Does not exist" - print "!!! PORT_LOGDIR:",mysettings["PORT_LOGDIR"] - mysettings["PORT_LOGDIR"]="" - + mystatus = prepare_build_dirs(myroot, mysettings, cleanup) + if mystatus: + return mystatus if mydo=="unmerge": return unmerge(mysettings["CATEGORY"],mysettings["PF"],myroot,mysettings) # if any of these are being called, handle them -- running them out of the sandbox -- and stop now. - if mydo=="clean": - logfile=None - if mydo in ["help","clean","setup"]: + if mydo in ["clean","cleanrm"]: + if "noclean" in features: + return 0 + 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(pkg_dir) + 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") else: @@ -2768,9 +2730,10 @@ def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,clea del mysettings["IMAGE"] return phase_retval elif mydo in ["prerm","postrm","postinst","config"]: - mysettings.load_infodir(pkg_dir) + mysettings.load_infodir(mysettings["O"]) return spawn(EBUILD_SH_BINARY+" "+mydo,mysettings,debug=debug,free=1,logfile=logfile) + mycpv = "/".join((mysettings["CATEGORY"], mysettings["PF"])) try: mysettings["SLOT"],mysettings["RESTRICT"] = db["/"]["porttree"].dbapi.aux_get(mycpv,["SLOT","RESTRICT"]) except (IOError,KeyError): @@ -2813,8 +2776,8 @@ def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,clea mystat=os.stat(mysettings["DISTDIR"]+"/cvs-src") if ((mystat[stat.ST_GID]!=portage_gid) or ((mystat[stat.ST_MODE]&02770)!=02770)) and not listonly: print "*** Adjusting cvs-src permissions for portage user..." - os.chown(mysettings["DISTDIR"]+"/cvs-src",0,portage_gid) - os.chmod(mysettings["DISTDIR"]+"/cvs-src",02770) + apply_secpass_permissions(mysettings["DISTDIR"]+"/cvs-src", + uid=0, gid=portage_gid, mode=02770, stat_cached=mystat) spawn("chgrp -R "+str(portage_gid)+" "+mysettings["DISTDIR"]+"/cvs-src", free=1) spawn("chmod -R g+rw "+mysettings["DISTDIR"]+"/cvs-src", free=1) except SystemExit, e: @@ -2847,8 +2810,7 @@ def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,clea print "!!! Failed reseting ebuild distdir path, " + edpath raise os.mkdir(edpath) - os.chown(edpath, -1, portage_gid) - os.chmod(edpath, 0775) + 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)) @@ -2887,18 +2849,19 @@ def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,clea nosandbox = ("sandbox" not in features and "usersandbox" not in features) sesandbox = selinux_enabled and "sesandbox" in features + ebuild_sh = EBUILD_SH_BINARY + " %s" + misc_sh = MISC_SH_BINARY + " dyn_%s" # args are for the to spawn function - # (droppriv, free, sesandbox) actionmap = { - "depend": {"args":(1, 0, 0)}, - "setup": {"args":(0, 1, 0)}, - "unpack": {"args":(1, 0, sesandbox)}, - "compile":{"args":(1, nosandbox, sesandbox)}, - "test": {"args":(1, nosandbox, sesandbox)}, - "install":{"args":(0, 0, sesandbox)}, - "rpm": {"args":(0, 0, 0)}, - "package":{"args":(0, 0, 0)}, + "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 @@ -3105,7 +3068,9 @@ def unmerge(cat,pkg,myroot,mysettings,mytrimworld=1): mylink=dblink(cat,pkg,myroot,mysettings,treetype="vartree") if mylink.exists(): mylink.unmerge(trimworld=mytrimworld,cleanup=1) - mylink.delete() + mylink.delete() + return 0 + return 1 def isvalidatom(atom): mycpv_cps = catpkgsplit(dep_getcpv(atom)) @@ -4328,7 +4293,7 @@ class vardbapi(dbapi): origpath=self.root+VDB_PATH+"/"+mycpv if not os.path.exists(origpath): continue - writemsg("@") + writemsg_stdout("@") if not os.path.exists(self.root+VDB_PATH+"/"+mynewcat): #create the directory os.makedirs(self.root+VDB_PATH+"/"+mynewcat) @@ -4387,7 +4352,7 @@ class vardbapi(dbapi): if (slot[0]!=origslot): continue - writemsg("s") + writemsg_stdout("s") write_atomic(os.path.join(origpath, "SLOT"), newslot+"\n") def cp_list(self,mycp,use_cache=1): @@ -5287,8 +5252,7 @@ class binarytree(packagetree): continue #print ">>> Updating data in:",mycpv - sys.stdout.write("%") - sys.stdout.flush() + writemsg_stdout("%") mytbz2 = xpak.tbz2(tbz2path) mydata = mytbz2.get_data() @@ -5338,8 +5302,7 @@ class binarytree(packagetree): if (slot[0]!=origslot): continue - sys.stdout.write("S") - sys.stdout.flush() + writemsg_stdout("S") mydata["SLOT"] = newslot+"\n" mytbz2.recompose_mem(xpak.xpak_mem(mydata)) return 1 @@ -5356,7 +5319,7 @@ class binarytree(packagetree): writemsg("!!! Cannot update readonly binary: "+mycpv+"\n") continue #print ">>> Updating binary data:",mycpv - writemsg("*") + writemsg_stdout("*") mytbz2 = xpak.tbz2(tbz2path) mydata = mytbz2.get_data() updated_items = update_dbentries(update_iter, mydata) @@ -5668,7 +5631,7 @@ class dblink: masked=len(pmpath) return (protected > masked) - def unmerge(self,pkgfiles=None,trimworld=1,cleanup=0): + def unmerge(self,pkgfiles=None,trimworld=1,cleanup=1): global dircache dircache={} @@ -5699,6 +5662,7 @@ class dblink: #do prerm script if myebuildpath and os.path.exists(myebuildpath): + # Eventually, we'd like to pass in the saved ebuild env here... a=doebuild(myebuildpath,"prerm",self.myroot,self.settings,cleanup=cleanup,use_cache=0,tree=self.treetype) # XXX: Decide how to handle failures here. if a != 0: @@ -5864,8 +5828,7 @@ class dblink: if a != 0: writemsg("!!! FAILED postrm: "+str(a)+"\n") sys.exit(123) - if "noclean" not in features: - doebuild(myebuildpath, "clean", self.myroot, self.settings, cleanup=cleanup, tree=self.treetype) + doebuild(myebuildpath, "cleanrm", self.myroot, self.settings, tree=self.treetype) self.unlockdb() def isowner(self,filename,destroot): @@ -6103,8 +6066,7 @@ class dblink: # Process ebuild logfiles elog_process(self.mycpv, self.settings) - if "noclean" not in features: - doebuild(myebuild, "clean", root, self.settings, tree=self.treetype) + doebuild(myebuild, "clean", root, self.settings, tree=self.treetype) return 0 def mergeme(self,srcroot,destroot,outfile,secondhand,stufftomerge,cfgfiledict,thismtime): @@ -6908,13 +6870,13 @@ def global_updates(): myupd = [] timestamps = {} for mykey, mystat, mycontent in update_data: - writemsg("\n\n") - writemsg(green("Performing Global Updates: ")+bold(mykey)+"\n") - writemsg("(Could take a couple of minutes if you have a lot of binary packages.)\n") - writemsg(" "+bold(".")+"='update pass' "+bold("*")+"='binary update' "+bold("@")+"='/var/db move'\n"+" "+bold("s")+"='/var/db SLOT move' "+bold("S")+"='binary SLOT move' "+bold("p")+"='update /etc/portage/package.*'\n") + writemsg_stdout("\n\n") + writemsg_stdout(green("Performing Global Updates: ")+bold(mykey)+"\n") + writemsg_stdout("(Could take a couple of minutes if you have a lot of binary packages.)\n") + writemsg_stdout(" "+bold(".")+"='update pass' "+bold("*")+"='binary update' "+bold("@")+"='/var/db move'\n"+" "+bold("s")+"='/var/db SLOT move' "+bold("S")+"='binary SLOT move' "+bold("p")+"='update /etc/portage/package.*'\n") valid_updates, errors = parse_updates(mycontent) myupd.extend(valid_updates) - print len(valid_updates) * "." + writemsg_stdout(len(valid_updates) * "." + "\n") if len(errors) == 0: # Update our internal mtime since we # processed all of our directives. @@ -6961,9 +6923,9 @@ def global_updates(): do_vartree(settings) if do_upgrade_packagesmessage and \ listdir(os.path.join(settings["PKGDIR"], "All"), EmptyOnError=1): - writemsg(" ** Skipping packages. Run 'fixpackages' or set it in FEATURES to fix the") - writemsg("\n tbz2's in the packages directory. "+bold("Note: This can take a very long time.")) - writemsg("\n") + writemsg_stdout(" ** Skipping packages. Run 'fixpackages' or set it in FEATURES to fix the") + writemsg_stdout("\n tbz2's in the packages directory. "+bold("Note: This can take a very long time.")) + writemsg_stdout("\n") if (secpass==2) and (not os.environ.has_key("SANDBOX_ACTIVE")): if settings["PORTAGE_CALLER"] in ["emerge","fixpackages"]: diff --git a/pym/portage_data.py b/pym/portage_data.py index 269c70033..28e1a0ec4 100644 --- a/pym/portage_data.py +++ b/pym/portage_data.py @@ -42,7 +42,17 @@ if not lchown: os.environ["USERLAND"]=userland -#Secpass will be set to 1 if the user is root or in the portage group. +# Portage has 3 security levels that depend on the uid and gid of the main +# process and are assigned according to the following table: +# +# Privileges secpass uid gid +# normal 0 any any +# group 1 any portage_gid +# super 2 0 any +# +# If the "wheel" group does not exist then wheelgid falls back to 0. +# If the "portage" group does not exist then portage_uid falls back to wheelgid. + secpass=0 uid=os.getuid() @@ -65,7 +75,7 @@ except KeyError: try: portage_uid=pwd.getpwnam(portage_const.portageuser)[2] portage_gid=grp.getgrnam(portage_const.portagegroup)[2] - if (secpass==0): + if secpass < 1 and portage_gid in os.getgroups(): secpass=1 except KeyError: portage_uid=0 diff --git a/pym/portage_exception.py b/pym/portage_exception.py index 27294da6e..0d0206df1 100644 --- a/pym/portage_exception.py +++ b/pym/portage_exception.py @@ -46,6 +46,8 @@ class FileNotFound(InvalidLocation): class DirectoryNotFound(InvalidLocation): """A directory was not found when it was expected to exist""" +class OperationNotPermitted(PortageException): + """An operation was not permitted operating system""" class CommandNotFound(PortageException): """A required binary was not available or executable""" diff --git a/pym/portage_util.py b/pym/portage_util.py index c8d55fcdd..697488697 100644 --- a/pym/portage_util.py +++ b/pym/portage_util.py @@ -2,8 +2,9 @@ # Distributed under the terms of the GNU General Public License v2 # $Id: /var/cvsroot/gentoo-src/portage/pym/portage_util.py,v 1.11.2.6 2005/04/23 07:26:04 jstubbs Exp $ +from portage_exception import FileNotFound, OperationNotPermitted -import sys,string,shlex,os.path +import sys,string,shlex,os,errno try: import cPickle except ImportError: @@ -454,26 +455,90 @@ def unique_array(s): u.append(x) return u -def apply_permissions(filename, uid=-1, gid=-1, mode=0, +def apply_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1, stat_cached=None): - """Apply user, group, and mode bits to a file - if the existing bits do not already match.""" + """Apply user, group, and mode bits to a file if the existing bits do not + already match. The default behavior is to force an exact match of mode + bits. When mask=0 is specified, mode bits on the target file are allowed + to be a superset of the mode argument (via logical OR). When mask>0, the + mode bits that the target file is allowed to have are restricted via + logical XOR.""" + try: + if stat_cached is None: + stat_cached = os.stat(filename) + + if (uid != -1 and uid != stat_cached.st_uid) or \ + (gid != -1 and gid != stat_cached.st_gid): + os.chown(filename, uid, gid) + + st_mode = stat_cached.st_mode & 07777 # protect from unwanted bits + if mask >= 0: + if mode == -1: + mode = 0 # Don't add any mode bits when mode is unspecified. + else: + mode = mode & 07777 + if (mode & st_mode != mode) or \ + (mask ^ st_mode != st_mode): + new_mode = mode | st_mode + new_mode = mask ^ new_mode + os.chmod(filename, new_mode) + elif mode != -1: + mode = mode & 07777 # protect from unwanted bits + if mode != st_mode: + os.chmod(filename, mode) + except OSError, oe: + if oe.errno == errno.EPERM: + raise OperationNotPermitted(oe) + elif oe.errno == errno.ENOENT: + raise FileNotFound(oe) + else: + raise oe + +def apply_stat_permissions(filename, newstat, **kwargs): + """A wrapper around apply_secpass_permissions that gets + uid, gid, and mode from a stat object""" + return apply_secpass_permissions(filename, uid=newstat.st_uid, gid=newstat.st_gid, + mode=newstat.st_mode, **kwargs) + +def apply_secpass_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1, + stat_cached=None): + """A wrapper around apply_permissions that uses secpass and simple + logic to apply as much of the permissions as possible without + generating an obviously avoidable permission exception. Despite + attempts to avoid an exception, it's possible that one will be raised + anyway, so be prepared. + Returns True if all permissions are applied and False if some are left + unapplied.""" if stat_cached is None: - stat_cached = os.stat(filename) + try: + stat_cached = os.stat(filename) + except OSError, oe: + if oe.errno == errno.EPERM: + raise OperationNotPermitted(oe) + elif oe.errno == errno.ENOENT: + raise FileNotFound(oe) + else: + raise oe - if (uid != -1 and uid != stat_cached.st_uid) or \ - (gid != -1 and gid != stat_cached.st_gid): - os.chown(filename, uid, gid) + all_applied = True - if mode & stat_cached.st_mode != mode: - os.chmod(filename, mode | stat_cached.st_mode) + import portage_data # not imported globally because of circular dep + if portage_data.secpass < 2: -def apply_stat_permissions(filename, newstat, stat_cached=None): - """wrapper around apply_permissions that gets - uid, gid, and mode from a stat object""" - apply_permissions(filename, uid=newstat.st_uid, gid=newstat.st_gid, - mode=newstat.st_mode, stat_cached=stat_cached) + if uid != -1 and \ + uid != stat_cached.st_uid: + all_applied = False + uid = -1 + + if gid != -1 and \ + gid != stat_cached.st_gid and \ + gid not in os.getgroups(): + all_applied = False + gid = -1 + + apply_permissions(filename, uid=uid, gid=gid, mode=mode, mask=mask, stat_cached=stat_cached) + return all_applied class atomic_ofstream(file): """Write a file atomically via os.rename(). Atomic replacement prevents @@ -498,12 +563,10 @@ class atomic_ofstream(file): if not self._aborted: try: apply_stat_permissions(self.name, os.stat(self._real_name)) - except OSError, oe: - import errno - if oe.errno in (errno.ENOENT,errno.EPERM): - pass - else: - raise oe + except OperationNotPermitted: + pass + except FileNotFound: + pass os.rename(self.name, self._real_name) finally: # Make sure we cleanup the temp file -- 2.26.2