From 253b1010cf9b02070b59dda42fd60b7e06b7fc8e Mon Sep 17 00:00:00 2001 From: Timerix22 Date: Fri, 16 Dec 2022 10:00:54 +0600 Subject: [PATCH] ProcessExtensions --- DTLib/Extensions/IfMethod.cs | 21 ----- .../IProcessSuspenderImpl.cs | 11 +++ .../ProcessExtensions/ProcessExtensions.cs | 38 ++++++++ .../ProcessSuspenderImplUnix.cs | 92 +++++++++++++++++++ .../ProcessSuspenderImplWindows.cs | 63 +++++++++++++ .../{OtherStuff.cs => StreamExtensions.cs} | 2 +- 6 files changed, 205 insertions(+), 22 deletions(-) delete mode 100644 DTLib/Extensions/IfMethod.cs create mode 100644 DTLib/Extensions/ProcessExtensions/IProcessSuspenderImpl.cs create mode 100644 DTLib/Extensions/ProcessExtensions/ProcessExtensions.cs create mode 100644 DTLib/Extensions/ProcessExtensions/ProcessSuspenderImplUnix.cs create mode 100644 DTLib/Extensions/ProcessExtensions/ProcessSuspenderImplWindows.cs rename DTLib/Extensions/{OtherStuff.cs => StreamExtensions.cs} (77%) diff --git a/DTLib/Extensions/IfMethod.cs b/DTLib/Extensions/IfMethod.cs deleted file mode 100644 index 8644cda..0000000 --- a/DTLib/Extensions/IfMethod.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace DTLib.Extensions; - -public static class IfMethod -{ - public static T If(this T input, bool condition, Func if_true, Func if_false) => - condition ? if_true(input) : if_false(input); - - public static void If(this T input, bool condition, Action if_true, Action if_false) - { - if (condition) if_true(input); - else if_false(input); - } - - public static T If(this T input, bool condition, Func if_true) => - condition ? if_true(input) : input; - - public static void If(this T input, bool condition, Action if_true) - { - if (condition) if_true(input); - } -} diff --git a/DTLib/Extensions/ProcessExtensions/IProcessSuspenderImpl.cs b/DTLib/Extensions/ProcessExtensions/IProcessSuspenderImpl.cs new file mode 100644 index 0000000..87cedb2 --- /dev/null +++ b/DTLib/Extensions/ProcessExtensions/IProcessSuspenderImpl.cs @@ -0,0 +1,11 @@ +using System.Diagnostics; + +namespace DTLib.Extensions; + +internal interface IProcessSuspenderImpl +{ + void Suspend(Process p); + void Suspend(int pid); + void Resume(Process p); + void Resume(int pid); +} \ No newline at end of file diff --git a/DTLib/Extensions/ProcessExtensions/ProcessExtensions.cs b/DTLib/Extensions/ProcessExtensions/ProcessExtensions.cs new file mode 100644 index 0000000..76d8d29 --- /dev/null +++ b/DTLib/Extensions/ProcessExtensions/ProcessExtensions.cs @@ -0,0 +1,38 @@ + +// ReSharper disable UnusedMember.Local + +using System.Diagnostics; + +namespace DTLib.Extensions; + +public static class ProcessExtensions +{ + private static IProcessSuspenderImpl processSuspender; + + + static ProcessExtensions() + { + if (Environment.OSVersion.Platform == PlatformID.Win32NT) + processSuspender = new ProcessSuspenderImplWindows(); + else + processSuspender = new ProcessSuspenderImplUnix(); + } + + public static void Suspend(this Process p) => processSuspender.Suspend(p); + + public static void Suspend(int pid) + { + if (pid <= 0) + throw new Exception($"invalid pid: {pid}"); + processSuspender.Suspend(pid); + } + + public static void Resume(this Process p) => processSuspender.Resume(p); + + public static void Resume(int pid) + { + if (pid <= 0) + throw new Exception($"invalid pid: {pid}"); + processSuspender.Resume(pid); + } +} \ No newline at end of file diff --git a/DTLib/Extensions/ProcessExtensions/ProcessSuspenderImplUnix.cs b/DTLib/Extensions/ProcessExtensions/ProcessSuspenderImplUnix.cs new file mode 100644 index 0000000..727c374 --- /dev/null +++ b/DTLib/Extensions/ProcessExtensions/ProcessSuspenderImplUnix.cs @@ -0,0 +1,92 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace DTLib.Extensions; + +// https://stackoverflow.com/questions/41041730/net-core-app-how-to-send-sigterm-to-child-processes +internal class ProcessSuspenderImplUnix : IProcessSuspenderImpl +{ + [SuppressMessage("ReSharper", "UnusedMember.Local")] + enum Signum + { + /// Hangup (POSIX). + SIGHUP = 1, + /// Interrupt (ANSI). + SIGINT = 2, + /// Quit (POSIX). + SIGQUIT = 3, + /// Illegal instruction (ANSI). + SIGILL = 4, + /// Trace trap (POSIX). + SIGTRAP = 5, + /// Abort (ANSI). + SIGABRT = 6, + /// IOT trap (4.2 BSD). + SIGIOT = 6, + /// BUS error (4.2 BSD). + SIGBUS = 7, + /// Floating-point exception (ANSI). + SIGFPE = 8, + /// Kill, unblockable (POSIX). + SIGKILL = 9, + /// User-defined signal 1 (POSIX). + SIGUSR1 = 10, + /// Segmentation violation (ANSI). + SIGSEGV = 11, + /// User-defined signal 2 (POSIX). + SIGUSR2 = 12, + /// Broken pipe (POSIX). + SIGPIPE = 13, + /// Alarm clock (POSIX). + SIGALRM = 14, + /// Termination (ANSI). + SIGTERM = 15, + /// Stack fault. + SIGSTKFLT = 16, + /// Same as SIGCHLD (System V). + SIGCLD = SIGCHLD, + /// Child status has changed (POSIX). + SIGCHLD = 17, + /// Continue (POSIX). + SIGCONT = 18, + /// Stop, unblockable (POSIX). + SIGSTOP = 19, + /// Keyboard stop (POSIX). + SIGTSTP = 20, + /// Background read from tty (POSIX). + SIGTTIN = 21, + /// Background write to tty (POSIX). + SIGTTOU = 22, + /// Urgent condition on socket (4.2 BSD). + SIGURG = 23, + /// CPU limit exceeded (4.2 BSD). + SIGXCPU = 24, + /// File size limit exceeded (4.2 BSD). + SIGXFSZ = 25, + /// Virtual alarm clock (4.2 BSD). + SIGVTALRM = 26, + /// Profiling alarm clock (4.2 BSD). + SIGPROF = 27, + /// Window size change (4.3 BSD, Sun). + SIGWINCH = 28, + /// Pollable event occurred (System V). + SIGPOLL = SIGIO, + /// I/O now possible (4.2 BSD). + SIGIO = 29, + /// Power failure restart (System V). + SIGPWR = 30, + /// Bad system call. + SIGSYS = 31, + SIGUNUSED = 31 + } + + [DllImport ("libc", SetLastError=true, EntryPoint="kill")] + private static extern int sys_kill (int pid, int sig); + + public void Suspend(Process p) => Suspend(p.Id); + public void Suspend(int pid) => sys_kill(pid, (int)Signum.SIGSTOP); + + public void Resume(Process p) => Resume(p.Id); + public void Resume(int pid) => sys_kill(pid, (int)Signum.SIGCONT); +} \ No newline at end of file diff --git a/DTLib/Extensions/ProcessExtensions/ProcessSuspenderImplWindows.cs b/DTLib/Extensions/ProcessExtensions/ProcessSuspenderImplWindows.cs new file mode 100644 index 0000000..8a87fa0 --- /dev/null +++ b/DTLib/Extensions/ProcessExtensions/ProcessSuspenderImplWindows.cs @@ -0,0 +1,63 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace DTLib.Extensions; + +// https://github.com/SarathR/ProcessUtil +internal class ProcessSuspenderImplWindows : IProcessSuspenderImpl +{ + [SuppressMessage("ReSharper", "UnusedMember.Local")] + enum ThreadAccess + { + TERMINATE = 0x0001, + SUSPEND_RESUME = 0x0002, + GET_CONTEXT = 0x0008, + SET_CONTEXT = 0x0010, + SET_INFORMATION = 0x0020, + QUERY_INFORMATION = 0x0040, + SET_THREAD_TOKEN = 0x0080, + IMPERSONATE = 0x0100, + DIRECT_IMPERSONATION = 0x0200 + } + + [DllImport("kernel32.dll", SetLastError = true)] + static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); + [DllImport("kernel32.dll", SetLastError = true)] + static extern uint SuspendThread(IntPtr hThread); + [DllImport("kernel32.dll", SetLastError = true)] + static extern int ResumeThread(IntPtr hThread); + + + public void Suspend(Process process) + { + foreach (ProcessThread thread in process.Threads) + { + var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id); + if (pOpenThread == IntPtr.Zero) + { + int errCode=Marshal.GetLastWin32Error(); + throw new Exception($"unmanaged function exited with error: {errCode}"); + } + SuspendThread(pOpenThread); + } + } + + public void Suspend(int pid) => Suspend(Process.GetProcessById(pid)); + + public void Resume(Process process) + { + foreach (ProcessThread thread in process.Threads) + { + var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id); + if (pOpenThread == IntPtr.Zero) + { + int errCode=Marshal.GetLastWin32Error(); + throw new Exception($"unmanaged function exited with error: {errCode}"); + } + ResumeThread(pOpenThread); + } + } + + public void Resume(int pid) => Resume(Process.GetProcessById(pid)); +} \ No newline at end of file diff --git a/DTLib/Extensions/OtherStuff.cs b/DTLib/Extensions/StreamExtensions.cs similarity index 77% rename from DTLib/Extensions/OtherStuff.cs rename to DTLib/Extensions/StreamExtensions.cs index b6260d5..06130fc 100644 --- a/DTLib/Extensions/OtherStuff.cs +++ b/DTLib/Extensions/StreamExtensions.cs @@ -2,7 +2,7 @@ using System.IO; namespace DTLib.Extensions; -public static class OtherStuff +public static class StreamExtensions { public static void Write(this Stream stream, byte[] buff) => stream.Write(buff, 0, buff.Length);