tgstation-server 6.12.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
WindowsProcessFeatures.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.ComponentModel;
4using System.Diagnostics;
5using System.IO;
6using System.Linq;
7using System.Management;
8using System.Runtime.Versioning;
9using System.Threading;
10using System.Threading.Tasks;
11
12using Microsoft.Extensions.Logging;
13
17
19{
21 [SupportedOSPlatform("windows")]
23 {
27 readonly ILogger<WindowsProcessFeatures> logger;
28
33 public WindowsProcessFeatures(ILogger<WindowsProcessFeatures> logger)
34 {
35 this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
36 }
37
39 public void ResumeProcess(global::System.Diagnostics.Process process)
40 {
41 ArgumentNullException.ThrowIfNull(process);
42
43 process.Refresh();
44 foreach (ProcessThread thread in process.Threads)
45 {
46 var threadId = (uint)thread.Id;
47 logger.LogTrace("Resuming thread {threadId}...", threadId);
48 var pOpenThread = NativeMethods.OpenThread(NativeMethods.ThreadAccess.SuspendResume, false, threadId);
49 if (pOpenThread == IntPtr.Zero)
50 {
51 logger.LogDebug(new Win32Exception(), "Failed to open thread {threadId}!", threadId);
52 continue;
53 }
54
55 try
56 {
57 if (NativeMethods.ResumeThread(pOpenThread) == UInt32.MaxValue)
58 throw new Win32Exception();
59 }
60 finally
61 {
62 NativeMethods.CloseHandle(pOpenThread);
63 }
64 }
65 }
66
68 public void SuspendProcess(global::System.Diagnostics.Process process)
69 {
70 ArgumentNullException.ThrowIfNull(process);
71
72 var suspendedThreadIds = new HashSet<uint>();
73 bool suspendedNewThreads;
74 do
75 {
76 suspendedNewThreads = false;
77 process.Refresh();
78 foreach (ProcessThread thread in process.Threads)
79 {
80 var threadId = (uint)thread.Id;
81
82 if (!suspendedThreadIds.Add(threadId))
83 continue;
84
85 suspendedNewThreads = true;
86 logger.LogTrace("Suspending thread {threadId}...", threadId);
87 var pOpenThread = NativeMethods.OpenThread(NativeMethods.ThreadAccess.SuspendResume, false, threadId);
88 if (pOpenThread == IntPtr.Zero)
89 {
90 logger.LogDebug(new Win32Exception(), "Failed to open thread {threadId}!", threadId);
91 continue;
92 }
93
94 try
95 {
96 if (NativeMethods.SuspendThread(pOpenThread) == UInt32.MaxValue)
97 throw new Win32Exception();
98 }
99 finally
100 {
101 NativeMethods.CloseHandle(pOpenThread);
102 }
103 }
104 }
105 while (suspendedNewThreads);
106 }
107
109 public string GetExecutingUsername(global::System.Diagnostics.Process process)
110 {
111 string query = $"SELECT * FROM Win32_Process WHERE ProcessId = {process?.Id ?? throw new ArgumentNullException(nameof(process))}";
112 using var searcher = new ManagementObjectSearcher(query);
113 foreach (var obj in searcher.Get().Cast<ManagementObject>())
114 {
115 var argList = new string[] { String.Empty, String.Empty };
116 var returnString = obj.InvokeMethod(
117 "GetOwner",
118 argList)
119 ?.ToString();
120
121 if (!Int32.TryParse(returnString, out var returnVal))
122 return $"BAD RETURN PARSE: {returnString}";
123
124 if (returnVal == 0)
125 {
126 // return DOMAIN\user
127 string owner = argList.Last() + "\\" + argList.First();
128 return owner;
129 }
130 }
131
132 return "NO OWNER";
133 }
134
136 public async ValueTask CreateDump(global::System.Diagnostics.Process process, string outputFile, bool minidump, CancellationToken cancellationToken)
137 {
138 try
139 {
140 if (process.HasExited)
141 throw new JobException(ErrorCode.GameServerOffline);
142 }
143 catch (InvalidOperationException ex)
144 {
145 throw new JobException(ErrorCode.GameServerOffline, ex);
146 }
147
148 await using var fileStream = new FileStream(outputFile, FileMode.CreateNew);
149
150 await Task.Factory.StartNew(
151 () =>
152 {
153 var flags = NativeMethods.MiniDumpType.WithHandleData
154 | NativeMethods.MiniDumpType.WithThreadInfo
155 | NativeMethods.MiniDumpType.WithUnloadedModules;
156
157 if (!minidump)
158 flags |= NativeMethods.MiniDumpType.WithDataSegs
159 | NativeMethods.MiniDumpType.WithFullMemory;
160
162 process.Handle,
163 (uint)process.Id,
164 fileStream.SafeFileHandle,
165 flags,
166 IntPtr.Zero,
167 IntPtr.Zero,
168 IntPtr.Zero))
169 throw new Win32Exception();
170 },
171 cancellationToken,
173 TaskScheduler.Current);
174 }
175
177 public ValueTask<int> HandleProcessStart(global::System.Diagnostics.Process process, CancellationToken cancellationToken)
178 => ValueTask.FromResult((process ?? throw new ArgumentNullException(nameof(process))).Id);
179 }
180}
IIOManager that resolves paths to Environment.CurrentDirectory.
const TaskCreationOptions BlockingTaskCreationOptions
The TaskCreationOptions used to spawn Tasks for potentially long running, blocking operations.
Operation exceptions thrown from the context of a Models.Job.
Native methods used by the code.
MiniDumpType
See https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ne-minidumpapiset-minidump_type...
static IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId)
See https://msdn.microsoft.com/en-us/library/windows/desktop/ms684335(v=vs.85).aspx.
static uint ResumeThread(IntPtr hThread)
See https://msdn.microsoft.com/en-us/library/windows/desktop/ms685086(v=vs.85).aspx.
static uint SuspendThread(IntPtr hThread)
See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686345(v=vs.85).aspx.
static bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, MiniDumpType dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam)
See https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwrite...
static bool CloseHandle(IntPtr hObject)
See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx.
ThreadAccess
See https://msdn.microsoft.com/en-us/library/windows/desktop/ms686769(v=vs.85).aspx.
void SuspendProcess(global::System.Diagnostics.Process process)
Suspend a given process .
void ResumeProcess(global::System.Diagnostics.Process process)
Resume a given suspended global::System.Diagnostics.Process.
WindowsProcessFeatures(ILogger< WindowsProcessFeatures > logger)
Initializes a new instance of the WindowsProcessFeatures class.
async ValueTask CreateDump(global::System.Diagnostics.Process process, string outputFile, bool minidump, CancellationToken cancellationToken)
Create a dump file for a given process .A ValueTask representing the running operation.
ValueTask< int > HandleProcessStart(global::System.Diagnostics.Process process, CancellationToken cancellationToken)
Run events on starting a process.A ValueTask<TResult> resulting in the process ID.
readonly ILogger< WindowsProcessFeatures > logger
The ILogger for the WindowsProcessFeatures.
string GetExecutingUsername(global::System.Diagnostics.Process process)
Get the name of the user executing a given process .The name of the user executing process .
Abstraction for suspending and resuming processes.
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition ErrorCode.cs:12