From e6e11590c833477bd6f18434374736e17064951f Mon Sep 17 00:00:00 2001 From: chertov Date: Sat, 19 Apr 2014 00:20:11 +0400 Subject: [PATCH] add mikhail4021 Win32Service, run i2pd as admin --- Win32/.gitignore | 7 +- Win32/Win32Service.cpp | 526 ++++++++++++++++++++++++++++++++++++ Win32/Win32Service.h | 89 ++++++ Win32/i2pd.vcxproj | 4 + Win32/i2pd.vcxproj.filters | 9 + Win32/install_service.bat | 1 + Win32/uninstall_service.bat | 1 + i2p.cpp | 6 + 8 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 Win32/Win32Service.cpp create mode 100644 Win32/Win32Service.h create mode 100644 Win32/install_service.bat create mode 100644 Win32/uninstall_service.bat diff --git a/Win32/.gitignore b/Win32/.gitignore index 447f966d..5aa0538d 100644 --- a/Win32/.gitignore +++ b/Win32/.gitignore @@ -1,9 +1,14 @@ * !*/ +!*.h +!*.cpp + +!*.bat + !*.sln !*.vcproj !*.vcxproj !*.vcxproj.filters !*.iss -!.gitignore \ No newline at end of file +!.gitignore diff --git a/Win32/Win32Service.cpp b/Win32/Win32Service.cpp new file mode 100644 index 00000000..0d75dd14 --- /dev/null +++ b/Win32/Win32Service.cpp @@ -0,0 +1,526 @@ +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS // to use freopen +#endif + +#include "Win32Service.h" +#include +#include +#include + +#include "Log.h" +#include "Transports.h" +#include "NTCPSession.h" +#include "Tunnel.h" +#include "NetDb.h" +#include "Garlic.h" +#include "util.h" +#include "Streaming.h" + +I2PService *I2PService::s_service = NULL; + + +BOOL I2PService::Run(I2PService &service) +{ + s_service = &service; + + SERVICE_TABLE_ENTRY serviceTable[] = + { + { service.m_name, ServiceMain }, + { NULL, NULL } + }; + + return StartServiceCtrlDispatcher(serviceTable); +} + + +void WINAPI I2PService::ServiceMain(DWORD dwArgc, PSTR *pszArgv) +{ + assert(s_service != NULL); + + s_service->m_statusHandle = RegisterServiceCtrlHandler( + s_service->m_name, ServiceCtrlHandler); + if (s_service->m_statusHandle == NULL) + { + throw GetLastError(); + } + + s_service->Start(dwArgc, pszArgv); +} + + +void WINAPI I2PService::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; + } +} + + +I2PService::I2PService(PSTR pszServiceName, + BOOL fCanStop, + BOOL fCanShutdown, + BOOL fCanPauseContinue) : _httpServer(nullptr), _httpProxy(nullptr) +{ + m_name = (pszServiceName == NULL) ? "" : pszServiceName; + + m_statusHandle = NULL; + + m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + + m_status.dwCurrentState = SERVICE_START_PENDING; + + 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; + + m_fStopping = FALSE; + + // Create a manual-reset event that is not signaled at first to indicate + // the stopped signal of the service. + m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (m_hStoppedEvent == NULL) + { + throw GetLastError(); + } +} + + +I2PService::~I2PService(void) +{ + if (m_hStoppedEvent) + { + CloseHandle(m_hStoppedEvent); + m_hStoppedEvent = NULL; + } +} + + +void I2PService::Start(DWORD dwArgc, PSTR *pszArgv) +{ + try + { + SetServiceStatus(SERVICE_START_PENDING); + + OnStart(dwArgc, pszArgv); + + SetServiceStatus(SERVICE_RUNNING); + } + catch (DWORD dwError) + { + LogPrint("Service Start", dwError); + + SetServiceStatus(SERVICE_STOPPED, dwError); + } + catch (...) + { + LogPrint("Service failed to start.", EVENTLOG_ERROR_TYPE); + + SetServiceStatus(SERVICE_STOPPED); + } +} + + +void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv) +{ + LogPrint("CppWindowsService in OnStart", + EVENTLOG_INFORMATION_TYPE); + + i2p::util::config::OptionParser(dwArgc, pszArgv); + i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs); + i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"), + i2p::util::config::GetArg("-port", 17070)); + + _httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070)); + _httpServer->Start(); + LogPrint("HTTPServer started", EVENTLOG_INFORMATION_TYPE); + + i2p::data::netdb.Start(); + LogPrint("NetDB started", EVENTLOG_INFORMATION_TYPE); + i2p::transports.Start(); + LogPrint("Transports started", EVENTLOG_INFORMATION_TYPE); + i2p::tunnel::tunnels.Start(); + LogPrint("Tunnels started", EVENTLOG_INFORMATION_TYPE); + i2p::garlic::routing.Start(); + LogPrint("Routing started", EVENTLOG_INFORMATION_TYPE); + i2p::stream::StartStreaming(); + LogPrint("Streaming started", EVENTLOG_INFORMATION_TYPE); + + _httpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446)); + _httpProxy->Start(); + LogPrint("Proxy started", EVENTLOG_INFORMATION_TYPE); + + _worker = new std::thread(std::bind(&I2PService::WorkerThread, this)); +} + + +void I2PService::WorkerThread() +{ + while (!m_fStopping) + { + ::Sleep(1000); // Simulate some lengthy operations. + } + + // Signal the stopped event. + SetEvent(m_hStoppedEvent); +} + + +void I2PService::Stop() +{ + DWORD dwOriginalState = m_status.dwCurrentState; + try + { + SetServiceStatus(SERVICE_STOP_PENDING); + + OnStop(); + + SetServiceStatus(SERVICE_STOPPED); + } + catch (DWORD dwError) + { + LogPrint("Service Stop", dwError); + + SetServiceStatus(dwOriginalState); + } + catch (...) + { + LogPrint("Service failed to stop.", EVENTLOG_ERROR_TYPE); + + SetServiceStatus(dwOriginalState); + } +} + + +void I2PService::OnStop() +{ + // Log a service stop message to the Application log. + LogPrint("CppWindowsService in OnStop", EVENTLOG_INFORMATION_TYPE); + + _httpProxy->Stop(); + LogPrint("HTTPProxy stoped", EVENTLOG_INFORMATION_TYPE); + delete _httpProxy; + i2p::stream::StopStreaming(); + LogPrint("Streaming stoped", EVENTLOG_INFORMATION_TYPE); + i2p::garlic::routing.Stop(); + LogPrint("Routing stoped", EVENTLOG_INFORMATION_TYPE); + i2p::tunnel::tunnels.Stop(); + LogPrint("Tunnels stoped", EVENTLOG_INFORMATION_TYPE); + i2p::transports.Stop(); + LogPrint("Transports stoped", EVENTLOG_INFORMATION_TYPE); + i2p::data::netdb.Stop(); + LogPrint("NetDB stoped", EVENTLOG_INFORMATION_TYPE); + _httpServer->Stop(); + LogPrint("HTTPServer stoped", EVENTLOG_INFORMATION_TYPE); + delete _httpServer; + + m_fStopping = TRUE; + if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0) + { + throw GetLastError(); + } + _worker->join(); + delete _worker; +} + + +void I2PService::Pause() +{ + try + { + SetServiceStatus(SERVICE_PAUSE_PENDING); + + OnPause(); + + SetServiceStatus(SERVICE_PAUSED); + } + catch (DWORD dwError) + { + LogPrint("Service Pause", dwError); + + SetServiceStatus(SERVICE_RUNNING); + } + catch (...) + { + LogPrint("Service failed to pause.", EVENTLOG_ERROR_TYPE); + + SetServiceStatus(SERVICE_RUNNING); + } +} + + +void I2PService::OnPause() +{ +} + + +void I2PService::Continue() +{ + try + { + SetServiceStatus(SERVICE_CONTINUE_PENDING); + + OnContinue(); + + SetServiceStatus(SERVICE_RUNNING); + } + catch (DWORD dwError) + { + LogPrint("Service Continue", dwError); + + SetServiceStatus(SERVICE_PAUSED); + } + catch (...) + { + LogPrint("Service failed to resume.", EVENTLOG_ERROR_TYPE); + + SetServiceStatus(SERVICE_PAUSED); + } +} + + +void I2PService::OnContinue() +{ +} + + +void I2PService::Shutdown() +{ + try + { + OnShutdown(); + + SetServiceStatus(SERVICE_STOPPED); + } + catch (DWORD dwError) + { + LogPrint("Service Shutdown", dwError); + } + catch (...) + { + LogPrint("Service failed to shut down.", EVENTLOG_ERROR_TYPE); + } +} + + +void I2PService::OnShutdown() +{ +} + + +void I2PService::SetServiceStatus(DWORD dwCurrentState, + DWORD dwWin32ExitCode, + DWORD dwWaitHint) +{ + static DWORD dwCheckPoint = 1; + + + m_status.dwCurrentState = dwCurrentState; + m_status.dwWin32ExitCode = dwWin32ExitCode; + m_status.dwWaitHint = dwWaitHint; + + m_status.dwCheckPoint = + ((dwCurrentState == SERVICE_RUNNING) || + (dwCurrentState == SERVICE_STOPPED)) ? + 0 : dwCheckPoint++; + + ::SetServiceStatus(m_statusHandle, &m_status); +} + +//***************************************************************************** + +void FreeHandles(SC_HANDLE schSCManager, SC_HANDLE schService) +{ + if (schSCManager) + { + CloseServiceHandle(schSCManager); + schSCManager = NULL; + } + if (schService) + { + CloseServiceHandle(schService); + schService = NULL; + } +} + +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) + { + printf("GetModuleFileName failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + // Open the local default service control manager database + schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | + SC_MANAGER_CREATE_SERVICE); + if (schSCManager == NULL) + { + printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + // 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) + { + printf("CreateService failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + printf("%s is installed.\n", pszServiceName); + + // Centralized cleanup for all allocated resources. + FreeHandles(schSCManager, schService); +} + +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) + { + printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + // Open the service with delete, stop, and query status permissions + schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP | + SERVICE_QUERY_STATUS | DELETE); + if (schService == NULL) + { + printf("OpenService failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + // Try to stop the service + if (ControlService(schService, SERVICE_CONTROL_STOP, &ssSvcStatus)) + { + printf("Stopping %s.", pszServiceName); + Sleep(1000); + + while (QueryServiceStatus(schService, &ssSvcStatus)) + { + if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) + { + printf("."); + Sleep(1000); + } + else break; + } + + if (ssSvcStatus.dwCurrentState == SERVICE_STOPPED) + { + printf("\n%s is stopped.\n", pszServiceName); + } + else + { + printf("\n%s failed to stop.\n", pszServiceName); + } + } + + // Now remove the service by calling DeleteService. + if (!DeleteService(schService)) + { + printf("DeleteService failed w/err 0x%08lx\n", GetLastError()); + FreeHandles(schSCManager, schService); + return; + } + + printf("%s is removed.\n", pszServiceName); + + // Centralized cleanup for all allocated resources. + FreeHandles(schSCManager, schService); +} + +void service_control(int isDaemon) +{ + std::string serviceControl = i2p::util::config::GetArg("-service", "none"); + if (serviceControl == "install") + { + InstallService( + SERVICE_NAME, // Name of service + SERVICE_DISPLAY_NAME, // Name to display + SERVICE_START_TYPE, // Service start type + SERVICE_DEPENDENCIES, // Dependencies + SERVICE_ACCOUNT, // Service running account + SERVICE_PASSWORD // Password of the account + ); + exit(0); + } + else if (serviceControl == "remove") + { + UninstallService(SERVICE_NAME); + exit(0); + } + else if (serviceControl != "none") + { + printf(" --service=install to install the service.\n"); + printf(" --service=remove to remove the service.\n"); + exit(0); + } + else if (isDaemon) + { + std::string logfile = i2p::util::filesystem::GetDataDir().string(); + logfile.append("\\debug.log"); + FILE* openResult = freopen(logfile.c_str(), "a", stdout); + if (!openResult) + { + exit(-17); + } + LogPrint("Service logging enabled."); + I2PService service(SERVICE_NAME); + if (!I2PService::Run(service)) + { + LogPrint("Service failed to run w/err 0x%08lx\n", GetLastError()); + } + exit(0); + } +} \ No newline at end of file diff --git a/Win32/Win32Service.h b/Win32/Win32Service.h new file mode 100644 index 00000000..5e8f89b4 --- /dev/null +++ b/Win32/Win32Service.h @@ -0,0 +1,89 @@ +#ifndef WIN_32_SERVICE_H__ +#define WIN_32_SERVICE_H__ + +#include "../HTTPServer.h" +#include "../HTTPProxy.h" +#include +#define WIN32_LEAN_AND_MEAN +#include + + +#ifdef _WIN32 +// Internal name of the service +#define SERVICE_NAME "i2pService" + +// Displayed name of the service +#define SERVICE_DISPLAY_NAME "i2p router service" + +// Service start options. +#define SERVICE_START_TYPE SERVICE_DEMAND_START + +// List of service dependencies - "dep1\0dep2\0\0" +#define SERVICE_DEPENDENCIES "" + +// The name of the account under which the service should run +#define SERVICE_ACCOUNT "NT AUTHORITY\\LocalService" + +// The password to the service account name +#define SERVICE_PASSWORD NULL +#endif + + +class I2PService +{ +public: + + I2PService(PSTR pszServiceName, + BOOL fCanStop = TRUE, + BOOL fCanShutdown = TRUE, + BOOL fCanPauseContinue = FALSE); + + virtual ~I2PService(void); + + static BOOL Run(I2PService &service); + void Stop(); + +protected: + + virtual void OnStart(DWORD dwArgc, PSTR *pszArgv); + virtual void OnStop(); + virtual void OnPause(); + virtual void OnContinue(); + virtual void OnShutdown(); + void SetServiceStatus(DWORD dwCurrentState, + DWORD dwWin32ExitCode = NO_ERROR, + DWORD dwWaitHint = 0); + +private: + + static void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArgv); + static void WINAPI ServiceCtrlHandler(DWORD dwCtrl); + void WorkerThread(); + void Start(DWORD dwArgc, PSTR *pszArgv); + void Pause(); + void Continue(); + void Shutdown(); + static I2PService* s_service; + PSTR m_name; + SERVICE_STATUS m_status; + SERVICE_STATUS_HANDLE m_statusHandle; + + BOOL m_fStopping; + HANDLE m_hStoppedEvent; + i2p::util::HTTPServer* _httpServer; + i2p::proxy::HTTPProxy* _httpProxy; + std::thread* _worker; +}; + +void InstallService(PSTR pszServiceName, + PSTR pszDisplayName, + DWORD dwStartType, + PSTR pszDependencies, + PSTR pszAccount, + PSTR pszPassword); + +void UninstallService(PSTR pszServiceName); + +void service_control(int isDaemon); + +#endif // WIN_32_SERVICE_H__ \ No newline at end of file diff --git a/Win32/i2pd.vcxproj b/Win32/i2pd.vcxproj index 87ee6151..64f9f574 100644 --- a/Win32/i2pd.vcxproj +++ b/Win32/i2pd.vcxproj @@ -37,6 +37,7 @@ + @@ -71,6 +72,7 @@ + {930568EC-31C9-406A-AD1C-9636DF5D8FAA} @@ -136,6 +138,8 @@ true true cryptlib.lib;%(AdditionalDependencies) + $(ProjectDir)$(TargetName)$(TargetExt) + RequireAdministrator diff --git a/Win32/i2pd.vcxproj.filters b/Win32/i2pd.vcxproj.filters index c6e9a980..38e0e9e0 100644 --- a/Win32/i2pd.vcxproj.filters +++ b/Win32/i2pd.vcxproj.filters @@ -13,6 +13,9 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {a880a08c-16b8-4243-82ea-6bfc63bb7dab} + @@ -93,6 +96,9 @@ Source Files + + Win32 + @@ -191,5 +197,8 @@ Header Files + + Win32 + \ No newline at end of file diff --git a/Win32/install_service.bat b/Win32/install_service.bat new file mode 100644 index 00000000..b03c11b6 --- /dev/null +++ b/Win32/install_service.bat @@ -0,0 +1 @@ +i2pd --service=install \ No newline at end of file diff --git a/Win32/uninstall_service.bat b/Win32/uninstall_service.bat new file mode 100644 index 00000000..0289c24a --- /dev/null +++ b/Win32/uninstall_service.bat @@ -0,0 +1 @@ +i2pd --service=remove \ No newline at end of file diff --git a/i2p.cpp b/i2p.cpp index cc2e5bcf..ddb0d219 100644 --- a/i2p.cpp +++ b/i2p.cpp @@ -11,6 +11,8 @@ #include #include #include +#else +#include "./Win32/Win32Service.h" #endif #include "Log.h" @@ -71,6 +73,9 @@ int main( int argc, char* argv[] ) setlocale(LC_ALL, "Russian"); #endif +#ifdef _WIN32 + service_control(isDaemon); +#endif LogPrint("\n\n\n\ni2pd starting\n"); LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string()); @@ -168,6 +173,7 @@ int main( int argc, char* argv[] ) } LogPrint("Shutdown started."); + httpProxy.Stop (); i2p::stream::StopStreaming (); i2p::garlic::routing.Stop (); i2p::tunnel::tunnels.Stop ();