diff --git a/hyprtester/src/tests/main/exec.cpp b/hyprtester/src/tests/main/exec.cpp new file mode 100644 index 00000000..a475ac0d --- /dev/null +++ b/hyprtester/src/tests/main/exec.cpp @@ -0,0 +1,64 @@ +#include "tests.hpp" +#include "../../shared.hpp" +#include "../../hyprctlCompat.hpp" +#include +#include +#include +#include +#include "../shared.hpp" + +static int ret = 0; + +using namespace Hyprutils::OS; +using namespace Hyprutils::Memory; + +#define UP CUniquePointer +#define SP CSharedPointer + +static std::string execAndGet(const std::string& cmd) { + CProcess proc("/bin/sh", {"-c", cmd}); + + if (!proc.runSync()) { + return "error"; + } + + return proc.stdOut(); +} + +static bool test() { + NLog::log("{}Testing process spawning", Colors::GREEN); + + // Note: POSIX sleep does not support fractional seconds, so + // can't sleep for less than 1 second. + OK(getFromSocket("/dispatch exec sleep 1")); + + // Ensure that sleep is our child + const std::string sleepPidS = execAndGet("pgrep sleep"); + pid_t sleepPid; + try { + sleepPid = std::stoull(sleepPidS); + } catch (...) { + NLog::log("{}Sleep was not spawned or several sleeps are running: pgrep returned '{}'", Colors::RED, sleepPidS); + return false; + } + + const std::string sleepParentComm = execAndGet("cat \"/proc/$(ps -o ppid:1= -p " + sleepPidS + ")/comm\""); + NLog::log("{}Expecting that sleep's parent is Hyprland", Colors::YELLOW); + EXPECT_CONTAINS(sleepParentComm, "Hyprland"); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Ensure that sleep did not become a zombie + EXPECT(Tests::processAlive(sleepPid), false); + + // kill all + NLog::log("{}Killing all windows", Colors::YELLOW); + Tests::killAllWindows(); + + NLog::log("{}Expecting 0 windows", Colors::YELLOW); + EXPECT(Tests::windowCount(), 0); + + return !ret; +} + +REGISTER_TEST_FN(test) diff --git a/src/main.cpp b/src/main.cpp index 8467ea6e..b0f8b66c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "init/initHelpers.hpp" #include "debug/HyprCtl.hpp" +#include #include #include #include @@ -35,6 +36,17 @@ static void help() { --version -v - Print this binary's version)"); } +static void reapZombieChildrenAutomatically() { + struct sigaction act; + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NOCLDWAIT; +#ifdef SA_RESTORER + act.sa_restorer = NULL; +#endif + sigaction(SIGCHLD, &act, nullptr); +} + int main(int argc, char** argv) { if (!getenv("XDG_RUNTIME_DIR")) @@ -183,6 +195,8 @@ int main(int argc, char** argv) { if (!envEnabled("HYPRLAND_NO_RT")) NInit::gainRealTime(); + reapZombieChildrenAutomatically(); + Debug::log(LOG, "Hyprland init finished."); // If all's good to go, start. diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index cd69289e..3120157f 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -949,17 +949,9 @@ uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWo const auto HLENV = getHyprlandLaunchEnv(pInitialWorkspace); - int socket[2]; - if (pipe(socket) != 0) { - Debug::log(LOG, "Unable to create pipe for fork"); - } - - CFileDescriptor pipeSock[2] = {CFileDescriptor{socket[0]}, CFileDescriptor{socket[1]}}; - - pid_t child, grandchild; - child = fork(); + pid_t child = fork(); if (child < 0) { - Debug::log(LOG, "Fail to create the first fork"); + Debug::log(LOG, "Fail to fork"); return 0; } if (child == 0) { @@ -970,41 +962,28 @@ uint64_t CKeybindManager::spawnRawProc(std::string args, PHLWORKSPACE pInitialWo sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, nullptr); - grandchild = fork(); - if (grandchild == 0) { - // run in grandchild - for (auto const& e : HLENV) { - setenv(e.first.c_str(), e.second.c_str(), 1); - } - setenv("WAYLAND_DISPLAY", g_pCompositor->m_wlDisplaySocket.c_str(), 1); - - int devnull = open("/dev/null", O_WRONLY | O_CLOEXEC); - if (devnull != -1) { - dup2(devnull, STDOUT_FILENO); - dup2(devnull, STDERR_FILENO); - close(devnull); - } - - execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr); - // exit grandchild - _exit(0); + for (auto const& e : HLENV) { + setenv(e.first.c_str(), e.second.c_str(), 1); } - write(pipeSock[1].get(), &grandchild, sizeof(grandchild)); + setenv("WAYLAND_DISPLAY", g_pCompositor->m_wlDisplaySocket.c_str(), 1); + + int devnull = open("/dev/null", O_WRONLY | O_CLOEXEC); + if (devnull != -1) { + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + close(devnull); + } + + execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr); + // exit child _exit(0); } // run in parent - read(pipeSock[0].get(), &grandchild, sizeof(grandchild)); - // clear child and leave grandchild to init - waitpid(child, nullptr, 0); - if (grandchild < 0) { - Debug::log(LOG, "Fail to create the second fork"); - return 0; - } - Debug::log(LOG, "Process Created with pid {}", grandchild); + Debug::log(LOG, "Process Created with pid {}", child); - return grandchild; + return child; } SDispatchResult CKeybindManager::killActive(std::string args) {