Merge branch 'master' into ci-fix

pull/449/head
Oleg Nenashev 2020-03-27 01:31:37 +01:00 committed by GitHub
commit aa4e7dc176
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 114 additions and 158 deletions

2
.github/CODEOWNERS vendored
View File

@ -1 +1 @@
* @oleg-nenashev
* @oleg-nenashev @NextTurn

View File

@ -3,7 +3,7 @@ Release Notes
##### Newer releases
See [GitHub Releases](https://github.com/kohsuke/winsw/releases)
See [GitHub Releases](https://github.com/winsw/winsw/releases)
##### 2.3.0

View File

@ -1,9 +1,10 @@
winsw: Windows service wrapper in less restrictive license
=========================
[![Github All Releases](https://img.shields.io/github/downloads/kohsuke/winsw/total.svg)](https://github.com/kohsuke/winsw/releases)
[![Github All Releases](https://img.shields.io/github/downloads/winsw/winsw/total.svg)](https://github.com/winsw/winsw/releases)
[![NuGet](https://img.shields.io/nuget/v/WinSW.svg)](https://www.nuget.org/packages/WinSW/)
[![Build status](https://ci.appveyor.com/api/projects/status/rspft8kdb33g6cis?svg=true)](https://ci.appveyor.com/project/winsw/winsw)
[![Gitter](https://badges.gitter.im/winsw/winsw.svg)](https://gitter.im/winsw/winsw?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
WinSW is an executable binary, which can be used to wrap and manage a custom process as a Windows service.
Once you download the installation package, you can rename `winsw.exe` to any name, e.g. `myService.exe`.
@ -14,7 +15,7 @@ See the [project manifest](MANIFEST.md).
### Download
Starting from WinSW `2.x`, the releases are being hosted on [GitHub](https://github.com/kohsuke/winsw/releases) and [nuget.org](https://www.nuget.org/packages/WinSW/).
Starting from WinSW `2.x`, the releases are being hosted on [GitHub](https://github.com/winsw/winsw/releases) and [nuget.org](https://www.nuget.org/packages/WinSW/).
Due to historical reasons, the project also uses [Jenkins Maven repository](https://jenkins.io/index.html) as a secondary source.
Binaries are available [here](http://repo.jenkins-ci.org/releases/com/sun/winsw/winsw/).

View File

@ -15,7 +15,7 @@ More info about the wrapper is available in the projects GitHub repository.
<authors>WinSW contributors</authors>
<owners>Oleg Nenashev, Kohsuke Kawaguchi</owners>
<license type="expression">MIT</license>
<projectUrl>https://github.com/kohsuke/winsw</projectUrl>
<projectUrl>https://github.com/winsw/winsw</projectUrl>
<!--<iconUrl>TODO</iconUrl>-->
<copyright>Copyright (c) 2010-2016 Kohsuke Kawaguchi, Sun Microsystems, Inc., CloudBees, Inc., Oleg Nenashev and other contributors</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
@ -27,7 +27,9 @@ More info about the wrapper is available in the projects GitHub repository.
<files>
<file src="artifacts\WinSW.NET2.exe" target="lib\net20-full\WinSW.NET2.exe" />
<file src="artifacts\WinSW.NET4.exe" target="lib\net40-full\WinSW.NET4.exe" />
<file src="artifacts\WinSW.NET461.exe" target="lib\net461-full\WinSW.NET461.exe" />
<file src="examples\sample-allOptions.xml" target="lib\net20-full\WinSW.NET2.xml" />
<file src="examples\sample-allOptions.xml" target="lib\net40-full\WinSW.NET4.xml" />
<file src="examples\sample-allOptions.xml" target="lib\net461-full\WinSW.NET461.xml" />
</files>
</package>

View File

@ -426,7 +426,7 @@ namespace winsw
_wrapperServiceStatus.waitHint = effectiveWaitHint;
// WriteEvent("SignalShutdownPending " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
_wrapperServiceStatus.currentState = (int)State.SERVICE_STOP_PENDING;
Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus);
Advapi32.SetServiceStatus(handle, _wrapperServiceStatus);
}
private void SignalShutdownComplete()
@ -435,7 +435,7 @@ namespace winsw
_wrapperServiceStatus.checkPoint++;
// WriteEvent("SignalShutdownComplete " + wrapperServiceStatus.checkPoint + ":" + wrapperServiceStatus.waitHint);
_wrapperServiceStatus.currentState = (int)State.SERVICE_STOPPED;
Advapi32.SetServiceStatus(handle, ref _wrapperServiceStatus);
Advapi32.SetServiceStatus(handle, _wrapperServiceStatus);
}
private void StartProcess(Process processToStart, string arguments, string executable, LogHandler? logHandler, bool redirectStdin)
@ -796,8 +796,7 @@ namespace winsw
// run restart from another process group. see README.md for why this is useful.
STARTUPINFO si = default;
bool result = Kernel32.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, 0x200/*CREATE_NEW_PROCESS_GROUP*/, IntPtr.Zero, null, ref si, out _);
bool result = Kernel32.CreateProcess(null, descriptor.ExecutablePath + " restart", IntPtr.Zero, IntPtr.Zero, false, Kernel32.CREATE_NEW_PROCESS_GROUP, IntPtr.Zero, null, default, out _);
if (!result)
{
throw new Exception("Failed to invoke restart: " + Marshal.GetLastWin32Error());

View File

@ -1,59 +0,0 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace winsw
{
public static class SigIntHelper
{
private const string KERNEL32 = "kernel32.dll";
[DllImport(KERNEL32, SetLastError = true)]
private static extern bool AttachConsole(uint dwProcessId);
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true)]
private static extern bool FreeConsole();
[DllImport(KERNEL32)]
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate? HandlerRoutine, bool Add);
// Delegate type to be used as the Handler Routine for SCCH
private delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType);
// Enumerated type for the control messages sent to the handler routine
private enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
[DllImport(KERNEL32)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
/// <summary>
/// Uses the native funciton "AttachConsole" to attach the thread to the executing process to try to trigger a CTRL_C event (SIGINT). If the application
/// doesn't honor the event and shut down gracefully, the. wait period will time out after 15 seconds.
/// </summary>
/// <param name="process">The process to attach to and send the SIGINT</param>
/// <returns>True if the process shut down successfully to the SIGINT, false if it did not.</returns>
public static bool SendSIGINTToProcess(Process process, TimeSpan shutdownTimeout)
{
if (AttachConsole((uint)process.Id))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
process.WaitForExit((int)shutdownTimeout.TotalMilliseconds);
return process.HasExited;
}
return false;
}
}
}

View File

@ -6,9 +6,7 @@ using System.Text;
using System.Threading.Tasks;
#endif
using System.Xml;
#if !VNEXT
using log4net;
#endif
using winsw.Util;
namespace winsw
@ -26,9 +24,7 @@ namespace winsw
basic
}
#if !VNEXT
private static readonly ILog Logger = LogManager.GetLogger(typeof(Download));
#endif
public readonly string From;
public readonly string To;
@ -163,25 +159,57 @@ namespace winsw
throw new WebException("Code defect. Unsupported authentication type: " + Auth);
}
string tmpFilePath = To + ".tmp";
#if VNEXT
using (WebResponse response = await request.GetResponseAsync())
#else
using (WebResponse response = request.GetResponse())
#endif
using (Stream responseStream = response.GetResponseStream())
using (FileStream tmpStream = new FileStream(tmpFilePath, FileMode.Create))
bool supportsIfModifiedSince = false;
if (request is HttpWebRequest httpRequest && File.Exists(To))
{
#if VNEXT
await responseStream.CopyToAsync(tmpStream);
#elif NET20
CopyStream(responseStream, tmpStream);
#else
responseStream.CopyTo(tmpStream);
#endif
supportsIfModifiedSince = true;
httpRequest.IfModifiedSince = File.GetLastWriteTime(To);
}
FileHelper.MoveOrReplaceFile(To + ".tmp", To);
DateTime lastModified = default;
string tmpFilePath = To + ".tmp";
try
{
#if VNEXT
using (WebResponse response = await request.GetResponseAsync())
#else
using (WebResponse response = request.GetResponse())
#endif
using (Stream responseStream = response.GetResponseStream())
using (FileStream tmpStream = new FileStream(tmpFilePath, FileMode.Create))
{
if (supportsIfModifiedSince)
{
lastModified = ((HttpWebResponse)response).LastModified;
}
#if VNEXT
await responseStream.CopyToAsync(tmpStream);
#elif NET20
CopyStream(responseStream, tmpStream);
#else
responseStream.CopyTo(tmpStream);
#endif
}
FileHelper.MoveOrReplaceFile(To + ".tmp", To);
if (supportsIfModifiedSince)
{
File.SetLastWriteTime(To, lastModified);
}
}
catch (WebException e)
{
if (supportsIfModifiedSince && ((HttpWebResponse)e.Response).StatusCode == HttpStatusCode.NotModified)
{
Logger.Info($"Skipped downloading unmodified resource '{From}'");
}
else
{
throw;
}
}
}
#if NET20

View File

@ -35,7 +35,7 @@ namespace winsw.Native
public void Dispose()
{
if (_handle != IntPtr.Zero)
Advapi32.CloseServiceHandle(_handle);
_ = Advapi32.CloseServiceHandle(_handle);
_handle = IntPtr.Zero;
}
}
@ -71,7 +71,7 @@ namespace winsw.Native
Marshal.StructureToPtr(actions[i], new IntPtr(sfa.lpsaActions.ToInt64() + i * len), false);
}
if (!Advapi32.ChangeServiceConfig2(Handle, SERVICE_CONFIG_INFOLEVEL.SERVICE_CONFIG_FAILURE_ACTIONS, ref sfa))
if (!Advapi32.ChangeServiceConfig2(Handle, SERVICE_CONFIG_INFOLEVEL.SERVICE_CONFIG_FAILURE_ACTIONS, sfa))
throw new Exception("Failed to change the failure actions", new Win32Exception());
}
finally
@ -94,7 +94,7 @@ namespace winsw.Native
fDelayedAutostart = enabled
};
if (!Advapi32.ChangeServiceConfig2(Handle, SERVICE_CONFIG_INFOLEVEL.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, ref settings))
if (!Advapi32.ChangeServiceConfig2(Handle, SERVICE_CONFIG_INFOLEVEL.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, settings))
{
throw new Exception("Failed to change the DelayedAutoStart setting", new Win32Exception());
}
@ -103,7 +103,7 @@ namespace winsw.Native
public void Dispose()
{
if (Handle != IntPtr.Zero)
Advapi32.CloseServiceHandle(Handle);
_ = Advapi32.CloseServiceHandle(Handle);
Handle = IntPtr.Zero;
}
}
@ -174,19 +174,16 @@ namespace winsw.Native
// StringBuilder and size for the domain name
StringBuilder domainName = new StringBuilder();
int nameSize = 0;
// account-type variable for lookup
int accountType = 0;
// get required buffer size
Advapi32.LookupAccountName(string.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType);
_ = Advapi32.LookupAccountName(null, accountName, sid, ref sidSize, domainName, ref nameSize, out _);
// allocate buffers
domainName = new StringBuilder(nameSize);
sid = Marshal.AllocHGlobal(sidSize);
// lookup the SID for the account
bool result = Advapi32.LookupAccountName(string.Empty, accountName, sid, ref sidSize, domainName, ref nameSize,
ref accountType);
bool result = Advapi32.LookupAccountName(null, accountName, sid, ref sidSize, domainName, ref nameSize, out _);
// say what you're doing
// Console.WriteLine("LookupAccountName result = " + result);
@ -195,13 +192,11 @@ namespace winsw.Native
if (!result)
{
winErrorCode = Kernel32.GetLastError();
winErrorCode = Marshal.GetLastWin32Error();
Console.WriteLine("LookupAccountName failed: " + winErrorCode);
}
else
{
// initialize an empty unicode-string
LSA_UNICODE_STRING systemName = default;
// combine all policies
const int access = (int)(
LSA_AccessPolicy.POLICY_AUDIT_LOG_ADMIN |
@ -220,18 +215,8 @@ namespace winsw.Native
);
// initialize a pointer for the policy handle
// these attributes are not used, but LsaOpenPolicy wants them to exists
LSA_OBJECT_ATTRIBUTES objectAttributes = new LSA_OBJECT_ATTRIBUTES
{
Length = 0,
RootDirectory = IntPtr.Zero,
Attributes = 0,
SecurityDescriptor = IntPtr.Zero,
SecurityQualityOfService = IntPtr.Zero
};
// get a policy handle
uint resultPolicy = Advapi32.LsaOpenPolicy(ref systemName, ref objectAttributes, access, out IntPtr policyHandle);
uint resultPolicy = Advapi32.LsaOpenPolicy(default, default, access, out IntPtr policyHandle);
winErrorCode = Advapi32.LsaNtStatusToWinError(resultPolicy);
if (winErrorCode != 0)
@ -257,7 +242,7 @@ namespace winsw.Native
Console.WriteLine("LsaAddAccountRights failed: " + winErrorCode);
}
Advapi32.LsaClose(policyHandle);
_ = Advapi32.LsaClose(policyHandle);
}
Advapi32.FreeSid(sid);
@ -273,52 +258,56 @@ namespace winsw.Native
/// </summary>
public class Advapi32
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, IntPtr lpInfo);
private const string Advapi32LibraryName = "advapi32.dll";
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, ref SERVICE_FAILURE_ACTIONS sfa);
[DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "ChangeServiceConfig2W")]
internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, in SERVICE_FAILURE_ACTIONS lpInfo);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, ref SERVICE_DELAYED_AUTO_START sfa);
[DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "ChangeServiceConfig2W")]
internal static extern bool ChangeServiceConfig2(IntPtr hService, SERVICE_CONFIG_INFOLEVEL dwInfoLevel, in SERVICE_DELAYED_AUTO_START lpInfo);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern IntPtr OpenSCManager(string? machineName, string? databaseName, uint dwAccess);
[DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "OpenSCManagerW")]
internal static extern IntPtr OpenSCManager(string? lpMachineName, string? lpDatabaseName, uint dwDesiredAccess);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "OpenServiceW")]
internal static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, uint dwDesiredAccess);
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport(Advapi32LibraryName, SetLastError = true)]
internal static extern bool CloseServiceHandle(IntPtr hSCObject);
[DllImport("advapi32.DLL")]
public static extern bool SetServiceStatus(IntPtr hServiceStatus, ref SERVICE_STATUS lpServiceStatus);
[DllImport(Advapi32LibraryName)]
public static extern bool SetServiceStatus(IntPtr hServiceStatus, in SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", PreserveSig = true)]
internal static extern uint LsaOpenPolicy(ref LSA_UNICODE_STRING SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, int DesiredAccess,
[DllImport(Advapi32LibraryName)]
internal static extern uint LsaOpenPolicy(
in LSA_UNICODE_STRING SystemName,
in LSA_OBJECT_ATTRIBUTES ObjectAttributes,
int DesiredAccess,
out IntPtr PolicyHandle);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
[DllImport(Advapi32LibraryName, SetLastError = true)]
internal static extern uint LsaAddAccountRights(IntPtr PolicyHandle, IntPtr AccountSid, LSA_UNICODE_STRING[] UserRights, uint CountOfRights);
[DllImport("advapi32")]
[DllImport(Advapi32LibraryName)]
internal static extern void FreeSid(IntPtr pSid);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)]
internal static extern bool LookupAccountName(string lpSystemName, string lpAccountName, IntPtr psid, ref int cbsid, StringBuilder domainName,
ref int cbdomainLength, ref int use);
[DllImport(Advapi32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "LookupAccountNameW")]
internal static extern bool LookupAccountName(
string? lpSystemName,
string lpAccountName,
IntPtr psid,
ref int cbsid,
StringBuilder domainName,
ref int cbdomainLength,
out int use);
[DllImport("advapi32.dll")]
[DllImport(Advapi32LibraryName)]
internal static extern bool IsValidSid(IntPtr pSid);
[DllImport("advapi32.dll", SetLastError = true)]
[DllImport(Advapi32LibraryName, SetLastError = true)]
internal static extern uint LsaClose(IntPtr ObjectHandle);
[DllImport("advapi32.dll", SetLastError = false)]
[DllImport(Advapi32LibraryName, SetLastError = false)]
internal static extern uint LsaNtStatusToWinError(uint status);
}
@ -584,9 +573,7 @@ namespace winsw.Native
/// </summary>
public int dwResetPeriod;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpRebootMsg;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpCommand;
public int cActions;
public IntPtr/*SC_ACTION[]*/ lpsaActions;
@ -596,7 +583,6 @@ namespace winsw.Native
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SERVICE_DELAYED_AUTO_START
{
[MarshalAs(UnmanagedType.Bool)]
public bool fDelayedAutostart;
}
}

View File

@ -9,24 +9,25 @@ namespace winsw.Native
/// </summary>
public class Kernel32
{
[DllImport("kernel32.dll", SetLastError = true)]
public const uint CREATE_NEW_PROCESS_GROUP = 0x00000200;
private const string Kernel32LibraryName = "kernel32.dll";
[DllImport(Kernel32LibraryName, SetLastError = true)]
public static extern bool SetStdHandle(int nStdHandle, SafeFileHandle handle);
[DllImport("kernel32.dll", SetLastError = true)]
[DllImport(Kernel32LibraryName, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateProcessW")]
public static extern bool CreateProcess(
string? lpApplicationName,
string lpCommandLine,
string? lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string? lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
in STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern int GetLastError();
}
[StructLayout(LayoutKind.Sequential)]

View File

@ -2,7 +2,6 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using log4net;
using winsw.Native;
namespace winsw.Util
{
@ -10,15 +9,15 @@ namespace winsw.Util
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(SigIntHelper));
private const string KERNEL32 = "kernel32.dll";
private const string Kernel32LibraryName = "kernel32.dll";
[DllImport(KERNEL32, SetLastError = true)]
[DllImport(Kernel32LibraryName, SetLastError = true)]
private static extern bool AttachConsole(uint dwProcessId);
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true)]
[DllImport(Kernel32LibraryName, SetLastError = true)]
private static extern bool FreeConsole();
[DllImport(KERNEL32)]
[DllImport(Kernel32LibraryName)]
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate? HandlerRoutine, bool Add);
// Delegate type to be used as the Handler Routine for SCCH
@ -34,8 +33,7 @@ namespace winsw.Util
CTRL_SHUTDOWN_EVENT
}
[DllImport(KERNEL32)]
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport(Kernel32LibraryName)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
/// <summary>
@ -49,8 +47,8 @@ namespace winsw.Util
if (AttachConsole((uint)process.Id))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
_ = SetConsoleCtrlHandler(null, true);
_ = GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
process.WaitForExit((int)shutdownTimeout.TotalMilliseconds);
@ -58,7 +56,7 @@ namespace winsw.Util
bool success = FreeConsole();
if (!success)
{
long errorCode = Kernel32.GetLastError();
long errorCode = Marshal.GetLastWin32Error();
Logger.Warn("Failed to detach from console. Error code: " + errorCode);
}