/* * Copyright (c) 2013-2020, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree */ #ifdef _WIN32 #define _CRT_SECURE_NO_WARNINGS // to use freopen #endif #include "Win32Service.h" #include //#include #include #include "Daemon.h" #include "Log.h" I2PService *I2PService::s_service = NULL; BOOL I2PService::isService() { BOOL bIsService = FALSE; HWINSTA hWinStation = GetProcessWindowStation(); if (hWinStation != NULL) { USEROBJECTFLAGS uof = { 0 }; if (GetUserObjectInformation(hWinStation, UOI_FLAGS, &uof, sizeof(USEROBJECTFLAGS), NULL) && ((uof.dwFlags & WSF_VISIBLE) == 0)) { bIsService = TRUE; } } return bIsService; } 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) { m_name = (pszServiceName == NULL) ? (PSTR)"" : 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(eLogError, "Win32Service Start", dwError); SetServiceStatus(SERVICE_STOPPED, dwError); } catch (...) { LogPrint(eLogError, "Win32Service failed to start.", EVENTLOG_ERROR_TYPE); SetServiceStatus(SERVICE_STOPPED); } } void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv) { LogPrint(eLogInfo, "Win32Service in OnStart", EVENTLOG_INFORMATION_TYPE); Daemon.start(); //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)); _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(eLogInfo, "Win32Service Stop", dwError); SetServiceStatus(dwOriginalState); } catch (...) { LogPrint(eLogError, "Win32Service failed to stop.", EVENTLOG_ERROR_TYPE); SetServiceStatus(dwOriginalState); } } void I2PService::OnStop() { // Log a service stop message to the Application log. LogPrint(eLogInfo, "Win32Service in OnStop", EVENTLOG_INFORMATION_TYPE); Daemon.stop(); 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(eLogError, "Win32Service Pause", dwError); SetServiceStatus(SERVICE_RUNNING); } catch (...) { LogPrint(eLogError, "Win32Service 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(eLogError, "Win32Service Continue", dwError); SetServiceStatus(SERVICE_PAUSED); } catch (...) { LogPrint(eLogError, "Win32Service failed to resume.", EVENTLOG_ERROR_TYPE); SetServiceStatus(SERVICE_PAUSED); } } void I2PService::OnContinue() { } void I2PService::Shutdown() { try { OnShutdown(); SetServiceStatus(SERVICE_STOPPED); } catch (DWORD dwError) { LogPrint(eLogError, "Win32Service Shutdown", dwError); } catch (...) { LogPrint(eLogError, "Win32Service 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(PCSTR pszServiceName, PCSTR pszDisplayName, DWORD dwStartType, PCSTR pszDependencies, PCSTR pszAccount, PCSTR pszPassword) { printf("Try to install Win32Service (%s).\n", pszServiceName); 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; } char SvcOpt[] = " --daemon"; strncat(szPath, SvcOpt, strlen(SvcOpt)); // 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("Win32Service is installed as %s.\n", pszServiceName); // Centralized cleanup for all allocated resources. FreeHandles(schSCManager, schService); } void UninstallService(PCSTR pszServiceName) { printf("Try to uninstall Win32Service (%s).\n", 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.\n", 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); }