diff options
Diffstat (limited to 'pym/portage_exec.py')
-rw-r--r-- | pym/portage_exec.py | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/pym/portage_exec.py b/pym/portage_exec.py new file mode 100644 index 000000000..c613ab235 --- /dev/null +++ b/pym/portage_exec.py @@ -0,0 +1,215 @@ +# portage.py -- core Portage functionality +# Copyright 1998-2004 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-src/portage/pym/portage_exec.py,v 1.13.2.4 2005/04/17 09:01:56 jstubbs Exp $ +cvs_id_string="$Id: portage_exec.py,v 1.13.2.4 2005/04/17 09:01:56 jstubbs Exp $"[5:-2] + +import os,types,atexit,string,stat +import signal +import portage_data +import portage_util + +try: + import resource + max_fd_limit=resource.getrlimit(RLIMIT_NOFILE) +except SystemExit, e: + raise +except: + # hokay, no resource module. + max_fd_limit=256 + +spawned_pids = [] +def cleanup(): + global spawned_pids + while spawned_pids: + pid = spawned_pids.pop() + try: + os.kill(pid,SIGKILL) + except SystemExit, e: + raise + except: + pass +atexit.register(cleanup) + +from portage_const import BASH_BINARY,SANDBOX_BINARY,SANDBOX_PIDS_FILE + +sandbox_capable = (os.path.exists(SANDBOX_BINARY) and os.access(SANDBOX_BINARY, os.X_OK)) + +def spawn_bash(mycommand,env={},debug=False,opt_name=None,**keywords): + args=[BASH_BINARY] + if not opt_name: + opt_name=mycommand.split()[0] + if not env.has_key("BASH_ENV"): + env["BASH_ENV"] = "/etc/spork/is/not/valid/profile.env" + if debug: + args.append("-x") + args.append("-c") + args.append(mycommand) + return spawn(args,env=env,opt_name=opt_name,**keywords) + +def spawn_sandbox(mycommand,uid=None,opt_name=None,**keywords): + if not sandbox_capable: + return spawn_bash(mycommand,opt_name=opt_name,**keywords) + args=[SANDBOX_BINARY] + if not opt_name: + opt_name=mycommand.split()[0] + args.append(mycommand) + if not uid: + uid=os.getuid() + try: + os.chown(SANDBOX_PIDS_FILE,uid,portage_data.portage_gid) + os.chmod(SANDBOX_PIDS_FILE,0664) + except SystemExit, e: + raise + except: + pass + return spawn(args,uid=uid,opt_name=opt_name,**keywords) + +# base spawn function +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): + if type(mycommand)==types.StringType: + mycommand=mycommand.split() + myc = mycommand[0] + if not os.access(myc, os.X_OK): + if not path_lookup: + return None + myc = find_binary(myc) + if myc == None: + return None + + mypid=[] + if logfile: + pr,pw=os.pipe() + mypid.extend(spawn(('tee','-i','-a',logfile),returnpid=True,fd_pipes={0:pr,1:1,2:2})) + retval=os.waitpid(mypid[-1],os.WNOHANG)[1] + if retval != 0: + # he's dead jim. + if (retval & 0xff)==0: + return (retval >> 8) # exit code + else: + return ((retval & 0xff) << 8) # signal + if not fd_pipes: + fd_pipes={} + fd_pipes[0] = 0 + fd_pipes[1]=pw + fd_pipes[2]=pw + + if not opt_name: + opt_name = mycommand[0] + myargs=[opt_name] + myargs.extend(mycommand[1:]) + mypid.append(os.fork()) + if mypid[-1] == 0: + # this may look ugly, but basically it moves file descriptors around to ensure no + # handles that are needed are accidentally closed during the final dup2 calls. + trg_fd=[] + if type(fd_pipes)==types.DictType: + src_fd=[] + k=fd_pipes.keys() + k.sort() + for x in k: + trg_fd.append(x) + src_fd.append(fd_pipes[x]) + for x in range(0,len(trg_fd)): + if trg_fd[x] == src_fd[x]: + continue + if trg_fd[x] in src_fd[x+1:]: + new=os.dup2(trg_fd[x],max(src_fd) + 1) + os.close(trg_fd[x]) + try: + while True: + src_fd[s.index(trg_fd[x])]=new + except SystemExit, e: + raise + except: + pass + for x in range(0,len(trg_fd)): + if trg_fd[x] != src_fd[x]: + os.dup2(src_fd[x], trg_fd[x]) + else: + trg_fd=[0,1,2] + for x in range(0,max_fd_limit): + if x not in trg_fd: + try: + os.close(x) + except SystemExit, e: + raise + except: + pass + # note this order must be preserved- can't change gid/groups if you change uid first. + if gid: + os.setgid(gid) + if groups: + os.setgroups(groups) + if uid: + os.setuid(uid) + if umask: + os.umask(umask) + try: + # XXX: We would do this to stop ebuild.sh from getting any + # XXX: output, and consequently, we'd get to handle the sigINT. + #os.close(sys.stdin.fileno()) + pass + except SystemExit, e: + raise + except: + pass + + try: + #print "execing", myc, myargs + os.execve(myc,myargs,env) + except SystemExit, e: + raise + except Exception, e: + raise str(e)+":\n "+myc+" "+string.join(myargs) + # If the execve fails, we need to report it, and exit + # *carefully* --- report error here + os._exit(1) + sys.exit(1) + return # should never get reached + + if logfile: + os.close(pr) + os.close(pw) + + if returnpid: + global spawned_pids + spawned_pids.append(mypid[-1]) + return mypid + while len(mypid): + retval=os.waitpid(mypid[-1],0)[1] + if retval != 0: + for x in mypid[0:-1]: + try: + os.kill(x,signal.SIGTERM) + if os.waitpid(x,os.WNOHANG)[1] == 0: + # feisty bugger, still alive. + os.kill(x,signal.SIGKILL) + os.waitpid(x,0) + except OSError, oe: + if oe.errno not in (10,3): + raise oe + + # at this point we've killed all other kid pids generated via this call. + # return now. + + if (retval & 0xff)==0: + return (retval >> 8) # return exit code + else: + return ((retval & 0xff) << 8) # interrupted by signal + else: + mypid.pop(-1) + return 0 + +def find_binary(myc): + p=os.getenv("PATH") + if p == None: + return None + for x in p.split(":"): + # if it exists, and is executable + if os.access("%s/%s" % (x,myc), os.X_OK): + return "%s/%s" % (x,myc) + + return None + + |