blob: 8a92c7ae2c642117da92e2fb18e700e30c89a11d [file] [log] [blame]
# portage.py -- core Portage functionality
# Copyright 1998-2004 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id: /var/cvsroot/gentoo-src/portage/pym/portage_exec.py,v 1.13.2.4 2005/04/17 09:01:56 jstubbs Exp $
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.path.isabs(myc) or 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