diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index fc7cd652..5e282d4b 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -37,6 +37,9 @@ jobs: runs-on: ubuntu-22.04 needs: ubuntu-base steps: + - name: Update Packages + run: | + sudo apt-get update - name: Install Dependencies run: | sudo apt-get install -y \ @@ -75,6 +78,9 @@ jobs: matrix: config_flag: ["--with-xmlrpc-c", "--with-xmlrpc-tinyxml2"] steps: + - name: Update Packages + run: | + sudo apt-get update - name: Install Dependencies run: | sudo apt-get install -y \ diff --git a/src/rpc/exec_file.cc b/src/rpc/exec_file.cc index c62ed03a..f6fce9da 100644 --- a/src/rpc/exec_file.cc +++ b/src/rpc/exec_file.cc @@ -38,24 +38,10 @@ ExecFile::execute(const char* file, char* const* argv, int flags) { result = write(m_log_fd, "\n---\n", sizeof("\n---\n")); } - int pipe_fd[2]; - - if ((flags & flag_capture) && pipe(pipe_fd)) - throw torrent::input_error("ExecFile::execute(...) Pipe creation failed."); - - auto clean_fn = [pipe_fd, flags]() { - if (flags & flag_capture) { - ::close(pipe_fd[0]); - ::close(pipe_fd[1]); - } - }; - posix_spawn_file_actions_t actions{}; - if (posix_spawn_file_actions_init(&actions) != 0) { - clean_fn(); + if (posix_spawn_file_actions_init(&actions) != 0) throw torrent::internal_error("ExecFile::execute(...) posix_spawn_file_actions_init failed."); - } // Handle standard input redirection (/dev/null), posix_spawn_file_actions_addopen handles opening // and dup2 natively @@ -64,6 +50,11 @@ ExecFile::execute(const char* file, char* const* argv, int flags) { posix_spawn_file_actions_addclose(&actions, 0); } + int pipe_fd[2] = {-1, -1}; + + if ((flags & flag_capture) && pipe(pipe_fd)) + throw torrent::input_error("ExecFile::execute(...) Pipe creation failed."); + // Handle standard output redirection if (flags & flag_capture) { posix_spawn_file_actions_adddup2(&actions, pipe_fd[1], 1); @@ -88,23 +79,36 @@ ExecFile::execute(const char* file, char* const* argv, int flags) { posix_spawnattr_t attr; posix_spawnattr_init(&attr); - // If you are using standard close-on-exec (O_CLOEXEC) across rtorrent, posix_spawn honors it - // automatically. If you want to explicitly enforce a clean slate, modern systems support - // POSIX_SPAWN_CLOEXEC_DEFAULT. + short spawn_flags = 0; #ifdef POSIX_SPAWN_CLOEXEC_DEFAULT - posix_spawnattr_setflags(&attr, POSIX_SPAWN_CLOEXEC_DEFAULT); + spawn_flags |= POSIX_SPAWN_CLOEXEC_DEFAULT; #endif - pid_t child_pid{}; + 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 + } - int spawn_status = posix_spawnp(&child_pid, file, &actions, &attr, argv, environ); + 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); if (spawn_status != 0) { - clean_fn(); + if (pipe_fd[0] != -1) + ::close(pipe_fd[0]); + + if (pipe_fd[1] != -1) + ::close(pipe_fd[1]); + throw torrent::input_error("ExecFile::execute(...) posix_spawn failed: " + std::string(std::strerror(spawn_status))); } @@ -131,6 +135,13 @@ ExecFile::execute(const char* file, char* const* argv, int flags) { } } + if (flags & flag_background) { + if (m_log_fd != -1) + result = write(m_log_fd, "\n--- Running in Background ---\n", sizeof("\n--- Running in Background ---\n")); + + return 0; + } + int status; while (waitpid(child_pid, &status, 0) == -1) { diff --git a/src/signal_handler.cc b/src/signal_handler.cc index 16bac789..3d5bbf27 100644 --- a/src/signal_handler.cc +++ b/src/signal_handler.cc @@ -81,6 +81,18 @@ SignalHandler::set_unblock(unsigned int signum) { throw std::logic_error("Could not unblock signal: " + std::string(std::strerror(errno))); } +void +SignalHandler::set_sigchild_ignore() { + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT | SA_RESTART; + + sigemptyset(&sa.sa_mask); + + if (sigaction(SIGCHLD, &sa, NULL) == -1) + throw std::logic_error("Could not set sigaction (ignore) for SIGCHLD: " + std::string(std::strerror(errno))); +} + void SignalHandler::set_sigaction_handler(unsigned int signum, handler_slot slot) { if (signum >= HIGHEST_SIGNAL) diff --git a/src/signal_handler.h b/src/signal_handler.h index 51a3b400..27000601 100644 --- a/src/signal_handler.h +++ b/src/signal_handler.h @@ -25,6 +25,8 @@ public: static void set_block(unsigned int signum); static void set_unblock(unsigned int signum); + static void set_sigchild_ignore(); + static void set_sigaction_handler(unsigned int signum, handler_slot slot); static const char* as_string(unsigned int signum);