diff --git a/product/dropkick.tests/Tasks/WinService/WinTestsWithAuthentication.cs b/product/dropkick.tests/Tasks/WinService/WinTestsWithAuthentication.cs
new file mode 100644
index 00000000..c6a6e82a
--- /dev/null
+++ b/product/dropkick.tests/Tasks/WinService/WinTestsWithAuthentication.cs
@@ -0,0 +1,126 @@
+using dropkick.DeploymentModel;
+using dropkick.Tasks.WinService;
+using dropkick.Wmi;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Xml.Serialization;
+
+namespace dropkick.tests.Tasks.WinService
+{
+ [TestFixture]
+ [Category("Integration")]
+ public class WinTestsWithAuthentication
+ {
+ public class WmiAuthenticationInfo
+ {
+ public string MachineName { get; set; }
+ public string WmiUserName { get; set; }
+ public string WmiPassword { get; set; }
+ public string ServiceUserName { get; set; }
+ public string ServicePassword { get; set; }
+ }
+
+ private WmiAuthenticationInfo GetAuthenticationInfo()
+ {
+ string path = System.IO.Path.GetFullPath("WmiAuthenticationInfo.xml");
+ if (!System.IO.File.Exists(path))
+ {
+ throw new Exception("Please create a settings file first at: " + path);
+ }
+ var serializer = new XmlSerializer(typeof(WmiAuthenticationInfo));
+ using (var reader = new System.IO.StreamReader(path))
+ {
+ return (WmiAuthenticationInfo)serializer.Deserialize(reader);
+ }
+ }
+
+ [Test]
+ [Explicit]
+ [Category("Integration")]
+ public void Start()
+ {
+ var authInfo = GetAuthenticationInfo();
+
+ WmiService.WithAuthentication(authInfo.WmiUserName, authInfo.WmiPassword);
+
+ var t = new WinServiceStopTask(authInfo.MachineName, "IISADMIN");
+ var verifyStopResult = t.VerifyCanRun();
+ Log(verifyStopResult);
+ AssertSuccess(verifyStopResult);
+
+ var stopResult = t.Execute();
+ Log(stopResult);
+ AssertSuccess(stopResult);
+
+ var t2 = new WinServiceStartTask(authInfo.MachineName, "IISADMIN");
+ var verifyStartResult = t2.VerifyCanRun();
+ Log(verifyStartResult);
+ AssertSuccess(verifyStartResult);
+
+ var startResult = t2.Execute();
+ Log(startResult);
+ AssertSuccess(startResult);
+ }
+
+ [Test]
+ [Explicit]
+ public void RemoteCreate()
+ {
+ var authInfo = GetAuthenticationInfo();
+
+ WmiService.WithAuthentication(authInfo.WmiUserName, authInfo.WmiPassword);
+ var t = new WinServiceCreateTask(authInfo.MachineName, "DropKicKTestService");
+
+ t.ServiceLocation = "C:\\Test\\TestService.exe";
+ t.StartMode = ServiceStartMode.Automatic;
+ t.UserName = authInfo.ServiceUserName;
+ t.Password = authInfo.ServicePassword;
+
+ DeploymentResult o = t.VerifyCanRun();
+ AssertSuccess(o);
+ var result = t.Execute();
+ Log(result);
+ AssertSuccess(result);
+ }
+
+ [Test]
+ [Explicit]
+ [Category("Integration")]
+ public void RemoteDelete()
+ {
+ var authInfo = GetAuthenticationInfo();
+
+ WmiService.WithAuthentication(authInfo.ServiceUserName, authInfo.ServicePassword);
+
+ var t = new WinServiceDeleteTask(authInfo.MachineName, "DropkicKTestService");
+
+ DeploymentResult o = t.VerifyCanRun();
+ Log(o);
+ AssertSuccess(o);
+ var result = t.Execute();
+ Log(result);
+ AssertSuccess(result);
+ }
+
+ private void AssertSuccess(DeploymentResult result)
+ {
+ Assert.IsFalse(result.Any(i => i.Status == DeploymentItemStatus.Alert || i.Status == DeploymentItemStatus.Error));
+ }
+
+ private void Log(DeploymentResult result)
+ {
+ if (result != null)
+ {
+ foreach (var item in result)
+ {
+ Debug.WriteLine(item.Message);
+ }
+ }
+ }
+
+ }
+}
diff --git a/product/dropkick.tests/dropkick.tests.csproj b/product/dropkick.tests/dropkick.tests.csproj
index 902aaa25..afa676b5 100644
--- a/product/dropkick.tests/dropkick.tests.csproj
+++ b/product/dropkick.tests/dropkick.tests.csproj
@@ -138,6 +138,7 @@
+
diff --git a/product/dropkick/Configuration/Dsl/Authentication/Extension.cs b/product/dropkick/Configuration/Dsl/Authentication/Extension.cs
new file mode 100644
index 00000000..e60e86a5
--- /dev/null
+++ b/product/dropkick/Configuration/Dsl/Authentication/Extension.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using dropkick.Wmi;
+using dropkick.StringInterpolation;
+
+namespace dropkick.Configuration.Dsl.Authentication
+{
+ public static class Extension
+ {
+ public static ProtoServer WithAuthentication(this ProtoServer server, string remoteUserName, string remotePassword)
+ {
+ var interpolator = new CaseInsensitiveInterpolator();
+ remoteUserName = interpolator.ReplaceTokens(HUB.Settings, remoteUserName);
+ remotePassword = interpolator.ReplaceTokens(HUB.Settings, remotePassword);
+ WmiService.WithAuthentication(remoteUserName, remotePassword);
+ return server;
+ }
+ }
+}
diff --git a/product/dropkick/Configuration/Dsl/Files/Extension.cs b/product/dropkick/Configuration/Dsl/Files/Extension.cs
index 92d94c86..1d9aba9a 100644
--- a/product/dropkick/Configuration/Dsl/Files/Extension.cs
+++ b/product/dropkick/Configuration/Dsl/Files/Extension.cs
@@ -98,5 +98,11 @@ public static ExistsOptions Exists(this ProtoServer protoserver, string reason,
protoserver.RegisterProtoTask(proto);
return proto;
}
+
+ public static void OpenFolderShareWithAuthentication(this ProtoServer protoServer, string folderName, string userName, string password)
+ {
+ var task = new OpenFolderShareAuthenticationProtoTask(folderName, userName, password);
+ protoServer.RegisterProtoTask(task);
+ }
}
}
\ No newline at end of file
diff --git a/product/dropkick/Configuration/Dsl/Files/OpenFolderShareAuthenticationProtoTask.cs b/product/dropkick/Configuration/Dsl/Files/OpenFolderShareAuthenticationProtoTask.cs
new file mode 100644
index 00000000..0db88863
--- /dev/null
+++ b/product/dropkick/Configuration/Dsl/Files/OpenFolderShareAuthenticationProtoTask.cs
@@ -0,0 +1,31 @@
+using dropkick.Tasks;
+using dropkick.Tasks.Files;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace dropkick.Configuration.Dsl.Files
+{
+ public class OpenFolderShareAuthenticationProtoTask : BaseProtoTask
+ {
+ private readonly string _folderName;
+ private readonly string _userName;
+ private readonly string _password;
+
+ public OpenFolderShareAuthenticationProtoTask(string folderName, string userName, string password)
+ {
+ _folderName = ReplaceTokens(folderName);
+ _userName = userName;
+ _password = password;
+ }
+
+ public override void RegisterRealTasks(dropkick.DeploymentModel.PhysicalServer server)
+ {
+ string to = server.MapPath(_folderName);
+
+ var task = new OpenFolderShareAuthenticationTask(to, _userName, _password);
+ server.AddTask(task);
+ }
+ }
+}
diff --git a/product/dropkick/Configuration/Dsl/WinService/Extension.cs b/product/dropkick/Configuration/Dsl/WinService/Extension.cs
index e6db68e3..7554592d 100644
--- a/product/dropkick/Configuration/Dsl/WinService/Extension.cs
+++ b/product/dropkick/Configuration/Dsl/WinService/Extension.cs
@@ -17,6 +17,6 @@ public static class Extension
public static WinServiceOptions WinService(this ProtoServer protoServer, string serviceName)
{
return new ProtoWinServiceTask(protoServer, serviceName);
- }
+ }
}
}
\ No newline at end of file
diff --git a/product/dropkick/Configuration/Dsl/WinService/ProtoWinServiceTask.cs b/product/dropkick/Configuration/Dsl/WinService/ProtoWinServiceTask.cs
index 9e524782..25df944c 100644
--- a/product/dropkick/Configuration/Dsl/WinService/ProtoWinServiceTask.cs
+++ b/product/dropkick/Configuration/Dsl/WinService/ProtoWinServiceTask.cs
@@ -27,7 +27,6 @@ public ProtoWinServiceTask(ProtoServer protoServer, string serviceName)
_serviceName = serviceName;
}
-
public WinServiceOptions Do(Action registerAdditionalActions)
{
_protoServer.RegisterProtoTask(new ProtoWinServiceStopTask(_serviceName));
@@ -35,7 +34,7 @@ public WinServiceOptions Do(Action registerAdditionalActions)
//child task
registerAdditionalActions(_protoServer);
-
+
_protoServer.RegisterProtoTask(new ProtoWinServiceStartTask(_serviceName));
return this;
diff --git a/product/dropkick/FileSystem/DotNetPath.cs b/product/dropkick/FileSystem/DotNetPath.cs
index 3b017316..8b6d7ff8 100644
--- a/product/dropkick/FileSystem/DotNetPath.cs
+++ b/product/dropkick/FileSystem/DotNetPath.cs
@@ -25,7 +25,7 @@ public class DotNetPath : Path
{
#region Path Members
- public string GetPhysicalPath(PhysicalServer site, string path,bool forceLocalPath)
+ public string GetPhysicalPath(PhysicalServer site, string path, bool forceLocalPath)
{
var standardizedPath = path;
if (!IsUncPath(standardizedPath))
diff --git a/product/dropkick/FileSystem/FileShareAuthenticator.cs b/product/dropkick/FileSystem/FileShareAuthenticator.cs
new file mode 100644
index 00000000..c28fa0f9
--- /dev/null
+++ b/product/dropkick/FileSystem/FileShareAuthenticator.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace dropkick.FileSystem
+{
+ public static class FileShareAuthenticator
+ {
+ public static FileShareAuthenticationContext BeginFileShareAuthentication(string remoteUnc, string userName, string password)
+ {
+ string error = PinvokeWindowsNetworking.connectToRemote(remoteUnc, userName, password);
+ if (!string.IsNullOrEmpty(error))
+ {
+ throw new Exception("Error calling PinvokeWindowsNetworking.connectToRemote: " + error);
+ }
+ return new FileShareAuthenticationContext(remoteUnc, userName, password);
+ }
+
+ public class FileShareAuthenticationContext : IDisposable
+ {
+ private readonly string _remoteUnc;
+ private readonly string _userName;
+ private readonly string _password;
+ private bool _active;
+
+ public FileShareAuthenticationContext(string remoteUnc, string userName, string password)
+ {
+ _remoteUnc = remoteUnc;
+ _userName = userName;
+ _password = password;
+ _active = true;
+ }
+
+ public void Dispose()
+ {
+ if (_active)
+ {
+ var error = PinvokeWindowsNetworking.disconnectRemote(_remoteUnc);
+ if (!string.IsNullOrEmpty(error))
+ {
+ throw new Exception("PinvokeWindowsNetworking.disconnectRemote failed: " + error);
+ }
+ _active = false;
+ }
+ }
+ }
+ }
+}
diff --git a/product/dropkick/FileSystem/PinvokeWindowsNetworking.cs b/product/dropkick/FileSystem/PinvokeWindowsNetworking.cs
new file mode 100644
index 00000000..db868893
--- /dev/null
+++ b/product/dropkick/FileSystem/PinvokeWindowsNetworking.cs
@@ -0,0 +1,178 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace dropkick.FileSystem
+{
+ //http://www.dimitrioskouzisloukas.com/blog/index.php?blog=2&title=c_windows_networking_library&more=1&c=1&tb=1&pb=1
+ //http://lookfwd.doitforme.gr/blog/media/PinvokeWindowsNetworking.cs
+ public class PinvokeWindowsNetworking
+ {
+ #region Consts
+ const int RESOURCE_CONNECTED = 0x00000001;
+ const int RESOURCE_GLOBALNET = 0x00000002;
+ const int RESOURCE_REMEMBERED = 0x00000003;
+
+ const int RESOURCETYPE_ANY = 0x00000000;
+ const int RESOURCETYPE_DISK = 0x00000001;
+ const int RESOURCETYPE_PRINT = 0x00000002;
+
+ const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
+ const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
+ const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
+ const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
+ const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
+ const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;
+
+ const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
+ const int RESOURCEUSAGE_CONTAINER = 0x00000002;
+
+
+ const int CONNECT_INTERACTIVE = 0x00000008;
+ const int CONNECT_PROMPT = 0x00000010;
+ const int CONNECT_REDIRECT = 0x00000080;
+ const int CONNECT_UPDATE_PROFILE = 0x00000001;
+ const int CONNECT_COMMANDLINE = 0x00000800;
+ const int CONNECT_CMD_SAVECRED = 0x00001000;
+
+ const int CONNECT_LOCALDRIVE = 0x00000100;
+ #endregion
+
+ #region Errors
+ const int NO_ERROR = 0;
+
+ const int ERROR_ACCESS_DENIED = 5;
+ const int ERROR_ALREADY_ASSIGNED = 85;
+ const int ERROR_BAD_DEVICE = 1200;
+ const int ERROR_BAD_NET_NAME = 67;
+ const int ERROR_BAD_PROVIDER = 1204;
+ const int ERROR_CANCELLED = 1223;
+ const int ERROR_EXTENDED_ERROR = 1208;
+ const int ERROR_INVALID_ADDRESS = 487;
+ const int ERROR_INVALID_PARAMETER = 87;
+ const int ERROR_INVALID_PASSWORD = 1216;
+ const int ERROR_MORE_DATA = 234;
+ const int ERROR_NO_MORE_ITEMS = 259;
+ const int ERROR_NO_NET_OR_BAD_PATH = 1203;
+ const int ERROR_NO_NETWORK = 1222;
+
+ const int ERROR_BAD_PROFILE = 1206;
+ const int ERROR_CANNOT_OPEN_PROFILE = 1205;
+ const int ERROR_DEVICE_IN_USE = 2404;
+ const int ERROR_NOT_CONNECTED = 2250;
+ const int ERROR_OPEN_FILES = 2401;
+
+ private struct ErrorClass
+ {
+ public int num;
+ public string message;
+ public ErrorClass(int num, string message)
+ {
+ this.num = num;
+ this.message = message;
+ }
+ }
+
+
+ // Created with excel formula:
+ // ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), "
+ private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
+ new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"),
+ new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"),
+ new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"),
+ new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"),
+ new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"),
+ new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"),
+ new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"),
+ new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"),
+ new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"),
+ new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"),
+ new ErrorClass(ERROR_MORE_DATA, "Error: More Data"),
+ new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"),
+ new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"),
+ new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"),
+ new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"),
+ new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"),
+ new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"),
+ new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"),
+ new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"),
+ new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"),
+ };
+
+ private static string getErrorForNumber(int errNum)
+ {
+ foreach (ErrorClass er in ERROR_LIST)
+ {
+ if (er.num == errNum) return er.message;
+ }
+ return new Win32Exception(errNum).Message;
+ //return "Error: Unknown, " + errNum;
+ }
+ #endregion
+
+ [DllImport("Mpr.dll")]
+ private static extern int WNetUseConnection(
+ IntPtr hwndOwner,
+ NETRESOURCE lpNetResource,
+ string lpPassword,
+ string lpUserID,
+ int dwFlags,
+ string lpAccessName,
+ string lpBufferSize,
+ string lpResult
+ );
+
+ [DllImport("Mpr.dll")]
+ private static extern int WNetCancelConnection2(
+ string lpName,
+ int dwFlags,
+ int fForce
+ );
+
+ [StructLayout(LayoutKind.Sequential)]
+ private class NETRESOURCE
+ {
+ public int dwScope = 0;
+ public int dwType = 0;
+ public int dwDisplayType = 0;
+ public int dwUsage = 0;
+ public string lpLocalName = "";
+ public string lpRemoteName = "";
+ public string lpComment = "";
+ public string lpProvider = "";
+ }
+
+
+ public static string connectToRemote(string remoteUNC, string username, string password)
+ {
+ return connectToRemote(remoteUNC, username, password, false);
+ }
+
+ public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser)
+ {
+ NETRESOURCE nr = new NETRESOURCE();
+ nr.dwType = RESOURCETYPE_DISK;
+ nr.lpRemoteName = remoteUNC;
+ // nr.lpLocalName = "F:";
+
+ int ret;
+ if (promptUser)
+ ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
+ else
+ ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
+
+ if (ret == NO_ERROR) return null;
+ return getErrorForNumber(ret);
+ }
+
+ public static string disconnectRemote(string remoteUNC)
+ {
+ int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, 1);
+ if (ret == NO_ERROR) return null;
+ return getErrorForNumber(ret);
+ }
+ }
+}
diff --git a/product/dropkick/Tasks/Files/OpenFolderShareAuthenticationTask.cs b/product/dropkick/Tasks/Files/OpenFolderShareAuthenticationTask.cs
new file mode 100644
index 00000000..f96978ba
--- /dev/null
+++ b/product/dropkick/Tasks/Files/OpenFolderShareAuthenticationTask.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using dropkick;
+using dropkick.DeploymentModel;
+using dropkick.FileSystem;
+using System.Text.RegularExpressions;
+
+namespace dropkick.Tasks.Files
+{
+ public class OpenFolderShareAuthenticationTask : Task
+ {
+ private readonly string _to;
+ private readonly string _userName;
+ private readonly string _password;
+
+ public OpenFolderShareAuthenticationTask(string to, string userName, string password)
+ {
+ _to = to;
+ _userName = userName;
+ _password = password;
+ }
+
+ public string Name
+ {
+ get { return "Creating new empty folder '{0}' with user name '{1}'".FormatWith(_to, _userName); }
+ }
+
+ public DeploymentResult VerifyCanRun()
+ {
+ var result = new DeploymentResult();
+ var to = new DotNetPath().GetFullPath(_to);
+ string toParent = GetRootShare(to);
+ try
+ {
+ using (var context = FileShareAuthenticator.BeginFileShareAuthentication(toParent, _userName, _password))
+ {
+ result.AddGood(System.IO.Directory.Exists(to) ? "'{0}' already exists.".FormatWith(to) : Name);
+ }
+ }
+ catch (Exception err)
+ {
+ result.AddError("Failed to access '{0}' as user '{1}'".FormatWith(toParent, _userName), err);
+ }
+ //TODO figure out a good verify step...
+ return result;
+ }
+
+ private string GetRootShare(string to)
+ {
+ var regex = new Regex(@"\\\\[^\\]*\\[^\\]*");
+ var match = regex.Match(to);
+ if (!match.Success)
+ {
+ throw new Exception("Unable to parse root share from " + to);
+ }
+ return match.Value;
+ }
+
+ public DeploymentResult Execute()
+ {
+ var result = new DeploymentResult();
+ var to = new DotNetPath().GetFullPath(_to);
+
+ var toParent = GetRootShare(to);
+ try
+ {
+ using (var context = FileShareAuthenticator.BeginFileShareAuthentication(toParent, _userName, _password))
+ {
+ result.AddGood("'{0}' authenticated with {1}.".FormatWith(to, _userName));
+ }
+ }
+ catch (Exception err)
+ {
+ result.AddError("Failed to access '{0}' as user '{1}'".FormatWith(toParent, _userName), err);
+ }
+ return result;
+ }
+ }
+}
diff --git a/product/dropkick/Tasks/WinService/BaseServiceTask.cs b/product/dropkick/Tasks/WinService/BaseServiceTask.cs
index bc687fb9..3ced947c 100644
--- a/product/dropkick/Tasks/WinService/BaseServiceTask.cs
+++ b/product/dropkick/Tasks/WinService/BaseServiceTask.cs
@@ -28,15 +28,64 @@ protected BaseServiceTask(string machineName, string serviceName)
public string MachineName { get; set; }
public string ServiceName { get; set; }
+ protected bool ServiceIsRunning()
+ {
+ try
+ {
+ if (!dropkick.Wmi.WmiService.AuthenticationSpecified)
+ {
+ using (var c = new ServiceController(ServiceName, MachineName))
+ {
+ return (c.Status == ServiceControllerStatus.Running);
+ }
+ }
+ else
+ {
+ var status = dropkick.Wmi.WmiService.QueryService(MachineName, ServiceName);
+ switch (status)
+ {
+ case Wmi.ServiceReturnCode.ServiceAlreadyRunning:
+ case Wmi.ServiceReturnCode.Success:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
protected bool ServiceExists()
{
try
{
- using (var c = new ServiceController(ServiceName, MachineName))
+ if(!dropkick.Wmi.WmiService.AuthenticationSpecified)
+ {
+ using (var c = new ServiceController(ServiceName, MachineName))
+ {
+ ServiceControllerStatus currentStatus = c.Status;
+ return true;
+ }
+ }
+ else
{
- ServiceControllerStatus currentStatus = c.Status;
- return true;
+ var status = dropkick.Wmi.WmiService.QueryService(MachineName, ServiceName);
+ switch(status)
+ {
+ case Wmi.ServiceReturnCode.DependentServicesRunning:
+ case Wmi.ServiceReturnCode.ServiceAlreadyPaused:
+ case Wmi.ServiceReturnCode.ServiceAlreadyRunning:
+ case Wmi.ServiceReturnCode.StatusServiceExists:
+ case Wmi.ServiceReturnCode.Success:
+ case Wmi.ServiceReturnCode.ServiceNotActive:
+ case Wmi.ServiceReturnCode.InvalidServiceControl:
+ return true;
+ default:
+ return false;
+ }
}
}
catch (Exception)
diff --git a/product/dropkick/Tasks/WinService/WinServiceCreateTask.cs b/product/dropkick/Tasks/WinService/WinServiceCreateTask.cs
index ceedb5d2..98770e5e 100644
--- a/product/dropkick/Tasks/WinService/WinServiceCreateTask.cs
+++ b/product/dropkick/Tasks/WinService/WinServiceCreateTask.cs
@@ -65,7 +65,11 @@ public override DeploymentResult Execute()
if (shouldPromptForPassword())
Password = _prompt.Prompt("Win Service '{0}' For User '{1}' Password".FormatWith(ServiceName, UserName));
- ServiceReturnCode returnCode = WmiService.Create(MachineName, ServiceName, ServiceDisplayName, ServiceLocation,
+ string displayName = ServiceDisplayName;
+ if(string.IsNullOrEmpty(displayName))
+ displayName = ServiceName;
+
+ ServiceReturnCode returnCode = WmiService.Create(MachineName, ServiceName, displayName, ServiceLocation,
StartMode, UserName, Password, Dependencies);
if (returnCode != ServiceReturnCode.Success)
diff --git a/product/dropkick/Tasks/WinService/WinServiceDeleteTask.cs b/product/dropkick/Tasks/WinService/WinServiceDeleteTask.cs
index 32c34f4f..00ea78dd 100644
--- a/product/dropkick/Tasks/WinService/WinServiceDeleteTask.cs
+++ b/product/dropkick/Tasks/WinService/WinServiceDeleteTask.cs
@@ -30,16 +30,29 @@ public override string Name
public override DeploymentResult VerifyCanRun()
{
var result = new DeploymentResult();
-
+ if(!ServiceExists())
+ {
+ result.AddNote("Cannot delete service '{0}', service does not exist".FormatWith(ServiceName));
+ }
return result;
}
public override DeploymentResult Execute()
{
- var result = new DeploymentResult();
-
- ServiceReturnCode returnCode = WmiService.Delete(MachineName, ServiceName);
-
+ var result = new DeploymentResult();
+
+ if (!ServiceExists())
+ {
+ result.AddNote("Cannot delete service '{0}', service does not exist".FormatWith(ServiceName));
+ }
+ else
+ {
+ ServiceReturnCode returnCode = WmiService.Delete(MachineName, ServiceName);
+ if(returnCode != ServiceReturnCode.Success)
+ {
+ result.AddAlert("Deleting service '{0}' failed: '{1}'".FormatWith(ServiceName, returnCode));
+ }
+ }
return result;
}
}
diff --git a/product/dropkick/Tasks/WinService/WinServiceStartTask.cs b/product/dropkick/Tasks/WinService/WinServiceStartTask.cs
index d580e463..7fb22bde 100644
--- a/product/dropkick/Tasks/WinService/WinServiceStartTask.cs
+++ b/product/dropkick/Tasks/WinService/WinServiceStartTask.cs
@@ -31,7 +31,7 @@ public override DeploymentResult VerifyCanRun()
}
else
{
- result.AddAlert(string.Format("Can't find service '{0}'", ServiceName));
+ result.AddError(string.Format("Can't find service '{0}'", ServiceName));
}
return result;
@@ -43,26 +43,68 @@ public override DeploymentResult Execute()
if (ServiceExists())
{
- using (var c = new ServiceController(ServiceName, MachineName))
+ if(!dropkick.Wmi.WmiService.AuthenticationSpecified)
{
- Logging.Coarse("[svc] Starting service '{0}'", ServiceName);
- try
+ using (var c = new ServiceController(ServiceName, MachineName))
{
- c.Start();
- LogCoarseGrain("[svc] Waiting up to 60 seconds because Windows can be silly");
- c.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(60));
+ Logging.Coarse("[svc] Starting service '{0}'", ServiceName);
+ try
+ {
+ c.Start();
+ LogCoarseGrain("[svc] Waiting up to 60 seconds because Windows can be silly");
+ c.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(60));
+ }
+ catch (InvalidOperationException ex)
+ {
+ result.AddError("The service '{0}' did not start, most likely due to a logon issue.".FormatWith(ServiceName), ex);
+ LogCoarseGrain("The service '{0}' did not start, most likely due to a logon issue.{1}{2}", ServiceName, Environment.NewLine, ex);
+ return result;
+ }
+ catch (TimeoutException)
+ {
+ result.AddAlert("Service '{0}' did not finish starting during the specified timeframe. You will need to manually verify if the service started successfully.", ServiceName);
+ LogCoarseGrain("Service '{0}' did not finish starting during the specified timeframe. You will need to manually verify if the service started successfully.", ServiceName);
+ return result;
+ }
}
- catch (InvalidOperationException ex)
+ }
+ else
+ {
+ Logging.Coarse("[svc] Starting service '{0}'", ServiceName);
+ if (ServiceIsRunning())
{
- result.AddError("The service '{0}' did not start, most likely due to a logon issue.".FormatWith(ServiceName), ex);
- LogCoarseGrain("The service '{0}' did not start, most likely due to a logon issue.{1}{2}", ServiceName, Environment.NewLine, ex);
- return result;
+ LogCoarseGrain("[svc] Service '{0}' is already running".FormatWith(ServiceName));
}
- catch (TimeoutException)
+ else
{
- result.AddAlert("Service '{0}' did not finish starting during the specified timeframe. You will need to manually verify if the service started successfully.", ServiceName);
- LogCoarseGrain("Service '{0}' did not finish starting during the specified timeframe. You will need to manually verify if the service started successfully.", ServiceName);
- return result;
+ try
+ {
+ var returnCode = dropkick.Wmi.WmiService.Start(MachineName, ServiceName);
+ switch(returnCode)
+ {
+ case Wmi.ServiceReturnCode.Success:
+ if(!ServiceIsRunning())
+ {
+ result.AddError("Failed to start service '{0}', most likely due to a logon issue.".FormatWith(ServiceName));
+ LogCoarseGrain("The service '{0}' did not start, most likely due to a logon issue.", ServiceName);
+ break;
+ }
+ else
+ {
+ //good!
+ }
+ break;
+ default:
+ result.AddError("Failed to start service '{0}', most likely due to a logon issue, result: {1}".FormatWith(ServiceName, returnCode));
+ LogCoarseGrain("The service '{0}' did not start, most likely due to a logon issue.", ServiceName);
+ break;
+ }
+ }
+ catch(Exception ex)
+ {
+ result.AddError("The service '{0}' did not start, most likely due to a logon issue.".FormatWith(ServiceName), ex);
+ LogCoarseGrain("The service '{0}' did not start, most likely due to a logon issue.{1}{2}", ServiceName, Environment.NewLine, ex);
+ }
}
}
result.AddGood("Started the service '{0}'", ServiceName);
diff --git a/product/dropkick/Tasks/WinService/WinServiceStopTask.cs b/product/dropkick/Tasks/WinService/WinServiceStopTask.cs
index c6e43db2..99f0b8b9 100644
--- a/product/dropkick/Tasks/WinService/WinServiceStopTask.cs
+++ b/product/dropkick/Tasks/WinService/WinServiceStopTask.cs
@@ -57,21 +57,49 @@ public override DeploymentResult Execute()
if (ServiceExists())
{
- using (var c = new ServiceController(ServiceName, MachineName))
+ if(!dropkick.Wmi.WmiService.AuthenticationSpecified)
{
- Logging.Coarse("[svc] Stopping service '{0}'", ServiceName);
- if (c.CanStop)
+ using (var c = new ServiceController(ServiceName, MachineName))
{
- int pid = GetProcessId(ServiceName);
+ Logging.Coarse("[svc] Stopping service '{0}'", ServiceName);
+ if (c.CanStop)
+ {
+ int pid = GetProcessId(ServiceName);
- c.Stop();
- c.WaitForStatus(ServiceControllerStatus.Stopped, 30.Seconds());
+ c.Stop();
+ c.WaitForStatus(ServiceControllerStatus.Stopped, 30.Seconds());
- WaitForProcessToDie(pid);
+ WaitForProcessToDie(pid);
+ }
}
+ result.AddGood("Stopped Service '{0}'", ServiceName);
+ Logging.Coarse("[svc] Stopped service '{0}'", ServiceName);
+ }
+ else
+ {
+ if(!ServiceIsRunning())
+ {
+ result.AddGood("Stopping Service '{0}', Service Already Stopped", ServiceName);
+ Logging.Coarse("[svc] Stopping service '{0}', service already stopped", ServiceName);
+ }
+ else
+ {
+ var status = dropkick.Wmi.WmiService.Stop(MachineName, ServiceName);
+ switch (status)
+ {
+ case Wmi.ServiceReturnCode.StatusServiceExists:
+ case Wmi.ServiceReturnCode.Success:
+ result.AddGood("Stopped Service '{0}'", ServiceName);
+ Logging.Coarse("[svc] Stopped service '{0}'", ServiceName);
+ break;
+ default:
+ //BAD
+ throw new Exception("Failed to stop service {0}: {1}".FormatWith(ServiceName, status));
+ }
+ }
+ result.AddGood("Stopped Service '{0}'", ServiceName);
+ Logging.Coarse("[svc] Stopped service '{0}'", ServiceName);
}
- result.AddGood("Stopped Service '{0}'", ServiceName);
- Logging.Coarse("[svc] Stopped service '{0}'", ServiceName);
}
else
{
diff --git a/product/dropkick/Wmi/WmiHelper.cs b/product/dropkick/Wmi/WmiHelper.cs
index 578a0dad..a72724cd 100644
--- a/product/dropkick/Wmi/WmiHelper.cs
+++ b/product/dropkick/Wmi/WmiHelper.cs
@@ -5,6 +5,17 @@ namespace dropkick.Wmi
public class WmiHelper
{
+ [ThreadStatic]
+ private static string _wmiUserName;
+
+ [ThreadStatic]
+ private static string _wmiPassword;
+
+ public static bool AuthenticationSpecified
+ {
+ get { return !string.IsNullOrEmpty(_wmiUserName) || !string.IsNullOrEmpty(_wmiPassword); }
+ }
+
public static ManagementScope Connect(string machineName)
{
var scope = new ManagementScope(@"\\{0}\root\cimv2".FormatWith(machineName))
@@ -12,17 +23,26 @@ public static ManagementScope Connect(string machineName)
Options =
{
Impersonation = ImpersonationLevel.Impersonate,
- EnablePrivileges = true
+ EnablePrivileges = true,
+ Username = _wmiUserName,
+ Password = _wmiPassword
}
};
-
+
try
{
scope.Connect();
}
catch (Exception exc)
{
- throw new SystemException("Problem connecting WMI scope on " + machineName + ".", exc);
+ if(string.IsNullOrEmpty(_wmiUserName))
+ {
+ throw new SystemException("Problem connecting WMI scope on " + machineName + " with current user account.", exc);
+ }
+ else
+ {
+ throw new SystemException("Problem connecting WMI scope on " + machineName + " with user account " + _wmiUserName, exc);
+ }
}
return scope;
@@ -100,5 +120,11 @@ public static int InvokeStaticMethod(string machineName, string className, strin
return -1;
}
}
+
+ public static void WithAuthentication(string remoteUserName, string remotePassword)
+ {
+ _wmiUserName = remoteUserName;
+ _wmiPassword = remotePassword;
+ }
}
}
\ No newline at end of file
diff --git a/product/dropkick/Wmi/WmiService.cs b/product/dropkick/Wmi/WmiService.cs
index be8eb212..89accf64 100644
--- a/product/dropkick/Wmi/WmiService.cs
+++ b/product/dropkick/Wmi/WmiService.cs
@@ -8,7 +8,12 @@ namespace dropkick.Wmi
public class WmiService
{
const string CLASSNAME = "Win32_Service";
- //private char NULL_VALUE = char(0);
+ //private char NULL_VALUE = char(0);
+
+ public static bool AuthenticationSpecified
+ {
+ get { return WmiHelper.AuthenticationSpecified; }
+ }
public static ServiceReturnCode Create(string machineName, string serviceName, string serviceDisplayName,
string serviceLocation, ServiceStartMode startMode, string userName,
@@ -129,6 +134,11 @@ public static ServiceReturnCode Resume(string machineName, string serviceName)
{
return ServiceReturnCode.UnknownFailure;
}
- }
+ }
+
+ public static void WithAuthentication(string remoteUserName, string remotePassword)
+ {
+ WmiHelper.WithAuthentication(remoteUserName, remotePassword);
+ }
}
}
\ No newline at end of file
diff --git a/product/dropkick/dropkick.csproj b/product/dropkick/dropkick.csproj
index e3305793..e980502f 100644
--- a/product/dropkick/dropkick.csproj
+++ b/product/dropkick/dropkick.csproj
@@ -91,9 +91,11 @@
+
+
@@ -139,6 +141,7 @@
+
@@ -162,6 +165,7 @@
+
@@ -350,6 +354,7 @@
+