diff --git a/Main.cs b/Main.cs index 5e07896..d7dd5b4 100644 --- a/Main.cs +++ b/Main.cs @@ -502,18 +502,27 @@ namespace winsw if (args[0] == "install") { string username=null, password=null; - if (args.Count > 1 && args[1] == "/p") { + if (args.Count > 1 && args[1] == "/p") + { // we expected username/password on stdin Console.Write("Username: "); username = Console.ReadLine(); Console.Write("Password: "); password = ReadPassword(); } + else + { + if (d.HasServiceAccount()) + { + username = d.ServiceAccountUser; + password = d.ServiceAccountPassword; + } + } svc.Create ( d.Id, d.Caption, - "\"" + ServiceDescriptor.ExecutablePath + "\"", + "\"" + d.ExecutablePath + "\"", WMI.ServiceType.OwnProcess, ErrorControl.UserNotified, StartMode.Automatic, diff --git a/README.markdown b/README.markdown index e4294bd..b1d3b3a 100644 --- a/README.markdown +++ b/README.markdown @@ -240,3 +240,17 @@ If the service keeps failing and it goes beyond the number of `` conf This optional element controls the timing in which Windows SCM resets the failure count. For example, if you specify `1 hour` and your service continues to run longer than one hour, then the failure count is reset to zero. This affects the behaviour of the failure actions (see `` above). In other words, this is the duration in which you consider the service has been running successfully. Defaults to 1 day. + +### Service account +It is possible to specify the useraccount (and password) that the service will run as. To do this, specify a `` element like this: + + + YOURDOMAIN + useraccount + Pa55w0rd + + +### Working directory +Some services need to run with a working directory specified. To do this, specify a `` element like this: + + C:\application \ No newline at end of file diff --git a/ServiceDescriptor.cs b/ServiceDescriptor.cs index a505356..7453a54 100755 --- a/ServiceDescriptor.cs +++ b/ServiceDescriptor.cs @@ -22,7 +22,7 @@ namespace winsw /// public class ServiceDescriptor { - private readonly XmlDocument dom = new XmlDocument(); + protected readonly XmlDocument dom = new XmlDocument(); /// /// Where did we find the configuration file? @@ -37,7 +37,7 @@ namespace winsw /// public readonly string BaseName; - public static string ExecutablePath + public virtual string ExecutablePath { get { @@ -157,7 +157,7 @@ namespace winsw { get { - return SingleElement("stopexecutable",true); + return SingleElement("stopexecutable"); } } @@ -511,5 +511,55 @@ namespace winsw } } + protected string GetServiceAccountPart(string subNodeName) + { + var node = dom.SelectSingleNode("//serviceaccount"); + + if (node != null) + { + var subNode = node.SelectSingleNode(subNodeName); + if (subNode != null) + { + return subNode.InnerText; + } + } + + return null; + + } + + protected string serviceAccountDomain + { + get{ + return GetServiceAccountPart("domain"); + } + } + + protected string serviceAccountName + { + get + { + return GetServiceAccountPart("user"); + } + } + + public string ServiceAccountPassword + { + get + { + return GetServiceAccountPart("password"); + } + } + + public string ServiceAccountUser + { + get { return (serviceAccountDomain ?? "NULL") + @"\" + (serviceAccountName ?? "NULL"); } + } + + public bool HasServiceAccount() + { + return !string.IsNullOrEmpty(serviceAccountDomain) && !string.IsNullOrEmpty(serviceAccountName); + } + } } diff --git a/Tests/Lib/nunit.framework.dll b/Tests/Lib/nunit.framework.dll new file mode 100644 index 0000000..3e24ba1 Binary files /dev/null and b/Tests/Lib/nunit.framework.dll differ diff --git a/Tests/winswTests/Properties/AssemblyInfo.cs b/Tests/winswTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..fddd287 --- /dev/null +++ b/Tests/winswTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("winswTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("winswTests")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("01ee65cd-18ae-4f3b-8eac-c9f790d5f24e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/winswTests/ServiceDescriptorTests.cs b/Tests/winswTests/ServiceDescriptorTests.cs new file mode 100644 index 0000000..c5dfb34 --- /dev/null +++ b/Tests/winswTests/ServiceDescriptorTests.cs @@ -0,0 +1,78 @@ +using NUnit.Framework; +using winsw; + +namespace winswTests +{ + + public class ServiceDescriptorExtended : ServiceDescriptor + { + + public ServiceDescriptorExtended(string descriptorXml) + { + LoadTestXml(descriptorXml); + } + + private void LoadTestXml(string xml) + { + dom.LoadXml(xml); + } + } + + + [TestFixture] + public class ServiceDescriptorTests + { + + private ServiceDescriptorExtended extendedServiceDescriptor; + + private const string ExpectedWorkingDirectory = @"Z:\Path\SubPath"; + private const string Username = "User"; + private const string Password = "Password"; + private const string Domain = "Domain"; + + [SetUp] + public void SetUp() + { + const string SeedXml = "" + + "service.exe" + + "Service" + + "The service." + + "node.exe" + + "My Arguments" + + "rotate" + + "" + + "" + Domain + "" + + "" + Username + "" + + "" + Password + "" + + "" + + "" + + ExpectedWorkingDirectory + + "" + + @"C:\logs" + + ""; + + extendedServiceDescriptor = new ServiceDescriptorExtended(SeedXml); + } + + [Test] + public void VerifyWorkingDirectory() + { + System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory); + Assert.That(extendedServiceDescriptor.WorkingDirectory, Is.EqualTo(ExpectedWorkingDirectory)); + } + + [Test] + public void VerifyUsername() + { + System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory); + Assert.That(extendedServiceDescriptor.ServiceAccountUser, Is.EqualTo(Domain + "\\" + Username)); + } + + [Test] + public void VerifyPassword() + { + System.Diagnostics.Debug.WriteLine("_extendedServiceDescriptor.WorkingDirectory :: " + extendedServiceDescriptor.WorkingDirectory); + Assert.That(extendedServiceDescriptor.ServiceAccountPassword, Is.EqualTo(Password)); + } + } +} diff --git a/Tests/winswTests/winswTests.csproj b/Tests/winswTests/winswTests.csproj new file mode 100644 index 0000000..07632c7 --- /dev/null +++ b/Tests/winswTests/winswTests.csproj @@ -0,0 +1,60 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {93843402-842B-44B4-B303-AEE829BE0B43} + Library + Properties + winswTests + winswTests + v2.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Lib\nunit.framework.dll + + + + + + + + + + + + {0DE77F55-ADE5-43C1-999A-0BC81153B039} + winsw + + + + + \ No newline at end of file diff --git a/WmiSchema.cs b/WmiSchema.cs index 8d8677d..4a241e3 100755 --- a/WmiSchema.cs +++ b/WmiSchema.cs @@ -50,6 +50,8 @@ namespace WMI // ReturnValue Create(bool desktopInteract, string displayName, int errorControl, string loadOrderGroup, string loadOrderGroupDependencies, string name, string pathName, string serviceDependencies, string serviceType, string startMode, string startName, string startPassword); void Create(string name, string displayName, string pathName, ServiceType serviceType, ErrorControl errorControl, StartMode startMode, bool desktopInteract, string startName, string startPassword, string[] serviceDependencies); + void Create(string name, string displayName, string pathName, ServiceType serviceType, ErrorControl errorControl, StartMode startMode, bool desktopInteract, string[] serviceDependencies); + Win32Service Select(string name); } diff --git a/winsw.sln b/winsw.sln index 38a18c3..e9f8b36 100644 --- a/winsw.sln +++ b/winsw.sln @@ -3,6 +3,8 @@ Microsoft Visual Studio Solution File, Format Version 10.00 # Visual C# Express 2008 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winsw", "winsw.csproj", "{0DE77F55-ADE5-43C1-999A-0BC81153B039}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "winswTests", "Tests\winswTests\winswTests.csproj", "{93843402-842B-44B4-B303-AEE829BE0B43}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -23,6 +25,16 @@ Global {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Mixed Platforms.Build.0 = Release|Any CPU {0DE77F55-ADE5-43C1-999A-0BC81153B039}.Release|Win32.ActiveCfg = Release|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Debug|Win32.ActiveCfg = Debug|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Any CPU.Build.0 = Release|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {93843402-842B-44B4-B303-AEE829BE0B43}.Release|Win32.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE