def __init__(self, node, argv, executable=None, stdin=None, stdout=None, stderr=None, bufsize=0, shell=False, cwd=None, env=None, user=None): """As in Subprocess, `node' specifies the nemu Node to run in. The `stdin', `stdout', and `stderr' parameters also accept the special values subprocess.PIPE or subprocess.STDOUT. Check the stdlib's subprocess module for more details. `bufsize' specifies the buffer size for the buffered IO provided for PIPE'd descriptors. """ self.stdin = self.stdout = self.stderr = None self._pid = self._returncode = None fdmap = {"stdin": stdin, "stdout": stdout, "stderr": stderr} # if PIPE: all should be closed at the end for k, v in fdmap.items(): if v == None: continue if v == PIPE: r, w = os.pipe() if k == "stdin": self.stdin = os.fdopen(w, 'wb', bufsize) fdmap[k] = r else: setattr(self, k, os.fdopen(r, 'rb', bufsize)) fdmap[k] = w elif isinstance(v, int): pass else: fdmap[k] = v.fileno() if stderr == STDOUT: fdmap['stderr'] = fdmap['stdout'] super(Popen, self).__init__(node, argv, executable=executable, stdin=fdmap['stdin'], stdout=fdmap['stdout'], stderr=fdmap['stderr'], shell=shell, cwd=cwd, env=env, user=user) # Close pipes, they have been dup()ed to the child for k, v in fdmap.items(): if getattr(self, k) != None: eintr_wrapper(os.close, v)
def __init__(self, node, argv, executable = None, stdin = None, stdout = None, stderr = None, bufsize = 0, shell = False, cwd = None, env = None, user = None): """As in Subprocess, `node' specifies the nemu Node to run in. The `stdin', `stdout', and `stderr' parameters also accept the special values subprocess.PIPE or subprocess.STDOUT. Check the stdlib's subprocess module for more details. `bufsize' specifies the buffer size for the buffered IO provided for PIPE'd descriptors. """ self.stdin = self.stdout = self.stderr = None self._pid = self._returncode = None fdmap = { "stdin": stdin, "stdout": stdout, "stderr": stderr } # if PIPE: all should be closed at the end for k, v in fdmap.items(): if v == None: continue if v == PIPE: r, w = os.pipe() if k == "stdin": self.stdin = os.fdopen(w, 'wb', bufsize) fdmap[k] = r else: setattr(self, k, os.fdopen(r, 'rb', bufsize)) fdmap[k] = w elif isinstance(v, int): pass else: fdmap[k] = v.fileno() if stderr == STDOUT: fdmap['stderr'] = fdmap['stdout'] super(Popen, self).__init__(node, argv, executable = executable, stdin = fdmap['stdin'], stdout = fdmap['stdout'], stderr = fdmap['stderr'], shell = shell, cwd = cwd, env = env, user = user) # Close pipes, they have been dup()ed to the child for k, v in fdmap.items(): if getattr(self, k) != None: eintr_wrapper(os.close, v)
def wait(pid): """Wait for process to die and return the exit code.""" return eintr_wrapper(os.waitpid, pid, 0)[1]
def spawn(executable, argv=None, cwd=None, env=None, close_fds=False, stdin=None, stdout=None, stderr=None, user=None): """Internal function that performs all the dirty work for Subprocess, Popen and friends. This is executed in the slave process, directly from the protocol.Server class. Parameters have the same meaning as the stdlib's subprocess.Popen class, with one addition: the `user` parameter, if not None, specifies a user name to run the command as, after setting its primary and secondary groups. If a numerical UID is given, a reverse lookup is performed to find the user name and then set correctly the groups. When close_fds is True, it closes all file descriptors bigger than 2. It can also be an iterable of file descriptors to close after fork. Note that 'std{in,out,err}' must be None, integers, or file objects, PIPE is not supported here. Also, the original descriptors are not closed. """ userfd = [stdin, stdout, stderr] filtered_userfd = filter(lambda x: x != None and x >= 0, userfd) for i in range(3): if userfd[i] != None and not isinstance(userfd[i], int): userfd[i] = userfd[i].fileno() # pragma: no cover # Verify there is no clash assert not (set([0, 1, 2]) & set(filtered_userfd)) if user != None: user, uid, gid = get_user(user) home = pwd.getpwuid(uid)[5] groups = [x[2] for x in grp.getgrall() if user in x[3]] if not env: env = dict(os.environ) env['HOME'] = home env['USER'] = user (r, w) = os.pipe() pid = os.fork() if pid == 0: # pragma: no cover # coverage doesn't seem to understand fork try: # Set up stdio piping for i in range(3): if userfd[i] != None and userfd[i] >= 0: os.dup2(userfd[i], i) if userfd[i] != i and userfd[i] not in userfd[0:i]: eintr_wrapper(os.close, userfd[i]) # only in child! # Set up special control pipe eintr_wrapper(os.close, r) flags = fcntl.fcntl(w, fcntl.F_GETFD) fcntl.fcntl(w, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) if close_fds == True: for i in xrange(3, MAXFD): if i != w: try: os.close(i) except: pass elif close_fds != False: for i in close_fds: os.close(i) # changing process group id # (it is necessary to kill the forked subprocesses) os.setpgrp() if user != None: # Change user os.setgid(gid) os.setgroups(groups) os.setuid(uid) if cwd != None: os.chdir(cwd) if not argv: argv = [executable] if '/' in executable: # Should not search in PATH if env != None: os.execve(executable, argv, env) else: os.execv(executable, argv) else: # use PATH if env != None: os.execvpe(executable, argv, env) else: os.execvp(executable, argv) raise RuntimeError("Unreachable reached!") except: try: (t, v, tb) = sys.exc_info() # Got the child_traceback attribute trick from Python's # subprocess.py v.child_traceback = "".join( traceback.format_exception(t, v, tb)) eintr_wrapper(os.write, w, pickle.dumps(v)) eintr_wrapper(os.close, w) #traceback.print_exc() except: traceback.print_exc() os._exit(1) eintr_wrapper(os.close, w) # read EOF for success, or a string as error info s = "" while True: s1 = eintr_wrapper(os.read, r, 4096) if s1 == "": break s += s1 eintr_wrapper(os.close, r) if s == "": return pid # It was an error eintr_wrapper(os.waitpid, pid, 0) exc = pickle.loads(s) # XXX: sys.excepthook #print exc.child_traceback raise exc
def spawn(executable, argv = None, cwd = None, env = None, close_fds = False, stdin = None, stdout = None, stderr = None, user = None): """Internal function that performs all the dirty work for Subprocess, Popen and friends. This is executed in the slave process, directly from the protocol.Server class. Parameters have the same meaning as the stdlib's subprocess.Popen class, with one addition: the `user` parameter, if not None, specifies a user name to run the command as, after setting its primary and secondary groups. If a numerical UID is given, a reverse lookup is performed to find the user name and then set correctly the groups. When close_fds is True, it closes all file descriptors bigger than 2. It can also be an iterable of file descriptors to close after fork. Note that 'std{in,out,err}' must be None, integers, or file objects, PIPE is not supported here. Also, the original descriptors are not closed. """ userfd = [stdin, stdout, stderr] filtered_userfd = filter(lambda x: x != None and x >= 0, userfd) for i in range(3): if userfd[i] != None and not isinstance(userfd[i], int): userfd[i] = userfd[i].fileno() # pragma: no cover # Verify there is no clash assert not (set([0, 1, 2]) & set(filtered_userfd)) if user != None: user, uid, gid = get_user(user) home = pwd.getpwuid(uid)[5] groups = [x[2] for x in grp.getgrall() if user in x[3]] if not env: env = dict(os.environ) env['HOME'] = home env['USER'] = user (r, w) = os.pipe() pid = os.fork() if pid == 0: # pragma: no cover # coverage doesn't seem to understand fork try: # Set up stdio piping for i in range(3): if userfd[i] != None and userfd[i] >= 0: os.dup2(userfd[i], i) if userfd[i] != i and userfd[i] not in userfd[0:i]: eintr_wrapper(os.close, userfd[i]) # only in child! # Set up special control pipe eintr_wrapper(os.close, r) flags = fcntl.fcntl(w, fcntl.F_GETFD) fcntl.fcntl(w, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC) if close_fds == True: for i in xrange(3, MAXFD): if i != w: try: os.close(i) except: pass elif close_fds != False: for i in close_fds: os.close(i) # changing process group id # (it is necessary to kill the forked subprocesses) os.setpgrp() if user != None: # Change user os.setgid(gid) os.setgroups(groups) os.setuid(uid) if cwd != None: os.chdir(cwd) if not argv: argv = [ executable ] if '/' in executable: # Should not search in PATH if env != None: os.execve(executable, argv, env) else: os.execv(executable, argv) else: # use PATH if env != None: os.execvpe(executable, argv, env) else: os.execvp(executable, argv) raise RuntimeError("Unreachable reached!") except: try: (t, v, tb) = sys.exc_info() # Got the child_traceback attribute trick from Python's # subprocess.py v.child_traceback = "".join( traceback.format_exception(t, v, tb)) eintr_wrapper(os.write, w, pickle.dumps(v)) eintr_wrapper(os.close, w) #traceback.print_exc() except: traceback.print_exc() os._exit(1) eintr_wrapper(os.close, w) # read EOF for success, or a string as error info s = "" while True: s1 = eintr_wrapper(os.read, r, 4096) if s1 == "": break s += s1 eintr_wrapper(os.close, r) if s == "": return pid # It was an error eintr_wrapper(os.waitpid, pid, 0) exc = pickle.loads(s) # XXX: sys.excepthook #print exc.child_traceback raise exc