mirror of https://github.com/winsw/winsw
Add `dev ps` command
parent
fcbc087b4b
commit
9573a78668
|
@ -139,6 +139,24 @@ namespace WinSW.Native
|
|||
|
||||
internal Service(IntPtr handle) => this.handle = handle;
|
||||
|
||||
internal unsafe int ProcessId
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!QueryServiceStatusEx(
|
||||
this.handle,
|
||||
ServiceStatusType.ProcessInfo,
|
||||
out SERVICE_STATUS_PROCESS status,
|
||||
sizeof(SERVICE_STATUS_PROCESS),
|
||||
out _))
|
||||
{
|
||||
Throw.Command.Win32Exception("Failed to query service status.");
|
||||
}
|
||||
|
||||
return status.CurrentState == ServiceControllerStatus.Running ? status.ProcessId : -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <exception cref="CommandException" />
|
||||
internal ServiceControllerStatus Status
|
||||
{
|
||||
|
|
|
@ -65,6 +65,14 @@ namespace WinSW.Native
|
|||
[DllImport(Libraries.Advapi32, SetLastError = true)]
|
||||
internal static extern bool QueryServiceStatus(IntPtr serviceHandle, out SERVICE_STATUS serviceStatus);
|
||||
|
||||
[DllImport(Libraries.Advapi32, SetLastError = true)]
|
||||
internal static extern bool QueryServiceStatusEx(
|
||||
IntPtr serviceHandle,
|
||||
ServiceStatusType infoLevel,
|
||||
out SERVICE_STATUS_PROCESS buffer,
|
||||
int bufferSize,
|
||||
out int bytesNeeded);
|
||||
|
||||
[DllImport(Libraries.Advapi32, SetLastError = true)]
|
||||
internal static extern bool SetServiceObjectSecurity(IntPtr serviceHandle, SecurityInfos securityInformation, byte[] securityDescriptor);
|
||||
|
||||
|
@ -145,6 +153,12 @@ namespace WinSW.Native
|
|||
MODIFY_BOOT_CONFIG,
|
||||
}
|
||||
|
||||
// SC_STATUS_
|
||||
internal enum ServiceStatusType
|
||||
{
|
||||
ProcessInfo = 0,
|
||||
}
|
||||
|
||||
internal struct SERVICE_DELAYED_AUTO_START_INFO
|
||||
{
|
||||
public bool DelayedAutostart;
|
||||
|
@ -182,5 +196,18 @@ namespace WinSW.Native
|
|||
public int CheckPoint;
|
||||
public int WaitHint;
|
||||
}
|
||||
|
||||
internal struct SERVICE_STATUS_PROCESS
|
||||
{
|
||||
public ServiceType ServiceType;
|
||||
public ServiceControllerStatus CurrentState;
|
||||
public int ControlsAccepted;
|
||||
public int Win32ExitCode;
|
||||
public int ServiceSpecificExitCode;
|
||||
public int CheckPoint;
|
||||
public int WaitHint;
|
||||
public int ProcessId;
|
||||
public int ServiceFlags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,20 +15,72 @@ namespace WinSW.Util
|
|||
{
|
||||
Stop(process, stopTimeout);
|
||||
|
||||
foreach (Process child in GetDescendants(process))
|
||||
foreach (Process child in GetChildren(process))
|
||||
{
|
||||
StopTree(child, stopTimeout);
|
||||
using (child)
|
||||
{
|
||||
StopTree(child, stopTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void StopDescendants(this Process process, TimeSpan stopTimeout)
|
||||
{
|
||||
foreach (Process child in GetDescendants(process))
|
||||
foreach (Process child in GetChildren(process))
|
||||
{
|
||||
StopTree(child, stopTimeout);
|
||||
using (child)
|
||||
{
|
||||
StopTree(child, stopTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static unsafe List<Process> GetChildren(this Process process)
|
||||
{
|
||||
DateTime startTime = process.StartTime;
|
||||
int processId = process.Id;
|
||||
|
||||
var children = new List<Process>();
|
||||
|
||||
foreach (Process other in Process.GetProcesses())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (other.StartTime <= startTime)
|
||||
{
|
||||
goto Next;
|
||||
}
|
||||
|
||||
IntPtr handle = other.Handle;
|
||||
|
||||
if (NtQueryInformationProcess(
|
||||
handle,
|
||||
PROCESSINFOCLASS.ProcessBasicInformation,
|
||||
out PROCESS_BASIC_INFORMATION information,
|
||||
sizeof(PROCESS_BASIC_INFORMATION)) != 0)
|
||||
{
|
||||
goto Next;
|
||||
}
|
||||
|
||||
if ((int)information.InheritedFromUniqueProcessId == processId)
|
||||
{
|
||||
Logger.Info($"Found child process '{other.Format()}'.");
|
||||
children.Add(other);
|
||||
continue;
|
||||
}
|
||||
|
||||
Next:
|
||||
other.Dispose();
|
||||
}
|
||||
catch (Exception e) when (e is InvalidOperationException || e is Win32Exception)
|
||||
{
|
||||
other.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
private static void Stop(Process process, TimeSpan stopTimeout)
|
||||
{
|
||||
Logger.Info("Stopping process " + process.Id);
|
||||
|
@ -61,51 +113,5 @@ namespace WinSW.Util
|
|||
|
||||
// TODO: Propagate error if process kill fails? Currently we use the legacy behavior
|
||||
}
|
||||
|
||||
private static unsafe List<Process> GetDescendants(Process root)
|
||||
{
|
||||
DateTime startTime = root.StartTime;
|
||||
int processId = root.Id;
|
||||
|
||||
var children = new List<Process>();
|
||||
|
||||
foreach (Process process in Process.GetProcesses())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (process.StartTime <= startTime)
|
||||
{
|
||||
goto Next;
|
||||
}
|
||||
|
||||
IntPtr handle = process.Handle;
|
||||
|
||||
if (NtQueryInformationProcess(
|
||||
handle,
|
||||
PROCESSINFOCLASS.ProcessBasicInformation,
|
||||
out PROCESS_BASIC_INFORMATION information,
|
||||
sizeof(PROCESS_BASIC_INFORMATION)) != 0)
|
||||
{
|
||||
goto Next;
|
||||
}
|
||||
|
||||
if ((int)information.InheritedFromUniqueProcessId == processId)
|
||||
{
|
||||
Logger.Info($"Found child process '{process.Format()}'.");
|
||||
children.Add(process);
|
||||
continue;
|
||||
}
|
||||
|
||||
Next:
|
||||
process.Dispose();
|
||||
}
|
||||
catch (Exception e) when (e is InvalidOperationException || e is Win32Exception)
|
||||
{
|
||||
process.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ using log4net.Core;
|
|||
using log4net.Layout;
|
||||
using WinSW.Logging;
|
||||
using WinSW.Native;
|
||||
using WinSW.Util;
|
||||
using Process = System.Diagnostics.Process;
|
||||
using TimeoutException = System.ServiceProcess.TimeoutException;
|
||||
|
||||
|
@ -238,6 +239,22 @@ namespace WinSW
|
|||
root.Add(refresh);
|
||||
}
|
||||
|
||||
{
|
||||
var dev = new Command("dev");
|
||||
|
||||
dev.Add(config);
|
||||
dev.Add(noElevate);
|
||||
|
||||
root.Add(dev);
|
||||
|
||||
var ps = new Command("ps")
|
||||
{
|
||||
Handler = CommandHandler.Create<string?, bool>(DevPs),
|
||||
};
|
||||
|
||||
dev.Add(ps);
|
||||
}
|
||||
|
||||
return new CommandLineBuilder(root)
|
||||
|
||||
// see UseDefaults
|
||||
|
@ -774,6 +791,58 @@ namespace WinSW
|
|||
}
|
||||
}
|
||||
|
||||
void DevPs(string? pathToConfig, bool noElevate)
|
||||
{
|
||||
XmlServiceConfig config = XmlServiceConfig.Create(pathToConfig);
|
||||
|
||||
if (!elevated)
|
||||
{
|
||||
Elevate(noElevate);
|
||||
return;
|
||||
}
|
||||
|
||||
using ServiceManager scm = ServiceManager.Open();
|
||||
using Service sc = scm.OpenService(config.Id);
|
||||
|
||||
int processId = sc.ProcessId;
|
||||
if (processId >= 0)
|
||||
{
|
||||
const string Vertical = " \u2502 ";
|
||||
const string Corner = " \u2514\u2500";
|
||||
const string Cross = " \u251c\u2500";
|
||||
const string Space = " ";
|
||||
|
||||
using Process process = Process.GetProcessById(processId);
|
||||
Draw(process, string.Empty, true);
|
||||
|
||||
static void Draw(Process process, string indentation, bool isLastChild)
|
||||
{
|
||||
Console.Write(indentation);
|
||||
|
||||
if (isLastChild)
|
||||
{
|
||||
Console.Write(Corner);
|
||||
indentation += Space;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write(Cross);
|
||||
indentation += Vertical;
|
||||
}
|
||||
|
||||
Console.WriteLine(process.Format());
|
||||
|
||||
List<Process> children = process.GetChildren();
|
||||
int count = children.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
using Process child = children[i];
|
||||
Draw(child, indentation, i == count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [DoesNotReturn]
|
||||
static void Elevate(bool noElevate)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue