#include "Executor.hpp" #include "../../../event/EventBus.hpp" #include "../../../Compositor.hpp" #include "../../../managers/input/InputManager.hpp" #include "../../../desktop/rule/windowRule/WindowRule.hpp" #include "../../../desktop/rule/Engine.hpp" #include "../../../desktop/state/FocusState.hpp" #include "../../../managers/TokenManager.hpp" #include "../../../helpers/Monitor.hpp" #include #include using namespace Config::Supplementary; using namespace Hyprutils::String; UP& Config::Supplementary::executor() { static UP p = makeUnique(); return p; } CExecutor::CExecutor() { m_listeners.init = Event::bus()->m_events.start.listen([this] { if (m_firstExecDispatched) return; // update dbus env if (g_pCompositor->m_aqBackend->hasSession()) spawnRaw( #ifdef USES_SYSTEMD "systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash " "dbus-update-activation-environment 2>/dev/null && " #endif "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS"); m_firstExecDispatched = true; for (auto const& c : m_execOnce) { auto res = c.withRules ? spawn(c.exec) : spawnRaw(c.exec); if (!res || !c.rule) continue; const auto TOKEN = g_pTokenManager->registerNewToken(nullptr, std::chrono::seconds(1)); applyRuleToProc(c.rule, *res, TOKEN); } m_execOnce.clear(); // free some kb of memory :P // set input, fixes some certain issues g_pInputManager->setKeyboardLayout(); g_pInputManager->setPointerConfigs(); g_pInputManager->setTouchDeviceConfigs(); g_pInputManager->setTabletConfigs(); // check for user's possible errors with their setup and notify them if needed // this is additionally guarded because exiting safe mode will re-run this. g_pCompositor->performUserChecks(); m_listeners.shutdown = Event::bus()->m_events.exit.listen([this] { for (auto const& c : m_execShutdown) { c.withRules ? spawn(c.exec) : spawnRaw(c.exec); } m_execShutdown.clear(); }); }); } void CExecutor::applyRuleToProc(SP rule, int64_t pid, const std::string& token) { rule->markAsExecRule(token, pid, false /* TODO: could be nice. */); rule->registerMatch(Desktop::Rule::RULE_PROP_EXEC_TOKEN, token); rule->registerMatch(Desktop::Rule::RULE_PROP_EXEC_PID, std::to_string(pid)); Desktop::Rule::ruleEngine()->registerRule(std::move(rule)); Log::logger->log(Log::DEBUG, "Applied rule arguments for exec, pid {}.", pid); } void CExecutor::addExecOnce(SExecRequest&& cmd) { m_execOnce.emplace_back(std::move(cmd)); } void CExecutor::addExecShutdown(SExecRequest&& cmd) { m_execShutdown.emplace_back(std::move(cmd)); } std::optional CExecutor::spawn(const std::string& args) { return spawnWithRules(args); } std::optional CExecutor::spawn(const SExecRequest& args) { auto res = spawn(args.exec); if (!args.rule) return res; const auto TOKEN = g_pTokenManager->registerNewToken(nullptr, std::chrono::seconds(1)); applyRuleToProc(args.rule, *res, TOKEN); return res; } std::optional CExecutor::spawnRaw(const std::string& args) { return spawnRawProc(args); } std::optional CExecutor::spawnWithRules(std::string args, PHLWORKSPACE pInitialWorkspace) { args = trim(args); std::string RULES = ""; if (args[0] == '[') { // we have exec rules const auto end = args.find_first_of(']'); if (end == std::string::npos) return std::nullopt; RULES = args.substr(1, end - 1); args = args.substr(end + 1); } std::string execToken = ""; if (!RULES.empty()) { auto rule = Desktop::Rule::CWindowRule::buildFromExecString(std::move(RULES)); if (!rule) { Log::logger->log(Log::ERR, "Failed to parse exec rule: {}", rule.error()); return std::nullopt; } const auto TOKEN = g_pTokenManager->registerNewToken(nullptr, std::chrono::seconds(1)); const auto PROC = spawnRawProc(args, pInitialWorkspace, TOKEN); if (!PROC) return std::nullopt; applyRuleToProc(*rule, *PROC, TOKEN); return PROC; } return spawnRawProc(args, pInitialWorkspace, execToken); } static std::vector> getHyprlandLaunchEnv(PHLWORKSPACE pInitialWorkspace) { static auto PINITIALWSTRACKING = CConfigValue("misc:initial_workspace_tracking"); if (!*PINITIALWSTRACKING) return {}; const auto PMONITOR = Desktop::focusState()->monitor(); if (!PMONITOR || !PMONITOR->m_activeWorkspace) return {}; std::vector> result; if (!pInitialWorkspace) { if (PMONITOR->m_activeSpecialWorkspace) pInitialWorkspace = PMONITOR->m_activeSpecialWorkspace; else pInitialWorkspace = PMONITOR->m_activeWorkspace; } result.push_back(std::make_pair<>("HL_INITIAL_WORKSPACE_TOKEN", g_pTokenManager->registerNewToken(Desktop::View::SInitialWorkspaceToken{{}, pInitialWorkspace->getConfigName()}, std::chrono::months(1337)))); return result; } std::optional CExecutor::spawnRawProc(const std::string& args, PHLWORKSPACE pInitialWorkspace, const std::string& execRuleToken) { Log::logger->log(Log::DEBUG, "[executor] Executing {}", args); const auto HLENV = getHyprlandLaunchEnv(pInitialWorkspace); pid_t child = fork(); if (child < 0) { Log::logger->log(Log::DEBUG, "Fail to fork"); return 0; } if (child == 0) { // run in child g_pCompositor->restoreNofile(); sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, nullptr); 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); if (!execRuleToken.empty()) setenv(Desktop::Rule::EXEC_RULE_ENV_NAME, execRuleToken.c_str(), true); 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 Log::logger->log(Log::DEBUG, "[executor] Process created with pid {}", child); return child; }