mirror of https://github.com/winsw/winsw
Merge pull request #81 from oleg-nenashev/winsw-help-commands
Add "version" and "help", handle unsupported commandspull/90/head
commit
95c9c23f59
87
Main.cs
87
Main.cs
|
@ -10,6 +10,7 @@ using System.Threading;
|
|||
using Microsoft.Win32;
|
||||
using WMI;
|
||||
using ServiceType = WMI.ServiceType;
|
||||
using System.Reflection;
|
||||
|
||||
namespace winsw
|
||||
{
|
||||
|
@ -28,9 +29,20 @@ namespace winsw
|
|||
private bool _orderlyShutdown;
|
||||
private bool _systemShuttingdown;
|
||||
|
||||
public WrapperService()
|
||||
/// <summary>
|
||||
/// Version of Windows service wrapper
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The version will be taken from <see cref="AssemblyInfo"/>
|
||||
/// </remarks>
|
||||
public static Version Version
|
||||
{
|
||||
_descriptor = new ServiceDescriptor();
|
||||
get { return Assembly.GetExecutingAssembly().GetName().Version; }
|
||||
}
|
||||
|
||||
public WrapperService(ServiceDescriptor descriptor)
|
||||
{
|
||||
_descriptor = descriptor;
|
||||
ServiceName = _descriptor.Id;
|
||||
CanShutdown = true;
|
||||
CanStop = true;
|
||||
|
@ -39,6 +51,10 @@ namespace winsw
|
|||
_systemShuttingdown = false;
|
||||
}
|
||||
|
||||
public WrapperService() : this (new ServiceDescriptor())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the file copy instructions, so that we can replace files that are always in use while
|
||||
/// the service runs.
|
||||
|
@ -511,11 +527,11 @@ namespace winsw
|
|||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void Run(string[] _args)
|
||||
public static void Run(string[] _args, ServiceDescriptor descriptor = null)
|
||||
{
|
||||
if (_args.Length > 0)
|
||||
{
|
||||
var d = new ServiceDescriptor();
|
||||
var d = descriptor ?? new ServiceDescriptor();
|
||||
Win32Services svc = new WmiRoot().GetCollection<Win32Services>();
|
||||
Win32Service s = svc.Select(d.Id);
|
||||
|
||||
|
@ -612,6 +628,7 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (args[0] == "uninstall")
|
||||
{
|
||||
|
@ -627,16 +644,19 @@ namespace winsw
|
|||
return; // it's already uninstalled, so consider it a success
|
||||
throw e;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (args[0] == "start")
|
||||
{
|
||||
if (s == null) ThrowNoSuchService();
|
||||
s.StartService();
|
||||
return;
|
||||
}
|
||||
if (args[0] == "stop")
|
||||
{
|
||||
if (s == null) ThrowNoSuchService();
|
||||
s.StopService();
|
||||
return;
|
||||
}
|
||||
if (args[0] == "restart")
|
||||
{
|
||||
|
@ -653,6 +673,7 @@ namespace winsw
|
|||
}
|
||||
|
||||
s.StartService();
|
||||
return;
|
||||
}
|
||||
if (args[0] == "restart!")
|
||||
{
|
||||
|
@ -666,6 +687,7 @@ namespace winsw
|
|||
{
|
||||
throw new Exception("Failed to invoke restart: "+Marshal.GetLastWin32Error());
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (args[0] == "status")
|
||||
{
|
||||
|
@ -675,6 +697,7 @@ namespace winsw
|
|||
Console.WriteLine("Started");
|
||||
else
|
||||
Console.WriteLine("Stopped");
|
||||
return;
|
||||
}
|
||||
if (args[0] == "test")
|
||||
{
|
||||
|
@ -682,8 +705,24 @@ namespace winsw
|
|||
wsvc.OnStart(args.ToArray());
|
||||
Thread.Sleep(1000);
|
||||
wsvc.OnStop();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
if (args[0] == "help" || args[0] == "--help" || args[0] == "-h"
|
||||
|| args[0] == "-?" || args[0] == "/?")
|
||||
{
|
||||
printHelp();
|
||||
return;
|
||||
}
|
||||
if (args[0] == "version")
|
||||
{
|
||||
printVersion();
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Unknown command: " + args[0]);
|
||||
printAvailableCommandsInfo();
|
||||
throw new Exception("Unknown command: " + args[0]);
|
||||
|
||||
}
|
||||
Run(new WrapperService());
|
||||
}
|
||||
|
@ -711,5 +750,43 @@ namespace winsw
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void printHelp()
|
||||
{
|
||||
Console.WriteLine("A wrapper binary that can be used to host executables as Windows services");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine("Usage: winsw [/redirect file] <command> [<args>]");
|
||||
Console.WriteLine(" Missing arguments trigger the service mode");
|
||||
Console.WriteLine("");
|
||||
printAvailableCommandsInfo();
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine("Extra options:");
|
||||
Console.WriteLine("- '/redirect' - redirect the wrapper's STDOUT and STDERR to the specified file");
|
||||
Console.WriteLine("");
|
||||
printVersion();
|
||||
Console.WriteLine("More info: https://github.com/kohsuke/winsw");
|
||||
Console.WriteLine("Bug tracker: https://github.com/kohsuke/winsw/issues");
|
||||
}
|
||||
|
||||
//TODO: Rework to enum in winsw-2.0
|
||||
private static void printAvailableCommandsInfo()
|
||||
{
|
||||
Console.WriteLine("Available commands:");
|
||||
Console.WriteLine("- 'install' - install the service to Windows Service Controller");
|
||||
Console.WriteLine("- 'uninstall' - uninstall the service");
|
||||
Console.WriteLine("- 'start' - start the service (must be installed before)");
|
||||
Console.WriteLine("- 'stop' - stop the service");
|
||||
Console.WriteLine("- 'restart' - restart the service");
|
||||
Console.WriteLine("- 'restart!' - self-restart (can be called from child processes)");
|
||||
Console.WriteLine("- 'status' - check the current status of the service");
|
||||
Console.WriteLine("- 'test' - check if the service can be started and then stopped");
|
||||
Console.WriteLine("- 'version' - print the version info");
|
||||
Console.WriteLine("- 'help' - print the help info (aliases: -h,--help,-?,/?)");
|
||||
}
|
||||
|
||||
private static void printVersion()
|
||||
{
|
||||
Console.WriteLine("WinSW " + Version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,5 +28,5 @@ using System.Runtime.InteropServices;
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.1.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.1.0.0")]
|
||||
[assembly: AssemblyVersion("1.17.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.17.0.0")]
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
using NUnit.Framework;
|
||||
using winsw;
|
||||
using winswTests.Util;
|
||||
|
||||
namespace winswTests
|
||||
{
|
||||
[TestFixture]
|
||||
class MainTest
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void PrintVersion()
|
||||
{
|
||||
string expectedVersion = WrapperService.Version.ToString();
|
||||
string cliOut = CLITestHelper.CLITest(new[] { "version" });
|
||||
StringAssert.Contains(expectedVersion, cliOut, "Expected that version contains " + expectedVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PrintHelp()
|
||||
{
|
||||
string expectedVersion = WrapperService.Version.ToString();
|
||||
string cliOut = CLITestHelper.CLITest(new[] { "help" });
|
||||
|
||||
StringAssert.Contains(expectedVersion, cliOut, "Expected that help contains " + expectedVersion);
|
||||
StringAssert.Contains("start", cliOut, "Expected that help refers start command");
|
||||
StringAssert.Contains("help", cliOut, "Expected that help refers help command");
|
||||
StringAssert.Contains("version", cliOut, "Expected that help refers version command");
|
||||
//TODO: check all commands after the migration of ccommands to enum
|
||||
|
||||
// Extra options
|
||||
StringAssert.Contains("/redirect", cliOut, "Expected that help message refers the redirect message");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FailOnUnsupportedCommand()
|
||||
{
|
||||
const string commandName = "nonExistentCommand";
|
||||
string expectedMessage = "Unknown command: " + commandName.ToLower();
|
||||
CLITestResult res = CLITestHelper.CLIErrorTest(new[] {commandName});
|
||||
|
||||
Assert.True(res.HasException, "Expected an exception due to the wrong command");
|
||||
StringAssert.Contains(expectedMessage, res.Out, "Expected the message about unknown command");
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
StringAssert.Contains(expectedMessage, res.Exception.Message, "Expected the message about unknown command");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using JetBrains.Annotations;
|
||||
using winsw;
|
||||
|
||||
namespace winswTests.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper for WinSW CLI testing
|
||||
/// </summary>
|
||||
public static class CLITestHelper
|
||||
{
|
||||
private const string SeedXml = "<service>"
|
||||
+ "<id>service.exe</id>"
|
||||
+ "<name>Service</name>"
|
||||
+ "<description>The service.</description>"
|
||||
+ "<executable>node.exe</executable>"
|
||||
+ "<arguments>My Arguments</arguments>"
|
||||
+ "<logmode>rotate</logmode>"
|
||||
+ "<workingdirectory>"
|
||||
+ @"C:\winsw\workdir"
|
||||
+ "</workingdirectory>"
|
||||
+ @"<logpath>C:\winsw\logs</logpath>"
|
||||
+ "</service>";
|
||||
private static readonly ServiceDescriptor DefaultServiceDescriptor = ServiceDescriptor.FromXML(SeedXml);
|
||||
|
||||
/// <summary>
|
||||
/// Runs a simle test, which returns the output CLI
|
||||
/// </summary>
|
||||
/// <param name="args">CLI arguments to be passed</param>
|
||||
/// <param name="descriptor">Optional Service descriptor (will be used for initializationpurposes)</param>
|
||||
/// <returns>STDOUT if there's no exceptions</returns>
|
||||
/// <exception cref="Exception">Command failure</exception>
|
||||
[NotNull]
|
||||
public static string CLITest(String[] args, ServiceDescriptor descriptor = null)
|
||||
{
|
||||
using (StringWriter sw = new StringWriter())
|
||||
{
|
||||
TextWriter tmp = Console.Out;
|
||||
Console.SetOut(sw);
|
||||
WrapperService.Run(args, descriptor ?? DefaultServiceDescriptor);
|
||||
Console.SetOut(tmp);
|
||||
Console.Write(sw.ToString());
|
||||
return sw.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs a simle test, which returns the output CLI
|
||||
/// </summary>
|
||||
/// <param name="args">CLI arguments to be passed</param>
|
||||
/// <param name="descriptor">Optional Service descriptor (will be used for initializationpurposes)</param>
|
||||
/// <returns>Test results</returns>
|
||||
[NotNull]
|
||||
public static CLITestResult CLIErrorTest(String[] args, ServiceDescriptor descriptor = null)
|
||||
{
|
||||
StringWriter swOut, swErr;
|
||||
Exception testEx = null;
|
||||
TextWriter tmpOut = Console.Out;
|
||||
TextWriter tmpErr = Console.Error;
|
||||
|
||||
using (swOut = new StringWriter())
|
||||
using (swErr = new StringWriter())
|
||||
try
|
||||
{
|
||||
Console.SetOut(swOut);
|
||||
Console.SetError(swErr);
|
||||
WrapperService.Run(args, descriptor ?? DefaultServiceDescriptor);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
testEx = ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.SetOut(tmpOut);
|
||||
Console.SetError(tmpErr);
|
||||
Console.WriteLine("\n>>> Output: ");
|
||||
Console.Write(swOut.ToString());
|
||||
Console.WriteLine("\n>>> Error: ");
|
||||
Console.Write(swErr.ToString());
|
||||
if (testEx != null)
|
||||
{
|
||||
Console.WriteLine("\n>>> Exception: ");
|
||||
Console.WriteLine(testEx);
|
||||
}
|
||||
}
|
||||
|
||||
return new CLITestResult(swOut.ToString(), swErr.ToString(), testEx);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregated test report
|
||||
/// </summary>
|
||||
public class CLITestResult
|
||||
{
|
||||
[NotNull]
|
||||
public String Out { get; private set; }
|
||||
|
||||
[NotNull]
|
||||
public String Err { get; private set; }
|
||||
|
||||
[CanBeNull]
|
||||
public Exception Exception { get; private set; }
|
||||
|
||||
public bool HasException { get { return Exception != null; } }
|
||||
|
||||
public CLITestResult(String output, String err, Exception exception = null)
|
||||
{
|
||||
Out = output;
|
||||
Err = err;
|
||||
Exception = exception;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="8.0.5.0" targetFramework="net20" />
|
||||
</packages>
|
|
@ -36,17 +36,24 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="JetBrains.Annotations, Version=8.0.5.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\JetBrains.Annotations.8.0.5.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=2.6.2.12296, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Lib\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MainTest.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ServiceDescriptorTests.cs" />
|
||||
<Compile Include="Util\CLITestHelper.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\winsw.csproj">
|
||||
|
@ -54,6 +61,9 @@
|
|||
<Name>winsw</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<repositories>
|
||||
<repository path="..\Tests\winswTests\packages.config" />
|
||||
</repositories>
|
Loading…
Reference in New Issue