From d5b50ee46621d4cdef5504419235383699873048 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 5 Feb 2014 16:37:50 -0800 Subject: [PATCH] C++ service base stuff taken from MS public domain example project and modified slightly. --- main.cpp | 6 + windows/ZeroTierOne/ServiceBase.cpp | 563 ++++++++++++++++++ windows/ZeroTierOne/ServiceBase.h | 122 ++++ windows/ZeroTierOne/ServiceInstaller.cpp | 192 ++++++ windows/ZeroTierOne/ServiceInstaller.h | 61 ++ windows/ZeroTierOne/ZeroTierOne.vcxproj | 6 + .../ZeroTierOne/ZeroTierOne.vcxproj.filters | 18 + windows/ZeroTierOne/ZeroTierOneService.cpp | 47 ++ windows/ZeroTierOne/ZeroTierOneService.h | 42 ++ 9 files changed, 1057 insertions(+) create mode 100644 windows/ZeroTierOne/ServiceBase.cpp create mode 100644 windows/ZeroTierOne/ServiceBase.h create mode 100644 windows/ZeroTierOne/ServiceInstaller.cpp create mode 100644 windows/ZeroTierOne/ServiceInstaller.h create mode 100644 windows/ZeroTierOne/ZeroTierOneService.cpp create mode 100644 windows/ZeroTierOne/ZeroTierOneService.h diff --git a/main.cpp b/main.cpp index 97b01c600..2d94efd7c 100644 --- a/main.cpp +++ b/main.cpp @@ -42,6 +42,8 @@ #include #include #include +#include "windows/ZeroTierOne/ServiceInstaller.h" +#include "windows/ZeroTierOne/ServiceBase.h" #else #include #include @@ -93,6 +95,10 @@ static void printHelp(const char *cn,FILE *out) fprintf(out," -c - Bind to this port for local control packets"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); +#ifdef __WINDOWS__ + fprintf(out," -I - Install Windows service"ZT_EOL_S); + fprintf(out," -R - Uninstall Windows service"ZT_EOL_S); +#endif } namespace ZeroTierCLI { // --------------------------------------------------- diff --git a/windows/ZeroTierOne/ServiceBase.cpp b/windows/ZeroTierOne/ServiceBase.cpp new file mode 100644 index 000000000..59d384c5d --- /dev/null +++ b/windows/ZeroTierOne/ServiceBase.cpp @@ -0,0 +1,563 @@ +/****************************** Module Header ******************************\ +* Module Name: ServiceBase.cpp +* Project: CppWindowsService +* Copyright (c) Microsoft Corporation. +* +* Provides a base class for a service that will exist as part of a service +* application. CServiceBase must be derived from when creating a new service +* class. +* +* This source is subject to the Microsoft Public License. +* See http://www.microsoft.com/en-us/openness/resources/licenses.aspx#MPL. +* All other rights reserved. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +\***************************************************************************/ + +#pragma region Includes +#include "ServiceBase.h" +#include +#include +#include +#pragma endregion + + +#pragma region Static Members + +// Initialize the singleton service instance. +CServiceBase *CServiceBase::s_service = NULL; + + +// +// FUNCTION: CServiceBase::Run(CServiceBase &) +// +// PURPOSE: Register the executable for a service with the Service Control +// Manager (SCM). After you call Run(ServiceBase), the SCM issues a Start +// command, which results in a call to the OnStart method in the service. +// This method blocks until the service has stopped. +// +// PARAMETERS: +// * service - the reference to a CServiceBase object. It will become the +// singleton service instance of this service application. +// +// RETURN VALUE: If the function succeeds, the return value is TRUE. If the +// function fails, the return value is FALSE. To get extended error +// information, call GetLastError. +// +BOOL CServiceBase::Run(CServiceBase &service) +{ + s_service = &service; + + SERVICE_TABLE_ENTRYA serviceTable[] = + { + { service.m_name, ServiceMain }, + { NULL, NULL } + }; + + // Connects the main thread of a service process to the service control + // manager, which causes the thread to be the service control dispatcher + // thread for the calling process. This call returns when the service has + // stopped. The process should simply terminate when the call returns. + return StartServiceCtrlDispatcher(serviceTable); +} + + +// +// FUNCTION: CServiceBase::ServiceMain(DWORD, PWSTR *) +// +// PURPOSE: Entry point for the service. It registers the handler function +// for the service and starts the service. +// +// PARAMETERS: +// * dwArgc - number of command line arguments +// * lpszArgv - array of command line arguments +// +void WINAPI CServiceBase::ServiceMain(DWORD dwArgc, PSTR *pszArgv) +{ + assert(s_service != NULL); + + // Register the handler function for the service + s_service->m_statusHandle = RegisterServiceCtrlHandler( + s_service->m_name, ServiceCtrlHandler); + if (s_service->m_statusHandle == NULL) + { + throw GetLastError(); + } + + // Start the service. + s_service->Start(dwArgc, pszArgv); +} + + +// +// FUNCTION: CServiceBase::ServiceCtrlHandler(DWORD) +// +// PURPOSE: The function is called by the SCM whenever a control code is +// sent to the service. +// +// PARAMETERS: +// * dwCtrlCode - the control code. This parameter can be one of the +// following values: +// +// SERVICE_CONTROL_CONTINUE +// SERVICE_CONTROL_INTERROGATE +// SERVICE_CONTROL_NETBINDADD +// SERVICE_CONTROL_NETBINDDISABLE +// SERVICE_CONTROL_NETBINDREMOVE +// SERVICE_CONTROL_PARAMCHANGE +// SERVICE_CONTROL_PAUSE +// SERVICE_CONTROL_SHUTDOWN +// SERVICE_CONTROL_STOP +// +// This parameter can also be a user-defined control code ranges from 128 +// to 255. +// +void WINAPI CServiceBase::ServiceCtrlHandler(DWORD dwCtrl) +{ + switch (dwCtrl) + { + case SERVICE_CONTROL_STOP: s_service->Stop(); break; + case SERVICE_CONTROL_PAUSE: s_service->Pause(); break; + case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break; + case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break; + case SERVICE_CONTROL_INTERROGATE: break; + default: break; + } +} + +#pragma endregion + + +#pragma region Service Constructor and Destructor + +// +// FUNCTION: CServiceBase::CServiceBase(PWSTR, BOOL, BOOL, BOOL) +// +// PURPOSE: The constructor of CServiceBase. It initializes a new instance +// of the CServiceBase class. The optional parameters (fCanStop, +/// fCanShutdown and fCanPauseContinue) allow you to specify whether the +// service can be stopped, paused and continued, or be notified when system +// shutdown occurs. +// +// PARAMETERS: +// * pszServiceName - the name of the service +// * fCanStop - the service can be stopped +// * fCanShutdown - the service is notified when system shutdown occurs +// * fCanPauseContinue - the service can be paused and continued +// +CServiceBase::CServiceBase(PSTR pszServiceName, + BOOL fCanStop, + BOOL fCanShutdown, + BOOL fCanPauseContinue) +{ + // Service name must be a valid string and cannot be NULL. + m_name = (pszServiceName == NULL) ? "" : pszServiceName; + + m_statusHandle = NULL; + + // The service runs in its own process. + m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + + // The service is starting. + m_status.dwCurrentState = SERVICE_START_PENDING; + + // The accepted commands of the service. + DWORD dwControlsAccepted = 0; + if (fCanStop) + dwControlsAccepted |= SERVICE_ACCEPT_STOP; + if (fCanShutdown) + dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN; + if (fCanPauseContinue) + dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE; + m_status.dwControlsAccepted = dwControlsAccepted; + + m_status.dwWin32ExitCode = NO_ERROR; + m_status.dwServiceSpecificExitCode = 0; + m_status.dwCheckPoint = 0; + m_status.dwWaitHint = 0; +} + + +// +// FUNCTION: CServiceBase::~CServiceBase() +// +// PURPOSE: The virtual destructor of CServiceBase. +// +CServiceBase::~CServiceBase(void) +{ +} + +#pragma endregion + + +#pragma region Service Start, Stop, Pause, Continue, and Shutdown + +// +// FUNCTION: CServiceBase::Start(DWORD, PWSTR *) +// +// PURPOSE: The function starts the service. It calls the OnStart virtual +// function in which you can specify the actions to take when the service +// starts. If an error occurs during the startup, the error will be logged +// in the Application event log, and the service will be stopped. +// +// PARAMETERS: +// * dwArgc - number of command line arguments +// * lpszArgv - array of command line arguments +// +void CServiceBase::Start(DWORD dwArgc, PSTR *pszArgv) +{ + try + { + // Tell SCM that the service is starting. + SetServiceStatus(SERVICE_START_PENDING); + + // Perform service-specific initialization. + OnStart(dwArgc, pszArgv); + + // Tell SCM that the service is started. + SetServiceStatus(SERVICE_RUNNING); + } + catch (DWORD dwError) + { + // Log the error. + WriteErrorLogEntry("Service Start", dwError); + + // Set the service status to be stopped. + SetServiceStatus(SERVICE_STOPPED, dwError); + } + catch (...) + { + // Log the error. + WriteEventLogEntry("Service failed to start.", EVENTLOG_ERROR_TYPE); + + // Set the service status to be stopped. + SetServiceStatus(SERVICE_STOPPED); + } +} + + +// +// FUNCTION: CServiceBase::OnStart(DWORD, PWSTR *) +// +// PURPOSE: When implemented in a derived class, executes when a Start +// command is sent to the service by the SCM or when the operating system +// starts (for a service that starts automatically). Specifies actions to +// take when the service starts. Be sure to periodically call +// CServiceBase::SetServiceStatus() with SERVICE_START_PENDING if the +// procedure is going to take long time. You may also consider spawning a +// new thread in OnStart to perform time-consuming initialization tasks. +// +// PARAMETERS: +// * dwArgc - number of command line arguments +// * lpszArgv - array of command line arguments +// +void CServiceBase::OnStart(DWORD dwArgc, PSTR *pszArgv) +{ +} + + +// +// FUNCTION: CServiceBase::Stop() +// +// PURPOSE: The function stops the service. It calls the OnStop virtual +// function in which you can specify the actions to take when the service +// stops. If an error occurs, the error will be logged in the Application +// event log, and the service will be restored to the original state. +// +void CServiceBase::Stop() +{ + DWORD dwOriginalState = m_status.dwCurrentState; + try + { + // Tell SCM that the service is stopping. + SetServiceStatus(SERVICE_STOP_PENDING); + + // Perform service-specific stop operations. + OnStop(); + + // Tell SCM that the service is stopped. + SetServiceStatus(SERVICE_STOPPED); + } + catch (DWORD dwError) + { + // Log the error. + WriteErrorLogEntry("Service Stop", dwError); + + // Set the orginal service status. + SetServiceStatus(dwOriginalState); + } + catch (...) + { + // Log the error. + WriteEventLogEntry("Service failed to stop.", EVENTLOG_ERROR_TYPE); + + // Set the orginal service status. + SetServiceStatus(dwOriginalState); + } +} + + +// +// FUNCTION: CServiceBase::OnStop() +// +// PURPOSE: When implemented in a derived class, executes when a Stop +// command is sent to the service by the SCM. Specifies actions to take +// when a service stops running. Be sure to periodically call +// CServiceBase::SetServiceStatus() with SERVICE_STOP_PENDING if the +// procedure is going to take long time. +// +void CServiceBase::OnStop() +{ +} + + +// +// FUNCTION: CServiceBase::Pause() +// +// PURPOSE: The function pauses the service if the service supports pause +// and continue. It calls the OnPause virtual function in which you can +// specify the actions to take when the service pauses. If an error occurs, +// the error will be logged in the Application event log, and the service +// will become running. +// +void CServiceBase::Pause() +{ + try + { + // Tell SCM that the service is pausing. + SetServiceStatus(SERVICE_PAUSE_PENDING); + + // Perform service-specific pause operations. + OnPause(); + + // Tell SCM that the service is paused. + SetServiceStatus(SERVICE_PAUSED); + } + catch (DWORD dwError) + { + // Log the error. + WriteErrorLogEntry("Service Pause", dwError); + + // Tell SCM that the service is still running. + SetServiceStatus(SERVICE_RUNNING); + } + catch (...) + { + // Log the error. + WriteEventLogEntry("Service failed to pause.", EVENTLOG_ERROR_TYPE); + + // Tell SCM that the service is still running. + SetServiceStatus(SERVICE_RUNNING); + } +} + + +// +// FUNCTION: CServiceBase::OnPause() +// +// PURPOSE: When implemented in a derived class, executes when a Pause +// command is sent to the service by the SCM. Specifies actions to take +// when a service pauses. +// +void CServiceBase::OnPause() +{ +} + + +// +// FUNCTION: CServiceBase::Continue() +// +// PURPOSE: The function resumes normal functioning after being paused if +// the service supports pause and continue. It calls the OnContinue virtual +// function in which you can specify the actions to take when the service +// continues. If an error occurs, the error will be logged in the +// Application event log, and the service will still be paused. +// +void CServiceBase::Continue() +{ + try + { + // Tell SCM that the service is resuming. + SetServiceStatus(SERVICE_CONTINUE_PENDING); + + // Perform service-specific continue operations. + OnContinue(); + + // Tell SCM that the service is running. + SetServiceStatus(SERVICE_RUNNING); + } + catch (DWORD dwError) + { + // Log the error. + WriteErrorLogEntry("Service Continue", dwError); + + // Tell SCM that the service is still paused. + SetServiceStatus(SERVICE_PAUSED); + } + catch (...) + { + // Log the error. + WriteEventLogEntry("Service failed to resume.", EVENTLOG_ERROR_TYPE); + + // Tell SCM that the service is still paused. + SetServiceStatus(SERVICE_PAUSED); + } +} + + +// +// FUNCTION: CServiceBase::OnContinue() +// +// PURPOSE: When implemented in a derived class, OnContinue runs when a +// Continue command is sent to the service by the SCM. Specifies actions to +// take when a service resumes normal functioning after being paused. +// +void CServiceBase::OnContinue() +{ +} + + +// +// FUNCTION: CServiceBase::Shutdown() +// +// PURPOSE: The function executes when the system is shutting down. It +// calls the OnShutdown virtual function in which you can specify what +// should occur immediately prior to the system shutting down. If an error +// occurs, the error will be logged in the Application event log. +// +void CServiceBase::Shutdown() +{ + try + { + // Perform service-specific shutdown operations. + OnShutdown(); + + // Tell SCM that the service is stopped. + SetServiceStatus(SERVICE_STOPPED); + } + catch (DWORD dwError) + { + // Log the error. + WriteErrorLogEntry("Service Shutdown", dwError); + } + catch (...) + { + // Log the error. + WriteEventLogEntry("Service failed to shut down.", EVENTLOG_ERROR_TYPE); + } +} + + +// +// FUNCTION: CServiceBase::OnShutdown() +// +// PURPOSE: When implemented in a derived class, executes when the system +// is shutting down. Specifies what should occur immediately prior to the +// system shutting down. +// +void CServiceBase::OnShutdown() +{ +} + +#pragma endregion + + +#pragma region Helper Functions + +// +// FUNCTION: CServiceBase::SetServiceStatus(DWORD, DWORD, DWORD) +// +// PURPOSE: The function sets the service status and reports the status to +// the SCM. +// +// PARAMETERS: +// * dwCurrentState - the state of the service +// * dwWin32ExitCode - error code to report +// * dwWaitHint - estimated time for pending operation, in milliseconds +// +void CServiceBase::SetServiceStatus(DWORD dwCurrentState, + DWORD dwWin32ExitCode, + DWORD dwWaitHint) +{ + static DWORD dwCheckPoint = 1; + + // Fill in the SERVICE_STATUS structure of the service. + + m_status.dwCurrentState = dwCurrentState; + m_status.dwWin32ExitCode = dwWin32ExitCode; + m_status.dwWaitHint = dwWaitHint; + + m_status.dwCheckPoint = + ((dwCurrentState == SERVICE_RUNNING) || + (dwCurrentState == SERVICE_STOPPED)) ? + 0 : dwCheckPoint++; + + // Report the status of the service to the SCM. + ::SetServiceStatus(m_statusHandle, &m_status); +} + + +// +// FUNCTION: CServiceBase::WriteEventLogEntry(PWSTR, WORD) +// +// PURPOSE: Log a message to the Application event log. +// +// PARAMETERS: +// * pszMessage - string message to be logged. +// * wType - the type of event to be logged. The parameter can be one of +// the following values. +// +// EVENTLOG_SUCCESS +// EVENTLOG_AUDIT_FAILURE +// EVENTLOG_AUDIT_SUCCESS +// EVENTLOG_ERROR_TYPE +// EVENTLOG_INFORMATION_TYPE +// EVENTLOG_WARNING_TYPE +// +void CServiceBase::WriteEventLogEntry(PSTR pszMessage, WORD wType) +{ + HANDLE hEventSource = NULL; + LPCSTR lpszStrings[2] = { NULL, NULL }; + + hEventSource = RegisterEventSource(NULL, m_name); + if (hEventSource) + { + lpszStrings[0] = m_name; + lpszStrings[1] = pszMessage; + + ReportEvent(hEventSource, // Event log handle + wType, // Event type + 0, // Event category + 0, // Event identifier + NULL, // No security identifier + 2, // Size of lpszStrings array + 0, // No binary data + lpszStrings, // Array of strings + NULL // No binary data + ); + + DeregisterEventSource(hEventSource); + } +} + + +// +// FUNCTION: CServiceBase::WriteErrorLogEntry(PWSTR, DWORD) +// +// PURPOSE: Log an error message to the Application event log. +// +// PARAMETERS: +// * pszFunction - the function that gives the error +// * dwError - the error code +// +void CServiceBase::WriteErrorLogEntry(PSTR pszFunction, DWORD dwError) +{ + char szMessage[260]; + StringCchPrintf(szMessage, ARRAYSIZE(szMessage), + "%s failed w/err 0x%08lx", pszFunction, dwError); + WriteEventLogEntry(szMessage, EVENTLOG_ERROR_TYPE); +} + +#pragma endregion \ No newline at end of file diff --git a/windows/ZeroTierOne/ServiceBase.h b/windows/ZeroTierOne/ServiceBase.h new file mode 100644 index 000000000..5288194f1 --- /dev/null +++ b/windows/ZeroTierOne/ServiceBase.h @@ -0,0 +1,122 @@ +/****************************** Module Header ******************************\ +* Module Name: ServiceBase.h +* Project: CppWindowsService +* Copyright (c) Microsoft Corporation. +* +* Provides a base class for a service that will exist as part of a service +* application. CServiceBase must be derived from when creating a new service +* class. +* +* This source is subject to the Microsoft Public License. +* See http://www.microsoft.com/en-us/openness/resources/licenses.aspx#MPL. +* All other rights reserved. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +\***************************************************************************/ + +#pragma once + +#include + + +class CServiceBase +{ +public: + + // Register the executable for a service with the Service Control Manager + // (SCM). After you call Run(ServiceBase), the SCM issues a Start command, + // which results in a call to the OnStart method in the service. This + // method blocks until the service has stopped. + static BOOL Run(CServiceBase &service); + + // Service object constructor. The optional parameters (fCanStop, + // fCanShutdown and fCanPauseContinue) allow you to specify whether the + // service can be stopped, paused and continued, or be notified when + // system shutdown occurs. + CServiceBase(LPSTR pszServiceName, + BOOL fCanStop = TRUE, + BOOL fCanShutdown = TRUE, + BOOL fCanPauseContinue = FALSE); + + // Service object destructor. + virtual ~CServiceBase(void); + + // Stop the service. + void Stop(); + +protected: + + // When implemented in a derived class, executes when a Start command is + // sent to the service by the SCM or when the operating system starts + // (for a service that starts automatically). Specifies actions to take + // when the service starts. + virtual void OnStart(DWORD dwArgc, PSTR *pszArgv); + + // When implemented in a derived class, executes when a Stop command is + // sent to the service by the SCM. Specifies actions to take when a + // service stops running. + virtual void OnStop(); + + // When implemented in a derived class, executes when a Pause command is + // sent to the service by the SCM. Specifies actions to take when a + // service pauses. + virtual void OnPause(); + + // When implemented in a derived class, OnContinue runs when a Continue + // command is sent to the service by the SCM. Specifies actions to take + // when a service resumes normal functioning after being paused. + virtual void OnContinue(); + + // When implemented in a derived class, executes when the system is + // shutting down. Specifies what should occur immediately prior to the + // system shutting down. + virtual void OnShutdown(); + + // Set the service status and report the status to the SCM. + void SetServiceStatus(DWORD dwCurrentState, + DWORD dwWin32ExitCode = NO_ERROR, + DWORD dwWaitHint = 0); + + // Log a message to the Application event log. + void WriteEventLogEntry(PSTR pszMessage, WORD wType); + + // Log an error message to the Application event log. + void WriteErrorLogEntry(PSTR pszFunction, + DWORD dwError = GetLastError()); + +private: + + // Entry point for the service. It registers the handler function for the + // service and starts the service. + static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv); + + // The function is called by the SCM whenever a control code is sent to + // the service. + static void WINAPI ServiceCtrlHandler(DWORD dwCtrl); + + // Start the service. + void Start(DWORD dwArgc, PSTR *pszArgv); + + // Pause the service. + void Pause(); + + // Resume the service after being paused. + void Continue(); + + // Execute when the system is shutting down. + void Shutdown(); + + // The singleton service instance. + static CServiceBase *s_service; + + // The name of the service + LPSTR m_name; + + // The status of the service + SERVICE_STATUS m_status; + + // The service status handle + SERVICE_STATUS_HANDLE m_statusHandle; +}; \ No newline at end of file diff --git a/windows/ZeroTierOne/ServiceInstaller.cpp b/windows/ZeroTierOne/ServiceInstaller.cpp new file mode 100644 index 000000000..9d7b22c71 --- /dev/null +++ b/windows/ZeroTierOne/ServiceInstaller.cpp @@ -0,0 +1,192 @@ +/****************************** Module Header ******************************\ +* Module Name: ServiceInstaller.cpp +* Project: CppWindowsService +* Copyright (c) Microsoft Corporation. +* +* The file implements functions that install and uninstall the service. +* +* This source is subject to the Microsoft Public License. +* See http://www.microsoft.com/en-us/openness/resources/licenses.aspx#MPL. +* All other rights reserved. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +\***************************************************************************/ + +#pragma region "Includes" +#include +#include +#include "ServiceInstaller.h" +#pragma endregion + + +// +// FUNCTION: InstallService +// +// PURPOSE: Install the current application as a service to the local +// service control manager database. +// +// PARAMETERS: +// * pszServiceName - the name of the service to be installed +// * pszDisplayName - the display name of the service +// * dwStartType - the service start option. This parameter can be one of +// the following values: SERVICE_AUTO_START, SERVICE_BOOT_START, +// SERVICE_DEMAND_START, SERVICE_DISABLED, SERVICE_SYSTEM_START. +// * pszDependencies - a pointer to a double null-terminated array of null- +// separated names of services or load ordering groups that the system +// must start before this service. +// * pszAccount - the name of the account under which the service runs. +// * pszPassword - the password to the account name. +// +// 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, + PSTR pszDisplayName, + DWORD dwStartType, + PSTR pszDependencies, + PSTR pszAccount, + PSTR pszPassword) +{ + 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()); + goto Cleanup; + } + + // Open the local default service control manager database + schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | + SC_MANAGER_CREATE_SERVICE); + if (schSCManager == NULL) + { + wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError()); + goto Cleanup; + } + + // Install the service into SCM by calling CreateService + schService = CreateService( + schSCManager, // SCManager database + pszServiceName, // Name of service + pszDisplayName, // Name to display + SERVICE_QUERY_STATUS, // Desired access + SERVICE_WIN32_OWN_PROCESS, // Service type + dwStartType, // Service start type + SERVICE_ERROR_NORMAL, // Error control type + szPath, // Service's binary + NULL, // No load ordering group + NULL, // No tag identifier + pszDependencies, // Dependencies + pszAccount, // Service running account + pszPassword // Password of the account + ); + if (schService == NULL) + { + wprintf(L"CreateService failed w/err 0x%08lx\n", GetLastError()); + goto Cleanup; + } + + wprintf(L"%s is installed.\n", pszServiceName); + +Cleanup: + // Centralized cleanup for all allocated resources. + if (schSCManager) + { + CloseServiceHandle(schSCManager); + schSCManager = NULL; + } + if (schService) + { + CloseServiceHandle(schService); + schService = NULL; + } +} + + +// +// FUNCTION: UninstallService +// +// PURPOSE: Stop and remove the service from the local service control +// manager database. +// +// PARAMETERS: +// * pszServiceName - the name of the service to be removed. +// +// 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) +{ + SC_HANDLE schSCManager = NULL; + SC_HANDLE schService = NULL; + SERVICE_STATUS ssSvcStatus = {}; + + // Open the local default service control manager database + schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (schSCManager == NULL) + { + wprintf(L"OpenSCManager failed w/err 0x%08lx\n", GetLastError()); + goto Cleanup; + } + + // Open the service with delete, stop, and query status permissions + schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP | + SERVICE_QUERY_STATUS | DELETE); + if (schService == NULL) + { + wprintf(L"OpenService failed w/err 0x%08lx\n", GetLastError()); + goto Cleanup; + } + + // Try to stop the service + if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus)) + { + wprintf(L"Stopping %s.", pszServiceName); + Sleep(1000); + + while (QueryServiceStatus(schService, &ssSvcStatus)) + { + if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) + { + wprintf(L"."); + Sleep(1000); + } + 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()); + goto Cleanup; + } + + wprintf(L"%s is removed.\n", pszServiceName); + +Cleanup: + // Centralized cleanup for all allocated resources. + if (schSCManager) + { + CloseServiceHandle(schSCManager); + schSCManager = NULL; + } + if (schService) + { + CloseServiceHandle(schService); + schService = NULL; + } +} \ No newline at end of file diff --git a/windows/ZeroTierOne/ServiceInstaller.h b/windows/ZeroTierOne/ServiceInstaller.h new file mode 100644 index 000000000..1f007c056 --- /dev/null +++ b/windows/ZeroTierOne/ServiceInstaller.h @@ -0,0 +1,61 @@ +/****************************** Module Header ******************************\ +* Module Name: ServiceInstaller.h +* Project: CppWindowsService +* Copyright (c) Microsoft Corporation. +* +* The file declares functions that install and uninstall the service. +* +* This source is subject to the Microsoft Public License. +* See http://www.microsoft.com/en-us/openness/resources/licenses.aspx#MPL. +* All other rights reserved. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +\***************************************************************************/ + +#pragma once + + +// +// FUNCTION: InstallService +// +// PURPOSE: Install the current application as a service to the local +// service control manager database. +// +// PARAMETERS: +// * pszServiceName - the name of the service to be installed +// * pszDisplayName - the display name of the service +// * dwStartType - the service start option. This parameter can be one of +// the following values: SERVICE_AUTO_START, SERVICE_BOOT_START, +// SERVICE_DEMAND_START, SERVICE_DISABLED, SERVICE_SYSTEM_START. +// * pszDependencies - a pointer to a double null-terminated array of null- +// separated names of services or load ordering groups that the system +// must start before this service. +// * pszAccount - the name of the account under which the service runs. +// * pszPassword - the password to the account name. +// +// 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, + PSTR pszDisplayName, + DWORD dwStartType, + PSTR pszDependencies, + PSTR pszAccount, + PSTR pszPassword); + + +// +// FUNCTION: UninstallService +// +// PURPOSE: Stop and remove the service from the local service control +// manager database. +// +// PARAMETERS: +// * pszServiceName - the name of the service to be removed. +// +// 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); \ No newline at end of file diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj b/windows/ZeroTierOne/ZeroTierOne.vcxproj index f739ea1a8..14dd0ea12 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj @@ -49,6 +49,9 @@ + + + @@ -97,6 +100,9 @@ + + + diff --git a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters index df6d9d7ab..71da70335 100644 --- a/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters +++ b/windows/ZeroTierOne/ZeroTierOne.vcxproj.filters @@ -105,6 +105,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -245,6 +254,15 @@ Header Files + + Header Files + + + Header Files + + + Header Files + diff --git a/windows/ZeroTierOne/ZeroTierOneService.cpp b/windows/ZeroTierOne/ZeroTierOneService.cpp new file mode 100644 index 000000000..f146ce287 --- /dev/null +++ b/windows/ZeroTierOne/ZeroTierOneService.cpp @@ -0,0 +1,47 @@ +/****************************** Module Header ******************************\ +* Module Name: SampleService.cpp +* Project: CppWindowsService +* Copyright (c) Microsoft Corporation. +* +* Provides a sample service class that derives from the service base class - +* CServiceBase. The sample service logs the service start and stop +* information to the Application event log, and shows how to run the main +* function of the service in a thread pool worker thread. +* +* This source is subject to the Microsoft Public License. +* See http://www.microsoft.com/en-us/openness/resources/licenses.aspx#MPL. +* All other rights reserved. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +\***************************************************************************/ + +#pragma region Includes +#include "ZeroTierOneService.h" +#pragma endregion + +ZeroTierOneService::ZeroTierOneService() : + CServiceBase(ZT_SERVICE_NAME,TRUE,TRUE,TRUE) +{ +} + +ZeroTierOneService::~ZeroTierOneService(void) +{ +} + +void ZeroTierOneService::OnStart(DWORD dwArgc, LPSTR *lpszArgv) +{ +} + +void ZeroTierOneService::OnStop() +{ +} + +void ZeroTierOneService::OnPause() +{ +} + +void ZeroTierOneService::OnContinue() +{ +} diff --git a/windows/ZeroTierOne/ZeroTierOneService.h b/windows/ZeroTierOne/ZeroTierOneService.h new file mode 100644 index 000000000..42f170aec --- /dev/null +++ b/windows/ZeroTierOne/ZeroTierOneService.h @@ -0,0 +1,42 @@ +/****************************** Module Header ******************************\ +* Module Name: SampleService.h +* Project: CppWindowsService +* Copyright (c) Microsoft Corporation. +* +* Provides a sample service class that derives from the service base class - +* CServiceBase. The sample service logs the service start and stop +* information to the Application event log, and shows how to run the main +* function of the service in a thread pool worker thread. +* +* This source is subject to the Microsoft Public License. +* See http://www.microsoft.com/en-us/openness/resources/licenses.aspx#MPL. +* All other rights reserved. +* +* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, +* EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +\***************************************************************************/ + +#pragma once + +#include "ServiceBase.h" + +#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_PASSWORD NULL + +class ZeroTierOneService : public CServiceBase +{ +public: + ZeroTierOneService(); + virtual ~ZeroTierOneService(void); + +protected: + virtual void OnStart(DWORD dwArgc, PSTR *pszArgv); + virtual void OnStop(); + virtual void OnPause(); + virtual void OnContinue(); +};