From cc50c4f2e4ef01746a918413805f48fb7a7340a0 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 7 May 2026 20:04:28 +0200 Subject: [PATCH] fix(hooks): run session power hooks before fallback actions --- src/app/application.cpp | 28 +++++++++++++++++++++++++--- src/app/application.h | 1 + src/hooks/hook_manager.cpp | 14 +++++++++++--- src/hooks/hook_manager.h | 5 +++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/app/application.cpp b/src/app/application.cpp index aa070bbe8..3e3af4664 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -402,6 +402,8 @@ void Application::initServices() { m_configService.addReloadCallback([this]() { m_idleManager.reload(m_configService.config().idle); }); m_hookManager.setCommandRunner([this](const std::string& command) { return runUserCommand(command); }); + m_hookManager.setBlockingCommandRunner( + [this](const std::string& command) { return runUserCommandBlocking(command); }); m_hookManager.reload(m_configService.config().hooks); m_configService.addReloadCallback([this]() { m_hookManager.reload(m_configService.config().hooks); }); m_nightLightManager.reload(m_configService.config().nightlight); @@ -756,9 +758,9 @@ void Application::initUi() { m_lockScreen.setSessionHooks([this]() { m_hookManager.fire(HookKind::SessionLocked); }, [this]() { m_hookManager.fire(HookKind::SessionUnlocked); }); - m_sessionActionHooks.onLogout = [this]() { m_hookManager.fire(HookKind::LoggingOut); }; - m_sessionActionHooks.onReboot = [this]() { m_hookManager.fire(HookKind::Rebooting); }; - m_sessionActionHooks.onShutdown = [this]() { m_hookManager.fire(HookKind::ShuttingDown); }; + m_sessionActionHooks.onLogout = [this]() { m_hookManager.fireBlocking(HookKind::LoggingOut); }; + m_sessionActionHooks.onReboot = [this]() { m_hookManager.fireBlocking(HookKind::Rebooting); }; + m_sessionActionHooks.onShutdown = [this]() { m_hookManager.fireBlocking(HookKind::ShuttingDown); }; m_wayland.setPointerEventCallback([this](const PointerEvent& event) { if (m_lockScreen.isActive()) { @@ -1160,6 +1162,26 @@ bool Application::runUserCommand(const std::string& command) { return true; } +bool Application::runUserCommandBlocking(const std::string& command) { + constexpr std::string_view prefix = "noctalia:"; + + if (command.rfind(prefix, 0) == 0) { + const std::string response = m_ipcService.execute(command.substr(prefix.size())); + if (response.rfind("error:", 0) == 0) { + kLog.warn("IPC command '{}' failed: {}", command, response.substr(0, response.find('\n'))); + return false; + } + return true; + } + + const auto result = process::runSync(command); + if (!result) { + kLog.warn("command failed: {} exit_code={} stderr={}", command, result.exitCode, result.err); + return false; + } + return true; +} + bool Application::runIdleCommand(const std::string& command) { return runUserCommand(command); } void Application::onIconThemeChanged() { diff --git a/src/app/application.h b/src/app/application.h index 98325b19d..e4f38e066 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -110,6 +110,7 @@ private: void syncNotificationDaemon(); void syncPolkitAgent(); bool runUserCommand(const std::string& command); + bool runUserCommandBlocking(const std::string& command); bool runIdleCommand(const std::string& command); void onIconThemeChanged(); void onUpowerStateChangedForHooks(); diff --git a/src/hooks/hook_manager.cpp b/src/hooks/hook_manager.cpp index ef4c29e55..b5df0e19d 100644 --- a/src/hooks/hook_manager.cpp +++ b/src/hooks/hook_manager.cpp @@ -12,10 +12,18 @@ namespace { void HookManager::setCommandRunner(CommandRunner runner) { m_runner = std::move(runner); } +void HookManager::setBlockingCommandRunner(CommandRunner runner) { m_blockingRunner = std::move(runner); } + void HookManager::reload(const HooksConfig& config) { m_config = config; } -void HookManager::fire(HookKind kind) const { - if (kind == HookKind::Count || !m_runner) { +void HookManager::fire(HookKind kind) const { fireWithRunner(kind, m_runner); } + +void HookManager::fireBlocking(HookKind kind) const { + fireWithRunner(kind, m_blockingRunner ? m_blockingRunner : m_runner); +} + +void HookManager::fireWithRunner(HookKind kind, const CommandRunner& runner) const { + if (kind == HookKind::Count || !runner) { return; } const auto& cmds = m_config.commands[static_cast(kind)]; @@ -25,7 +33,7 @@ void HookManager::fire(HookKind kind) const { const std::string_view name = hookKindKey(kind); kLog.debug("hook '{}' running {} command(s)", name, cmds.size()); for (const auto& cmd : cmds) { - if (!m_runner(cmd)) { + if (!runner(cmd)) { kLog.warn("hook '{}' command failed: {}", name, cmd); } } diff --git a/src/hooks/hook_manager.h b/src/hooks/hook_manager.h index bfb5dd0cc..962693361 100644 --- a/src/hooks/hook_manager.h +++ b/src/hooks/hook_manager.h @@ -13,13 +13,18 @@ public: using EnvVar = std::pair; void setCommandRunner(CommandRunner runner); + void setBlockingCommandRunner(CommandRunner runner); void reload(const HooksConfig& config); void fire(HookKind kind) const; + void fireBlocking(HookKind kind) const; void fire(HookKind kind, std::initializer_list env) const; [[nodiscard]] const HooksConfig& config() const noexcept { return m_config; } private: + void fireWithRunner(HookKind kind, const CommandRunner& runner) const; + HooksConfig m_config; CommandRunner m_runner; + CommandRunner m_blockingRunner; };