Browse Source

Windows service works now!

Adam Ierymenko 11 years ago
parent
commit
9d05897f7a

+ 2 - 0
ZeroTierUI/installdialog.h

@@ -38,6 +38,8 @@
 
 #include "../node/Address.hpp"
 
+// Right now InstallDialog is only used on Mac
+
 namespace Ui {
 class InstallDialog;
 }

+ 100 - 4
ZeroTierUI/main.cpp

@@ -25,6 +25,10 @@
  * LLC. Start here: http://www.zerotier.com/
  */
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
 #include "mainwindow.h"
 #include "installdialog.h"
 #include "licensedialog.h"
@@ -33,12 +37,62 @@
 #include <QDir>
 #include <QString>
 #include <QFont>
+#include <QMessageBox>
+
+#include "../node/Constants.hpp"
+#include "../node/Defaults.hpp"
 
 #ifdef __WINDOWS__
 #include <WinSock2.h>
 #include <windows.h>
-#endif
+#include "../windows/ZeroTierOne/ZeroTierOneService.h"
+
+// Returns true if started or already running, false if failed or not installed
+static bool startWindowsService()
+{
+	SERVICE_STATUS ssSvcStatus;
+	SC_HANDLE schSCManager = NULL;
+	SC_HANDLE schService = NULL;
+
+	schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+	if (schSCManager == NULL)
+		return false;
+
+	schService = OpenService(schSCManager, ZT_SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START);
+	if (schService == NULL) {
+		CloseServiceHandle(schSCManager);
+		return false;
+	}
+
+	int tries = 0;
+	bool running = true;
+
+	for(;;) {
+		memset(&ssSvcStatus,0,sizeof(ssSvcStatus));
+		if ((++tries > 20)||(!QueryServiceStatus(schService,&ssSvcStatus))) {
+			running = false;
+			break;
+		}
+
+		if (ssSvcStatus.dwCurrentState == SERVICE_RUNNING) {
+			break;
+		} else if (ssSvcStatus.dwCurrentState == SERVICE_START_PENDING) {
+			Sleep(500);
+			continue;
+		}
+
+		memset(&ssSvcStatus,0,sizeof(ssSvcStatus));
+		ControlService(schService, SERVICE_CONTROL_START, &ssSvcStatus);
+		Sleep(500);
+	}
+
+	CloseServiceHandle(schService);
+	CloseServiceHandle(schSCManager);
+	return running;
+}
+#endif // __WINDOWS__
 
+// Globally visible settings for the app
 QSettings *settings = (QSettings *)0;
 
 int main(int argc, char *argv[])
@@ -46,6 +100,7 @@ int main(int argc, char *argv[])
 	QApplication a(argc, argv);
 
 #ifdef __WINDOWS__
+	// Start up Winsock2
 	{
 		WSADATA wsaData;
 		WSAStartup(MAKEWORD(2,2),&wsaData);
@@ -65,12 +120,11 @@ int main(int argc, char *argv[])
 		// InstallDialog is an alternative main window. It will re-launch the app
 		// when done.
 		InstallDialog id;
+		id.setStyleSheet(a.styleSheet());
 		id.show();
 		return a.exec();
 	}
-#endif
 
-#ifdef __APPLE__
 	{
 		// Put QSettings here because this is one of the writable directories allowed
 		// in Apple's app store sandbox specs. We might end up in app store someday.
@@ -78,7 +132,7 @@ int main(int argc, char *argv[])
 		QDir::root().mkpath(zt1AppSupport);
 		settings = new QSettings(zt1AppSupport + "/ui.ini",QSettings::IniFormat);
 	}
-#else
+#else // on non-Apple boxen put it in the standard place using the default format
 	settings = new QSettings("ZeroTier Networks","ZeroTier One");
 #endif
 
@@ -88,6 +142,48 @@ int main(int argc, char *argv[])
 		ld.exec();
 	}
 
+#ifdef __WINDOWS__
+	{
+		bool winSvcInstalled = false;
+		while (!startWindowsService()) {
+			if (winSvcInstalled) {
+				// Service was installed and subsequently failed to start again, so
+				// something is wrong!
+				QMessageBox::critical((QWidget *)0,"Service Not Available","Unable to locate or start ZeroTier One service. There may be a problem with the installation. Try installing from the .msi file again or e-mail [email protected] if you cannot install. (Error: service failed to start)",QMessageBox::Ok);
+				return 1;
+			}
+
+#ifdef _WIN64
+			BOOL is64Bit = TRUE;
+#else
+			BOOL is64Bit = FALSE;
+			IsWow64Process(GetCurrentProcess(),&is64Bit);
+#endif
+			std::string exe(ZeroTier::ZT_DEFAULTS.defaultHomePath + "\\zerotier-one_");
+			exe.append((is64Bit == TRUE) ? "x64.exe" : "x86.exe");
+
+			if (QFile::exists(exe.c_str())) {
+				STARTUPINFOA si;
+				PROCESS_INFORMATION pi;
+				memset(&si,0,sizeof(si));
+				memset(&pi,0,sizeof(pi));
+				if (CreateProcessA(NULL,const_cast <LPSTR>((exe + " -I").c_str()),NULL,NULL,FALSE,CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP,NULL,NULL,&si,&pi)) {
+					WaitForSingleObject(pi.hProcess,INFINITE);
+					CloseHandle(pi.hProcess);
+					CloseHandle(pi.hThread);
+					winSvcInstalled = true;
+				}
+			}
+
+			if (!winSvcInstalled) {
+				// Service failed to install -- installation problem like missing .exe
+				QMessageBox::critical((QWidget *)0,"Service Not Available","Unable to locate or start ZeroTier One service. There may be a problem with the installation. Try installing from the .msi file again or e-mail [email protected] if you cannot install. (Error: service not installed)",QMessageBox::Ok);
+				return 1;
+			}
+		}
+	}
+#endif
+
 	MainWindow w;
 	w.show();
 	return a.exec();

+ 27 - 4
ext/installfiles/windows/ZeroTier One.aip

@@ -38,32 +38,56 @@
     <ROW Directory="ZeroTier_1_Dir" Directory_Parent="ProgramFilesFolder" DefaultDir="ZeroTier"/>
     <ROW Directory="ZeroTier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="ZeroTier"/>
     <ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/>
+    <ROW Directory="platforms_Dir" Directory_Parent="One_1_Dir" DefaultDir="PLATFO~1|platforms"/>
     <ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/>
     <ROW Directory="updates.d_Dir" Directory_Parent="One_Dir" DefaultDir="updates.d"/>
     <ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/>
     <ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
-    <ROW Component="One" ComponentId="{5CAAC183-3291-4660-9065-438314DC5181}" Directory_="One_1_Dir" Attributes="0"/>
     <ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
+    <ROW Component="Qt5Core.dll" ComponentId="{F6BFD713-0DD7-411C-BE9A-7A5A902814F2}" Directory_="One_1_Dir" Attributes="0" KeyPath="Qt5Core.dll"/>
+    <ROW Component="Qt5Gui.dll" ComponentId="{9005A0ED-9E05-4E7B-8083-AC57131BCF98}" Directory_="One_1_Dir" Attributes="0" KeyPath="Qt5Gui.dll"/>
+    <ROW Component="Qt5Network.dll" ComponentId="{0ECD4DCF-8E1D-4FF7-BB0D-3A9E1629AFC7}" Directory_="One_1_Dir" Attributes="0" KeyPath="Qt5Network.dll"/>
+    <ROW Component="Qt5Widgets.dll" ComponentId="{7B35E61D-D2F2-4605-AE92-9F0E0765831D}" Directory_="One_1_Dir" Attributes="0" KeyPath="Qt5Widgets.dll"/>
     <ROW Component="WdfCoinstaller01011.dll" ComponentId="{A417293D-AA26-447A-9A16-E0BCB2084CBA}" Directory_="x64_Dir" Attributes="256" KeyPath="WdfCoinstaller01011.dll"/>
     <ROW Component="WdfCoinstaller01011.dll_1" ComponentId="{C629091A-4845-4BD8-9E49-3A051FDDBEF9}" Directory_="x86_Dir" Attributes="0" KeyPath="WdfCoinstaller01011.dll_1"/>
     <ROW Component="devcon_x64.exe" ComponentId="{0711ACF9-EEF5-48B0-95D7-8421B74AE314}" Directory_="One_Dir" Attributes="256" KeyPath="devcon_x64.exe"/>
     <ROW Component="devcon_x86.exe" ComponentId="{335F6945-AC5D-40DD-B671-C9BA9C304623}" Directory_="One_Dir" Attributes="0" KeyPath="devcon_x86.exe"/>
+    <ROW Component="icudt51.dll" ComponentId="{413E1355-FEFE-4767-95A0-8A4B61B77821}" Directory_="One_1_Dir" Attributes="0" KeyPath="icudt51.dll"/>
+    <ROW Component="icuin51.dll" ComponentId="{2BD5EEFC-E613-49B6-9CC7-01E377F2C73C}" Directory_="One_1_Dir" Attributes="0" KeyPath="icuin51.dll"/>
+    <ROW Component="icuuc51.dll" ComponentId="{CCFECFF4-2B24-4A4B-8D77-2C6E6BBEEB2C}" Directory_="One_1_Dir" Attributes="0" KeyPath="icuuc51.dll"/>
+    <ROW Component="libEGL.dll" ComponentId="{D0C896BF-4145-4C0F-8CE1-577283DA7B4A}" Directory_="One_1_Dir" Attributes="0" KeyPath="libEGL.dll"/>
+    <ROW Component="libGLESv2.dll" ComponentId="{C4DD4C75-1EA8-4679-8706-0E4CD2358D3F}" Directory_="One_1_Dir" Attributes="0" KeyPath="libGLESv2.dll"/>
     <ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
+    <ROW Component="qwindows.dll" ComponentId="{5B31F279-3A03-4BED-B777-05554F7B00EF}" Directory_="platforms_Dir" Attributes="0" KeyPath="qwindows.dll"/>
     <ROW Component="updates.d" ComponentId="{E07A5480-3942-4529-A976-E7764542274C}" Directory_="updates.d_Dir" Attributes="0"/>
+    <ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" KeyPath="zerotierone_x64.exe"/>
+    <ROW Component="zerotierone_x86.exe" ComponentId="{5D2F3366-4FE1-40A4-A81A-66C49FA11F1C}" Directory_="One_Dir" Attributes="0" KeyPath="zerotierone_x86.exe"/>
     <ROW Component="zttap200.cat" ComponentId="{CCBE3FBA-1D6E-4486-914D-7444954DE12B}" Directory_="x64_Dir" Attributes="0" KeyPath="zttap200.cat" Type="0"/>
     <ROW Component="zttap200.cat_1" ComponentId="{BA0FB826-479C-46E8-AB2C-9017D40A99D8}" Directory_="x86_Dir" Attributes="0" KeyPath="zttap200.cat_1" Type="0"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
-    <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="One ProductInformation WdfCoinstaller01011.dll WdfCoinstaller01011.dll_1 devcon_x64.exe devcon_x86.exe networks.d updates.d zttap200.cat zttap200.cat_1"/>
+    <ROW Feature="MainFeature" Title="MainFeature" Description="Description" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="ProductInformation Qt5Core.dll Qt5Gui.dll Qt5Network.dll Qt5Widgets.dll WdfCoinstaller01011.dll WdfCoinstaller01011.dll_1 devcon_x64.exe devcon_x86.exe icudt51.dll icuin51.dll icuuc51.dll libEGL.dll libGLESv2.dll networks.d qwindows.dll updates.d zerotierone_x64.exe zerotierone_x86.exe zttap200.cat zttap200.cat_1"/>
     <ATTRIBUTE name="CurrentFeature" value="MainFeature"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
+    <ROW File="Qt5Core.dll" Component_="Qt5Core.dll" FileName="Qt5Core.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\Qt5Core.dll" SelfReg="false" NextFile="Qt5Gui.dll"/>
+    <ROW File="Qt5Gui.dll" Component_="Qt5Gui.dll" FileName="Qt5Gui.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\Qt5Gui.dll" SelfReg="false" NextFile="Qt5Network.dll"/>
+    <ROW File="Qt5Network.dll" Component_="Qt5Network.dll" FileName="QT5NET~1.DLL|Qt5Network.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\Qt5Network.dll" SelfReg="false" NextFile="Qt5Widgets.dll"/>
+    <ROW File="Qt5Widgets.dll" Component_="Qt5Widgets.dll" FileName="QT5WID~1.DLL|Qt5Widgets.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\Qt5Widgets.dll" SelfReg="false" NextFile="qwindows.dll"/>
     <ROW File="WdfCoinstaller01011.dll" Component_="WdfCoinstaller01011.dll" FileName="WDFCOI~1.DLL|WdfCoinstaller01011.dll" Attributes="0" SourcePath="..\..\bin\tap-windows\x64\WdfCoinstaller01011.dll" SelfReg="false" NextFile="zttap200.cat"/>
     <ROW File="WdfCoinstaller01011.dll_1" Component_="WdfCoinstaller01011.dll_1" FileName="WDFCOI~1.DLL|WdfCoinstaller01011.dll" Attributes="0" SourcePath="..\..\bin\tap-windows\x86\WdfCoinstaller01011.dll" SelfReg="false" NextFile="zttap200.cat_1"/>
     <ROW File="devcon_x64.exe" Component_="devcon_x64.exe" FileName="DEVCON~1.EXE|devcon_x64.exe" Attributes="0" SourcePath="..\..\bin\devcon\devcon_x64.exe" SelfReg="false" NextFile="devcon_x86.exe" DigSign="true"/>
-    <ROW File="devcon_x86.exe" Component_="devcon_x86.exe" FileName="DEVCON~2.EXE|devcon_x86.exe" Attributes="0" SourcePath="..\..\bin\devcon\devcon_x86.exe" SelfReg="false" DigSign="true"/>
+    <ROW File="devcon_x86.exe" Component_="devcon_x86.exe" FileName="DEVCON~2.EXE|devcon_x86.exe" Attributes="0" SourcePath="..\..\bin\devcon\devcon_x86.exe" SelfReg="false" NextFile="icudt51.dll" DigSign="true"/>
+    <ROW File="icudt51.dll" Component_="icudt51.dll" FileName="icudt51.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\icudt51.dll" SelfReg="false" NextFile="icuin51.dll"/>
+    <ROW File="icuin51.dll" Component_="icuin51.dll" FileName="icuin51.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\icuin51.dll" SelfReg="false" NextFile="icuuc51.dll"/>
+    <ROW File="icuuc51.dll" Component_="icuuc51.dll" FileName="icuuc51.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\icuuc51.dll" SelfReg="false" NextFile="libEGL.dll"/>
+    <ROW File="libEGL.dll" Component_="libEGL.dll" FileName="libEGL.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\libEGL.dll" SelfReg="false" NextFile="libGLESv2.dll"/>
+    <ROW File="libGLESv2.dll" Component_="libGLESv2.dll" FileName="LIBGLE~1.DLL|libGLESv2.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\libGLESv2.dll" SelfReg="false" NextFile="Qt5Core.dll"/>
+    <ROW File="qwindows.dll" Component_="qwindows.dll" FileName="qwindows.dll" Attributes="0" SourcePath="..\..\..\..\..\..\QtWin32Dlls\platforms\qwindows.dll" SelfReg="false" NextFile="zerotierone_x86.exe"/>
+    <ROW File="zerotierone_x64.exe" Component_="zerotierone_x64.exe" FileName="ZEROTI~2.EXE|zerotier-one_x64.exe" Attributes="0" SourcePath="..\..\..\windows\Build\x64\Release\zerotier-one_x64.exe" SelfReg="false" DigSign="true"/>
+    <ROW File="zerotierone_x86.exe" Component_="zerotierone_x86.exe" FileName="ZEROTI~1.EXE|zerotier-one_x86.exe" Attributes="0" SourcePath="..\..\..\windows\Build\Win32\Release\zerotier-one_x86.exe" SelfReg="false" NextFile="zerotierone_x64.exe" DigSign="true"/>
     <ROW File="zttap200.cat" Component_="zttap200.cat" FileName="zttap200.cat" Attributes="0" SourcePath="..\..\bin\tap-windows\x64\zttap200.cat" SelfReg="false" NextFile="zttap200.inf"/>
     <ROW File="zttap200.cat_1" Component_="zttap200.cat_1" FileName="zttap200.cat" Attributes="0" SourcePath="..\..\bin\tap-windows\x86\zttap200.cat" SelfReg="false" NextFile="zttap200.inf_1"/>
     <ROW File="zttap200.inf" Component_="zttap200.cat" FileName="zttap200.inf" Attributes="0" SourcePath="..\..\bin\tap-windows\x64\zttap200.inf" SelfReg="false" NextFile="zttap200.sys"/>
@@ -133,7 +157,6 @@
   <COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
     <ROW Directory_="networks.d_Dir" Component_="networks.d"/>
     <ROW Directory_="updates.d_Dir" Component_="updates.d"/>
-    <ROW Directory_="One_1_Dir" Component_="One"/>
   </COMPONENT>
   <COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
     <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>

+ 58 - 57
main.cpp

@@ -382,7 +382,7 @@ static void sighandlerQuit(int sig)
 
 #ifdef __WINDOWS__
 // Console signal handler routine to allow CTRL+C to work, mostly for testing
-static BOOL WINAPI _handlerRoutine(DWORD dwCtrlType)
+static BOOL WINAPI _winConsoleCtrlHandler(DWORD dwCtrlType)
 {
 	switch(dwCtrlType) {
 		case CTRL_C_EVENT:
@@ -508,7 +508,6 @@ int main(int argc,char **argv)
 #ifdef __WINDOWS__
 	WSADATA wsaData;
 	WSAStartup(MAKEWORD(2,2),&wsaData);
-	SetConsoleCtrlHandler(&_handlerRoutine,TRUE);
 #endif
 
 	if ((strstr(argv[0],"zerotier-cli"))||(strstr(argv[0],"ZEROTIER-CLI")))
@@ -580,7 +579,7 @@ int main(int argc,char **argv)
 						}
 						return 0;
 					} break;
-#endif
+#endif // __WINDOWS__
 				case 'h':
 				case '?':
 				default:
@@ -596,7 +595,6 @@ int main(int argc,char **argv)
 			break;
 		}
 	}
-
 	if ((!homeDir)||(strlen(homeDir) == 0))
 		homeDir = ZT_DEFAULTS.defaultHomePath.c_str();
 
@@ -607,6 +605,7 @@ int main(int argc,char **argv)
 	}
 	mkdir(homeDir,0755); // will fail if it already exists
 	{
+		// Write .pid file to home folder
 		char pidpath[4096];
 		Utils::snprintf(pidpath,sizeof(pidpath),"%s/zerotier-one.pid",homeDir);
 		FILE *pf = fopen(pidpath,"w");
@@ -615,76 +614,78 @@ int main(int argc,char **argv)
 			fclose(pf);
 		}
 	}
-#else
-#ifdef __WINDOWS__
-	if (IsCurrentUserLocalAdministrator() != TRUE) {
-		fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
-		return 1;
-	}
-#endif
 #endif
 
 #ifdef __WINDOWS__
-	if (!winRunFromCommandLine) {
+	if (winRunFromCommandLine) {
+		// Running in "interactive" mode (mostly for debugging)
+		if (IsCurrentUserLocalAdministrator() != TRUE) {
+			fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
+			return 1;
+		}
+		SetConsoleCtrlHandler(&_winConsoleCtrlHandler,TRUE);
+		// continues on to ordinary command line execution code below...
+	} else {
+		// Running from service manager
 		ZeroTierOneService zt1Service;
 		if (CServiceBase::Run(zt1Service) == TRUE) {
-			// Normal termination of service process
 			return 0;
 		} else {
 			fprintf(stderr,"%s: unable to start service (try -h for help)"ZT_EOL_S,argv[0]);
 			return 1;
 		}
-	} else
+	}
 #endif
-	{
-		int exitCode = 0;
-		try {
-			node = new Node(homeDir,port,controlPort);
-			switch(node->run()) {
+
+	int exitCode = 0;
+	try {
+		node = new Node(homeDir,port,controlPort);
+		switch(node->run()) {
 #ifdef __WINDOWS__
-				case Node::NODE_RESTART_FOR_UPGRADE: {
-					const char *upgPath = node->reasonForTermination();
-					if (upgPath) {
-						if (!ZeroTierOneService::doStartUpgrade(std::string(upgPath))) {
-							exitCode = 3;
-							fprintf(stderr,"%s: abnormal termination: unable to execute update at %s (doStartUpgrade failed)\n",argv[0],(upgPath) ? upgPath : "(unknown path)");
-						}
-					} else {
+			case Node::NODE_RESTART_FOR_UPGRADE: {
+				const char *upgPath = node->reasonForTermination();
+				if (upgPath) {
+					if (!ZeroTierOneService::doStartUpgrade(std::string(upgPath))) {
 						exitCode = 3;
-						fprintf(stderr,"%s: abnormal termination: unable to execute update at %s (no upgrade path provided)\n",argv[0],(upgPath) ? upgPath : "(unknown path)");
-					}
-				}	break;
-#else // __UNIX_LIKE__
-				case Node::NODE_RESTART_FOR_UPGRADE: {
-					const char *upgPath = node->reasonForTermination();
-					// On Unix-type OSes we exec() right into the upgrade. This in turn will
-					// end with us being re-launched either via the upgrade itself or something
-					// like OSX's launchd.
-					if (upgPath) {
-						Utils::rm((std::string(homeDir)+"/zerotier-one.pid").c_str());
-						::execl(upgPath,upgPath,(char *)0);
+						fprintf(stderr,"%s: abnormal termination: unable to execute update at %s (doStartUpgrade failed)\n",argv[0],(upgPath) ? upgPath : "(unknown path)");
 					}
+				} else {
 					exitCode = 3;
-					fprintf(stderr,"%s: abnormal termination: unable to execute update at %s\n",argv[0],(upgPath) ? upgPath : "(unknown path)");
-				}	break;
+					fprintf(stderr,"%s: abnormal termination: unable to execute update at %s (no upgrade path provided)\n",argv[0],(upgPath) ? upgPath : "(unknown path)");
+				}
+			}	break;
+#else // __UNIX_LIKE__
+			case Node::NODE_RESTART_FOR_UPGRADE: {
+				const char *upgPath = node->reasonForTermination();
+				// On Unix-type OSes we exec() right into the upgrade. This in turn will
+				// end with us being re-launched either via the upgrade itself or something
+				// like OSX's launchd.
+				if (upgPath) {
+					Utils::rm((std::string(homeDir)+"/zerotier-one.pid").c_str());
+					::execl(upgPath,upgPath,(char *)0);
+				}
+				exitCode = 3;
+				fprintf(stderr,"%s: abnormal termination: unable to execute update at %s\n",argv[0],(upgPath) ? upgPath : "(unknown path)");
+			}	break;
 #endif
-				case Node::NODE_UNRECOVERABLE_ERROR: {
-					exitCode = 3;
-					const char *termReason = node->reasonForTermination();
-					fprintf(stderr,"%s: abnormal termination: %s\n",argv[0],(termReason) ? termReason : "(unknown reason)");
-				}	break;
-				default:
-					break;
-			}
-			delete node;
-			node = (Node *)0;
-		} catch ( ... ) {
-			fprintf(stderr,"%s: unexpected exception!"ZT_EOL_S,argv[0]);
-			exitCode = 3;
+			case Node::NODE_UNRECOVERABLE_ERROR: {
+				exitCode = 3;
+				const char *termReason = node->reasonForTermination();
+				fprintf(stderr,"%s: abnormal termination: %s\n",argv[0],(termReason) ? termReason : "(unknown reason)");
+			}	break;
+			default:
+				break;
 		}
+		delete node;
+		node = (Node *)0;
+	} catch ( ... ) {
+		fprintf(stderr,"%s: unexpected exception!"ZT_EOL_S,argv[0]);
+		exitCode = 3;
+	}
+
 #ifdef __UNIX_LIKE__
-		Utils::rm((std::string(homeDir)+"/zerotier-one.pid").c_str());
+	Utils::rm((std::string(homeDir)+"/zerotier-one.pid").c_str());
 #endif
-		return exitCode;
-	}
+
+	return exitCode;
 }

+ 5 - 2
windows/ZeroTierOne/ServiceInstaller.cpp

@@ -50,16 +50,19 @@ std::string InstallService(PSTR pszServiceName,
                     PSTR pszPassword)
 {
 	std::string ret;
-    char szPath[MAX_PATH];
+    char szPathTmp[MAX_PATH],szPath[MAX_PATH];
     SC_HANDLE schSCManager = NULL;
     SC_HANDLE schService = NULL;
 
-    if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0)
+    if (GetModuleFileName(NULL, szPathTmp, ARRAYSIZE(szPath)) == 0)
     {
 		ret = "GetModuleFileName failed, unable to get path to self";
         goto Cleanup;
     }
 
+	// Quote path in case it contains spaces
+	_snprintf_s(szPath,sizeof(szPath),"\"%s\"",szPathTmp);
+
     // Open the local default service control manager database
     schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | 
         SC_MANAGER_CREATE_SERVICE);

+ 31 - 2
windows/ZeroTierOne/ZeroTierOneService.cpp

@@ -36,19 +36,44 @@
 #include "../../node/Utils.hpp"
 #pragma endregion
 
+#ifdef ZT_DEBUG_SERVICE
+FILE *SVCDBGfile = (FILE *)0;
+ZeroTier::Mutex SVCDBGfile_m;
+#endif
+
 ZeroTierOneService::ZeroTierOneService() :
 	CServiceBase(ZT_SERVICE_NAME,TRUE,TRUE,FALSE),
 	_node((ZeroTier::Node *)0)
 {
+#ifdef ZT_DEBUG_SERVICE
+	SVCDBGfile_m.lock();
+	if (!SVCDBGfile)
+		SVCDBGfile = fopen(ZT_DEBUG_SERVICE,"a");
+	SVCDBGfile_m.unlock();
+#endif
+
+	ZT_SVCDBG("ZeroTierOneService::ZeroTierOneService()\r\n");
 }
 
 ZeroTierOneService::~ZeroTierOneService(void)
 {
+	ZT_SVCDBG("ZeroTierOneService::~ZeroTierOneService()\r\n");
+
+#ifdef ZT_DEBUG_SERVICE
+	SVCDBGfile_m.lock();
+	if (SVCDBGfile) {
+		fclose(SVCDBGfile);
+		SVCDBGfile = (FILE *)0;
+	}
+	SVCDBGfile_m.unlock();
+#endif
 }
 
 void ZeroTierOneService::threadMain()
 	throw()
 {
+	ZT_SVCDBG("ZeroTierOneService::threadMain()\r\n");
+
 restart_node:
 	try {
 		{
@@ -144,8 +169,8 @@ bool ZeroTierOneService::doStartUpgrade(const std::string &msiPath)
 
 void ZeroTierOneService::OnStart(DWORD dwArgc, LPSTR *lpszArgv)
 {
-	if (_node)
-		return; // sanity check
+	ZT_SVCDBG("ZeroTierOneService::OnStart()\r\n");
+
 	try {
 		_thread = ZeroTier::Thread::start(this);
 	} catch ( ... ) {
@@ -155,6 +180,8 @@ void ZeroTierOneService::OnStart(DWORD dwArgc, LPSTR *lpszArgv)
 
 void ZeroTierOneService::OnStop()
 {
+	ZT_SVCDBG("ZeroTierOneService::OnStop()\r\n");
+
 	_lock.lock();
 	ZeroTier::Node *n = _node;
 	_lock.unlock();
@@ -166,6 +193,8 @@ void ZeroTierOneService::OnStop()
 
 void ZeroTierOneService::OnShutdown()
 {
+	ZT_SVCDBG("ZeroTierOneService::OnShutdown()\r\n");
+
 	// stop thread on system shutdown (if it hasn't happened already)
 	OnStop();
 }

+ 15 - 1
windows/ZeroTierOne/ZeroTierOneService.h

@@ -27,6 +27,8 @@
 
 #pragma once
 
+#include <stdio.h>
+
 #include "ServiceBase.h"
 
 #include <string>
@@ -37,11 +39,23 @@
 #include "../../node/Mutex.hpp"
 #include "../../node/Utils.hpp"
 
+// Uncomment to make debugging Windows services suck slightly less hard.
+//#define ZT_DEBUG_SERVICE "C:\\ZeroTierOneServiceDebugLog.txt"
+
+#ifdef ZT_DEBUG_SERVICE
+extern FILE *SVCDBGfile;
+extern ZeroTier::Mutex SVCDBGfile_m;
+#define ZT_SVCDBG(f,...) { SVCDBGfile_m.lock(); fprintf(SVCDBGfile,f,##__VA_ARGS__); fflush(SVCDBGfile); SVCDBGfile_m.unlock(); }
+#else
+#define ZT_SVCDBG(f,...) {}
+#endif
+
 #define ZT_SERVICE_NAME "ZeroTierOneService"
 #define ZT_SERVICE_DISPLAY_NAME "ZeroTier One"
 #define ZT_SERVICE_START_TYPE SERVICE_AUTO_START
 #define ZT_SERVICE_DEPENDENCIES ""
-#define ZT_SERVICE_ACCOUNT "NT AUTHORITY\\LocalService"
+//#define ZT_SERVICE_ACCOUNT "NT AUTHORITY\\LocalService"
+#define ZT_SERVICE_ACCOUNT NULL
 #define ZT_SERVICE_PASSWORD NULL
 
 class ZeroTierOneService : public CServiceBase