diff options
author | Adam Ierymenko <adam.ierymenko@zerotier.com> | 2014-02-27 16:28:55 -0800 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@zerotier.com> | 2014-02-27 16:28:55 -0800 |
commit | 9d05897f7a3d06f5504b3bb75dd7e822e5faebf9 (patch) | |
tree | 94b4fca19138ef431d547fa9cbee4f429c7b7de9 | |
parent | cbeb9c523644a49abb679bc4c97859238d28da2d (diff) | |
download | infinitytier-9d05897f7a3d06f5504b3bb75dd7e822e5faebf9.tar.gz infinitytier-9d05897f7a3d06f5504b3bb75dd7e822e5faebf9.zip |
Windows service works now!
-rw-r--r-- | ZeroTierUI/installdialog.h | 2 | ||||
-rw-r--r-- | ZeroTierUI/main.cpp | 104 | ||||
-rw-r--r-- | ext/installfiles/windows/ZeroTier One.aip | 31 | ||||
-rw-r--r-- | main.cpp | 115 | ||||
-rw-r--r-- | windows/ZeroTierOne/ServiceInstaller.cpp | 7 | ||||
-rw-r--r-- | windows/ZeroTierOne/ZeroTierOneService.cpp | 33 | ||||
-rw-r--r-- | windows/ZeroTierOne/ZeroTierOneService.h | 16 |
7 files changed, 238 insertions, 70 deletions
diff --git a/ZeroTierUI/installdialog.h b/ZeroTierUI/installdialog.h index 498bc16b..2b38a775 100644 --- a/ZeroTierUI/installdialog.h +++ b/ZeroTierUI/installdialog.h @@ -38,6 +38,8 @@ #include "../node/Address.hpp" +// Right now InstallDialog is only used on Mac + namespace Ui { class InstallDialog; } diff --git a/ZeroTierUI/main.cpp b/ZeroTierUI/main.cpp index c2849edc..84e1e121 100644 --- a/ZeroTierUI/main.cpp +++ b/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 contact@zerotier.com 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 contact@zerotier.com if you cannot install. (Error: service not installed)",QMessageBox::Ok); + return 1; + } + } + } +#endif + MainWindow w; w.show(); return a.exec(); diff --git a/ext/installfiles/windows/ZeroTier One.aip b/ext/installfiles/windows/ZeroTier One.aip index d03c0aaf..e8208817 100644 --- a/ext/installfiles/windows/ZeroTier One.aip +++ b/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"/> @@ -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; } diff --git a/windows/ZeroTierOne/ServiceInstaller.cpp b/windows/ZeroTierOne/ServiceInstaller.cpp index 5945ecdf..d302d9f6 100644 --- a/windows/ZeroTierOne/ServiceInstaller.cpp +++ b/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); diff --git a/windows/ZeroTierOne/ZeroTierOneService.cpp b/windows/ZeroTierOne/ZeroTierOneService.cpp index daff0bb0..364e93cb 100644 --- a/windows/ZeroTierOne/ZeroTierOneService.cpp +++ b/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(); } diff --git a/windows/ZeroTierOne/ZeroTierOneService.h b/windows/ZeroTierOne/ZeroTierOneService.h index 042a398a..2f6f733d 100644 --- a/windows/ZeroTierOne/ZeroTierOneService.h +++ b/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 |