summaryrefslogtreecommitdiffstats
path: root/pym
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2007-05-17 09:13:00 +0000
committerZac Medico <zmedico@gentoo.org>2007-05-17 09:13:00 +0000
commit851d97cf07ce846079e02c800daa54bab8314c2a (patch)
treef17b33a8697c83d86ca18351a60fbbdb56bc615a /pym
parentca1947cd9295a0a094e8eaf4503eebb3c1ecebd9 (diff)
downloadportage-851d97cf07ce846079e02c800daa54bab8314c2a.tar.gz
portage-851d97cf07ce846079e02c800daa54bab8314c2a.tar.bz2
portage-851d97cf07ce846079e02c800daa54bab8314c2a.zip
For bug #162404, use a pseudo-terminal device pair (instead of a normal pipe) for moving ouput out of the sesandbox domain.
svn path=/main/trunk/; revision=6541
Diffstat (limited to 'pym')
-rw-r--r--pym/portage/__init__.py72
1 files changed, 47 insertions, 25 deletions
diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py
index cf27256aa..acb403b80 100644
--- a/pym/portage/__init__.py
+++ b/pym/portage/__init__.py
@@ -2258,10 +2258,12 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, **keyw
# from shells and from binaries that belong to portage (the number of entry
# points is minimized). The "tee" binary is not among the allowed entry
# points, so it is spawned outside of the sesandbox domain and reads from a
- # pipe between two domains.
+ # pseudo-terminal that connects two domains.
logfile = keywords.get("logfile")
mypids = []
- pw = None
+ slave_fd = None
+ output_pid = None
+ input_pid = None
if logfile:
del keywords["logfile"]
fd_pipes = keywords.get("fd_pipes")
@@ -2269,12 +2271,29 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, **keyw
fd_pipes = {0:0, 1:1, 2:2}
elif 1 not in fd_pipes or 2 not in fd_pipes:
raise ValueError(fd_pipes)
- pr, pw = os.pipe()
- mypids.extend(portage.process.spawn(('tee', '-i', '-a', logfile),
- returnpid=True, fd_pipes={0:pr, 1:fd_pipes[1], 2:fd_pipes[2]}))
- os.close(pr)
- fd_pipes[1] = pw
- fd_pipes[2] = pw
+ from pty import openpty
+ master_fd, slave_fd = openpty()
+ # Disable the ECHO attribute so the terminal behaves properly
+ # if the subprocess needs to read input from stdin.
+ import termios
+ term_attr = termios.tcgetattr(master_fd)
+ term_attr[3] &= ~termios.ECHO
+ termios.tcsetattr(master_fd, termios.TCSAFLUSH, term_attr)
+ # tee will always exit with an IO error, so ignore it's stderr.
+ null_file = open('/dev/null', 'w')
+ mypids.extend(portage.process.spawn(['tee', '-i', '-a', logfile],
+ returnpid=True, fd_pipes={0:master_fd, 1:fd_pipes[1],
+ 2:null_file.fileno()}))
+ output_pid = mypids[-1]
+ mypids.extend(portage.process.spawn(['cat'],
+ returnpid=True, fd_pipes={0:fd_pipes[0], 1:master_fd,
+ 2:null_file.fileno()}))
+ input_pid = mypids[-1]
+ os.close(master_fd)
+ null_file.close()
+ fd_pipes[0] = slave_fd
+ fd_pipes[1] = slave_fd
+ fd_pipes[2] = slave_fd
keywords["fd_pipes"] = fd_pipes
features = mysettings.features
@@ -2307,29 +2326,32 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, **keyw
try:
mypids.extend(spawn_func(mystring, env=env, **keywords))
finally:
- if pw:
- os.close(pw)
+ if slave_fd:
+ os.close(slave_fd)
if sesandbox:
selinux.setexec(None)
if returnpid:
return mypids
- while mypids:
- pid = mypids.pop(0)
- retval = os.waitpid(pid, 0)[1]
- portage.process.spawned_pids.remove(pid)
- if retval != os.EX_OK:
- for pid in mypids:
- if os.waitpid(pid, os.WNOHANG) == (0,0):
- import signal
- os.kill(pid, signal.SIGTERM)
- os.waitpid(pid, 0)
- portage.process.spawned_pids.remove(pid)
- if retval & 0xff:
- return (retval & 0xff) << 8
- return retval >> 8
- return os.EX_OK
+ if output_pid:
+ # tee will exit when the other end of the pseudo-terminal is closed.
+ os.waitpid(output_pid, 0)
+ portage.process.spawned_pids.remove(output_pid)
+ if input_pid:
+ # cat is blocking on stdin, so it must be killed.
+ import signal
+ os.kill(input_pid, signal.SIGTERM)
+ os.waitpid(input_pid, 0)
+ portage.process.spawned_pids.remove(input_pid)
+ pid = mypids[-1]
+ retval = os.waitpid(pid, 0)[1]
+ portage.process.spawned_pids.remove(pid)
+ if retval != os.EX_OK:
+ if retval & 0xff:
+ return (retval & 0xff) << 8
+ return retval >> 8
+ return retval
def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",use_locks=1, try_mirrors=1):
"fetch files. Will use digest file if available."