aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/process.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2024-04-17 12:42:01 +0200
committerGitHub Enterprise <[email protected]>2024-04-17 12:42:01 +0200
commitc956e958e0a386f24e6865ad62ee4fe640f93b18 (patch)
tree96a2d52c5e33e9af76d36535b0c675d56b685617 /src/zencore/process.cpp
parentgc v2 disk freed space fix and oplog stats report improvement (#45) (diff)
downloadzen-c956e958e0a386f24e6865ad62ee4fe640f93b18.tar.xz
zen-c956e958e0a386f24e6865ad62ee4fe640f93b18.zip
zen startup hardening (#49)
- Feature: `zen up` command improvements - --`port` allows you to specify a base port when starting an instance - --`base-dir` allows you to specify a base directory for the zenserver executable if it is not located next to the zen.exe executable - Feature: `zen down` - --`port` allows you to specify a base port when shutting down an instance - --`base-dir` allows you to specify a base directory for the zenserver executable if it is not located next to the zen.exe executable - --`force` if regular shutdown fails it tries to find a running zenserver.exe process and terminate it - If it fails to attach to the running server it now waits for it to exit when setting the RequestExit shared memory flag - Improvement: zenserver now checks the RequestExit flag in the shared memory and exist gracefully if it is set - Improvement: When adding a sponsor process to a running zenserver instance, we wait for it to be picked up from the shared memory section to determine success/fail
Diffstat (limited to 'src/zencore/process.cpp')
-rw-r--r--src/zencore/process.cpp257
1 files changed, 241 insertions, 16 deletions
diff --git a/src/zencore/process.cpp b/src/zencore/process.cpp
index d8be0d343..df2a87352 100644
--- a/src/zencore/process.cpp
+++ b/src/zencore/process.cpp
@@ -14,6 +14,7 @@
#if ZEN_PLATFORM_WINDOWS
# include <shellapi.h>
# include <Shlobj.h>
+# include <TlHelp32.h>
# include <zencore/windows.h>
#else
# include <fcntl.h>
@@ -23,11 +24,16 @@
# include <sys/sem.h>
# include <sys/stat.h>
# include <sys/syscall.h>
+# include <sys/sysctl.h>
# include <sys/wait.h>
# include <time.h>
# include <unistd.h>
#endif
+#if ZEN_PLATFORM_MAC
+# include <libproc.h>
+#endif
+
ZEN_THIRD_PARTY_INCLUDES_START
#include <fmt/format.h>
ZEN_THIRD_PARTY_INCLUDES_END
@@ -48,6 +54,36 @@ const bool bNoZombieChildren = []() {
sigaction(SIGCHLD, &Action, nullptr);
return true;
}();
+
+static char
+GetPidStatus(int Pid)
+{
+ std::filesystem::path EntryPath = std::filesystem::path("/proc") / fmt::format("{}", Pid);
+ std::filesystem::path StatPath = EntryPath / "stat";
+ if (std::filesystem::is_regular_file(StatPath))
+ {
+ FILE* StatFile = fopen(StatPath.c_str(), "r");
+ if (StatFile)
+ {
+ char Buffer[5120];
+ int Size = fread(Buffer, 1, 5120 - 1, StatFile);
+ fclose(StatFile);
+ if (Size > 0)
+ {
+ Buffer[Size + 1] = 0;
+ char* ScanPtr = strrchr(Buffer, ')');
+ if (ScanPtr && ScanPtr[1] != '\0')
+ {
+ ScanPtr += 2;
+ char State = *ScanPtr;
+ return State;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
#endif
ProcessHandle::ProcessHandle() = default;
@@ -121,7 +157,8 @@ ProcessHandle::IsRunning() const
GetExitCodeProcess(m_ProcessHandle, &ExitCode);
bActive = (ExitCode == STILL_ACTIVE);
#elif ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
- bActive = (kill(pid_t(m_Pid), 0) == 0);
+ std::error_code _;
+ bActive = IsProcessRunning(m_Pid, _);
#endif
return bActive;
@@ -133,29 +170,40 @@ ProcessHandle::IsValid() const
return (m_ProcessHandle != nullptr);
}
-void
+bool
ProcessHandle::Terminate(int ExitCode)
{
if (!IsRunning())
{
- return;
+ return true;
}
- bool bSuccess = false;
-
#if ZEN_PLATFORM_WINDOWS
- TerminateProcess(m_ProcessHandle, ExitCode);
+ BOOL bTerminated = TerminateProcess(m_ProcessHandle, ExitCode);
+ if (!bTerminated)
+ {
+ return false;
+ }
DWORD WaitResult = WaitForSingleObject(m_ProcessHandle, INFINITE);
- bSuccess = (WaitResult != WAIT_OBJECT_0);
+ bool bSuccess = (WaitResult == WAIT_OBJECT_0) || (WaitResult == WAIT_ABANDONED_0);
+ if (!bSuccess)
+ {
+ return false;
+ }
#elif ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
ZEN_UNUSED(ExitCode);
- bSuccess = (kill(m_Pid, SIGKILL) == 0);
-#endif
-
- if (!bSuccess)
+ int Res = kill(pid_t(m_Pid), SIGKILL);
+ if (Res != 0)
{
- // What might go wrong here, and what is meaningful to act on?
+ int err = errno;
+ if (err != ESRCH)
+ {
+ return false;
+ }
}
+#endif
+ Reset();
+ return true;
}
void
@@ -649,7 +697,7 @@ ProcessMonitor::IsActive() const
//////////////////////////////////////////////////////////////////////////
bool
-IsProcessRunning(int pid)
+IsProcessRunning(int pid, std::error_code& OutEc)
{
// This function is arguably not super useful, a pid can be re-used
// by the OS so holding on to a pid and polling it over some time
@@ -665,7 +713,8 @@ IsProcessRunning(int pid)
{
return false;
}
- ThrowSystemError(Error, fmt::format("failed to open process with pid {}", pid));
+ OutEc = MakeErrorCode(Error);
+ return false;
}
auto _ = MakeGuard([hProc]() { CloseHandle(hProc); });
@@ -678,27 +727,72 @@ IsProcessRunning(int pid)
else
{
DWORD Error = GetLastError();
- ThrowSystemError(Error, fmt::format("failed to get process exit code for pid {}", pid));
+ OutEc = MakeErrorCode(Error);
+ return false;
}
return bStillActive;
#elif ZEN_PLATFORM_LINUX || ZEN_PLATFORM_MAC
int Res = kill(pid_t(pid), 0);
if (Res == 0)
{
+# if ZEN_PLATFORM_MAC
+ struct kinfo_proc Info;
+ int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+ size_t InfoSize = sizeof Info;
+
+ int Res = sysctl(Mib, 4, &Info, &InfoSize, NULL, 0);
+ if (Res != 0)
+ {
+ int Error = errno;
+ OutEc = MakeErrorCode(Error);
+ return false;
+ }
+ ZEN_INFO("Found process {} with status {}", pid, (int)Info.kp_proc.p_stat);
+ if (Info.kp_proc.p_stat == SZOMB)
+ {
+ // Zombie process
+ return false;
+ }
+ return true;
+# endif // ZEN_PLATFORM_MAC
+# if ZEN_PLATFORM_LINUX
+ char Status = GetPidStatus(pid);
+ if (Status == 'Z' || Status == 0)
+ {
+ return false;
+ }
return true;
+# endif // ZEN_PLATFORM_LINUX
}
int Error = errno;
if (Error == ESRCH) // No such process
{
return false;
}
+ else if (Error == ENOENT)
+ {
+ return false;
+ }
else
{
- ThrowSystemError(Error, fmt::format("Failed to signal running process %d: %d", pid, Error));
+ OutEc = MakeErrorCode(Error);
+ return false;
}
#endif
}
+bool
+IsProcessRunning(int pid)
+{
+ std::error_code Ec;
+ bool IsRunning = IsProcessRunning(pid, Ec);
+ if (Ec)
+ {
+ ThrowSystemError(Ec.value(), fmt::format("Failed determining if process with pid {} is running", pid));
+ }
+ return IsRunning;
+}
+
int
GetCurrentProcessId()
{
@@ -719,6 +813,129 @@ GetProcessId(CreateProcResult ProcId)
#endif
}
+std::error_code
+FindProcess(const std::filesystem::path& ExecutableImage, ProcessHandle& OutHandle)
+{
+#if ZEN_PLATFORM_WINDOWS
+ HANDLE ProcessSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (ProcessSnapshotHandle == INVALID_HANDLE_VALUE)
+ {
+ return MakeErrorCodeFromLastError();
+ }
+ auto _ = MakeGuard([&]() { CloseHandle(ProcessSnapshotHandle); });
+
+ PROCESSENTRY32 Entry;
+ Entry.dwSize = sizeof(PROCESSENTRY32);
+ if (Process32First(ProcessSnapshotHandle, (LPPROCESSENTRY32)&Entry))
+ {
+ do
+ {
+ if (ExecutableImage.filename() == Entry.szExeFile)
+ {
+ HANDLE ModuleSnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, Entry.th32ProcessID);
+ if (ModuleSnapshotHandle != INVALID_HANDLE_VALUE)
+ {
+ auto __ = MakeGuard([&]() { CloseHandle(ModuleSnapshotHandle); });
+ MODULEENTRY32 ModuleEntry;
+ ModuleEntry.dwSize = sizeof(MODULEENTRY32);
+ if (Module32First(ModuleSnapshotHandle, (LPMODULEENTRY32)&ModuleEntry))
+ {
+ std::filesystem::path EntryPath(ModuleEntry.szExePath);
+ if (EntryPath == ExecutableImage)
+ {
+ HANDLE Handle =
+ OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, Entry.th32ProcessID);
+ if (Handle == NULL)
+ {
+ return MakeErrorCodeFromLastError();
+ }
+ OutHandle.Initialize((void*)Handle);
+ return {};
+ }
+ }
+ }
+ }
+ } while (::Process32Next(ProcessSnapshotHandle, (LPPROCESSENTRY32)&Entry));
+ }
+ return MakeErrorCodeFromLastError();
+#endif // ZEN_PLATFORM_WINDOWS
+#if ZEN_PLATFORM_MAC
+ int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
+ size_t BufferSize = 0;
+
+ struct kinfo_proc* Processes = nullptr;
+ uint32_t ProcCount = 0;
+
+ if (sysctl(Mib, 4, NULL, &BufferSize, NULL, 0) != -1 && BufferSize > 0)
+ {
+ struct kinfo_proc* Processes = (struct kinfo_proc*)malloc(BufferSize);
+ auto _ = MakeGuard([&]() { free(Processes); });
+ if (sysctl(Mib, 4, Processes, &BufferSize, NULL, 0) != -1)
+ {
+ ProcCount = (uint32_t)(BufferSize / sizeof(struct kinfo_proc));
+ char Buffer[PROC_PIDPATHINFO_MAXSIZE];
+ for (uint32_t ProcIndex = 0; ProcIndex < ProcCount; ProcIndex++)
+ {
+ if (Processes[ProcIndex].kp_proc.p_stat != SZOMB)
+ {
+ pid_t Pid = Processes[ProcIndex].kp_proc.p_pid;
+ int Res = proc_pidpath(Pid, Buffer, sizeof(Buffer));
+ if (Res > 0)
+ {
+ std::filesystem::path EntryPath(Buffer);
+ if (EntryPath == ExecutableImage)
+ {
+ std::error_code Ec;
+ OutHandle.Initialize(Pid, Ec);
+ return Ec;
+ }
+ }
+ }
+ }
+ }
+ }
+ return MakeErrorCodeFromLastError();
+#endif // ZEN_PLATFORM_MAC
+#if ZEN_PLATFORM_LINUX
+ std::vector<uint32_t> RunningPids;
+ DirectoryContent ProcList;
+ GetDirectoryContent("/proc", DirectoryContent::IncludeDirsFlag, ProcList);
+ for (const std::filesystem::path& EntryPath : ProcList.Directories)
+ {
+ std::string EntryName = EntryPath.stem();
+ std::optional<uint32_t> Pid = ParseInt<uint32_t>(EntryName);
+ if (Pid.has_value())
+ {
+ RunningPids.push_back(Pid.value());
+ }
+ }
+
+ for (uint32_t Pid : RunningPids)
+ {
+ char Status = GetPidStatus(Pid);
+ if (Status && (Status != 'Z'))
+ {
+ std::filesystem::path EntryPath = std::filesystem::path("/proc") / fmt::format("{}", Pid);
+ std::filesystem::path ExeLinkPath = EntryPath / "exe";
+ char Link[4096];
+ ssize_t BytesRead = readlink(ExeLinkPath.c_str(), Link, sizeof(Link) - 1);
+ if (BytesRead > 0)
+ {
+ Link[BytesRead] = '\0';
+ std::filesystem::path ExePath(Link);
+ if (ExePath == ExecutableImage)
+ {
+ std::error_code Ec;
+ OutHandle.Initialize(Pid, Ec);
+ return Ec;
+ }
+ }
+ }
+ }
+ return {};
+#endif // ZEN_PLATFORM_LINUX
+}
+
#if ZEN_WITH_TESTS
void
@@ -735,6 +952,14 @@ TEST_CASE("Process")
CHECK(IsProcessRunning(Pid));
}
+TEST_CASE("FindProcess")
+{
+ ProcessHandle Process;
+ std::error_code Ec = FindProcess(GetRunningExecutablePath(), Process);
+ CHECK(!Ec);
+ CHECK(Process.IsValid());
+}
+
TEST_CASE("BuildArgV")
{
const char* Words[] = {"one", "two", "three", "four", "five"};