diff --git a/src/rpc/exec_file.cc b/src/rpc/exec_file.cc index c355e90b..d5dc43e5 100644 --- a/src/rpc/exec_file.cc +++ b/src/rpc/exec_file.cc @@ -1,23 +1,22 @@ #include "config.h" -#include -#include -#include -#include -#include -#include +#include "rpc/exec_file.h" + +// #include +// #include +// #include +// #include +// #include +// #include #include -#include -#include -#include +// #include +#include +// #include #include +#include #include -#include "exec_file.h" -#include "parse.h" - -// Standard POSIX environment pointer -extern char** environ; +#include "rpc/parse.h" namespace rpc { @@ -25,218 +24,56 @@ namespace rpc { int ExecFile::execute(const char* file, char* const* argv, int flags) { - assert(!((flags & flag_capture) && (flags & flag_background))); + torrent::system::SpawnProcess spawn_process; - // Write the executed command and its parameters to the log fd. - [[maybe_unused]] int result; + spawn_process.set_log_fd(m_log_fd); + spawn_process.set_background(flags & flag_background); + spawn_process.set_capture_output(flags & flag_capture); if (m_log_fd != -1) { - for (char* const* itr = argv; *itr != NULL; itr++) { - if (itr == argv) - result = write(m_log_fd, "\n---\n", sizeof("\n---\n")); - else - result = write(m_log_fd, " ", 1); + std::vector iovecs; + iovecs.reserve(32); + iovecs.push_back({const_cast("\n---\n"), 5}); - result = write(m_log_fd, *itr, std::strlen(*itr)); + for (auto* itr = argv; *itr != nullptr; itr++) { + if (itr != argv) + iovecs.push_back({const_cast(" "), 1}); + + iovecs.push_back({*itr, std::strlen(*itr)}); } - result = write(m_log_fd, "\n---\n", sizeof("\n---\n")); + iovecs.push_back({const_cast("\n---\n"), 5}); + + [[maybe_unused]] int result = ::writev(m_log_fd, iovecs.data(), iovecs.size()); } - posix_spawn_file_actions_t actions{}; - - if (posix_spawn_file_actions_init(&actions) != 0) - throw torrent::internal_error("ExecFile::execute(...) posix_spawn_file_actions_init failed."); - - posix_spawnattr_t attr; - posix_spawnattr_init(&attr); - - // Try to avoid leaking open fds to the spawned process. Prefer POSIX_SPAWN_CLOEXEC_DEFAULT - // (macOS-only) or posix_spawn_file_actions_addclosefrom_np (glibc >= 2.34, FreeBSD >= 13.1). - // - // Other platforms like musl libc, OpenBSD and NetBSD must rely on explicit O_CLOEXEC. - - // Handle standard input redirection (/dev/null), posix_spawn_file_actions_addopen handles opening - // and dup2 natively - if (posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_RDWR, 0) != 0) { - // Fallback if open fails inside action setup - posix_spawn_file_actions_addclose(&actions, 0); - } - - int pipe_0 = -1; - int pipe_1 = -1; - - // Handle standard output redirection - if (flags & flag_capture) { - torrent::fd_open_pipe(pipe_0, pipe_1); - - posix_spawn_file_actions_adddup2(&actions, pipe_1, 1); - - // Ensure the write end of the pipe is closed in the child after duplicating. - posix_spawn_file_actions_addclose(&actions, pipe_0); - posix_spawn_file_actions_addclose(&actions, pipe_1); - - } else if (m_log_fd != -1) { - posix_spawn_file_actions_adddup2(&actions, m_log_fd, 1); - - } else { - posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_RDWR, 0); - } - - if (m_log_fd != -1) { - posix_spawn_file_actions_adddup2(&actions, m_log_fd, 2); - } else { - posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_RDWR, 0); - } - - short spawn_flags = 0; - -#if defined(POSIX_SPAWN_CLOEXEC_DEFAULT) - spawn_flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; -#elif defined(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP) - posix_spawn_file_actions_addclosefrom_np(&actions, 3); -#endif - - if (flags & flag_background) { -#ifdef POSIX_SPAWN_SETSID - spawn_flags |= POSIX_SPAWN_SETSID; -#else - spawn_flags |= POSIX_SPAWN_SETPGROUP; - posix_spawnattr_setpgroup(&attr, 0); -#endif - } - - posix_spawnattr_setflags(&attr, spawn_flags); - - pid_t child_pid{}; - int spawn_status = posix_spawnp(&child_pid, file, &actions, &attr, argv, environ); - - posix_spawn_file_actions_destroy(&actions); - posix_spawnattr_destroy(&attr); + int spawn_status = spawn_process.execute(file, argv); if (spawn_status != 0) { - if (pipe_0 != -1) - torrent::fd_close(pipe_0); + if (m_log_fd != -1) { + auto prefix = "\n--- posix_spawn failed: "; + auto errno_str = torrent::system::errno_enum_str(spawn_status) + " ---\n"; - if (pipe_1 != -1) - torrent::fd_close(pipe_1); + struct iovec iovecs[2] = { + {const_cast(prefix), std::strlen(prefix)}, + {const_cast(errno_str.c_str()), errno_str.size()}, + }; + + [[maybe_unused]] int result = ::writev(m_log_fd, iovecs, 2); + } throw torrent::input_error("ExecFile::execute() posix_spawn failed: " + torrent::system::errno_enum_str(spawn_status)); } - if (flags & flag_capture) { - m_capture = std::string(); - torrent::fd_close(pipe_1); - - char buffer[4096]; - ssize_t length; - - do { - length = read(pipe_0, buffer, sizeof(buffer)); - - if (length > 0) - m_capture += std::string(buffer, length); - - } while (length > 0); - - torrent::fd_close(pipe_0); - - if (m_log_fd != -1) { - result = write(m_log_fd, "Captured output:\n", sizeof("Captured output:\n")); - result = write(m_log_fd, m_capture.data(), m_capture.length()); - } - } - if (flags & flag_background) { - m_waitpid_queue.close_pid(child_pid); - - if (m_log_fd != -1) - result = write(m_log_fd, "\n--- Running in Background ---\n", sizeof("\n--- Running in Background ---\n")); - + m_waitpid_queue.close_pid(spawn_process.child_pid()); return 0; } - int status; - - while (::waitpid(child_pid, &status, 0) == -1) { - switch (errno) { - case EINTR: - continue; - case ECHILD: - throw torrent::internal_error("ExecFile::execute(...) waitpid failed with ECHILD, child process not found."); - case EINVAL: - throw torrent::internal_error("ExecFile::execute(...) waitpid failed with EINVAL."); - default: - throw torrent::internal_error("ExecFile::execute(...) waitpid failed with unexpected error: " + std::string(std::strerror(errno))); - } - }; - - // Check return value? - if (m_log_fd != -1) { - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) - result = write(m_log_fd, "\n--- Success ---\n", sizeof("\n--- Success ---\n")); - else - result = write(m_log_fd, "\n--- Error ---\n", sizeof("\n--- Error ---\n")); - } - - return status; -} - -torrent::Object -ExecFile::execute_object(const torrent::Object& rawArgs, int flags) { - char* argsBuffer[max_args]; - char** argsCurrent = argsBuffer; - - // Size of value strings are less than 24. - char valueBuffer[buffer_size+1]; - char* valueCurrent = valueBuffer; - - if (rawArgs.is_list()) { - const torrent::Object::list_type& args = rawArgs.as_list(); - - if (args.empty()) - throw torrent::input_error("Too few arguments."); - - for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++, argsCurrent++) { - if (argsCurrent == argsBuffer + max_args - 1) - throw torrent::input_error("Too many arguments."); - - if (itr->is_string() && (!(flags & flag_expand_tilde) || *itr->as_string().c_str() != '~')) { - *argsCurrent = const_cast(itr->as_string().c_str()); - - } else { - *argsCurrent = valueCurrent; - valueCurrent = print_object(valueCurrent, valueBuffer + buffer_size, &*itr, flags) + 1; - - if (valueCurrent >= valueBuffer + buffer_size) - throw torrent::input_error("Overflowed execute arg buffer."); - } - } - - } else { - const torrent::Object::string_type& args = rawArgs.as_string(); - - if ((flags & flag_expand_tilde) && args.c_str()[0] == '~') { - *argsCurrent = valueCurrent; - valueCurrent = print_object(valueCurrent, valueBuffer + buffer_size, &rawArgs, flags) + 1; - } else { - *argsCurrent = const_cast(args.c_str()); - } - - argsCurrent++; - } - - *argsCurrent = NULL; - - int status = execute(argsBuffer[0], argsBuffer, flags); - - if ((flags & flag_throw) && status != 0) - throw torrent::input_error("Bad return code."); - if (flags & flag_capture) - return m_capture; + m_capture = spawn_process.capture_child_output(); - return torrent::Object((int64_t)status); + return spawn_process.wait_for_child(); } -} +} // namespace rpc diff --git a/src/utils/waitpid_queue.h b/src/utils/waitpid_queue.h index b034af9c..70f736e7 100644 --- a/src/utils/waitpid_queue.h +++ b/src/utils/waitpid_queue.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace utils {