Windows service work, remove old installer... not sure exactly what we're going to use.

This commit is contained in:
Adam Ierymenko 2014-02-06 22:06:27 -08:00
parent 6d17993eb6
commit 8a7486577a
7 changed files with 255 additions and 6109 deletions

178
main.cpp
View File

@ -44,6 +44,7 @@
#include <lmcons.h>
#include "windows/ZeroTierOne/ServiceInstaller.h"
#include "windows/ZeroTierOne/ServiceBase.h"
#include "windows/ZeroTierOne/ZeroTierOneService.h"
#else
#include <unistd.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," -i - Run idtool command (zerotier-idtool)"ZT_EOL_S);
#ifdef __WINDOWS__
fprintf(out," -I - Install Windows service"ZT_EOL_S);
fprintf(out," -R - Uninstall Windows service"ZT_EOL_S);
fprintf(out," -C - Run from command line instead of as service (Windows)"ZT_EOL_S);
fprintf(out," -I - Install Windows service (Windows)"ZT_EOL_S);
fprintf(out," -R - Uninstall Windows service (Windows)"ZT_EOL_S);
#endif
}
@ -379,6 +381,7 @@ static void sighandlerQuit(int sig)
#endif
#ifdef __WINDOWS__
// Console signal handler routine to allow CTRL+C to work, mostly for testing
static BOOL WINAPI _handlerRoutine(DWORD dwCtrlType)
{
switch(dwCtrlType) {
@ -394,6 +397,7 @@ static BOOL WINAPI _handlerRoutine(DWORD dwCtrlType)
return FALSE;
}
// Returns true if this is running as the local administrator
static BOOL IsCurrentUserLocalAdministrator(void)
{
BOOL fReturn = FALSE;
@ -414,139 +418,49 @@ static BOOL IsCurrentUserLocalAdministrator(void)
PSECURITY_DESCRIPTOR psdAdmin = NULL;
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
/*
Determine if the current thread is running as a user that is a member
of
the local admins group. To do this, create a security descriptor
that
has a DACL which has an ACE that allows only local aministrators
access.
Then, call AccessCheck with the current thread's token and the
security
descriptor. It will say whether the user could access an object if
it
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 (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY,TRUE,&hToken))
{
if (GetLastError() != ERROR_NO_TOKEN)
__leave;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
if (!OpenProcessToken(GetCurrentProcess(),TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
__leave;
}
if (!DuplicateToken (hToken, SecurityImpersonation,
&hImpersonationToken))
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))
if (!InitializeSecurityDescriptor(psdAdmin,SECURITY_DESCRIPTOR_REVISION))
__leave;
// Compute size needed for the ACL.
dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) +
GetLengthSid(psidAdmin) - sizeof(DWORD);
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))
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;
@ -605,6 +519,9 @@ int main(int argc,char **argv)
const char *homeDir = (const char *)0;
unsigned int port = 0;
unsigned int controlPort = 0;
#ifdef __WINDOWS__
bool winRunFromCommandLine = false;
#endif
for(int i=1;i<argc;++i) {
if (argv[i][0] == '-') {
switch(argv[i][1]) {
@ -635,6 +552,35 @@ int main(int argc,char **argv)
printHelp(argv[0],stderr);
return 0;
} 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 '?':
default:
@ -678,14 +624,26 @@ int main(int argc,char **argv)
#endif
#endif
#ifdef __WINDOWS__
if (!winRunFromCommandLine) {
ZeroTierOneService zt1Service;
if (CServiceBase::Run(zt1Service) == TRUE) {
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()) {
#ifndef __WINDOWS__
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.
@ -693,22 +651,10 @@ int main(int argc,char **argv)
Utils::rm((std::string(homeDir)+"/zerotier-one.pid").c_str());
::execl(upgPath,upgPath,(char *)0);
}
exitCode = 2;
exitCode = 3;
fprintf(stderr,"%s: abnormal termination: unable to execute update at %s\n",argv[0],(upgPath) ? upgPath : "(unknown path)");
#else // not __UNIX_LIKE
#ifdef __WINDOWS__
// On Windows the service checks updates.d and invokes updates if they are
// found there. This only happens after exit code 4. The Windows service
// will listen to stdout as well to catch the filename.
if (upgPath) {
printf("[[[ UPDATE AVAILABLE: \"%s\" ]]]\r\n",upgPath);
exitCode = 4;
} else {
exitCode = 2;
}
#endif // __WINDOWS__
#endif // not __UNIX_LIKE__
} break;
#endif
case Node::NODE_UNRECOVERABLE_ERROR: {
exitCode = 3;
const char *termReason = node->reasonForTermination();
@ -719,11 +665,15 @@ int main(int argc,char **argv)
}
delete node;
node = (Node *)0;
} catch ( ... ) {}
} 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());
#endif
return exitCode;
}
}

View File

@ -42,20 +42,21 @@
// NOTE: If the function fails to install the service, it prints the error
// in the standard output stream for users to diagnose the problem.
//
void InstallService(PSTR pszServiceName,
std::string InstallService(PSTR pszServiceName,
PSTR pszDisplayName,
DWORD dwStartType,
PSTR pszDependencies,
PSTR pszAccount,
PSTR pszPassword)
{
std::string ret;
char szPath[MAX_PATH];
SC_HANDLE schSCManager = NULL;
SC_HANDLE schService = NULL;
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;
}
@ -64,7 +65,7 @@ void InstallService(PSTR pszServiceName,
SC_MANAGER_CREATE_SERVICE);
if (schSCManager == NULL)
{
wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError());
ret = "OpenSCManager failed";
goto Cleanup;
}
@ -86,12 +87,10 @@ void InstallService(PSTR pszServiceName,
);
if (schService == NULL)
{
wprintf(L"CreateService failed w/err 0x%08lx\n", GetLastError());
ret = "CreateService failed";
goto Cleanup;
}
wprintf(L"%s is installed.\n", pszServiceName);
Cleanup:
// Centralized cleanup for all allocated resources.
if (schSCManager)
@ -104,6 +103,8 @@ Cleanup:
CloseServiceHandle(schService);
schService = NULL;
}
return ret;
}
@ -119,8 +120,9 @@ Cleanup:
// NOTE: If the function fails to uninstall the service, it prints the
// 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 schService = NULL;
SERVICE_STATUS ssSvcStatus = {};
@ -129,7 +131,7 @@ void UninstallService(PSTR pszServiceName)
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (schSCManager == NULL)
{
wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError());
ret = "OpenSCManager failed";
goto Cleanup;
}
@ -138,45 +140,32 @@ void UninstallService(PSTR pszServiceName)
SERVICE_QUERY_STATUS | DELETE);
if (schService == NULL)
{
wprintf(L"OpenService failed w/err 0x%08lx\n", GetLastError());
ret = "OpenService failed (is service installed?)";
goto Cleanup;
}
// Try to stop the service
if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus))
{
wprintf(L"Stopping %s.", pszServiceName);
Sleep(1000);
Sleep(500);
while (QueryServiceStatus(schService, &ssSvcStatus))
{
if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING)
{
wprintf(L".");
Sleep(1000);
Sleep(500);
}
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.
if (!DeleteService(schService))
{
wprintf(L"DeleteService failed w/err 0x%08lx\n", GetLastError());
ret = "DeleteService failed (is service running?)";
goto Cleanup;
}
wprintf(L"%s is removed.\n", pszServiceName);
Cleanup:
// Centralized cleanup for all allocated resources.
if (schSCManager)
@ -189,4 +178,6 @@ Cleanup:
CloseServiceHandle(schService);
schService = NULL;
}
return ret;
}

View File

@ -16,6 +16,7 @@
#pragma once
#include <string>
//
// FUNCTION: InstallService
@ -38,7 +39,8 @@
// NOTE: If the function fails to install the service, it prints the error
// 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,
DWORD dwStartType,
PSTR pszDependencies,
@ -58,4 +60,5 @@ void InstallService(PSTR pszServiceName,
// NOTE: If the function fails to uninstall the service, it prints the
// 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);

View File

@ -27,29 +27,81 @@
#pragma region Includes
#include "ZeroTierOneService.h"
#include "../../node/Node.hpp"
#include "../../node/Defaults.hpp"
#include "../../node/Thread.hpp"
#pragma endregion
using namespace ZeroTier;
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)
{
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)
{
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()
{
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::OnContinue()
void ZeroTierOneService::OnShutdown()
{
// make sure it's stopped
OnStop();
}

View File

@ -36,15 +36,29 @@
#define ZT_SERVICE_ACCOUNT "NT AUTHORITY\\LocalService"
#define ZT_SERVICE_PASSWORD NULL
namespace ZeroTier {
class Node;
class Thread;
} // namespace ZeroTier
class ZeroTierOneService : public CServiceBase
{
public:
ZeroTierOneService();
virtual ~ZeroTierOneService(void);
/**
* Thread main method; do not call elsewhere
*/
void threadMain()
throw();
protected:
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
virtual void OnStop();
virtual void OnPause();
virtual void OnContinue();
virtual void OnShutdown();
private:
ZeroTier::Node *volatile _node;
ZeroTier::Thread *volatile _thread;
};

File diff suppressed because it is too large Load Diff

View File

@ -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>