From a6d7663f7f3256cc54e1c918830c8f445f9ada7b Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Sun, 24 Dec 2006 10:00:55 +0000 Subject: [PATCH] Merged from trunk 5350:5366 I hate docstrings (WarnerBro) Remove two old functions, appear to be relics from 2.0.X branch, no longer used by portage or gentoolkit or esearch (WarnerBro) When an unprivileged user runs portage (not in the portage group), use metadata_overlay together with a volatile in-memory cache module in order to implement cache writes. This allows, for example, a user who's not in the portage group to run `ebuild foo.ebuild digest` (issue reported by Charlie Shepherd ). Add volatile cache module missing from the previous commit. Pass a cache instance into metadata_overlay since it doesn't have enough information to construct one itself. Make metadata_overlay use the autocommits attribute of the underlying database. Fix inverted EAPI default logic. Combine two default EAPI settings in portdbapi.aux_get(). Only write repo.stats and repo.fails when the repoman user has write access to /var/cache/edb. Show the "Calculating dependencies .. done!" message with --resume since the spinner now runs inside the depgraph constructor. Thanks to antarus for reporting. startime is set up in treewalk, so no need to do it globally... (WarnerBro) List package in summary logfile (genone) disable display of elapsed time for collision-protect as it's for benchmarking purposes and useless to most people. (genone) More Docstrings in portage.py (WarnerBro) move actionmap_deps out of global scope and into doebuild where it is actually used, add docstrings to config class in the meantime. (WarnerBro) svn path=/main/branches/prefix/; revision=5374 --- bin/ebuild.sh | 2 +- bin/emerge | 4 + bin/repoman | 22 +-- pym/cache/flat_hash.py | 2 +- pym/cache/metadata_overlay.py | 10 +- pym/cache/volatile.py | 25 +++ pym/elog_modules/mod_save_summary.py | 4 +- pym/portage.py | 256 +++++++++++++++++++-------- pym/portage_exec.py | 78 ++++++++ 9 files changed, 310 insertions(+), 93 deletions(-) create mode 100644 pym/cache/volatile.py diff --git a/bin/ebuild.sh b/bin/ebuild.sh index 8e8c3dd96..e1df76da4 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -1774,7 +1774,7 @@ for myarg in ${EBUILD_SH_ARGS} ; do #the extra $(echo) commands remove newlines unset CDEPEND - [ -n "${EAPI}" ] && EAPI=0 + [ -n "${EAPI}" ] || EAPI=0 if [ -n "${dbkey}" ] ; then > "${dbkey}" for f in ${auxdbkeys} ; do diff --git a/bin/emerge b/bin/emerge index d005c4859..cd7948cee 100755 --- a/bin/emerge +++ b/bin/emerge @@ -4387,8 +4387,12 @@ def action_build(settings, trees, mtimedb, myresumeopts[myopt] = myarg myopts=myresumeopts myparams = create_depgraph_params(myopts, myaction) + if not "--quiet" in myopts: + print "Calculating dependencies ", mydepgraph = depgraph(settings, trees, myopts, myparams, spinner) + if not "--quiet" in myopts: + print "\b\b... done!" else: if ("--resume" in myopts): print darkgreen("emerge: It seems we have nothing to resume...") diff --git a/bin/repoman b/bin/repoman index ad02c3d59..9181be345 100755 --- a/bin/repoman +++ b/bin/repoman @@ -1489,20 +1489,14 @@ for x in scanlist: fails["ebuild.allmasked"].append(x) #Pickle and save results for instant reuse in last and lfull -statsfile=os.path.normpath(portage_const.CACHE_PATH+"/repo.stats") -failsfile=os.path.normpath(portage_const.CACHE_PATH+"/repo.fails") -savef=open(statsfile,'w') -pickle.dump(stats,savef) -savef.close() -savef=open(failsfile,'w') -pickle.dump(fails,savef) -savef.close() -if not (os.stat(statsfile)[ST_GID] == getgrnam('@portagegroup@')[2]): - os.chown(statsfile,os.geteuid(),getgrnam('@portagegroup@')[2]) - os.chmod(statsfile,0664) -if not (os.stat(failsfile)[ST_GID] == getgrnam('@portagegroup@')[2]): - os.chown(failsfile,os.geteuid(),getgrnam('@portagegroup@')[2]) - os.chmod(failsfile,0664) +if os.access(portage_const.CACHE_PATH, os.W_OK): + for myobj, fname in (stats, "repo.stats"), (fails, "repo.fails"): + fpath = os.path.join(portage_const.CACHE_PATH, fname) + savef = open(fpath, 'w') + pickle.dump(myobj, savef) + savef.close() + portage.apply_secpass_permissions(fpath, gid=portage.portage_gid, + mode=0664) if quiet < 2: print #dofail will be set to 1 if we have failed in at least one non-warning category diff --git a/pym/cache/flat_hash.py b/pym/cache/flat_hash.py index bbfb55720..aa916cb62 100644 --- a/pym/cache/flat_hash.py +++ b/pym/cache/flat_hash.py @@ -17,7 +17,7 @@ class database(fs_template.FsBased): self.location = os.path.join(self.location, self.label.lstrip(os.path.sep).rstrip(os.path.sep)) - if not os.path.exists(self.location): + if not self.readonly and not os.path.exists(self.location): self._ensure_dirs() def __getitem__(self, cpv): diff --git a/pym/cache/metadata_overlay.py b/pym/cache/metadata_overlay.py index 24ab16349..91066fb9c 100644 --- a/pym/cache/metadata_overlay.py +++ b/pym/cache/metadata_overlay.py @@ -12,13 +12,19 @@ from cache.metadata import database as db_ro class database(template.database): - autocommits = True serialize_eclasses = False def __init__(self, location, label, auxdbkeys, **config): super(database, self).__init__(location, label, auxdbkeys) self.db_rw = db_rw(location, label, auxdbkeys, **config) - self.db_ro = db_ro(label,"metadata/cache",auxdbkeys) + self.commit = self.db_rw.commit + self.autocommits = self.db_rw.autocommits + if isinstance(db_ro, type): + ro_config = config.copy() + ro_config["readonly"] = True + self.db_ro = db_ro(label, "metadata/cache", auxdbkeys, **ro_config) + else: + self.db_ro = db_ro def __getitem__(self, cpv): """funnel whiteout validation through here, since value needs to be fetched""" diff --git a/pym/cache/volatile.py b/pym/cache/volatile.py new file mode 100644 index 000000000..068a4b817 --- /dev/null +++ b/pym/cache/volatile.py @@ -0,0 +1,25 @@ +# Copyright 1999-2006 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +import copy +if not hasattr(__builtins__, "set"): + from sets import Set as set +from cache import template + +class database(template.database): + + autocommits = True + serialize_eclasses = False + + def __init__(self, *args, **config): + config.pop("gid", None) + super(database, self).__init__(*args, **config) + self._data = {} + self.iterkeys = self._data.iterkeys + self._setitem = self._data.__setitem__ + self._delitem = self._data.__delitem__ + self.__contains__ = self._data.__contains__ + + def _getitem(self, cpv): + return copy.deepcopy(self._data[cpv]) diff --git a/pym/elog_modules/mod_save_summary.py b/pym/elog_modules/mod_save_summary.py index dd4dad138..7cb310d95 100644 --- a/pym/elog_modules/mod_save_summary.py +++ b/pym/elog_modules/mod_save_summary.py @@ -14,8 +14,8 @@ def process(mysettings, cpv, logentries, fulltext): # TODO: Locking elogfilename = elogdir+"/summary.log" elogfile = open(elogfilename, "a") - elogfile.write(">>> Messages generated by process %d on %s:\n\n" % \ - (os.getpid(), time.strftime("%Y%m%d-%H%M%S", time.gmtime(time.time())))) + elogfile.write(">>> Messages generated by process %d on %s for package %s:\n\n" % \ + (os.getpid(), time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(time.time())), cpv)) elogfile.write(fulltext) elogfile.write("\n") elogfile.close() diff --git a/pym/portage.py b/pym/portage.py index 43787bbe1..9d729fce1 100644 --- a/pym/portage.py +++ b/pym/portage.py @@ -168,32 +168,6 @@ def abssymlink(symlink): mylink=mydir+"/"+mylink return os.path.normpath(mylink) -def suffix_array(array,suffix,doblanks=1): - """Appends a given suffix to each element in an Array/List/Tuple. - Returns a List.""" - if type(array) not in [types.ListType, types.TupleType]: - raise TypeError, "List or Tuple expected. Got %s" % type(array) - newarray=[] - for x in array: - if x or doblanks: - newarray.append(x + suffix) - else: - newarray.append(x) - return newarray - -def prefix_array(array,prefix,doblanks=1): - """Prepends a given prefix to each element in an Array/List/Tuple. - Returns a List.""" - if type(array) not in [types.ListType, types.TupleType]: - raise TypeError, "List or Tuple expected. Got %s" % type(array) - newarray=[] - for x in array: - if x or doblanks: - newarray.append(prefix + x) - else: - newarray.append(x) - return newarray - dircache = {} cacheHit=0 cacheMiss=0 @@ -255,9 +229,30 @@ def cacheddir(my_original_path, ignorecvs, ignorelist, EmptyOnError, followSymli writemsg("cacheddirStats: H:%d/M:%d/S:%d\n" % (cacheHit, cacheMiss, cacheStale),10) return ret_list, ret_ftype - def listdir(mypath, recursive=False, filesonly=False, ignorecvs=False, ignorelist=[], followSymlinks=True, EmptyOnError=False, dirsonly=False): + """ + Portage-specific implementation of os.listdir + + @param mypath: Path whose contents you wish to list + @type mypath: String + @param recursive: Recursively scan directories contained within mypath + @type recursive: Boolean + @param filesonly; Only return files, not more directories + @type filesonly: Boolean + @param ignorecvs: Ignore CVS directories ('CVS','.svn','SCCS') + @type ignorecvs: Boolean + @param ignorelist: List of filenames/directories to exclude + @type ignorelist: List + @param followSymlinks: Follow Symlink'd files and directories + @type followSymlinks: Boolean + @param EmptyOnError: Return [] if an error occurs. + @type EmptyOnError: Boolean + @param dirsonly: Only return directories. + @type dirsonly: Boolean + @rtype: List + @returns: A list of files and directories (or just files or just directories) or an empty list. + """ list, ftype = cacheddir(mypath, ignorecvs, ignorelist, EmptyOnError, followSymlinks) @@ -297,8 +292,6 @@ def listdir(mypath, recursive=False, filesonly=False, ignorecvs=False, ignorelis return rlist -starttime=long(time.time()) - def flatten(mytokens): """this function now turns a [1,[2,3]] list into a [1,2,3] list and returns it.""" @@ -754,12 +747,17 @@ def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None): outfile.write("setenv %s '%s'\n" % (x, env[x])) outfile.close() -# returns a tuple. (version[string], error[string]) -# They are pretty much mutually exclusive. -# Either version is a string and error is none, or -# version is None and error is a string -# def ExtractKernelVersion(base_dir): + """ + Try to figure out what kernel version we are running + @param base_dir: Path to sources (usually /usr/src/linux) + @type base_dir: string + @rtype: tuple( version[string], error[string]) + @returns: + 1. tuple( version[string], error[string]) + Either version or error is populated (but never both) + + """ lines = [] pathname = os.path.join(base_dir, 'Makefile') try: @@ -815,7 +813,18 @@ def ExtractKernelVersion(base_dir): return (version,None) def autouse(myvartree, use_cache=1, mysettings=None): - "returns set of USE variables auto-enabled due to packages being installed" + """ + autuse returns a list of USE variables auto-enabled to packages being installed + + @param myvartree: Instance of the vartree class (from /var/db/pkg...) + @type myvartree: vartree + @param use_cache: read values from cache + @type use_cache: Boolean + @param mysettings: Instance of config + @type mysettings: config + @rtype: string + @returns: A string containing a list of USE variables that are enabled via use.defaults + """ if mysettings is None: global settings mysettings = settings @@ -838,9 +847,37 @@ def check_config_instance(test): raise TypeError, "Invalid type for config object: %s" % test.__class__ class config: + """ + This class encompasses the main portage configuration. Data is pulled from + ROOT/PORTDIR/profiles/, from ROOT/etc/make.profile incrementally through all + parent profiles as well as from ROOT/PORTAGE_CONFIGROOT/* for user specified + overrides. + + Generally if you need data like USE flags, FEATURES, environment variables, + virtuals ...etc you look in here. + """ + def __init__(self, clone=None, mycpv=None, config_profile_path=None, config_incrementals=None, config_root=None, target_root=None, local_config=True): + """ + @param clone: If provided, init will use deepcopy to copy by value the instance. + @type clone: Instance of config class. + @param mycpv: CPV to load up (see setcpv), this is the same as calling init with mycpv=None + and then calling instance.setcpv(mycpv). + @type mycpv: String + @param config_profile_path: Configurable path to the profile (usually PROFILE_PATH from portage_const) + @type config_profile_path: String + @param config_incrementals: List of incremental variables (usually portage_const.INCREMENTALS) + @type config_incrementals: List + @param config_root: path to read local config from (defaults to "/", see PORTAGE_CONFIGROOT) + @type config_root: String + @param target_root: __init__ override of $ROOT env variable. + @type target_root: String + @param local_config: Enables loading of local config (/etc/portage); used most by repoman to + ignore local config (keywording and unmasking) + @type local_config: Boolean + """ self.already_in_regenerate = 0 @@ -1397,8 +1434,9 @@ class config: self.setcpv(mycpv) def _init_dirs(self): - """Create tmp, var/tmp and var/lib/portage (relative to $ROOT).""" - + """ + Create a few directories that are critical to portage operation + """ if not os.access(self["ROOT"] + EPREFIX, os.W_OK): return @@ -1481,7 +1519,14 @@ class config: raise KeyError, "No such key defined in environment: %s" % key def reset(self,keeping_pkg=0,use_cache=1): - "reset environment to original settings" + """ + Restore environment from self.backupenv, call self.regenerate() + @param keeping_pkg: Should we keep the set_cpv() data or delete it. + @type keeping_pkg: Boolean + @param use_cache: Should self.regenerate use the cache or not + @type use_cache: Boolean + @rype: None + """ self.modifying() self.configdict["env"].clear() self.configdict["env"].update(self.backupenv) @@ -1551,6 +1596,20 @@ class config: return 0 def setcpv(self, mycpv, use_cache=1, mydb=None): + """ + Load a particular CPV into the config, this lets us see the + Default USE flags for a particular ebuild as well as the USE + flags from package.use. + + @param mycpv: A cpv to load + @type mycpv: string + @param use_cache: Enables caching + @type use_cache: Boolean + @param mydb: a dbapi instance that supports aux_get with the IUSE key. + @type mydb: dbapi or derivative. + @rtype: None + """ + self.modifying() if self.mycpv == mycpv: return @@ -1641,6 +1700,22 @@ class config: def regenerate(self,useonly=0,use_cache=1): + """ + Regenerate settings + This involves regenerating valid USE flags, re-expanding USE_EXPAND flags + re-stacking USE flags (-flag and -*), as well as any other INCREMENTAL + variables. This also updates the env.d configdict; useful in case an ebuild + changes the environment. + + If FEATURES has already stacked, it is not stacked twice. + + @param useonly: Only regenerate USE flags (not any other incrementals) + @type useonly: Boolean + @param use_cache: Enable Caching (only for autouse) + @type use_cache: Boolean + @rtype: None + """ + self.modifying() if self.already_in_regenerate: # XXX: THIS REALLY NEEDS TO GET FIXED. autouse() loops. @@ -1998,13 +2073,39 @@ class config: # XXX This would be to replace getstatusoutput completely. # XXX Issue: cannot block execution. Deadlock condition. def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, **keywords): - """spawn a subprocess with optional sandbox protection, - depending on whether sandbox is enabled. The "free" argument, - when set to 1, will disable sandboxing. This allows us to - spawn processes that are supposed to modify files outside of the - sandbox. We can't use os.system anymore because it messes up - signal handling. Using spawn allows our Portage signal handler - to work.""" + """ + Spawn a subprocess with extra portage-specific options. + Optiosn include: + + Sandbox: Sandbox means the spawned process will be limited in its ability t + read and write files (normally this means it is restricted to ${IMAGE}/) + SElinux Sandbox: Enables sandboxing on SElinux + Reduced Privileges: Drops privilages such that the process runs as portage:portage + instead of as root. + + Notes: os.system cannot be used because it messes with signal handling. Instead we + use the portage_exec spawn* family of functions. + + This function waits for the process to terminate. + + @param mystring: Command to run + @type mystring: String + @param mysettings: Either a Dict of Key,Value pairs or an instance of portage.config + @type mysettings: Dictionary or config instance + @param debug: Ignored + @type debug: Boolean + @param free: Enable sandboxing for this process + @type free: Boolean + @param droppriv: Drop to portage:portage when running this command + @type droppriv: Boolean + @param sesandbox: Enable SELinux Sandboxing (toggles a context switch) + @type sesandbox: Boolean + @param keywords: Extra options encoded as a dict, to be passed to spawn + @type keywords: Dictionary + @rtype: Integer + @returns: + 1. The return code of the spawned process. + """ if type(mysettings) == types.DictType: env=mysettings @@ -2479,7 +2580,8 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks", return 1 def digestgen(myarchives, mysettings, overwrite=1, manifestonly=0, myportdb=None): - """Generates a digest file if missing. Assumes all files are available. + """ + Generates a digest file if missing. Assumes all files are available. DEPRECATED: this now only is a compability wrapper for portage_manifest.Manifest() NOTE: manifestonly and overwrite are useless with manifest2 and @@ -2709,19 +2811,6 @@ def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None): return qa_retval return phase_retval -# chunked out deps for each phase, so that ebuild binary can use it -# to collapse targets down. -actionmap_deps={ - "depend": [], - "setup": [], - "unpack": ["setup"], - "compile":["unpack"], - "test": ["compile"], - "install":["test"], - "rpm": ["install"], - "package":["install"], -} - def eapi_is_supported(eapi): return str(eapi).strip() == str(portage_const.EAPI).strip() @@ -3037,7 +3126,21 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, if not tree: writemsg("Warning: tree not specified to doebuild\n") tree = "porttree" - global db, actionmap_deps + global db + + # chunked out deps for each phase, so that ebuild binary can use it + # to collapse targets down. + actionmap_deps={ + "depend": [], + "setup": [], + "unpack": ["setup"], + "compile":["unpack"], + "test": ["compile"], + "install":["test"], + "rpm": ["install"], + "package":["install"], + } + if mydbapi is None: mydbapi = db[myroot][tree].dbapi @@ -3269,8 +3372,8 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, # 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) + need_distfiles = (mydo in ("fetch", "unpack") or \ + mydo not in ("digest", "manifest") and "noauto" not in features) if need_distfiles and not fetch( fetchme, mysettings, listonly=listonly, fetchonly=fetchonly): return 1 @@ -5201,9 +5304,20 @@ class portdbapi(dbapi): # XXX: REMOVE THIS ONCE UNUSED_0 IS YANKED FROM auxdbkeys # ~harring filtered_auxdbkeys = filter(lambda x: not x.startswith("UNUSED_0"), auxdbkeys) - for x in self.porttrees: - # location, label, auxdbkeys - self.auxdb[x] = self.auxdbmodule(self.depcachedir, x, filtered_auxdbkeys, gid=portage_gid) + if secpass < 1: + from cache import metadata_overlay, volatile + for x in self.porttrees: + db_ro = self.auxdbmodule(self.depcachedir, x, + filtered_auxdbkeys, gid=portage_gid, readonly=True) + self.auxdb[x] = metadata_overlay.database( + self.depcachedir, x, filtered_auxdbkeys, + gid=portage_gid, db_rw=volatile.database, + db_ro=db_ro) + else: + for x in self.porttrees: + # location, label, auxdbkeys + self.auxdb[x] = self.auxdbmodule( + self.depcachedir, x, filtered_auxdbkeys, gid=portage_gid) # Selectively cache metadata in order to optimize dep matching. self._aux_cache_keys = set(["EAPI", "KEYWORDS", "SLOT"]) self._aux_cache = {} @@ -5398,6 +5512,9 @@ class portdbapi(dbapi): self.auxdb[mylocation][mycpv] = mydata + if not mydata.setdefault("EAPI", "0"): + mydata["EAPI"] = "0" + #finally, we look at our internal cache entry and return the requested data. returnme = [] for x in mylist: @@ -5406,15 +5523,8 @@ class portdbapi(dbapi): else: returnme.append(mydata.get(x,"")) - if "EAPI" in mylist: - idx = mylist.index("EAPI") - if not returnme[idx]: - returnme[idx] = "0" - if cache_me: aux_cache = {} - if not mydata.setdefault("EAPI", "0"): - mydata["EAPI"] = "0" for x in self._aux_cache_keys: aux_cache[x] = mydata.get(x, "") self._aux_cache[mycpv] = aux_cache @@ -6676,7 +6786,7 @@ class dblink: if f.startswith(myignore + os.path.sep): stopmerge = False break - print green("*")+" spent "+str(time.time()-starttime)+" seconds checking for file collisions" + #print green("*")+" spent "+str(time.time()-starttime)+" seconds checking for file collisions" if stopmerge: print red("*")+" This package is blocked because it wants to overwrite" print red("*")+" files belonging to other packages (see messages above)." diff --git a/pym/portage_exec.py b/pym/portage_exec.py index a53d3afdc..988865ad6 100644 --- a/pym/portage_exec.py +++ b/pym/portage_exec.py @@ -28,6 +28,19 @@ sandbox_capable = (os.path.isfile(SANDBOX_BINARY) and os.access(SANDBOX_BINARY, os.X_OK)) def spawn_bash(mycommand, debug=False, opt_name=None, **keywords): + """ + Spawns a bash shell running a specific commands + + @param mycommand: The command for bash to run + @type mycommand: String + @param debug: Turn bash debugging on (set -x) + @type debug: Boolean + @param opt_name: Name of the spawned process (detaults to binary name) + @type opt_name: String + @param keywords: Extra Dictionary arguments to pass to spawn + @type keywords: Dictionary + """ + args = [BASH_BINARY] if not opt_name: opt_name = os.path.basename(mycommand.split()[0]) @@ -101,6 +114,37 @@ atexit_register(cleanup) def spawn(mycommand, env={}, opt_name=None, fd_pipes=None, returnpid=False, uid=None, gid=None, groups=None, umask=None, logfile=None, path_lookup=True): + """ + Spawns a given command. + + @param mycommand: the command to execute + @type mycommand: String or List (Popen style list) + @param env: A dict of Key=Value pairs for env variables + @type env: Dictionary + @param opt_name: an optional name for the spawn'd process (defaults to the binary name) + @type opt_name: String + @param fd_pipes: A dict of mapping for pipes, { '0': stdin, '1': stdout } for example + @type fd_pipes: Dictionary + @param returnpid: Return the Process IDs for a successful spawn. + NOTE: This requires the caller clean up all the PIDs, otherwise spawn will clean them. + @type returnpid: Boolean + @param uid: User ID to spawn as; useful for dropping privilages + @type uid: Integer + @param gid: Group ID to spawn as; useful for dropping privilages + @type gid: Integer + @param groups: Group ID's to spawn in: useful for having the process run in multiple group contexts. + @type groups: List + @param umask: An integer representing the umask for the process (see man chmod for umask details) + @type umask: Integer + @param logfile: name of a file to use for logging purposes + @type logfile: String + @param path_lookup: If the binary is not fully specified then look for it in PATH + @type path_lookup: Boolean + + logfile requires stdout and stderr to be assigned to this process (ie not pointed + somewhere else.) + + """ # mycommand is either a str or a list if isinstance(mycommand, str): @@ -208,6 +252,31 @@ def spawn(mycommand, env={}, opt_name=None, fd_pipes=None, returnpid=False, def _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups, uid, umask): + """ + Execute a given binary with options + + @param binary: Name of program to execute + @type binary: String + @param mycommand: Options for program + @type mycommand: String + @param opt_name: Name of process (defaults to binary) + @type opt_name: String + @param fd_pipes: Mapping pipes to destination; { 0:0, 1:1, 2:2 } + @type fd_pipes: Dictionary + @param env: Key,Value mapping for Environmental Variables + @type env: Dictionary + @param gid: Group ID to run the process under + @type gid: Integer + @param groups: Groups the Process should be in. + @type groups: Integer + @param uid: User ID to run the process under + @type uid: Integer + @param umask: an int representing a unix umask (see man chmod for umask details) + @type umask: Integer + @rtype: None + @returns: Never returns (calls os.execve) + """ + # If the process we're creating hasn't been given a name # assign it the name of the executable. if not opt_name: @@ -250,6 +319,15 @@ def _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups, uid, umask): os.execve(binary, myargs, env) def find_binary(binary): + """ + Given a binary name, find the binary in PATH + + @param binary: Name of the binary to find + @type string + @rtype: None or string + @returns: full path to binary or None if the binary could not be located. + """ + for path in os.getenv("PATH", "").split(":"): filename = "%s/%s" % (path, binary) if os.access(filename, os.X_OK) and os.path.isfile(filename): -- 2.26.2