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 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" />
|
/// <exception cref="CommandException" />
|
||||||
internal ServiceControllerStatus Status
|
internal ServiceControllerStatus Status
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,6 +65,14 @@ namespace WinSW.Native
|
||||||
[DllImport(Libraries.Advapi32, SetLastError = true)]
|
[DllImport(Libraries.Advapi32, SetLastError = true)]
|
||||||
internal static extern bool QueryServiceStatus(IntPtr serviceHandle, out SERVICE_STATUS serviceStatus);
|
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)]
|
[DllImport(Libraries.Advapi32, SetLastError = true)]
|
||||||
internal static extern bool SetServiceObjectSecurity(IntPtr serviceHandle, SecurityInfos securityInformation, byte[] securityDescriptor);
|
internal static extern bool SetServiceObjectSecurity(IntPtr serviceHandle, SecurityInfos securityInformation, byte[] securityDescriptor);
|
||||||
|
|
||||||
|
@ -145,6 +153,12 @@ namespace WinSW.Native
|
||||||
MODIFY_BOOT_CONFIG,
|
MODIFY_BOOT_CONFIG,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SC_STATUS_
|
||||||
|
internal enum ServiceStatusType
|
||||||
|
{
|
||||||
|
ProcessInfo = 0,
|
||||||
|
}
|
||||||
|
|
||||||
internal struct SERVICE_DELAYED_AUTO_START_INFO
|
internal struct SERVICE_DELAYED_AUTO_START_INFO
|
||||||
{
|
{
|
||||||
public bool DelayedAutostart;
|
public bool DelayedAutostart;
|
||||||
|
@ -182,5 +196,18 @@ namespace WinSW.Native
|
||||||
public int CheckPoint;
|
public int CheckPoint;
|
||||||
public int WaitHint;
|
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);
|
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)
|
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)
|
private static void Stop(Process process, TimeSpan stopTimeout)
|
||||||
{
|
{
|
||||||
Logger.Info("Stopping process " + process.Id);
|
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
|
// 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 log4net.Layout;
|
||||||
using WinSW.Logging;
|
using WinSW.Logging;
|
||||||
using WinSW.Native;
|
using WinSW.Native;
|
||||||
|
using WinSW.Util;
|
||||||
using Process = System.Diagnostics.Process;
|
using Process = System.Diagnostics.Process;
|
||||||
using TimeoutException = System.ServiceProcess.TimeoutException;
|
using TimeoutException = System.ServiceProcess.TimeoutException;
|
||||||
|
|
||||||
|
@ -238,6 +239,22 @@ namespace WinSW
|
||||||
root.Add(refresh);
|
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)
|
return new CommandLineBuilder(root)
|
||||||
|
|
||||||
// see UseDefaults
|
// 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]
|
// [DoesNotReturn]
|
||||||
static void Elevate(bool noElevate)
|
static void Elevate(bool noElevate)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue