std::experimental::optional<Command::Result> Command::run() const { Pipe stdout_pipe, stderr_pipe; pid_t pid = fork(); if (pid == -1) return std::experimental::nullopt; if (pid == 0) child_exec(args, stdout_pipe, stderr_pipe); stdout_pipe.close_write_fd(); stderr_pipe.close_write_fd(); Result result; bool read_success = read_pipes(stdout_pipe, stderr_pipe, result.stdout, result.stderr); stdout_pipe.close_read_fd(); stderr_pipe.close_read_fd(); int status; while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) return std::experimental::nullopt; if (!WIFEXITED(status)) return std::experimental::nullopt; result.exit_code = WEXITSTATUS(status); if (result.exit_code == CHILD_SETUP_FAILURE_EXIT_CODE || !read_success) return std::experimental::nullopt; return result; }
/// Tests an exec function with no arguments. /// /// \param tc The calling test case. /// \param do_exec The exec function to test. static void check_exec_no_args(const atf::tests::tc* tc, const exec_function do_exec) { std::auto_ptr< process::child > child = process::child::fork_files( child_exec(do_exec, get_helpers(tc), process::args_vector()), fs::path("stdout"), fs::path("stderr")); const process::status status = child->wait(); ATF_REQUIRE(status.exited()); ATF_REQUIRE_EQ(EXIT_FAILURE, status.exitstatus()); ATF_REQUIRE(atf::utils::grep_file("Must provide a helper name", "stderr")); }
static void main_loop(void) { struct pollfd pollv[1]; int i; int e = 0; char *c; pollv[0].fd = my_sigpipe[0]; pollv[0].events = POLLIN; for(;;){ if(flag_term){ break; } /* start/restart children: */ /* deux[0] is logger, check it first: */ for(i = 0; i < 2; ++i){ if(deux[i].pid == 0){ child_exec(i); } } /* poll on sigpipe: */ sigset_unblock(&my_sigset); do{ e = poll(pollv, 1, -1); }while((e == -1) && (errno == EINTR)); sigset_block(&my_sigset); /* consume sigpipe: */ while(read(my_sigpipe[0], &c, 1) == 1){/*empty*/ ;} /* consume dead children: */ child_wait(); } /* here on SIGTERM: */ if(deux[1].pid){ /* harvest deux[1]: */ waitpid(deux[1].pid, NULL, 0); } if(deux[0].pid){ /* logger should exit on eof: */ close(my_logpipe[1]); do_kill(deux[0].pid, SIGCONT); waitpid(deux[0].pid, NULL, 0); } return; }
int child_start(Widget top, char *cmd, void (*cb)(int) ) { int hc = child_init(cmd, 0); struct child_stat *child = mls(CLIST, hc); child->cb = cb; child_exec(hc); child->id = XtAppAddInput(XtWidgetToApplicationContext(top), child_read_fd(hc), (XtPointer) (XtInputReadMask), child_input_cb,(XtPointer) hc ); return hc; }
int child_startv(Widget top, int cmd_m, void (*cb)(int) ) { int l = m_len(cmd_m); int i; int hc = child_init(STR(cmd_m,0), 0); struct child_stat *child = mls(CLIST, hc); child->cb = cb; for(i=1;i<l;i++) v_kset(child->args, STR(cmd_m, i), -1 ); child_exec(hc); child->id = XtAppAddInput(XtWidgetToApplicationContext(top), child_read_fd(hc), (XtPointer) (XtInputReadMask), child_input_cb,(XtPointer) hc ); return hc; }
/// Tests an exec function with some arguments. /// /// \param tc The calling test case. /// \param do_exec The exec function to test. static void check_exec_some_args(const atf::tests::tc* tc, const exec_function do_exec) { process::args_vector args; args.push_back("print-args"); args.push_back("foo"); args.push_back("bar"); std::auto_ptr< process::child > child = process::child::fork_files( child_exec(do_exec, get_helpers(tc), args), fs::path("stdout"), fs::path("stderr")); const process::status status = child->wait(); ATF_REQUIRE(status.exited()); ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); ATF_REQUIRE(atf::utils::grep_file("argv\\[1\\] = print-args", "stdout")); ATF_REQUIRE(atf::utils::grep_file("argv\\[2\\] = foo", "stdout")); ATF_REQUIRE(atf::utils::grep_file("argv\\[3\\] = bar", "stdout")); }
void block_spawn(struct block *block, struct click *click) { const unsigned long now = time(NULL); int out[2], err[2]; if (!*COMMAND(block)) { bdebug(block, "no command, skipping"); return; } if (block->pid > 0) { bdebug(block, "process already spawned"); return; } if (pipe(out) == -1 || pipe(err) == -1) { berrorx(block, "pipe"); return mark_as_failed(block, strerror(errno)); } if (block->interval == INTER_PERSIST) { if (io_signal(out[0], SIGRTMIN)) return mark_as_failed(block, "event I/O impossible"); } block->pid = fork(); if (block->pid == -1) { berrorx(block, "fork"); return mark_as_failed(block, strerror(errno)); } /* Child? */ if (block->pid == 0) { /* Error messages are merged into the parent's stderr... */ child_setup_env(block, click); child_reset_signals(block); child_redirect_write(block, out, STDOUT_FILENO); child_redirect_write(block, err, STDERR_FILENO); /* ... until here */ child_exec(block); } /* * Note: for non-persistent blocks, no need to set the pipe read end as * non-blocking, since it is meant to be read once the child has exited * (and thus the write end is closed and read is available). */ /* Parent */ if (close(out[1]) == -1) berrorx(block, "close stdout"); if (close(err[1]) == -1) berrorx(block, "close stderr"); block->out = out[0]; block->err = err[0]; if (!click) block->timestamp = now; bdebug(block, "forked child %d at %ld", block->pid, now); }
static PyObject * subprocess_fork_exec(PyObject* self, PyObject *args) { PyObject *gc_module = NULL; PyObject *executable_list, *py_fds_to_keep; PyObject *env_list, *preexec_fn; PyObject *process_args, *converted_args = NULL, *fast_args = NULL; PyObject *preexec_fn_args_tuple = NULL; int p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite; int errpipe_read, errpipe_write, close_fds, restore_signals; int call_setsid; PyObject *cwd_obj, *cwd_obj2; const char *cwd; pid_t pid; int need_to_reenable_gc = 0; char *const *exec_array, *const *argv = NULL, *const *envp = NULL; Py_ssize_t arg_num; if (!PyArg_ParseTuple( args, "OOpOOOiiiiiiiiiiO:fork_exec", &process_args, &executable_list, &close_fds, &py_fds_to_keep, &cwd_obj, &env_list, &p2cread, &p2cwrite, &c2pread, &c2pwrite, &errread, &errwrite, &errpipe_read, &errpipe_write, &restore_signals, &call_setsid, &preexec_fn)) return NULL; if (close_fds && errpipe_write < 3) { /* precondition */ PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3"); return NULL; } if (PySequence_Length(py_fds_to_keep) < 0) { PyErr_SetString(PyExc_ValueError, "cannot get length of fds_to_keep"); return NULL; } if (_sanity_check_python_fd_sequence(py_fds_to_keep)) { PyErr_SetString(PyExc_ValueError, "bad value(s) in fds_to_keep"); return NULL; } /* We need to call gc.disable() when we'll be calling preexec_fn */ if (preexec_fn != Py_None) { PyObject *result; _Py_IDENTIFIER(isenabled); _Py_IDENTIFIER(disable); gc_module = PyImport_ImportModule("gc"); if (gc_module == NULL) return NULL; result = _PyObject_CallMethodId(gc_module, &PyId_isenabled, NULL); if (result == NULL) { Py_DECREF(gc_module); return NULL; } need_to_reenable_gc = PyObject_IsTrue(result); Py_DECREF(result); if (need_to_reenable_gc == -1) { Py_DECREF(gc_module); return NULL; } result = _PyObject_CallMethodId(gc_module, &PyId_disable, NULL); if (result == NULL) { Py_DECREF(gc_module); return NULL; } Py_DECREF(result); } exec_array = _PySequence_BytesToCharpArray(executable_list); if (!exec_array) { Py_XDECREF(gc_module); return NULL; } /* 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. */ if (process_args != Py_None) { Py_ssize_t num_args; /* Equivalent to: */ /* tuple(PyUnicode_FSConverter(arg) for arg in process_args) */ fast_args = PySequence_Fast(process_args, "argv must be a tuple"); if (fast_args == NULL) goto cleanup; num_args = PySequence_Fast_GET_SIZE(fast_args); converted_args = PyTuple_New(num_args); if (converted_args == NULL) goto cleanup; for (arg_num = 0; arg_num < num_args; ++arg_num) { PyObject *borrowed_arg, *converted_arg; borrowed_arg = PySequence_Fast_GET_ITEM(fast_args, arg_num); if (PyUnicode_FSConverter(borrowed_arg, &converted_arg) == 0) goto cleanup; PyTuple_SET_ITEM(converted_args, arg_num, converted_arg); } argv = _PySequence_BytesToCharpArray(converted_args); Py_CLEAR(converted_args); Py_CLEAR(fast_args); if (!argv) goto cleanup; } if (env_list != Py_None) { envp = _PySequence_BytesToCharpArray(env_list); if (!envp) goto cleanup; } if (preexec_fn != Py_None) { preexec_fn_args_tuple = PyTuple_New(0); if (!preexec_fn_args_tuple) goto cleanup; _PyImport_AcquireLock(); } if (cwd_obj != Py_None) { if (PyUnicode_FSConverter(cwd_obj, &cwd_obj2) == 0) goto cleanup; cwd = PyBytes_AsString(cwd_obj2); } else { cwd = NULL; cwd_obj2 = NULL; } pid = fork(); if (pid == 0) { /* Child process */ /* * Code from here to _exit() must only use async-signal-safe functions, * listed at `man 7 signal` or * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. */ if (preexec_fn != Py_None) { /* 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... */ PyOS_AfterFork(); } child_exec(exec_array, argv, envp, cwd, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, errpipe_read, errpipe_write, close_fds, restore_signals, call_setsid, py_fds_to_keep, preexec_fn, preexec_fn_args_tuple); _exit(255); return NULL; /* Dead code to avoid a potential compiler warning. */ } Py_XDECREF(cwd_obj2); if (pid == -1) { /* Capture the errno exception before errno can be clobbered. */ PyErr_SetFromErrno(PyExc_OSError); } if (preexec_fn != Py_None && _PyImport_ReleaseLock() < 0 && !PyErr_Occurred()) { PyErr_SetString(PyExc_RuntimeError, "not holding the import lock"); } /* Parent process */ if (envp) _Py_FreeCharPArray(envp); if (argv) _Py_FreeCharPArray(argv); _Py_FreeCharPArray(exec_array); /* Reenable gc in the parent process (or if fork failed). */ if (need_to_reenable_gc && _enable_gc(gc_module)) { Py_XDECREF(gc_module); return NULL; } Py_XDECREF(preexec_fn_args_tuple); Py_XDECREF(gc_module); if (pid == -1) return NULL; /* fork() failed. Exception set earlier. */ return PyLong_FromPid(pid); cleanup: if (envp) _Py_FreeCharPArray(envp); if (argv) _Py_FreeCharPArray(argv); _Py_FreeCharPArray(exec_array); Py_XDECREF(converted_args); Py_XDECREF(fast_args); Py_XDECREF(preexec_fn_args_tuple); /* Reenable gc if it was disabled. */ if (need_to_reenable_gc) _enable_gc(gc_module); Py_XDECREF(gc_module); return NULL; }