mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-26 16:11:07 +00:00
Windows service work, remove old installer... not sure exactly what we're going to use.
This commit is contained in:
parent
6d17993eb6
commit
8a7486577a
368
main.cpp
368
main.cpp
@ -44,6 +44,7 @@
|
|||||||
#include <lmcons.h>
|
#include <lmcons.h>
|
||||||
#include "windows/ZeroTierOne/ServiceInstaller.h"
|
#include "windows/ZeroTierOne/ServiceInstaller.h"
|
||||||
#include "windows/ZeroTierOne/ServiceBase.h"
|
#include "windows/ZeroTierOne/ServiceBase.h"
|
||||||
|
#include "windows/ZeroTierOne/ZeroTierOneService.h"
|
||||||
#else
|
#else
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
@ -96,8 +97,9 @@ static void printHelp(const char *cn,FILE *out)
|
|||||||
fprintf(out," -q - Send a query to a running service (zerotier-cli)"ZT_EOL_S);
|
fprintf(out," -q - Send a query to a running service (zerotier-cli)"ZT_EOL_S);
|
||||||
fprintf(out," -i - Run idtool command (zerotier-idtool)"ZT_EOL_S);
|
fprintf(out," -i - Run idtool command (zerotier-idtool)"ZT_EOL_S);
|
||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
fprintf(out," -I - Install Windows service"ZT_EOL_S);
|
fprintf(out," -C - Run from command line instead of as service (Windows)"ZT_EOL_S);
|
||||||
fprintf(out," -R - Uninstall Windows service"ZT_EOL_S);
|
fprintf(out," -I - Install Windows service (Windows)"ZT_EOL_S);
|
||||||
|
fprintf(out," -R - Uninstall Windows service (Windows)"ZT_EOL_S);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,6 +381,7 @@ static void sighandlerQuit(int sig)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
|
// Console signal handler routine to allow CTRL+C to work, mostly for testing
|
||||||
static BOOL WINAPI _handlerRoutine(DWORD dwCtrlType)
|
static BOOL WINAPI _handlerRoutine(DWORD dwCtrlType)
|
||||||
{
|
{
|
||||||
switch(dwCtrlType) {
|
switch(dwCtrlType) {
|
||||||
@ -394,183 +397,94 @@ static BOOL WINAPI _handlerRoutine(DWORD dwCtrlType)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if this is running as the local administrator
|
||||||
static BOOL IsCurrentUserLocalAdministrator(void)
|
static BOOL IsCurrentUserLocalAdministrator(void)
|
||||||
{
|
{
|
||||||
BOOL fReturn = FALSE;
|
BOOL fReturn = FALSE;
|
||||||
DWORD dwStatus;
|
DWORD dwStatus;
|
||||||
DWORD dwAccessMask;
|
DWORD dwAccessMask;
|
||||||
DWORD dwAccessDesired;
|
DWORD dwAccessDesired;
|
||||||
DWORD dwACLSize;
|
DWORD dwACLSize;
|
||||||
DWORD dwStructureSize = sizeof(PRIVILEGE_SET);
|
DWORD dwStructureSize = sizeof(PRIVILEGE_SET);
|
||||||
PACL pACL = NULL;
|
PACL pACL = NULL;
|
||||||
PSID psidAdmin = NULL;
|
PSID psidAdmin = NULL;
|
||||||
|
|
||||||
HANDLE hToken = NULL;
|
HANDLE hToken = NULL;
|
||||||
HANDLE hImpersonationToken = NULL;
|
HANDLE hImpersonationToken = NULL;
|
||||||
|
|
||||||
PRIVILEGE_SET ps;
|
PRIVILEGE_SET ps;
|
||||||
GENERIC_MAPPING GenericMapping;
|
GENERIC_MAPPING GenericMapping;
|
||||||
|
|
||||||
PSECURITY_DESCRIPTOR psdAdmin = NULL;
|
PSECURITY_DESCRIPTOR psdAdmin = NULL;
|
||||||
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
|
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
|
||||||
|
|
||||||
|
const DWORD ACCESS_READ = 1;
|
||||||
|
const DWORD ACCESS_WRITE = 2;
|
||||||
|
|
||||||
/*
|
__try
|
||||||
Determine if the current thread is running as a user that is a member
|
{
|
||||||
|
if (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY,TRUE,&hToken))
|
||||||
|
{
|
||||||
|
if (GetLastError() != ERROR_NO_TOKEN)
|
||||||
|
__leave;
|
||||||
|
if (!OpenProcessToken(GetCurrentProcess(),TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
if (!DuplicateToken (hToken, SecurityImpersonation,&hImpersonationToken))
|
||||||
|
__leave;
|
||||||
|
if (!AllocateAndInitializeSid(&SystemSidAuthority, 2,
|
||||||
|
SECURITY_BUILTIN_DOMAIN_RID,
|
||||||
|
DOMAIN_ALIAS_RID_ADMINS,
|
||||||
|
0, 0, 0, 0, 0, 0, &psidAdmin))
|
||||||
|
__leave;
|
||||||
|
psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
|
||||||
|
if (psdAdmin == NULL)
|
||||||
|
__leave;
|
||||||
|
if (!InitializeSecurityDescriptor(psdAdmin,SECURITY_DESCRIPTOR_REVISION))
|
||||||
|
__leave;
|
||||||
|
dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidAdmin) - sizeof(DWORD);
|
||||||
|
pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
|
||||||
|
if (pACL == NULL)
|
||||||
|
__leave;
|
||||||
|
if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
|
||||||
|
__leave;
|
||||||
|
dwAccessMask= ACCESS_READ | ACCESS_WRITE;
|
||||||
|
if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask, psidAdmin))
|
||||||
|
__leave;
|
||||||
|
if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE))
|
||||||
|
__leave;
|
||||||
|
|
||||||
of
|
SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE);
|
||||||
the local admins group. To do this, create a security descriptor
|
SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE);
|
||||||
|
|
||||||
that
|
if (!IsValidSecurityDescriptor(psdAdmin))
|
||||||
has a DACL which has an ACE that allows only local aministrators
|
__leave;
|
||||||
|
dwAccessDesired = ACCESS_READ;
|
||||||
|
|
||||||
access.
|
GenericMapping.GenericRead = ACCESS_READ;
|
||||||
Then, call AccessCheck with the current thread's token and the
|
GenericMapping.GenericWrite = ACCESS_WRITE;
|
||||||
|
GenericMapping.GenericExecute = 0;
|
||||||
|
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
|
||||||
|
|
||||||
security
|
if (!AccessCheck(psdAdmin, hImpersonationToken, dwAccessDesired,
|
||||||
descriptor. It will say whether the user could access an object if
|
&GenericMapping, &ps, &dwStructureSize, &dwStatus,
|
||||||
|
&fReturn))
|
||||||
|
{
|
||||||
|
fReturn = FALSE;
|
||||||
|
__leave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__finally
|
||||||
|
{
|
||||||
|
// Clean up.
|
||||||
|
if (pACL) LocalFree(pACL);
|
||||||
|
if (psdAdmin) LocalFree(psdAdmin);
|
||||||
|
if (psidAdmin) FreeSid(psidAdmin);
|
||||||
|
if (hImpersonationToken) CloseHandle (hImpersonationToken);
|
||||||
|
if (hToken) CloseHandle (hToken);
|
||||||
|
}
|
||||||
|
|
||||||
it
|
return fReturn;
|
||||||
had that security descriptor. Note: you do not need to actually
|
|
||||||
|
|
||||||
create
|
|
||||||
the object. Just checking access against the security descriptor
|
|
||||||
|
|
||||||
alone
|
|
||||||
will be sufficient.
|
|
||||||
*/
|
|
||||||
const DWORD ACCESS_READ = 1;
|
|
||||||
const DWORD ACCESS_WRITE = 2;
|
|
||||||
|
|
||||||
|
|
||||||
__try
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
|
||||||
AccessCheck() requires an impersonation token. We first get a
|
|
||||||
|
|
||||||
primary
|
|
||||||
token and then create a duplicate impersonation token. The
|
|
||||||
impersonation token is not actually assigned to the thread, but is
|
|
||||||
used in the call to AccessCheck. Thus, this function itself never
|
|
||||||
impersonates, but does use the identity of the thread. If the
|
|
||||||
|
|
||||||
thread
|
|
||||||
was impersonating already, this function uses that impersonation
|
|
||||||
|
|
||||||
context.
|
|
||||||
*/
|
|
||||||
if (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY,
|
|
||||||
|
|
||||||
TRUE, &hToken))
|
|
||||||
{
|
|
||||||
if (GetLastError() != ERROR_NO_TOKEN)
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
if (!OpenProcessToken(GetCurrentProcess(),
|
|
||||||
|
|
||||||
TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
|
|
||||||
__leave;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DuplicateToken (hToken, SecurityImpersonation,
|
|
||||||
|
|
||||||
&hImpersonationToken))
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
Create the binary representation of the well-known SID that
|
|
||||||
represents the local administrators group. Then create the
|
|
||||||
|
|
||||||
security
|
|
||||||
descriptor and DACL with an ACE that allows only local admins
|
|
||||||
|
|
||||||
access.
|
|
||||||
After that, perform the access check. This will determine whether
|
|
||||||
the current user is a local admin.
|
|
||||||
*/
|
|
||||||
if (!AllocateAndInitializeSid(&SystemSidAuthority, 2,
|
|
||||||
SECURITY_BUILTIN_DOMAIN_RID,
|
|
||||||
DOMAIN_ALIAS_RID_ADMINS,
|
|
||||||
0, 0, 0, 0, 0, 0, &psidAdmin))
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
|
|
||||||
if (psdAdmin == NULL)
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
if (!InitializeSecurityDescriptor(psdAdmin,
|
|
||||||
|
|
||||||
SECURITY_DESCRIPTOR_REVISION))
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
// Compute size needed for the ACL.
|
|
||||||
dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) +
|
|
||||||
GetLengthSid(psidAdmin) - sizeof(DWORD);
|
|
||||||
|
|
||||||
pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
|
|
||||||
if (pACL == NULL)
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
dwAccessMask= ACCESS_READ | ACCESS_WRITE;
|
|
||||||
|
|
||||||
if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask,
|
|
||||||
|
|
||||||
psidAdmin))
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE))
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
/*
|
|
||||||
AccessCheck validates a security descriptor somewhat; set the
|
|
||||||
|
|
||||||
group
|
|
||||||
and owner so that enough of the security descriptor is filled out
|
|
||||||
|
|
||||||
to
|
|
||||||
make AccessCheck happy.
|
|
||||||
*/
|
|
||||||
SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE);
|
|
||||||
SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE);
|
|
||||||
|
|
||||||
if (!IsValidSecurityDescriptor(psdAdmin))
|
|
||||||
__leave;
|
|
||||||
|
|
||||||
dwAccessDesired = ACCESS_READ;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Initialize GenericMapping structure even though you
|
|
||||||
do not use generic rights.
|
|
||||||
*/
|
|
||||||
GenericMapping.GenericRead = ACCESS_READ;
|
|
||||||
GenericMapping.GenericWrite = ACCESS_WRITE;
|
|
||||||
GenericMapping.GenericExecute = 0;
|
|
||||||
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
|
|
||||||
|
|
||||||
if (!AccessCheck(psdAdmin, hImpersonationToken, dwAccessDesired,
|
|
||||||
&GenericMapping, &ps, &dwStructureSize, &dwStatus,
|
|
||||||
&fReturn))
|
|
||||||
{
|
|
||||||
fReturn = FALSE;
|
|
||||||
__leave;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
__finally
|
|
||||||
{
|
|
||||||
// Clean up.
|
|
||||||
if (pACL) LocalFree(pACL);
|
|
||||||
if (psdAdmin) LocalFree(psdAdmin);
|
|
||||||
if (psidAdmin) FreeSid(psidAdmin);
|
|
||||||
if (hImpersonationToken) CloseHandle (hImpersonationToken);
|
|
||||||
if (hToken) CloseHandle (hToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fReturn;
|
|
||||||
}
|
}
|
||||||
#endif // __WINDOWS__
|
#endif // __WINDOWS__
|
||||||
|
|
||||||
@ -605,6 +519,9 @@ int main(int argc,char **argv)
|
|||||||
const char *homeDir = (const char *)0;
|
const char *homeDir = (const char *)0;
|
||||||
unsigned int port = 0;
|
unsigned int port = 0;
|
||||||
unsigned int controlPort = 0;
|
unsigned int controlPort = 0;
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
bool winRunFromCommandLine = false;
|
||||||
|
#endif
|
||||||
for(int i=1;i<argc;++i) {
|
for(int i=1;i<argc;++i) {
|
||||||
if (argv[i][0] == '-') {
|
if (argv[i][0] == '-') {
|
||||||
switch(argv[i][1]) {
|
switch(argv[i][1]) {
|
||||||
@ -635,6 +552,35 @@ int main(int argc,char **argv)
|
|||||||
printHelp(argv[0],stderr);
|
printHelp(argv[0],stderr);
|
||||||
return 0;
|
return 0;
|
||||||
} else return ZeroTierIdTool::main(argc,argv);
|
} else return ZeroTierIdTool::main(argc,argv);
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
case 'C':
|
||||||
|
winRunFromCommandLine = true;
|
||||||
|
break;
|
||||||
|
case 'I': { // install self as service
|
||||||
|
if (IsCurrentUserLocalAdministrator() != TRUE) {
|
||||||
|
fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string ret(InstallService(ZT_SERVICE_NAME,ZT_SERVICE_DISPLAY_NAME,ZT_SERVICE_START_TYPE,ZT_SERVICE_DEPENDENCIES,ZT_SERVICE_ACCOUNT,ZT_SERVICE_PASSWORD));
|
||||||
|
if (ret.length()) {
|
||||||
|
fprintf(stderr,"%s: unable to install service: %s"ZT_EOL_S,argv[0],ret.c_str());
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} break;
|
||||||
|
case 'R': { // uninstall self as service
|
||||||
|
if (IsCurrentUserLocalAdministrator() != TRUE) {
|
||||||
|
fprintf(stderr,"%s: must be run as a local administrator."ZT_EOL_S,argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::string ret(UninstallService(ZT_SERVICE_NAME));
|
||||||
|
if (ret.length()) {
|
||||||
|
fprintf(stderr,"%s: unable to uninstall service: %s"ZT_EOL_S,argv[0],ret.c_str());
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} break;
|
||||||
|
#endif
|
||||||
case 'h':
|
case 'h':
|
||||||
case '?':
|
case '?':
|
||||||
default:
|
default:
|
||||||
@ -678,52 +624,56 @@ int main(int argc,char **argv)
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int exitCode = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
node = new Node(homeDir,port,controlPort);
|
|
||||||
switch(node->run()) {
|
|
||||||
case Node::NODE_RESTART_FOR_UPGRADE: {
|
|
||||||
const char *upgPath = node->reasonForTermination();
|
|
||||||
#ifdef __UNIX_LIKE__
|
|
||||||
// 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 = 2;
|
|
||||||
fprintf(stderr,"%s: abnormal termination: unable to execute update at %s\n",argv[0],(upgPath) ? upgPath : "(unknown path)");
|
|
||||||
#else // not __UNIX_LIKE
|
|
||||||
#ifdef __WINDOWS__
|
#ifdef __WINDOWS__
|
||||||
// On Windows the service checks updates.d and invokes updates if they are
|
if (!winRunFromCommandLine) {
|
||||||
// found there. This only happens after exit code 4. The Windows service
|
ZeroTierOneService zt1Service;
|
||||||
// will listen to stdout as well to catch the filename.
|
if (CServiceBase::Run(zt1Service) == TRUE) {
|
||||||
if (upgPath) {
|
return 0;
|
||||||
printf("[[[ UPDATE AVAILABLE: \"%s\" ]]]\r\n",upgPath);
|
} else {
|
||||||
exitCode = 4;
|
fprintf(stderr,"%s: unable to start service (try -h for help)"ZT_EOL_S,argv[0]);
|
||||||
} else {
|
return 1;
|
||||||
exitCode = 2;
|
}
|
||||||
}
|
} else
|
||||||
#endif // __WINDOWS__
|
#endif
|
||||||
#endif // not __UNIX_LIKE__
|
{
|
||||||
} break;
|
int exitCode = 0;
|
||||||
case Node::NODE_UNRECOVERABLE_ERROR: {
|
|
||||||
exitCode = 3;
|
try {
|
||||||
const char *termReason = node->reasonForTermination();
|
node = new Node(homeDir,port,controlPort);
|
||||||
fprintf(stderr,"%s: abnormal termination: %s\n",argv[0],(termReason) ? termReason : "(unknown reason)");
|
switch(node->run()) {
|
||||||
} break;
|
#ifndef __WINDOWS__
|
||||||
default:
|
case Node::NODE_RESTART_FOR_UPGRADE: {
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
delete node;
|
|
||||||
node = (Node *)0;
|
|
||||||
} catch ( ... ) {}
|
|
||||||
|
|
||||||
#ifdef __UNIX_LIKE__
|
#ifdef __UNIX_LIKE__
|
||||||
Utils::rm((std::string(homeDir)+"/zerotier-one.pid").c_str());
|
Utils::rm((std::string(homeDir)+"/zerotier-one.pid").c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return exitCode;
|
return exitCode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,20 +42,21 @@
|
|||||||
// NOTE: If the function fails to install the service, it prints the error
|
// NOTE: If the function fails to install the service, it prints the error
|
||||||
// in the standard output stream for users to diagnose the problem.
|
// in the standard output stream for users to diagnose the problem.
|
||||||
//
|
//
|
||||||
void InstallService(PSTR pszServiceName,
|
std::string InstallService(PSTR pszServiceName,
|
||||||
PSTR pszDisplayName,
|
PSTR pszDisplayName,
|
||||||
DWORD dwStartType,
|
DWORD dwStartType,
|
||||||
PSTR pszDependencies,
|
PSTR pszDependencies,
|
||||||
PSTR pszAccount,
|
PSTR pszAccount,
|
||||||
PSTR pszPassword)
|
PSTR pszPassword)
|
||||||
{
|
{
|
||||||
|
std::string ret;
|
||||||
char szPath[MAX_PATH];
|
char szPath[MAX_PATH];
|
||||||
SC_HANDLE schSCManager = NULL;
|
SC_HANDLE schSCManager = NULL;
|
||||||
SC_HANDLE schService = NULL;
|
SC_HANDLE schService = NULL;
|
||||||
|
|
||||||
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0)
|
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)) == 0)
|
||||||
{
|
{
|
||||||
wprintf(L"GetModuleFileName failed w/err 0x%08lx\n", GetLastError());
|
ret = "GetModuleFileName failed, unable to get path to self";
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ void InstallService(PSTR pszServiceName,
|
|||||||
SC_MANAGER_CREATE_SERVICE);
|
SC_MANAGER_CREATE_SERVICE);
|
||||||
if (schSCManager == NULL)
|
if (schSCManager == NULL)
|
||||||
{
|
{
|
||||||
wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError());
|
ret = "OpenSCManager failed";
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,12 +87,10 @@ void InstallService(PSTR pszServiceName,
|
|||||||
);
|
);
|
||||||
if (schService == NULL)
|
if (schService == NULL)
|
||||||
{
|
{
|
||||||
wprintf(L"CreateService failed w/err 0x%08lx\n", GetLastError());
|
ret = "CreateService failed";
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintf(L"%s is installed.\n", pszServiceName);
|
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
// Centralized cleanup for all allocated resources.
|
// Centralized cleanup for all allocated resources.
|
||||||
if (schSCManager)
|
if (schSCManager)
|
||||||
@ -104,6 +103,8 @@ Cleanup:
|
|||||||
CloseServiceHandle(schService);
|
CloseServiceHandle(schService);
|
||||||
schService = NULL;
|
schService = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -119,8 +120,9 @@ Cleanup:
|
|||||||
// NOTE: If the function fails to uninstall the service, it prints the
|
// NOTE: If the function fails to uninstall the service, it prints the
|
||||||
// error in the standard output stream for users to diagnose the problem.
|
// error in the standard output stream for users to diagnose the problem.
|
||||||
//
|
//
|
||||||
void UninstallService(PSTR pszServiceName)
|
std::string UninstallService(PSTR pszServiceName)
|
||||||
{
|
{
|
||||||
|
std::string ret;
|
||||||
SC_HANDLE schSCManager = NULL;
|
SC_HANDLE schSCManager = NULL;
|
||||||
SC_HANDLE schService = NULL;
|
SC_HANDLE schService = NULL;
|
||||||
SERVICE_STATUS ssSvcStatus = {};
|
SERVICE_STATUS ssSvcStatus = {};
|
||||||
@ -129,7 +131,7 @@ void UninstallService(PSTR pszServiceName)
|
|||||||
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
||||||
if (schSCManager == NULL)
|
if (schSCManager == NULL)
|
||||||
{
|
{
|
||||||
wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError());
|
ret = "OpenSCManager failed";
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,45 +140,32 @@ void UninstallService(PSTR pszServiceName)
|
|||||||
SERVICE_QUERY_STATUS | DELETE);
|
SERVICE_QUERY_STATUS | DELETE);
|
||||||
if (schService == NULL)
|
if (schService == NULL)
|
||||||
{
|
{
|
||||||
wprintf(L"OpenService failed w/err 0x%08lx\n", GetLastError());
|
ret = "OpenService failed (is service installed?)";
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to stop the service
|
// Try to stop the service
|
||||||
if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus))
|
if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus))
|
||||||
{
|
{
|
||||||
wprintf(L"Stopping %s.", pszServiceName);
|
Sleep(500);
|
||||||
Sleep(1000);
|
|
||||||
|
|
||||||
while (QueryServiceStatus(schService, &ssSvcStatus))
|
while (QueryServiceStatus(schService, &ssSvcStatus))
|
||||||
{
|
{
|
||||||
if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING)
|
if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING)
|
||||||
{
|
{
|
||||||
wprintf(L".");
|
Sleep(500);
|
||||||
Sleep(1000);
|
|
||||||
}
|
}
|
||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED)
|
|
||||||
{
|
|
||||||
wprintf(L"\n%s is stopped.\n", pszServiceName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wprintf(L"\n%s failed to stop.\n", pszServiceName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now remove the service by calling DeleteService.
|
// Now remove the service by calling DeleteService.
|
||||||
if (!DeleteService(schService))
|
if (!DeleteService(schService))
|
||||||
{
|
{
|
||||||
wprintf(L"DeleteService failed w/err 0x%08lx\n", GetLastError());
|
ret = "DeleteService failed (is service running?)";
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintf(L"%s is removed.\n", pszServiceName);
|
|
||||||
|
|
||||||
Cleanup:
|
Cleanup:
|
||||||
// Centralized cleanup for all allocated resources.
|
// Centralized cleanup for all allocated resources.
|
||||||
if (schSCManager)
|
if (schSCManager)
|
||||||
@ -189,4 +178,6 @@ Cleanup:
|
|||||||
CloseServiceHandle(schService);
|
CloseServiceHandle(schService);
|
||||||
schService = NULL;
|
schService = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
//
|
//
|
||||||
// FUNCTION: InstallService
|
// FUNCTION: InstallService
|
||||||
@ -38,7 +39,8 @@
|
|||||||
// NOTE: If the function fails to install the service, it prints the error
|
// NOTE: If the function fails to install the service, it prints the error
|
||||||
// in the standard output stream for users to diagnose the problem.
|
// in the standard output stream for users to diagnose the problem.
|
||||||
//
|
//
|
||||||
void InstallService(PSTR pszServiceName,
|
// modified for ZT1 to return an error or empty string on success
|
||||||
|
std::string InstallService(PSTR pszServiceName,
|
||||||
PSTR pszDisplayName,
|
PSTR pszDisplayName,
|
||||||
DWORD dwStartType,
|
DWORD dwStartType,
|
||||||
PSTR pszDependencies,
|
PSTR pszDependencies,
|
||||||
@ -58,4 +60,5 @@ void InstallService(PSTR pszServiceName,
|
|||||||
// NOTE: If the function fails to uninstall the service, it prints the
|
// NOTE: If the function fails to uninstall the service, it prints the
|
||||||
// error in the standard output stream for users to diagnose the problem.
|
// error in the standard output stream for users to diagnose the problem.
|
||||||
//
|
//
|
||||||
void UninstallService(PSTR pszServiceName);
|
// Also modified to return rather than print errors
|
||||||
|
std::string UninstallService(PSTR pszServiceName);
|
||||||
|
@ -27,29 +27,81 @@
|
|||||||
|
|
||||||
#pragma region Includes
|
#pragma region Includes
|
||||||
#include "ZeroTierOneService.h"
|
#include "ZeroTierOneService.h"
|
||||||
|
#include "../../node/Node.hpp"
|
||||||
|
#include "../../node/Defaults.hpp"
|
||||||
|
#include "../../node/Thread.hpp"
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
|
using namespace ZeroTier;
|
||||||
|
|
||||||
ZeroTierOneService::ZeroTierOneService() :
|
ZeroTierOneService::ZeroTierOneService() :
|
||||||
CServiceBase(ZT_SERVICE_NAME,TRUE,TRUE,TRUE)
|
CServiceBase(ZT_SERVICE_NAME,TRUE,TRUE,FALSE),
|
||||||
|
_thread(new Thread()),
|
||||||
|
_node((Node *)0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ZeroTierOneService::~ZeroTierOneService(void)
|
ZeroTierOneService::~ZeroTierOneService(void)
|
||||||
{
|
{
|
||||||
|
delete _thread;
|
||||||
|
delete _node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZeroTierOneService::threadMain()
|
||||||
|
throw()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Since Windows doesn't auto-restart services, we'll restart the node
|
||||||
|
// on normal termination.
|
||||||
|
for(;;) {
|
||||||
|
switch(_node->run()) {
|
||||||
|
case Node::NODE_NORMAL_TERMINATION:
|
||||||
|
delete _node;
|
||||||
|
_node = new Node(ZT_DEFAULTS.defaultHomePath.c_str(),0,0);
|
||||||
|
break; // restart
|
||||||
|
case Node::NODE_RESTART_FOR_UPGRADE: {
|
||||||
|
} return; // terminate thread
|
||||||
|
case Node::NODE_UNRECOVERABLE_ERROR: {
|
||||||
|
std::string err("unrecoverable error: ");
|
||||||
|
const char *r = _node->reasonForTermination;
|
||||||
|
if (r)
|
||||||
|
err.append(r);
|
||||||
|
else err.append("(unknown error)");
|
||||||
|
WriteEventLogEntry(const_cast <PSTR>(err.c_str()),EVENTLOG_ERROR_TYPE);
|
||||||
|
} return; // terminate thread
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch ( ... ) {
|
||||||
|
WriteEventLogEntry("unexpected exception in Node::run() or during restart",EVENTLOG_ERROR_TYPE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZeroTierOneService::OnStart(DWORD dwArgc, LPSTR *lpszArgv)
|
void ZeroTierOneService::OnStart(DWORD dwArgc, LPSTR *lpszArgv)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
|
_node = new Node(ZT_DEFAULTS.defaultHomePath.c_str(),0,0);
|
||||||
|
*_thread = Thread::start(this);
|
||||||
|
} catch ( ... ) {
|
||||||
|
// shouldn't happen unless something weird occurs like out of memory...
|
||||||
|
throw (DWORD)ERROR_EXCEPTION_IN_SERVICE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZeroTierOneService::OnStop()
|
void ZeroTierOneService::OnStop()
|
||||||
{
|
{
|
||||||
|
Node *n = _node;
|
||||||
|
_node = (Node *)0;
|
||||||
|
if (n) {
|
||||||
|
n->terminate(Node::NODE_NORMAL_TERMINATION,"Service Shutdown");
|
||||||
|
Thread::join(*_thread);
|
||||||
|
delete n;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZeroTierOneService::OnPause()
|
void ZeroTierOneService::OnShutdown()
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ZeroTierOneService::OnContinue()
|
|
||||||
{
|
{
|
||||||
|
// make sure it's stopped
|
||||||
|
OnStop();
|
||||||
}
|
}
|
||||||
|
@ -36,15 +36,29 @@
|
|||||||
#define ZT_SERVICE_ACCOUNT "NT AUTHORITY\\LocalService"
|
#define ZT_SERVICE_ACCOUNT "NT AUTHORITY\\LocalService"
|
||||||
#define ZT_SERVICE_PASSWORD NULL
|
#define ZT_SERVICE_PASSWORD NULL
|
||||||
|
|
||||||
|
namespace ZeroTier {
|
||||||
|
class Node;
|
||||||
|
class Thread;
|
||||||
|
} // namespace ZeroTier
|
||||||
|
|
||||||
class ZeroTierOneService : public CServiceBase
|
class ZeroTierOneService : public CServiceBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ZeroTierOneService();
|
ZeroTierOneService();
|
||||||
virtual ~ZeroTierOneService(void);
|
virtual ~ZeroTierOneService(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread main method; do not call elsewhere
|
||||||
|
*/
|
||||||
|
void threadMain()
|
||||||
|
throw();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
|
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
|
||||||
virtual void OnStop();
|
virtual void OnStop();
|
||||||
virtual void OnPause();
|
virtual void OnShutdown();
|
||||||
virtual void OnContinue();
|
|
||||||
|
private:
|
||||||
|
ZeroTier::Node *volatile _node;
|
||||||
|
ZeroTier::Thread *volatile _thread;
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup>
|
|
||||||
<!-- Select a Product Configuration -->
|
|
||||||
<InstallShieldProductConfiguration>Express</InstallShieldProductConfiguration>
|
|
||||||
<!-- Select a Visual Studio Configuration / InstallShield Release -->
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<InstallShieldRelease>$(Configuration)</InstallShieldRelease>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<!-- The InstallShieldProject item selects the project to build -->
|
|
||||||
<InstallShieldProject Include="$(MSBuildProjectDirectory)\$(MSBuildProjectName).isl"/>
|
|
||||||
<!-- The InstallShieldReleaseFlags sets Release Flags -->
|
|
||||||
<!--<InstallShieldReleaseFlags Include=""/>-->
|
|
||||||
<!-- The InstallShieldMergeModulePath specifies what directories are
|
|
||||||
searched for Merge Modules -->
|
|
||||||
<!--<InstallShieldMergeModulePath Include=""/>-->
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<!-- The ProjectReference items refer to any Visual Studio solutions you want to automatically probe for Project Output Groups. -->
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<!-- The TaggedOutputs items allow you to explicitly add extra files to output groups. Each item must include both Name and OutputGroup, as well as TargetPath metadata values. -->
|
|
||||||
<!--<TaggedOutputs Include="C:\My Test Exe.exe">
|
|
||||||
<Name>My Test Project</Name>
|
|
||||||
<OutputGroup>Primary output</OutputGroup>
|
|
||||||
<TargetPath>My Test Exe.exe</TargetPath>
|
|
||||||
</TaggedOutputs> -->
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildExtensionsPath32)\InstallShield\2013Limited\InstallShield.targets"/>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\ZeroTierOneService\ZeroTierOneService.csproj">
|
|
||||||
<Name>ZeroTierOneService</Name>
|
|
||||||
<Project>{63D28112-9A56-42FA-9C3E-EF6C58AF09B3}</Project>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
Loading…
Reference in New Issue
Block a user