/ Win32 / Win32Service.cpp
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  }