tgstation-server  4.3.2
The /tg/station 13 server suite
Process.cs
Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Diagnostics;
4 using System.Text;
5 using System.Threading;
6 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host.System
9 {
11  sealed class Process : IProcess
12  {
14  public int Id { get; }
15 
17  public Task Startup { get; }
18 
20  public Task<int> Lifetime { get; }
21 
26 
30  readonly ILogger<Process> logger;
31 
32  readonly global::System.Diagnostics.Process handle;
33 
34  readonly StringBuilder outputStringBuilder;
35  readonly StringBuilder errorStringBuilder;
36  readonly StringBuilder combinedStringBuilder;
37 
49  public Process(
50  IProcessFeatures processFeatures,
51  global::System.Diagnostics.Process handle,
52  Task<int> lifetime,
53  StringBuilder outputStringBuilder,
54  StringBuilder errorStringBuilder,
55  StringBuilder combinedStringBuilder,
56  ILogger<Process> logger,
57  bool preExisting)
58  {
59  this.processFeatures = processFeatures ?? throw new ArgumentNullException(nameof(processFeatures));
60  this.handle = handle ?? throw new ArgumentNullException(nameof(handle));
61 
62  this.outputStringBuilder = outputStringBuilder;
63  this.errorStringBuilder = errorStringBuilder;
64  this.combinedStringBuilder = combinedStringBuilder;
65 
66  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
67 
68  Lifetime = WrapLifetimeTask(lifetime ?? throw new ArgumentNullException(nameof(lifetime)));
69 
70  Id = handle.Id;
71 
72  if (preExisting)
73  {
74  Startup = Task.CompletedTask;
75  return;
76  }
77 
78  Startup = Task.Factory.StartNew(() =>
79  {
80  try
81  {
82  handle.WaitForInputIdle();
83  }
84  catch (InvalidOperationException) { }
85  }, default, TaskCreationOptions.LongRunning, TaskScheduler.Current);
86 
87  logger.LogTrace("Created process ID: {0}", Id);
88  }
89 
91  public void Dispose() => handle.Dispose();
92 
93  async Task<int> WrapLifetimeTask(Task<int> lifetimeTask)
94  {
95  var result = await lifetimeTask.ConfigureAwait(false);
96  logger.LogTrace("PID {0} ended with code {1}", Id, result);
97  return result;
98  }
99 
101  public string GetCombinedOutput()
102  {
103  if (combinedStringBuilder == null)
104  throw new InvalidOperationException("Output/Error reading was not enabled!");
105  return combinedStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
106  }
107 
109  public string GetErrorOutput()
110  {
111  if (errorStringBuilder == null)
112  throw new InvalidOperationException("Error reading was not enabled!");
113  return errorStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
114  }
115 
117  public string GetStandardOutput()
118  {
119  if (outputStringBuilder == null)
120  throw new InvalidOperationException("Output reading was not enabled!");
121  return outputStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
122  }
123 
125  public void Terminate()
126  {
127  if (handle.HasExited)
128  return;
129  try
130  {
131  logger.LogTrace("Terminating PID {0}...", Id);
132  handle.Kill();
133  handle.WaitForExit();
134  }
135  catch (Exception e)
136  {
137  logger.LogDebug("Process termination exception: {0}", e);
138  }
139  }
140 
142  public void SetHighPriority()
143  {
144  try
145  {
146  handle.PriorityClass = ProcessPriorityClass.AboveNormal;
147  logger.LogTrace("Set PID {0} to above normal priority", Id);
148  }
149  catch (Exception e)
150  {
151  logger.LogWarning("Unable to raise process priority for PID {0}! Exception: {1}", Id, e);
152  }
153  }
154 
156  public void Suspend() => processFeatures.SuspendProcess(handle);
157 
159  public void Resume() => processFeatures.ResumeProcess(handle);
160 
162  public async Task<string> GetExecutingUsername(CancellationToken cancellationToken)
163  {
164  var result = await processFeatures.GetExecutingUsername(handle, cancellationToken).ConfigureAwait(false);
165  logger.LogTrace("PID {0} Username: {1}", Id, result);
166  return result;
167  }
168  }
169 }
string GetCombinedOutput()
Get the stderr and stdout output of the IProcess
Definition: Process.cs:101
void SetHighPriority()
Set&#39;s the owned global::System.Diagnostics.Process.PriorityClass to global::System.Diagnostics.ProcessPriorityClass.AboveNormal
Definition: Process.cs:142
string GetStandardOutput()
Get the stdout output of the IProcess
Definition: Process.cs:117
string GetErrorOutput()
Get the stderr output of the IProcess
Definition: Process.cs:109
readonly StringBuilder errorStringBuilder
Definition: Process.cs:35
void Terminate()
Terminates the process
Definition: Process.cs:125
async Task< string > GetExecutingUsername(CancellationToken cancellationToken)
Get the name of the account executing the IProcess.
Definition: Process.cs:162
readonly IProcessFeatures processFeatures
The IProcessFeatures for the Process.
Definition: Process.cs:25
Abstraction over a global::System.Diagnostics.Process
Definition: IProcess.cs:9
readonly global::System.Diagnostics.Process handle
Definition: Process.cs:32
Process(IProcessFeatures processFeatures, global::System.Diagnostics.Process handle, Task< int > lifetime, StringBuilder outputStringBuilder, StringBuilder errorStringBuilder, StringBuilder combinedStringBuilder, ILogger< Process > logger, bool preExisting)
Construct a Process
Definition: Process.cs:49
readonly ILogger< Process > logger
The ILogger for the Process
Definition: Process.cs:30
Abstraction for suspending and resuming processes.
readonly StringBuilder outputStringBuilder
Definition: Process.cs:34
readonly StringBuilder combinedStringBuilder
Definition: Process.cs:36
async Task< int > WrapLifetimeTask(Task< int > lifetimeTask)
Definition: Process.cs:93