def test_unsetCloseOnExec(self): """ A file descriptor passed to L{fdesc._unsetCloseOnExec} is inherited by a new process image created with one of the exec family of functions. """ with open(self.mktemp(), 'wb') as fObj: fdesc._setCloseOnExec(fObj.fileno()) fdesc._unsetCloseOnExec(fObj.fileno()) status = self._execWithFileDescriptor(fObj) self.assertTrue(os.WIFEXITED(status)) self.assertEqual(os.WEXITSTATUS(status), 20)
def _setupChild(self, fdmap): """ fdmap[childFD] = parentFD The child wants to end up with 'childFD' attached to what used to be the parent's parentFD. As an example, a bash command run like 'command 2>&1' would correspond to an fdmap of {0:0, 1:1, 2:1}. 'command >foo.txt' would be {0:0, 1:os.open('foo.txt'), 2:2}. This is accomplished in two steps:: 1. close all file descriptors that aren't values of fdmap. This means 0 .. maxfds (or just the open fds within that range, if the platform supports '/proc/<pid>/fd'). 2. for each childFD:: - if fdmap[childFD] == childFD, the descriptor is already in place. Make sure the CLOEXEC flag is not set, then delete the entry from fdmap. - if childFD is in fdmap.values(), then the target descriptor is busy. Use os.dup() to move it elsewhere, update all fdmap[childFD] items that point to it, then close the original. Then fall through to the next case. - now fdmap[childFD] is not in fdmap.values(), and is free. Use os.dup2() to move it to the right place, then close the original. """ debug = self.debug_child if debug: errfd = sys.stderr errfd.write("starting _setupChild\n") destList = fdmap.values() for fd in _listOpenFDs(): if fd in destList: continue if debug and fd == errfd.fileno(): continue try: os.close(fd) except BaseException: pass # at this point, the only fds still open are the ones that need to # be moved to their appropriate positions in the child (the targets # of fdmap, i.e. fdmap.values() ) if debug: print("fdmap", fdmap, file=errfd) for child in sorted(fdmap.keys()): target = fdmap[child] if target == child: # fd is already in place if debug: print("%d already in place" % target, file=errfd) fdesc._unsetCloseOnExec(child) else: if child in fdmap.values(): # we can't replace child-fd yet, as some other mapping # still needs the fd it wants to target. We must preserve # that old fd by duping it to a new home. newtarget = os.dup(child) # give it a safe home if debug: print("os.dup(%d) -> %d" % (child, newtarget), file=errfd) os.close(child) # close the original for c, p in list(fdmap.items()): if p == child: fdmap[c] = newtarget # update all pointers # now it should be available if debug: print("os.dup2(%d,%d)" % (target, child), file=errfd) os.dup2(target, child) # At this point, the child has everything it needs. We want to close # everything that isn't going to be used by the child, i.e. # everything not in fdmap.keys(). The only remaining fds open are # those in fdmap.values(). # Any given fd may appear in fdmap.values() multiple times, so we # need to remove duplicates first. old = [] for fd in fdmap.values(): if fd not in old: if fd not in fdmap.keys(): old.append(fd) if debug: print("old", old, file=errfd) for fd in old: os.close(fd) self._resetSignalDisposition()
def _setupChild(self, fdmap): """ fdmap[childFD] = parentFD The child wants to end up with 'childFD' attached to what used to be the parent's parentFD. As an example, a bash command run like 'command 2>&1' would correspond to an fdmap of {0:0, 1:1, 2:1}. 'command >foo.txt' would be {0:0, 1:os.open('foo.txt'), 2:2}. This is accomplished in two steps:: 1. close all file descriptors that aren't values of fdmap. This means 0 .. maxfds (or just the open fds within that range, if the platform supports '/proc/<pid>/fd'). 2. for each childFD:: - if fdmap[childFD] == childFD, the descriptor is already in place. Make sure the CLOEXEC flag is not set, then delete the entry from fdmap. - if childFD is in fdmap.values(), then the target descriptor is busy. Use os.dup() to move it elsewhere, update all fdmap[childFD] items that point to it, then close the original. Then fall through to the next case. - now fdmap[childFD] is not in fdmap.values(), and is free. Use os.dup2() to move it to the right place, then close the original. """ debug = self.debug_child if debug: errfd = sys.stderr errfd.write("starting _setupChild\n") destList = fdmap.values() for fd in _listOpenFDs(): if fd in destList: continue if debug and fd == errfd.fileno(): continue try: os.close(fd) except: pass # at this point, the only fds still open are the ones that need to # be moved to their appropriate positions in the child (the targets # of fdmap, i.e. fdmap.values() ) if debug: print >>errfd, "fdmap", fdmap childlist = fdmap.keys() childlist.sort() for child in childlist: target = fdmap[child] if target == child: # fd is already in place if debug: print >>errfd, "%d already in place" % target fdesc._unsetCloseOnExec(child) else: if child in fdmap.values(): # we can't replace child-fd yet, as some other mapping # still needs the fd it wants to target. We must preserve # that old fd by duping it to a new home. newtarget = os.dup(child) # give it a safe home if debug: print >>errfd, "os.dup(%d) -> %d" % (child, newtarget) os.close(child) # close the original for c, p in fdmap.items(): if p == child: fdmap[c] = newtarget # update all pointers # now it should be available if debug: print >>errfd, "os.dup2(%d,%d)" % (target, child) os.dup2(target, child) # At this point, the child has everything it needs. We want to close # everything that isn't going to be used by the child, i.e. # everything not in fdmap.keys(). The only remaining fds open are # those in fdmap.values(). # Any given fd may appear in fdmap.values() multiple times, so we # need to remove duplicates first. old = [] for fd in fdmap.values(): if not fd in old: if not fd in fdmap.keys(): old.append(fd) if debug: print >>errfd, "old", old for fd in old: os.close(fd) self._resetSignalDisposition()
def __iter__(self): for description, fileno in self._descriptionsAndFilenos: fdesc._unsetCloseOnExec(fileno) yield description, fileno