diff options
author | Zac Medico <zmedico@gentoo.org> | 2007-05-17 09:13:00 +0000 |
---|---|---|
committer | Zac Medico <zmedico@gentoo.org> | 2007-05-17 09:13:00 +0000 |
commit | 851d97cf07ce846079e02c800daa54bab8314c2a (patch) | |
tree | f17b33a8697c83d86ca18351a60fbbdb56bc615a | |
parent | ca1947cd9295a0a094e8eaf4503eebb3c1ecebd9 (diff) | |
download | portage-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
-rw-r--r-- | pym/portage/__init__.py | 72 |
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." |