Win32Service.cpp
1 /* 2 * Copyright (c) 2013-2022, The PurpleI2P Project 3 * 4 * This file is part of Purple i2pd project and licensed under BSD3 5 * 6 * See full license text in LICENSE file at top of project tree 7 */ 8 9 #include "Win32Service.h" 10 #include <assert.h> 11 #include <windows.h> 12 13 #include "Daemon.h" 14 #include "Log.h" 15 16 I2PService *I2PService::s_service = NULL; 17 18 BOOL I2PService::isService() 19 { 20 BOOL bIsService = FALSE; 21 HWINSTA hWinStation = GetProcessWindowStation(); 22 if (hWinStation != NULL) 23 { 24 USEROBJECTFLAGS uof = { FALSE, FALSE, 0 }; 25 if (GetUserObjectInformation(hWinStation, UOI_FLAGS, &uof, sizeof(USEROBJECTFLAGS), NULL) && ((uof.dwFlags & WSF_VISIBLE) == 0)) 26 { 27 bIsService = TRUE; 28 } 29 } 30 return bIsService; 31 } 32 33 BOOL I2PService::Run(I2PService &service) 34 { 35 s_service = &service; 36 SERVICE_TABLE_ENTRY serviceTable[] = 37 { 38 { service.m_name, ServiceMain }, 39 { NULL, NULL } 40 }; 41 return StartServiceCtrlDispatcher(serviceTable); 42 } 43 44 void WINAPI I2PService::ServiceMain(DWORD dwArgc, PSTR *pszArgv) 45 { 46 assert(s_service != NULL); 47 s_service->m_statusHandle = RegisterServiceCtrlHandler( 48 s_service->m_name, ServiceCtrlHandler); 49 if (s_service->m_statusHandle == NULL) 50 { 51 throw GetLastError(); 52 } 53 s_service->Start(dwArgc, pszArgv); 54 } 55 56 57 void WINAPI I2PService::ServiceCtrlHandler(DWORD dwCtrl) 58 { 59 switch (dwCtrl) 60 { 61 case SERVICE_CONTROL_STOP: s_service->Stop(); break; 62 case SERVICE_CONTROL_PAUSE: s_service->Pause(); break; 63 case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break; 64 case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break; 65 case SERVICE_CONTROL_INTERROGATE: break; 66 default: break; 67 } 68 } 69 70 I2PService::I2PService(PSTR pszServiceName, 71 BOOL fCanStop, 72 BOOL fCanShutdown, 73 BOOL fCanPauseContinue) 74 { 75 m_name = (pszServiceName == NULL) ? (PSTR)"" : pszServiceName; 76 m_statusHandle = NULL; 77 m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 78 m_status.dwCurrentState = SERVICE_START_PENDING; 79 80 DWORD dwControlsAccepted = 0; 81 if (fCanStop) 82 dwControlsAccepted |= SERVICE_ACCEPT_STOP; 83 if (fCanShutdown) 84 dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN; 85 if (fCanPauseContinue) 86 dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE; 87 88 m_status.dwControlsAccepted = dwControlsAccepted; 89 m_status.dwWin32ExitCode = NO_ERROR; 90 m_status.dwServiceSpecificExitCode = 0; 91 m_status.dwCheckPoint = 0; 92 m_status.dwWaitHint = 0; 93 m_fStopping = FALSE; 94 // Create a manual-reset event that is not signaled at first to indicate 95 // the stopped signal of the service. 96 m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 97 if (m_hStoppedEvent == NULL) 98 { 99 throw GetLastError(); 100 } 101 } 102 103 I2PService::~I2PService(void) 104 { 105 if (m_hStoppedEvent) 106 { 107 CloseHandle(m_hStoppedEvent); 108 m_hStoppedEvent = NULL; 109 } 110 } 111 112 void I2PService::Start(DWORD dwArgc, PSTR *pszArgv) 113 { 114 try 115 { 116 SetServiceStatus(SERVICE_START_PENDING); 117 OnStart(dwArgc, pszArgv); 118 SetServiceStatus(SERVICE_RUNNING); 119 } 120 catch (DWORD dwError) 121 { 122 LogPrint(eLogCritical, "Win32Service: Start error: ", dwError); 123 SetServiceStatus(SERVICE_STOPPED, dwError); 124 } 125 catch (...) 126 { 127 LogPrint(eLogCritical, "Win32Service: failed to start: ", EVENTLOG_ERROR_TYPE); 128 SetServiceStatus(SERVICE_STOPPED); 129 } 130 } 131 132 void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv) 133 { 134 LogPrint(eLogInfo, "Win32Service: in OnStart (", EVENTLOG_INFORMATION_TYPE, ")"); 135 Daemon.start(); 136 _worker = new std::thread(std::bind(&I2PService::WorkerThread, this)); 137 } 138 139 void I2PService::WorkerThread() 140 { 141 while (!m_fStopping) 142 { 143 ::Sleep(1000); // Simulate some lengthy operations. 144 } 145 // Signal the stopped event. 146 SetEvent(m_hStoppedEvent); 147 } 148 149 void I2PService::Stop() 150 { 151 DWORD dwOriginalState = m_status.dwCurrentState; 152 try 153 { 154 SetServiceStatus(SERVICE_STOP_PENDING); 155 OnStop(); 156 SetServiceStatus(SERVICE_STOPPED); 157 } 158 catch (DWORD dwError) 159 { 160 LogPrint(eLogInfo, "Win32Service: Stop error: ", dwError); 161 SetServiceStatus(dwOriginalState); 162 } 163 catch (...) 164 { 165 LogPrint(eLogCritical, "Win32Service: Failed to stop: ", EVENTLOG_ERROR_TYPE); 166 SetServiceStatus(dwOriginalState); 167 } 168 } 169 170 void I2PService::OnStop() 171 { 172 // Log a service stop message to the Application log. 173 LogPrint(eLogInfo, "Win32Service: in OnStop (", EVENTLOG_INFORMATION_TYPE, ")"); 174 Daemon.stop(); 175 m_fStopping = TRUE; 176 if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0) 177 { 178 throw GetLastError(); 179 } 180 _worker->join(); 181 delete _worker; 182 } 183 184 void I2PService::Pause() 185 { 186 try 187 { 188 SetServiceStatus(SERVICE_PAUSE_PENDING); 189 OnPause(); 190 SetServiceStatus(SERVICE_PAUSED); 191 } 192 catch (DWORD dwError) 193 { 194 LogPrint(eLogCritical, "Win32Service: Pause error: ", dwError); 195 SetServiceStatus(SERVICE_RUNNING); 196 } 197 catch (...) 198 { 199 LogPrint(eLogCritical, "Win32Service: Failed to pause: ", EVENTLOG_ERROR_TYPE); 200 SetServiceStatus(SERVICE_RUNNING); 201 } 202 } 203 204 void I2PService::OnPause() 205 { 206 } 207 208 void I2PService::Continue() 209 { 210 try 211 { 212 SetServiceStatus(SERVICE_CONTINUE_PENDING); 213 OnContinue(); 214 SetServiceStatus(SERVICE_RUNNING); 215 } 216 catch (DWORD dwError) 217 { 218 LogPrint(eLogCritical, "Win32Service: Continue error: ", dwError); 219 SetServiceStatus(SERVICE_PAUSED); 220 } 221 catch (...) 222 { 223 LogPrint(eLogCritical, "Win32Service: Failed to resume: ", EVENTLOG_ERROR_TYPE); 224 SetServiceStatus(SERVICE_PAUSED); 225 } 226 } 227 228 void I2PService::OnContinue() 229 { 230 } 231 232 void I2PService::Shutdown() 233 { 234 try 235 { 236 OnShutdown(); 237 SetServiceStatus(SERVICE_STOPPED); 238 } 239 catch (DWORD dwError) 240 { 241 LogPrint(eLogCritical, "Win32Service: Shutdown error: ", dwError); 242 } 243 catch (...) 244 { 245 LogPrint(eLogCritical, "Win32Service: Failed to shut down: ", EVENTLOG_ERROR_TYPE); 246 } 247 } 248 249 void I2PService::OnShutdown() 250 { 251 } 252 253 void I2PService::SetServiceStatus(DWORD dwCurrentState, 254 DWORD dwWin32ExitCode, 255 DWORD dwWaitHint) 256 { 257 static DWORD dwCheckPoint = 1; 258 m_status.dwCurrentState = dwCurrentState; 259 m_status.dwWin32ExitCode = dwWin32ExitCode; 260 m_status.dwWaitHint = dwWaitHint; 261 m_status.dwCheckPoint = 262 ((dwCurrentState == SERVICE_RUNNING) || 263 (dwCurrentState == SERVICE_STOPPED)) ? 264 0 : dwCheckPoint++; 265 266 ::SetServiceStatus(m_statusHandle, &m_status); 267 } 268 269 //***************************************************************************** 270 271 void FreeHandles(SC_HANDLE schSCManager, SC_HANDLE schService) 272 { 273 if (schSCManager) 274 { 275 CloseServiceHandle(schSCManager); 276 schSCManager = NULL; 277 } 278 if (schService) 279 { 280 CloseServiceHandle(schService); 281 schService = NULL; 282 } 283 }