From 29b103c3762d01c9711aba33985f27869238143d Mon Sep 17 00:00:00 2001 From: Nikolai Nechaev Date: Wed, 24 Sep 2025 02:32:48 +0900 Subject: exec: Spawn processes as direct children (#11735) * exec: Spawn processes as direct children Spawn processes as children rather than grandchildren. This way, spawned processes may track Hyprland's state by watching their parent, either directly or indirectly (e.g., Linux's `PR_SET_PDEATH_SIG`). Fixes #11728 * tests/exec: Add the test on process spawning Add a test that ensures that: - A spawned process remains a direct child of Hyprland; - Upon termination, the process does not become a zombie. --- src/managers/KeybindManager.cpp | 53 +++++++++++++---------------------------- 1 file changed, 16 insertions(+), 37 deletions(-) (limited to 'src/managers/KeybindManager.cpp') 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); - } + 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); - execl("/bin/sh", "/bin/sh", "-c", args.c_str(), nullptr); - // exit grandchild - _exit(0); + int devnull = open("/dev/null", O_WRONLY | O_CLOEXEC); + if (devnull != -1) { + dup2(devnull, STDOUT_FILENO); + dup2(devnull, STDERR_FILENO); + close(devnull); } - write(pipeSock[1].get(), &grandchild, sizeof(grandchild)); + + 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) { -- cgit v1.2.3