def spawn(loop, process, file, args, env, streams): """ The file descriptor list should be a list of streams to wire up to FDs in the child. A None stream is mapped to UV_IGNORE. """ with rffi.scoped_str2charp(file) as rawFile: rawArgs = rffi.liststr2charpp(args) rawEnv = rffi.liststr2charpp(env) with rffi.scoped_str2charp(".") as rawCWD: options = rffi.make(cConfig["process_options_t"], c_file=rawFile, c_args=rawArgs, c_env=rawEnv, c_cwd=rawCWD) with lltype.scoped_alloc(rffi.CArray(stdio_container_t), len(streams)) as rawStreams: for i, stream in enumerate(streams): if stream == lltype.nullptr(stream_t): flags = UV_IGNORE else: flags = UV_CREATE_PIPE if i == 0: flags |= UV_READABLE_PIPE elif i in (1, 2): flags |= UV_WRITABLE_PIPE set_stdio_stream(rawStreams[i], stream) rffi.setintfield(rawStreams[i], "c_flags", flags) options.c_stdio = rawStreams rffi.setintfield(options, "c_stdio_count", len(streams)) add_exit_cb(options, processDiscard) rffi.setintfield(options, "c_flags", UV_PROCESS_WINDOWS_HIDE) rv = uv_spawn(loop, process, options) free(options) rffi.free_charpp(rawEnv) rffi.free_charpp(rawArgs) check("spawn", rv)
def spawn(loop, process, file, args, env): with rffi.scoped_str2charp(file) as rawFile: rawArgs = rffi.liststr2charpp(args) rawEnv = rffi.liststr2charpp(env) with rffi.scoped_str2charp(".") as rawCWD: options = rffi.make(cConfig["process_options_t"], c_file=rawFile, c_args=rawArgs, c_env=rawEnv, c_cwd=rawCWD) add_exit_cb(options, processDiscard) rffi.setintfield(options, "c_flags", UV_PROCESS_WINDOWS_HIDE) rffi.setintfield(options, "c_stdio_count", 0) rv = uv_spawn(loop, process, options) free(options) rffi.free_charpp(rawEnv) rffi.free_charpp(rawArgs) check("spawn", rv)
def spawn(loop, process, file, args, env, streams): """ The file descriptor list should be a list of streams to wire up to FDs in the child. A None stream is mapped to UV_IGNORE. """ with rffi.scoped_str2charp(file) as rawFile: rawArgs = rffi.liststr2charpp(args) rawEnv = rffi.liststr2charpp(env) with rffi.scoped_str2charp(".") as rawCWD: options = rffi.make(cConfig["process_options_t"], c_file=rawFile, c_args=rawArgs, c_env=rawEnv, c_cwd=rawCWD) with lltype.scoped_alloc(rffi.CArray(stdio_container_t), len(streams)) as rawStreams: for i, stream in enumerate(streams): if stream == lltype.nullptr(stream_t): flags = UV_IGNORE else: flags = UV_CREATE_PIPE if i == 0: flags |= UV_READABLE_PIPE elif i in (1, 2): flags |= UV_WRITABLE_PIPE if not we_are_translated(): # doing extra allocations here to work around ll2ctypes' # desire to gratuitously copy arrays with lltype.scoped_alloc(stdio_container_t) as con: set_stdio_stream(con, stream) for field in rawStreams[i]._T._names: setattr(rawStreams[i], field, getattr(con, field)) else: set_stdio_stream(rawStreams[i], stream) rffi.setintfield(rawStreams[i], "c_flags", flags) options.c_stdio = rawStreams rffi.setintfield(options, "c_stdio_count", len(streams)) add_exit_cb(options, processDiscard) # On Windows, ask to *not* have one of those annoying # console/terminal windows pop up. ~ C. rffi.setintfield(options, "c_flags", UV_PROCESS_WINDOWS_HIDE) rv = uv_spawn(loop, process, options) free(options) rffi.free_charpp(rawEnv) rffi.free_charpp(rawArgs) check("spawn", rv)
def fork_exec(space, w_process_args, w_executable_list, w_close_fds, w_fds_to_keep, w_cwd, w_env_list, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, restore_signals, call_setsid, w_preexec_fn): """\ fork_exec(args, executable_list, close_fds, cwd, env, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, restore_signals, call_setsid, preexec_fn) Forks a child process, closes parent file descriptors as appropriate in the child and dups the few that are needed before calling exec() in the child process. The preexec_fn, if supplied, will be called immediately before exec. WARNING: preexec_fn is NOT SAFE if your application uses threads. It may trigger infrequent, difficult to debug deadlocks. If an error occurs in the child process before the exec, it is serialized and written to the errpipe_write fd per subprocess.py. Returns: the child process's PID. Raises: Only on an error in the parent process. """ close_fds = space.is_true(w_close_fds) if close_fds and errpipe_write < 3: # precondition raise oefmt(space.w_ValueError, "errpipe_write must be >= 3") fds_to_keep = build_fd_sequence(space, w_fds_to_keep) # No need to disable GC in PyPy: # - gc.disable() only disables __del__ anyway. # - appelvel __del__ are only called at specific points of the # interpreter. l_exec_array = lltype.nullptr(rffi.CCHARPP.TO) l_argv = lltype.nullptr(rffi.CCHARPP.TO) l_envp = lltype.nullptr(rffi.CCHARPP.TO) l_cwd = lltype.nullptr(rffi.CCHARP.TO) l_fds_to_keep = lltype.nullptr(rffi.CArrayPtr(rffi.LONG).TO) # Convert args and env into appropriate arguments for exec() # These conversions are done in the parent process to avoid allocating # or freeing memory in the child process. try: l_exec_array = seqstr2charpp(space, w_executable_list) if not space.is_none(w_process_args): w_iter = space.iter(w_process_args) argv = [ space.fsencode_w(space.next(w_iter)) for i in range(space.len_w(w_process_args)) ] l_argv = rffi.liststr2charpp(argv) if not space.is_none(w_env_list): l_envp = seqstr2charpp(space, w_env_list) l_fds_to_keep = lltype.malloc(rffi.CArrayPtr(rffi.LONG).TO, len(fds_to_keep) + 1, flavor='raw') for i in range(len(fds_to_keep)): l_fds_to_keep[i] = fds_to_keep[i] if not space.is_none(w_preexec_fn): preexec.space = space preexec.w_preexec_fn = w_preexec_fn else: preexec.w_preexec_fn = None if not space.is_none(w_cwd): cwd = space.fsencode_w(w_cwd) l_cwd = rffi.str2charp(cwd) run_fork_hooks('before', space) try: try: pid = os.fork() except OSError as e: raise wrap_oserror(space, e) if pid == 0: # Child process # Code from here to _exit() must only use # async-signal-safe functions, listed at `man 7 signal` # http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. if not space.is_none(w_preexec_fn): # We'll be calling back into Python later so we need # to do this. This call may not be async-signal-safe # but neither is calling back into Python. The user # asked us to use hope as a strategy to avoid # deadlock... run_fork_hooks('child', space) c_child_exec(l_exec_array, l_argv, l_envp, l_cwd, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, l_fds_to_keep, len(fds_to_keep), PreexecCallback.run_function, None) os._exit(255) finally: # parent process run_fork_hooks('parent', space) finally: preexec.w_preexec_fn = None if l_cwd: rffi.free_charp(l_cwd) if l_envp: rffi.free_charpp(l_envp) if l_argv: rffi.free_charpp(l_argv) if l_exec_array: rffi.free_charpp(l_exec_array) if l_fds_to_keep: lltype.free(l_fds_to_keep, flavor='raw') return space.newint(pid)
def rffi_list_example(): l = ["Hello", ",", "World", "!"] l_charpp = rffi.liststr2charpp(l) r = c_strlen_list(l_charpp) rffi.free_charpp(l_charpp) print r
errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, l_fds_to_keep, len(fds_to_keep), PreexecCallback.run_function, None) os._exit(255) finally: # parent process run_fork_hooks('parent', space) finally: preexec.w_preexec_fn = None if l_cwd: rffi.free_charp(l_cwd) if l_envp: rffi.free_charpp(l_envp) if l_argv: rffi.free_charpp(l_argv) if l_exec_array: rffi.free_charpp(l_exec_array) if l_fds_to_keep: lltype.free(l_fds_to_keep, flavor='raw') return space.wrap(pid) def cloexec_pipe(space): """cloexec_pipe() -> (read_end, write_end) Create a pipe whose ends have the cloexec flag set."""
errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, l_fds_to_keep, len(fds_to_keep), PreexecCallback.run_function, None) os._exit(255) finally: # parent process run_fork_hooks('parent', space) finally: preexec.w_preexec_fn = None if l_cwd: rffi.free_charp(l_cwd) if l_envp: rffi.free_charpp(l_envp) if l_argv: rffi.free_charpp(l_argv) if l_exec_array: rffi.free_charpp(l_exec_array) if l_fds_to_keep: lltype.free(l_fds_to_keep, flavor='raw') return space.wrap(pid) def cloexec_pipe(space): """cloexec_pipe() -> (read_end, write_end) Create a pipe whose ends have the cloexec flag set."""