From 2696ea2322dfdd7b7a5b08843b6d8aca9aedd741 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Wed, 8 Jan 2025 14:21:11 +0100 Subject: use attachconsole to send ctrl+c to running process --- src/zenutil/service.cpp | 83 ++++++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 42 deletions(-) (limited to 'src/zenutil/service.cpp') diff --git a/src/zenutil/service.cpp b/src/zenutil/service.cpp index 44aa50494..3d7808bba 100644 --- a/src/zenutil/service.cpp +++ b/src/zenutil/service.cpp @@ -6,11 +6,6 @@ #include #include #include -#include - -ZEN_THIRD_PARTY_INCLUDES_START -#include -ZEN_THIRD_PARTY_INCLUDES_END #if ZEN_PLATFORM_WINDOWS # include @@ -750,6 +745,46 @@ StartService(std::string_view ServiceName, ServiceLevel Level) } } +std::error_code +SendSignalToProcess(DWORD dwProcessId, DWORD dwCtrlEvent) +{ + std::error_code Ec = {}; + DWORD ConsoleId = GetCurrentProcessId(); + // Leave current console if it exists + // (otherwise AttachConsole will return ERROR_ACCESS_DENIED) + bool ConsoleDetached = FreeConsole() != FALSE; + + if (AttachConsole(dwProcessId) != FALSE) + { + // Add a fake Ctrl-C handler for avoid instant kill is this console + // WARNING: do not revert it or current program will be also killed + SetConsoleCtrlHandler(nullptr, true); + if (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) == FALSE) + { + Ec = MakeErrorCodeFromLastError(); + } + FreeConsole(); + } + else + { + Ec = MakeErrorCodeFromLastError(); + } + + if (ConsoleDetached) + { + // Create a new console if previous was deleted by OS + if (AttachConsole(ConsoleId) == FALSE) + { + int errorCode = GetLastError(); + if (errorCode == 31) // 31=ERROR_GEN_FAILURE + { + AllocConsole(); + } + } + } + return Ec; +} + std::error_code StopRunService(bool AllUsers, std::string_view ServiceName) { @@ -777,43 +812,7 @@ StopRunService(bool AllUsers, std::string_view ServiceName) } else { - // This is hacky and checks if the running service is a zenserver instance and tries to shut down using the shutdown - // event - ExtendableStringBuilder<32> ChildShutdownEventName; - ZenServerState State; - if (State.InitializeReadOnly()) - { - State.Snapshot([&](const ZenServerState::ZenServerEntry& Entry) { - if (Entry.Pid == gsl::narrow(Proc.Pid())) - { - ChildShutdownEventName << "Zen_" << Entry.EffectiveListenPort; - ChildShutdownEventName << "_Shutdown"; - } - }); - if (ChildShutdownEventName.Size() > 0) - { - NamedEvent Event(ChildShutdownEventName); - Ec = Event.Set(); - if (Ec) - { - return Ec; - } - return {}; - } - } - // This only works for a running process that does not already have a console attached - zenserver does have one - // hence the attempt to shut down using event above - if (AttachConsole(Proc.Pid())) - { - if (SetConsoleCtrlHandler(NULL, TRUE)) - { - if (GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) - { - return {}; - } - } - } - return MakeErrorCodeFromLastError(); + return SendSignalToProcess(Proc.Pid(), CTRL_C_EVENT); } } return MakeErrorCode(ERROR_INVALID_PARAMETER); -- cgit v1.2.3