mirror of https://github.com/winsw/winsw
Merge shared directory mapping into core
parent
4b00bba644
commit
3c5d67e287
|
@ -77,13 +77,15 @@ namespace WinSW.Configuration
|
||||||
public virtual string ErrFilePattern => ".err.log";
|
public virtual string ErrFilePattern => ".err.log";
|
||||||
|
|
||||||
// Environment
|
// Environment
|
||||||
public virtual List<Download> Downloads => new List<Download>(0);
|
public virtual List<Download> Downloads => new(0);
|
||||||
|
|
||||||
public virtual Dictionary<string, string> EnvironmentVariables => new Dictionary<string, string>(0);
|
public virtual Dictionary<string, string> EnvironmentVariables => new(0);
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
public virtual bool BeepOnShutdown => false;
|
public virtual bool BeepOnShutdown => false;
|
||||||
|
|
||||||
|
public virtual List<SharedDirectoryMapperConfig> SharedDirectories => new(0);
|
||||||
|
|
||||||
// Extensions
|
// Extensions
|
||||||
public virtual XmlNode? ExtensionsConfiguration => null;
|
public virtual XmlNode? ExtensionsConfiguration => null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace WinSW
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class XmlServiceConfig : ServiceConfig
|
public class XmlServiceConfig : ServiceConfig
|
||||||
{
|
{
|
||||||
protected readonly XmlDocument dom = new XmlDocument();
|
protected readonly XmlDocument dom = new();
|
||||||
|
|
||||||
private readonly XmlNode root;
|
private readonly XmlNode root;
|
||||||
private readonly Dictionary<string, string> environmentVariables;
|
private readonly Dictionary<string, string> environmentVariables;
|
||||||
|
@ -145,7 +145,7 @@ namespace WinSW
|
||||||
return value is null ? defaultValue : ParseTimeSpan(value);
|
return value is null ? defaultValue : ParseTimeSpan(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<string, long> Suffix = new Dictionary<string, long>
|
private static readonly Dictionary<string, long> Suffix = new()
|
||||||
{
|
{
|
||||||
{ "ms", 1 },
|
{ "ms", 1 },
|
||||||
{ "sec", 1000L },
|
{ "sec", 1000L },
|
||||||
|
@ -581,6 +581,31 @@ namespace WinSW
|
||||||
|
|
||||||
public bool AutoRefresh => this.SingleBoolElementOrDefault("autoRefresh", true);
|
public bool AutoRefresh => this.SingleBoolElementOrDefault("autoRefresh", true);
|
||||||
|
|
||||||
|
public override List<SharedDirectoryMapperConfig> SharedDirectories
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var mapNodes = this.root.SelectSingleNode("sharedDirectoryMapping")?.SelectNodes("map");
|
||||||
|
if (mapNodes is null)
|
||||||
|
{
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new List<SharedDirectoryMapperConfig>(mapNodes.Count);
|
||||||
|
for (int i = 0; i < mapNodes.Count; i++)
|
||||||
|
{
|
||||||
|
if (mapNodes[i] is XmlElement mapElement)
|
||||||
|
{
|
||||||
|
string label = XmlHelper.SingleAttribute<string>(mapElement, "label");
|
||||||
|
string uncPath = XmlHelper.SingleAttribute<string>(mapElement, "uncpath");
|
||||||
|
result.Add(new(label, uncPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Dictionary<string, string> LoadEnvironmentVariables()
|
private Dictionary<string, string> LoadEnvironmentVariables()
|
||||||
{
|
{
|
||||||
var nodeList = this.root.SelectNodes("env")!;
|
var nodeList = this.root.SelectNodes("env")!;
|
||||||
|
|
|
@ -419,7 +419,7 @@ namespace WinSW
|
||||||
private async Task CopyStreamWithRotationAsync(StreamReader reader, string extension)
|
private async Task CopyStreamWithRotationAsync(StreamReader reader, string extension)
|
||||||
{
|
{
|
||||||
// lock required as the timer thread and the thread that will write to the stream could try and access the file stream at the same time
|
// lock required as the timer thread and the thread that will write to the stream could try and access the file stream at the same time
|
||||||
object? fileLock = new object();
|
object? fileLock = new();
|
||||||
|
|
||||||
string? baseDirectory = Path.GetDirectoryName(this.BaseLogFileName)!;
|
string? baseDirectory = Path.GetDirectoryName(this.BaseLogFileName)!;
|
||||||
string? baseFileName = Path.GetFileName(this.BaseLogFileName);
|
string? baseFileName = Path.GetFileName(this.BaseLogFileName);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
internal const string Advapi32 = "advapi32.dll";
|
internal const string Advapi32 = "advapi32.dll";
|
||||||
internal const string CredUI = "credui.dll";
|
internal const string CredUI = "credui.dll";
|
||||||
internal const string Kernel32 = "kernel32.dll";
|
internal const string Kernel32 = "kernel32.dll";
|
||||||
|
internal const string Mpr = "mpr.dll";
|
||||||
internal const string NtDll = "ntdll.dll";
|
internal const string NtDll = "ntdll.dll";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||||
|
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using static WinSW.Native.Libraries;
|
||||||
|
|
||||||
|
namespace WinSW.Native
|
||||||
|
{
|
||||||
|
internal static class NetworkApis
|
||||||
|
{
|
||||||
|
internal const uint RESOURCETYPE_DISK = 0x00000001;
|
||||||
|
|
||||||
|
[DllImport(Mpr, SetLastError = true, CharSet = CharSet.Unicode)]
|
||||||
|
internal static extern int WNetAddConnection2W(in NETRESOURCEW netResource, string? password = null, string? userName = null, uint flags = 0);
|
||||||
|
|
||||||
|
[DllImport(Mpr, SetLastError = true, CharSet = CharSet.Unicode)]
|
||||||
|
internal static extern int WNetCancelConnection2W(string name, uint flags = 0, bool force = false);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
internal struct NETRESOURCEW
|
||||||
|
{
|
||||||
|
public uint Scope;
|
||||||
|
public uint Type;
|
||||||
|
public uint DisplayType;
|
||||||
|
public uint Usage;
|
||||||
|
public string LocalName;
|
||||||
|
public string RemoteName;
|
||||||
|
public string Comment;
|
||||||
|
public string Provider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,9 +7,9 @@ namespace WinSW.Native
|
||||||
{
|
{
|
||||||
internal static class ResourceApis
|
internal static class ResourceApis
|
||||||
{
|
{
|
||||||
internal static readonly IntPtr VS_VERSION_INFO = new IntPtr(1);
|
internal static readonly IntPtr VS_VERSION_INFO = new(1);
|
||||||
|
|
||||||
internal static readonly IntPtr RT_VERSION = new IntPtr(16);
|
internal static readonly IntPtr RT_VERSION = new(16);
|
||||||
|
|
||||||
[DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode)]
|
[DllImport(Libraries.Kernel32, SetLastError = true, CharSet = CharSet.Unicode)]
|
||||||
internal static extern IntPtr BeginUpdateResourceW(string fileName, bool deleteExistingResources);
|
internal static extern IntPtr BeginUpdateResourceW(string fileName, bool deleteExistingResources);
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using WinSW.Native;
|
||||||
|
using static WinSW.Native.NetworkApis;
|
||||||
|
|
||||||
|
namespace WinSW
|
||||||
|
{
|
||||||
|
public sealed class SharedDirectoryMapper
|
||||||
|
{
|
||||||
|
private readonly List<SharedDirectoryMapperConfig> entries;
|
||||||
|
|
||||||
|
public SharedDirectoryMapper(List<SharedDirectoryMapperConfig> entries)
|
||||||
|
{
|
||||||
|
this.entries = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Map()
|
||||||
|
{
|
||||||
|
foreach (var config in this.entries)
|
||||||
|
{
|
||||||
|
string label = config.Label;
|
||||||
|
string uncPath = config.UncPath;
|
||||||
|
|
||||||
|
int error = WNetAddConnection2W(new()
|
||||||
|
{
|
||||||
|
Type = RESOURCETYPE_DISK,
|
||||||
|
LocalName = label,
|
||||||
|
RemoteName = uncPath,
|
||||||
|
});
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
Throw.Command.Win32Exception(error, $"Failed to map {label}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unmap()
|
||||||
|
{
|
||||||
|
foreach (var config in this.entries)
|
||||||
|
{
|
||||||
|
string label = config.Label;
|
||||||
|
|
||||||
|
int error = WNetCancelConnection2W(label);
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
Throw.Command.Win32Exception(error, $"Failed to unmap {label}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace WinSW
|
||||||
|
{
|
||||||
|
public sealed class SharedDirectoryMapperConfig
|
||||||
|
{
|
||||||
|
public string Label { get; }
|
||||||
|
|
||||||
|
public string UncPath { get; }
|
||||||
|
|
||||||
|
public SharedDirectoryMapperConfig(string driveLabel, string directoryUncPath)
|
||||||
|
{
|
||||||
|
this.Label = driveLabel;
|
||||||
|
this.UncPath = directoryUncPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,9 +17,9 @@ namespace WinSW
|
||||||
{
|
{
|
||||||
public sealed class WrapperService : ServiceBase, IEventLogger, IServiceEventLog
|
public sealed class WrapperService : ServiceBase, IEventLogger, IServiceEventLog
|
||||||
{
|
{
|
||||||
internal static readonly WrapperServiceEventLogProvider eventLogProvider = new WrapperServiceEventLogProvider();
|
internal static readonly WrapperServiceEventLogProvider EventLogProvider = new();
|
||||||
|
|
||||||
private static readonly int additionalStopTimeout = 1_000;
|
private static readonly int AdditionalStopTimeout = 1_000;
|
||||||
|
|
||||||
private static readonly ILog Log = LogManager.GetLogger(typeof(WrapperService));
|
private static readonly ILog Log = LogManager.GetLogger(typeof(WrapperService));
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ namespace WinSW
|
||||||
|
|
||||||
internal WinSWExtensionManager ExtensionManager { get; }
|
internal WinSWExtensionManager ExtensionManager { get; }
|
||||||
|
|
||||||
|
private SharedDirectoryMapper? sharedDirectoryMapper;
|
||||||
|
|
||||||
private bool shuttingdown;
|
private bool shuttingdown;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -51,7 +53,7 @@ namespace WinSW
|
||||||
this.ExtensionManager = new WinSWExtensionManager(config);
|
this.ExtensionManager = new WinSWExtensionManager(config);
|
||||||
|
|
||||||
// Register the event log provider
|
// Register the event log provider
|
||||||
eventLogProvider.Service = this;
|
EventLogProvider.Service = this;
|
||||||
|
|
||||||
if (config.Preshutdown)
|
if (config.Preshutdown)
|
||||||
{
|
{
|
||||||
|
@ -297,6 +299,13 @@ namespace WinSW
|
||||||
throw new AggregateException(exceptions);
|
throw new AggregateException(exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sharedDirectories = this.config.SharedDirectories;
|
||||||
|
if (sharedDirectories.Count > 0)
|
||||||
|
{
|
||||||
|
this.sharedDirectoryMapper = new(sharedDirectories);
|
||||||
|
this.sharedDirectoryMapper.Map();
|
||||||
|
}
|
||||||
|
|
||||||
var prestart = this.config.Prestart;
|
var prestart = this.config.Prestart;
|
||||||
string? prestartExecutable = prestart.Executable;
|
string? prestartExecutable = prestart.Executable;
|
||||||
if (prestartExecutable != null)
|
if (prestartExecutable != null)
|
||||||
|
@ -306,7 +315,7 @@ namespace WinSW
|
||||||
using var process = this.StartProcess(prestartExecutable, prestart.Arguments, prestart.CreateLogHandler());
|
using var process = this.StartProcess(prestartExecutable, prestart.Arguments, prestart.CreateLogHandler());
|
||||||
this.WaitForProcessToExit(process);
|
this.WaitForProcessToExit(process);
|
||||||
this.LogExited($"Pre-start process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
this.LogExited($"Pre-start process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
||||||
process.StopDescendants(additionalStopTimeout);
|
process.StopDescendants(AdditionalStopTimeout);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -335,7 +344,7 @@ namespace WinSW
|
||||||
using var process = StartProcessLocked();
|
using var process = StartProcessLocked();
|
||||||
this.WaitForProcessToExit(process);
|
this.WaitForProcessToExit(process);
|
||||||
this.LogExited($"Post-start process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
this.LogExited($"Post-start process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
||||||
process.StopDescendants(additionalStopTimeout);
|
process.StopDescendants(AdditionalStopTimeout);
|
||||||
this.startingProcess = null;
|
this.startingProcess = null;
|
||||||
|
|
||||||
Process StartProcessLocked()
|
Process StartProcessLocked()
|
||||||
|
@ -367,7 +376,7 @@ namespace WinSW
|
||||||
using var process = StartProcessLocked(prestopExecutable, prestop.Arguments, prestop.CreateLogHandler());
|
using var process = StartProcessLocked(prestopExecutable, prestop.Arguments, prestop.CreateLogHandler());
|
||||||
this.WaitForProcessToExit(process);
|
this.WaitForProcessToExit(process);
|
||||||
this.LogExited($"Pre-stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
this.LogExited($"Pre-stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
||||||
process.StopDescendants(additionalStopTimeout);
|
process.StopDescendants(AdditionalStopTimeout);
|
||||||
this.stoppingProcess = null;
|
this.stoppingProcess = null;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -408,7 +417,7 @@ namespace WinSW
|
||||||
|
|
||||||
Log.Debug("WaitForProcessToExit " + this.process.Id + "+" + stopProcess.Id);
|
Log.Debug("WaitForProcessToExit " + this.process.Id + "+" + stopProcess.Id);
|
||||||
this.WaitForProcessToExit(stopProcess);
|
this.WaitForProcessToExit(stopProcess);
|
||||||
stopProcess.StopDescendants(additionalStopTimeout);
|
stopProcess.StopDescendants(AdditionalStopTimeout);
|
||||||
this.stoppingProcess = null;
|
this.stoppingProcess = null;
|
||||||
|
|
||||||
this.WaitForProcessToExit(this.process);
|
this.WaitForProcessToExit(this.process);
|
||||||
|
@ -429,8 +438,8 @@ namespace WinSW
|
||||||
{
|
{
|
||||||
using var process = StartProcessLocked(poststopExecutable, poststop.Arguments, poststop.CreateLogHandler());
|
using var process = StartProcessLocked(poststopExecutable, poststop.Arguments, poststop.CreateLogHandler());
|
||||||
this.WaitForProcessToExit(process);
|
this.WaitForProcessToExit(process);
|
||||||
this.LogExited($"Post-Stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
this.LogExited($"Post-stop process '{process.Format()}' exited with code {process.ExitCode}.", process.ExitCode);
|
||||||
process.StopDescendants(additionalStopTimeout);
|
process.StopDescendants(AdditionalStopTimeout);
|
||||||
this.stoppingProcess = null;
|
this.stoppingProcess = null;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -439,6 +448,15 @@ namespace WinSW
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.sharedDirectoryMapper?.Unmap();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e);
|
||||||
|
}
|
||||||
|
|
||||||
// Stop extensions
|
// Stop extensions
|
||||||
this.ExtensionManager.FireBeforeWrapperStopped();
|
this.ExtensionManager.FireBeforeWrapperStopped();
|
||||||
|
|
||||||
|
@ -513,8 +531,8 @@ namespace WinSW
|
||||||
|
|
||||||
process.StopDescendants(this.config.StopTimeoutInMs);
|
process.StopDescendants(this.config.StopTimeoutInMs);
|
||||||
|
|
||||||
this.startingProcess?.StopTree(additionalStopTimeout);
|
this.startingProcess?.StopTree(AdditionalStopTimeout);
|
||||||
this.stoppingProcess?.StopTree(additionalStopTimeout);
|
this.stoppingProcess?.StopTree(AdditionalStopTimeout);
|
||||||
|
|
||||||
// if we finished orderly, report that to SCM.
|
// if we finished orderly, report that to SCM.
|
||||||
// by not reporting unclean shutdown, we let Windows SCM to decide if it wants to
|
// by not reporting unclean shutdown, we let Windows SCM to decide if it wants to
|
|
@ -1,121 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Xml;
|
|
||||||
using log4net;
|
|
||||||
using WinSW.Extensions;
|
|
||||||
using WinSW.Util;
|
|
||||||
using static WinSW.Plugins.SharedDirectoryMapper.SharedDirectoryMapper.Native;
|
|
||||||
|
|
||||||
namespace WinSW.Plugins.SharedDirectoryMapper
|
|
||||||
{
|
|
||||||
public class SharedDirectoryMapper : AbstractWinSWExtension
|
|
||||||
{
|
|
||||||
private readonly List<SharedDirectoryMapperConfig> entries = new List<SharedDirectoryMapperConfig>();
|
|
||||||
|
|
||||||
public override string DisplayName => "Shared Directory Mapper";
|
|
||||||
|
|
||||||
private static readonly ILog Logger = LogManager.GetLogger(typeof(SharedDirectoryMapper));
|
|
||||||
|
|
||||||
public SharedDirectoryMapper()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SharedDirectoryMapper(bool enableMapping, string directoryUNC, string driveLabel)
|
|
||||||
{
|
|
||||||
var config = new SharedDirectoryMapperConfig(enableMapping, driveLabel, directoryUNC);
|
|
||||||
this.entries.Add(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Configure(XmlServiceConfig config, XmlNode node)
|
|
||||||
{
|
|
||||||
var mapNodes = XmlHelper.SingleNode(node, "mapping", false)!.SelectNodes("map");
|
|
||||||
if (mapNodes != null)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < mapNodes.Count; i++)
|
|
||||||
{
|
|
||||||
if (mapNodes[i] is XmlElement mapElement)
|
|
||||||
{
|
|
||||||
this.entries.Add(SharedDirectoryMapperConfig.FromXml(mapElement));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnWrapperStarted()
|
|
||||||
{
|
|
||||||
foreach (var config in this.entries)
|
|
||||||
{
|
|
||||||
string label = config.Label;
|
|
||||||
string uncPath = config.UNCPath;
|
|
||||||
if (config.EnableMapping)
|
|
||||||
{
|
|
||||||
Logger.Info(this.DisplayName + ": Mapping shared directory " + uncPath + " to " + label);
|
|
||||||
|
|
||||||
int error = WNetAddConnection2(new NETRESOURCE
|
|
||||||
{
|
|
||||||
Type = RESOURCETYPE_DISK,
|
|
||||||
LocalName = label,
|
|
||||||
RemoteName = uncPath,
|
|
||||||
});
|
|
||||||
if (error != 0)
|
|
||||||
{
|
|
||||||
this.ThrowExtensionException(error, $"Mapping of {label} failed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Warn(this.DisplayName + ": Mapping of " + label + " is disabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void BeforeWrapperStopped()
|
|
||||||
{
|
|
||||||
foreach (var config in this.entries)
|
|
||||||
{
|
|
||||||
string label = config.Label;
|
|
||||||
if (config.EnableMapping)
|
|
||||||
{
|
|
||||||
int error = WNetCancelConnection2(label);
|
|
||||||
if (error != 0)
|
|
||||||
{
|
|
||||||
this.ThrowExtensionException(error, $"Unmapping of {label} failed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ThrowExtensionException(int error, string message)
|
|
||||||
{
|
|
||||||
var inner = new Win32Exception(error);
|
|
||||||
throw new ExtensionException(this.Descriptor.Id, $"{this.DisplayName}: {message} {inner.Message}", inner);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class Native
|
|
||||||
{
|
|
||||||
internal const uint RESOURCETYPE_DISK = 0x00000001;
|
|
||||||
|
|
||||||
private const string MprLibraryName = "mpr.dll";
|
|
||||||
|
|
||||||
[DllImport(MprLibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "WNetAddConnection2W")]
|
|
||||||
internal static extern int WNetAddConnection2(in NETRESOURCE netResource, string? password = null, string? userName = null, uint flags = 0);
|
|
||||||
|
|
||||||
[DllImport(MprLibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "WNetCancelConnection2W")]
|
|
||||||
internal static extern int WNetCancelConnection2(string name, uint flags = 0, bool force = false);
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
||||||
internal struct NETRESOURCE
|
|
||||||
{
|
|
||||||
public uint Scope;
|
|
||||||
public uint Type;
|
|
||||||
public uint DisplayType;
|
|
||||||
public uint Usage;
|
|
||||||
public string LocalName;
|
|
||||||
public string RemoteName;
|
|
||||||
public string Comment;
|
|
||||||
public string Provider;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
using System.Xml;
|
|
||||||
using WinSW.Util;
|
|
||||||
|
|
||||||
namespace WinSW.Plugins.SharedDirectoryMapper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stores configuration entries for SharedDirectoryMapper extension.
|
|
||||||
/// </summary>
|
|
||||||
public class SharedDirectoryMapperConfig
|
|
||||||
{
|
|
||||||
public bool EnableMapping { get; }
|
|
||||||
|
|
||||||
public string Label { get; }
|
|
||||||
|
|
||||||
public string UNCPath { get; }
|
|
||||||
|
|
||||||
public SharedDirectoryMapperConfig(bool enableMapping, string label, string uncPath)
|
|
||||||
{
|
|
||||||
this.EnableMapping = enableMapping;
|
|
||||||
this.Label = label;
|
|
||||||
this.UNCPath = uncPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SharedDirectoryMapperConfig FromXml(XmlElement node)
|
|
||||||
{
|
|
||||||
bool enableMapping = XmlHelper.SingleAttribute(node, "enabled", true);
|
|
||||||
string label = XmlHelper.SingleAttribute<string>(node, "label");
|
|
||||||
string uncPath = XmlHelper.SingleAttribute<string>(node, "uncpath");
|
|
||||||
return new SharedDirectoryMapperConfig(enableMapping, label, uncPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ namespace WinSW.Tests
|
||||||
{
|
{
|
||||||
public class DownloadTests : IDisposable
|
public class DownloadTests : IDisposable
|
||||||
{
|
{
|
||||||
private readonly HttpListener globalListener = new HttpListener();
|
private readonly HttpListener globalListener = new();
|
||||||
|
|
||||||
private readonly byte[] contents = { 0x57, 0x69, 0x6e, 0x53, 0x57 };
|
private readonly byte[] contents = { 0x57, 0x69, 0x6e, 0x53, 0x57 };
|
||||||
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace WinSW.Tests.Extensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base class for testing of WinSW Extensions.
|
|
||||||
/// </summary>
|
|
||||||
public class ExtensionTestBase
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the name of the extension to be passed in the configuration.
|
|
||||||
/// This name should point to assembly in tests, because we do not merge extension DLLs for testing purposes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">Type of the extension</param>
|
|
||||||
/// <returns>String for Type locator, which includes class and assembly names</returns>
|
|
||||||
public static string GetExtensionClassNameWithAssembly(Type type)
|
|
||||||
{
|
|
||||||
return type.ToString() + ", " + type.Assembly;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
using WinSW.Extensions;
|
|
||||||
using WinSW.Plugins.SharedDirectoryMapper;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace WinSW.Tests.Extensions
|
|
||||||
{
|
|
||||||
public class SharedDirectoryMapperConfigTest : ExtensionTestBase
|
|
||||||
{
|
|
||||||
private readonly XmlServiceConfig serviceConfig;
|
|
||||||
|
|
||||||
private readonly string testExtension = GetExtensionClassNameWithAssembly(typeof(SharedDirectoryMapper));
|
|
||||||
|
|
||||||
public SharedDirectoryMapperConfigTest()
|
|
||||||
{
|
|
||||||
string seedXml =
|
|
||||||
$@"<service>
|
|
||||||
<id>SERVICE_NAME</id>
|
|
||||||
<name>Jenkins Slave</name>
|
|
||||||
<description>This service runs a slave for Jenkins continuous integration system.</description>
|
|
||||||
<executable>C:\Program Files\Java\jre7\bin\java.exe</executable>
|
|
||||||
<arguments>-Xrs -jar \""%BASE%\slave.jar\"" -jnlpUrl ...</arguments>
|
|
||||||
<log mode=""roll""></log>
|
|
||||||
<extensions>
|
|
||||||
<extension enabled=""true"" className=""{this.testExtension}"" id=""mapNetworDirs"">
|
|
||||||
<mapping>
|
|
||||||
<map enabled=""false"" label=""N:"" uncpath=""\\UNC""/>
|
|
||||||
<map enabled=""false"" label=""M:"" uncpath=""\\UNC2""/>
|
|
||||||
</mapping>
|
|
||||||
</extension>
|
|
||||||
<extension enabled=""true"" className=""{this.testExtension}"" id=""mapNetworDirs2"">
|
|
||||||
<mapping>
|
|
||||||
<map enabled=""false"" label=""X:"" uncpath=""\\UNC""/>
|
|
||||||
<map enabled=""false"" label=""Y:"" uncpath=""\\UNC2""/>
|
|
||||||
</mapping>
|
|
||||||
</extension>
|
|
||||||
</extensions>
|
|
||||||
</service>";
|
|
||||||
this.serviceConfig = XmlServiceConfig.FromXml(seedXml);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void LoadExtensions()
|
|
||||||
{
|
|
||||||
var manager = new WinSWExtensionManager(this.serviceConfig);
|
|
||||||
manager.LoadExtensions();
|
|
||||||
Assert.Equal(2, manager.Extensions.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void StartStopExtension()
|
|
||||||
{
|
|
||||||
var manager = new WinSWExtensionManager(this.serviceConfig);
|
|
||||||
manager.LoadExtensions();
|
|
||||||
manager.FireOnWrapperStarted();
|
|
||||||
manager.FireBeforeWrapperStopped();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -420,5 +420,26 @@ $@"<service>
|
||||||
Assert.Equal(expected.StderrPath, actual.StderrPath);
|
Assert.Equal(expected.StderrPath, actual.StderrPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SharedDirectoryMapping()
|
||||||
|
{
|
||||||
|
string seedXml =
|
||||||
|
@"<service>
|
||||||
|
<sharedDirectoryMapping>
|
||||||
|
<map label=""N:"" uncpath=""\\UNC"" />
|
||||||
|
<map label=""M:"" uncpath=""\\UNC2"" />
|
||||||
|
</sharedDirectoryMapping>
|
||||||
|
</service>";
|
||||||
|
|
||||||
|
var config = XmlServiceConfig.FromXml(seedXml);
|
||||||
|
|
||||||
|
var sharedDirectories = config.SharedDirectories;
|
||||||
|
Assert.Equal(2, sharedDirectories.Count);
|
||||||
|
Assert.Equal("N:", sharedDirectories[0].Label);
|
||||||
|
Assert.Equal(@"\\UNC", sharedDirectories[0].UncPath);
|
||||||
|
Assert.Equal("M:", sharedDirectories[1].Label);
|
||||||
|
Assert.Equal(@"\\UNC2", sharedDirectories[1].UncPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,52 +3,45 @@ using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using WinSW.Plugins.SharedDirectoryMapper;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WinSW.Tests.Extensions
|
namespace WinSW.Tests.Extensions
|
||||||
{
|
{
|
||||||
// TODO: Assert.Throws<ExtensionException>
|
|
||||||
public class SharedDirectoryMapperTests
|
public class SharedDirectoryMapperTests
|
||||||
{
|
{
|
||||||
|
private static SharedDirectoryMapper CreateMapper(string driveLabel, string directoryUncPath)
|
||||||
|
{
|
||||||
|
return new(new(1)
|
||||||
|
{
|
||||||
|
new(driveLabel, directoryUncPath),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[ElevatedFact]
|
[ElevatedFact]
|
||||||
public void TestMap()
|
public void TestMap()
|
||||||
{
|
{
|
||||||
using var data = TestData.Create();
|
using var data = TestData.Create();
|
||||||
|
|
||||||
const string label = "W:";
|
const string label = "W:";
|
||||||
var mapper = new SharedDirectoryMapper(true, $@"\\{Environment.MachineName}\{data.name}", label);
|
var mapper = CreateMapper(label, $@"\\{Environment.MachineName}\{data.name}");
|
||||||
|
|
||||||
mapper.OnWrapperStarted();
|
mapper.Map();
|
||||||
Assert.True(Directory.Exists($@"{label}\"));
|
Assert.True(Directory.Exists($@"{label}\"));
|
||||||
mapper.BeforeWrapperStopped();
|
mapper.Unmap();
|
||||||
Assert.False(Directory.Exists($@"{label}\"));
|
Assert.False(Directory.Exists($@"{label}\"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[ElevatedFact]
|
|
||||||
public void TestDisableMapping()
|
|
||||||
{
|
|
||||||
using var data = TestData.Create();
|
|
||||||
|
|
||||||
const string label = "W:";
|
|
||||||
var mapper = new SharedDirectoryMapper(enableMapping: false, $@"\\{Environment.MachineName}\{data.name}", label);
|
|
||||||
|
|
||||||
mapper.OnWrapperStarted();
|
|
||||||
Assert.False(Directory.Exists($@"{label}\"));
|
|
||||||
mapper.BeforeWrapperStopped();
|
|
||||||
}
|
|
||||||
|
|
||||||
[ElevatedFact]
|
[ElevatedFact]
|
||||||
public void TestMap_PathEndsWithSlash_Throws()
|
public void TestMap_PathEndsWithSlash_Throws()
|
||||||
{
|
{
|
||||||
using var data = TestData.Create();
|
using var data = TestData.Create();
|
||||||
|
|
||||||
const string label = "W:";
|
const string label = "W:";
|
||||||
var mapper = new SharedDirectoryMapper(true, $@"\\{Environment.MachineName}\{data.name}\", label);
|
var mapper = CreateMapper(label, $@"\\{Environment.MachineName}\{data.name}\");
|
||||||
|
|
||||||
_ = Assert.ThrowsAny<Exception>(() => mapper.OnWrapperStarted());
|
_ = Assert.ThrowsAny<Exception>(() => mapper.Map());
|
||||||
Assert.False(Directory.Exists($@"{label}\"));
|
Assert.False(Directory.Exists($@"{label}\"));
|
||||||
_ = Assert.ThrowsAny<Exception>(() => mapper.BeforeWrapperStopped());
|
_ = Assert.ThrowsAny<Exception>(() => mapper.Unmap());
|
||||||
}
|
}
|
||||||
|
|
||||||
[ElevatedFact]
|
[ElevatedFact]
|
||||||
|
@ -57,11 +50,11 @@ namespace WinSW.Tests.Extensions
|
||||||
using var data = TestData.Create();
|
using var data = TestData.Create();
|
||||||
|
|
||||||
const string label = "W";
|
const string label = "W";
|
||||||
var mapper = new SharedDirectoryMapper(true, $@"\\{Environment.MachineName}\{data.name}", label);
|
var mapper = CreateMapper(label, $@"\\{Environment.MachineName}\{data.name}");
|
||||||
|
|
||||||
_ = Assert.ThrowsAny<Exception>(() => mapper.OnWrapperStarted());
|
_ = Assert.ThrowsAny<Exception>(() => mapper.Map());
|
||||||
Assert.False(Directory.Exists($@"{label}\"));
|
Assert.False(Directory.Exists($@"{label}\"));
|
||||||
_ = Assert.ThrowsAny<Exception>(() => mapper.BeforeWrapperStopped());
|
_ = Assert.ThrowsAny<Exception>(() => mapper.Unmap());
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly ref struct TestData
|
private readonly ref struct TestData
|
|
@ -23,7 +23,7 @@ namespace WinSW.Tests.Util
|
||||||
|
|
||||||
public List<string> ExtensionXmls { get; } = new List<string>();
|
public List<string> ExtensionXmls { get; } = new List<string>();
|
||||||
|
|
||||||
private readonly List<string> configEntries = new List<string>();
|
private readonly List<string> configEntries = new();
|
||||||
|
|
||||||
private readonly ITestOutputHelper output;
|
private readonly ITestOutputHelper output;
|
||||||
|
|
||||||
|
|
|
@ -1099,7 +1099,7 @@ namespace WinSW
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var eventLogAppender = new ServiceEventLogAppender(WrapperService.eventLogProvider)
|
var eventLogAppender = new ServiceEventLogAppender(WrapperService.EventLogProvider)
|
||||||
{
|
{
|
||||||
Name = "Wrapper event log",
|
Name = "Wrapper event log",
|
||||||
Threshold = eventLogLevel,
|
Threshold = eventLogLevel,
|
||||||
|
|
Loading…
Reference in New Issue