Pol  Revision:4b29d2b
NTService.cpp
Go to the documentation of this file.
1 
9 #include "NTService.h"
10 #include "ntservmsg.h" // Event message ids
11 #include <stdio.h>
12 
13 // Custom Controls Below Here
14 #define SERVICE_CONTROL_BEEP 0x000004B0
15 #define SERVICE_CONTROL_STOP_POL 0x000004B1
16 namespace Pol
17 {
18 namespace Clib
19 {
20 // static variables
21 CNTService* CNTService::m_pThis = NULL;
22 
23 CNTService::CNTService( const char* szServiceName )
24 {
25  // copy the address of the current object so we can access it from
26  // the static member callback functions.
27  // WARNING: This limits the application to only one CNTService object.
28  m_pThis = this;
29 
30  // Set the default service name and version
31  strncpy( m_szServiceName, szServiceName, sizeof( m_szServiceName ) - 1 );
32  m_iMajorVersion = 1;
33  m_iMinorVersion = 0;
34  m_hEventSource = NULL;
35 
36  // set up the initial service status
37  m_hServiceStatus = NULL;
38  m_Status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
39  m_Status.dwCurrentState = SERVICE_STOPPED;
40  m_Status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
41  m_Status.dwWin32ExitCode = 0;
42  m_Status.dwServiceSpecificExitCode = 0;
43  m_Status.dwCheckPoint = 0;
44  m_Status.dwWaitHint = 0;
45  m_bIsRunning = FALSE;
46 }
47 
49 {
50  DebugMsg( "CNTService::~CNTService()" );
51  if ( m_hEventSource )
52  {
53  ::DeregisterEventSource( m_hEventSource );
54  }
55 }
56 
58 // Default command line argument parsing
59 
60 // Returns TRUE if it found an arg it recognised, FALSE if not
61 // Note: processing some arguments causes output to stdout to be generated.
62 BOOL CNTService::ParseStandardArgs( int argc, char* argv[] )
63 {
64  // See if we have any command line args we recognise
65  if ( argc <= 1 )
66  return FALSE;
67 
68  if ( _stricmp( argv[1], "-v" ) == 0 )
69  {
70  // Spit out version info
71  printf( "%s Version %d.%d\n", m_szServiceName, m_iMajorVersion, m_iMinorVersion );
72  printf( "The service is %s installed\n", IsInstalled() ? "currently" : "not" );
73  return TRUE; // say we processed the argument
74  }
75  else if ( _stricmp( argv[1], "-i" ) == 0 )
76  {
77  // Request to install.
78  if ( IsInstalled() )
79  {
80  printf( "%s is already installed\n", m_szServiceName );
81  }
82  else
83  {
84  // Try and install the copy that's running
85  if ( Install() )
86  {
87  printf( "%s installed\n", m_szServiceName );
88  }
89  else
90  {
91  printf( "%s failed to install. Error %u\n", m_szServiceName, GetLastError() );
92  }
93  }
94  return TRUE; // say we processed the argument
95  }
96  else if ( _stricmp( argv[1], "-u" ) == 0 )
97  {
98  // Request to uninstall.
99  if ( !IsInstalled() )
100  {
101  printf( "%s is not installed\n", m_szServiceName );
102  }
103  else
104  {
105  // Try and remove the copy that's installed
106  if ( Uninstall() )
107  {
108  // Get the executable file path
109  char szFilePath[MAX_PATH];
110  ::GetModuleFileName( NULL, szFilePath, sizeof( szFilePath ) );
111  printf( "%s removed. (You must delete the file (%s) yourself.)\n", m_szServiceName,
112  szFilePath );
113  }
114  else
115  {
116  printf( "Could not remove %s. Error %u\n", m_szServiceName, GetLastError() );
117  }
118  }
119  return TRUE; // say we processed the argument
120  }
121  else if ( _stricmp( argv[1], "-s" ) == 0 )
122  {
123  // run as a service
124  printf( "Running as a service.\n" );
125 
126  if ( !StartService() )
127  {
128  printf( "Unable to run as a service.\n" );
129  }
130 
131  return TRUE;
132  }
133 
134 
135  // Don't recognise the args
136  return FALSE;
137 }
138 
140 // Install/uninstall routines
141 
142 // Test if the service is currently installed
144 {
145  BOOL bResult = FALSE;
146 
147  // Open the Service Control Manager
148  SC_HANDLE hSCM = ::OpenSCManager( NULL, // local machine
149  NULL, // ServicesActive database
150  SC_MANAGER_ALL_ACCESS ); // full access
151  if ( hSCM )
152  {
153  // Try to open the service
154  SC_HANDLE hService = ::OpenService( hSCM, m_szServiceName, SERVICE_QUERY_CONFIG );
155  if ( hService )
156  {
157  bResult = TRUE;
158  ::CloseServiceHandle( hService );
159  }
160 
161  ::CloseServiceHandle( hSCM );
162  }
163 
164  return bResult;
165 }
166 
168 {
169  // Open the Service Control Manager
170  SC_HANDLE hSCM = ::OpenSCManager( NULL, // local machine
171  NULL, // ServicesActive database
172  SC_MANAGER_ALL_ACCESS ); // full access
173  if ( !hSCM )
174  return FALSE;
175 
176  // Get the executable file path
177  char szFilePath[MAX_PATH];
178  ::GetModuleFileName( NULL, szFilePath, sizeof( szFilePath ) );
179  strcat( szFilePath, " -s" );
180 
181  // Create the service
182  SC_HANDLE hService =
183  ::CreateService( hSCM, m_szServiceName, m_szServiceName, SERVICE_ALL_ACCESS,
184  SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
185  SERVICE_DEMAND_START, // start condition
186  SERVICE_ERROR_NORMAL, szFilePath, NULL, NULL, NULL, NULL, NULL );
187  if ( !hService )
188  {
189  ::CloseServiceHandle( hSCM );
190  return FALSE;
191  }
192 
193  // make registry entries to support logging messages
194  // Add the source name as a subkey under the Application
195  // key in the EventLog service portion of the registry.
196  char szKey[256];
197  HKEY hKey = NULL;
198  strcpy( szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\" );
199  strcat( szKey, m_szServiceName );
200  if (::RegCreateKey( HKEY_LOCAL_MACHINE, szKey, &hKey ) != ERROR_SUCCESS )
201  {
202  ::CloseServiceHandle( hService );
203  ::CloseServiceHandle( hSCM );
204  return FALSE;
205  }
206 
207  // Add the Event ID message-file name to the 'EventMessageFile' subkey.
208  ::RegSetValueEx( hKey, "EventMessageFile", 0, REG_EXPAND_SZ, (CONST BYTE*)szFilePath,
209  static_cast<DWORD>( strlen( szFilePath ) + 1 ) );
210 
211  // Set the supported types flags.
212  DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
213  ::RegSetValueEx( hKey, "TypesSupported", 0, REG_DWORD, (CONST BYTE*)&dwData, sizeof( DWORD ) );
214  ::RegCloseKey( hKey );
215 
216  LogEvent( EVENTLOG_INFORMATION_TYPE, EVMSG_INSTALLED, m_szServiceName );
217 
218  // tidy up
219  ::CloseServiceHandle( hService );
220  ::CloseServiceHandle( hSCM );
221  return TRUE;
222 }
223 
225 {
226  // Open the Service Control Manager
227  SC_HANDLE hSCM = ::OpenSCManager( NULL, // local machine
228  NULL, // ServicesActive database
229  SC_MANAGER_ALL_ACCESS ); // full access
230  if ( !hSCM )
231  return FALSE;
232 
233  BOOL bResult = FALSE;
234  SC_HANDLE hService = ::OpenService( hSCM, m_szServiceName, DELETE );
235  if ( hService )
236  {
237  if (::DeleteService( hService ) )
238  {
239  LogEvent( EVENTLOG_INFORMATION_TYPE, EVMSG_REMOVED, m_szServiceName );
240  bResult = TRUE;
241  }
242  else
243  {
244  LogEvent( EVENTLOG_ERROR_TYPE, EVMSG_NOTREMOVED, m_szServiceName );
245  }
246  ::CloseServiceHandle( hService );
247  }
248 
249  ::CloseServiceHandle( hSCM );
250  return bResult;
251 }
252 
254 // Logging functions
255 
256 // This function makes an entry into the application event log
257 void CNTService::LogEvent( WORD wType, DWORD dwID, const char* pszS1, const char* pszS2,
258  const char* pszS3 )
259 {
260  const char* ps[3];
261  ps[0] = pszS1;
262  ps[1] = pszS2;
263  ps[2] = pszS3;
264 
265  WORD iStr = 0;
266  for ( WORD i = 0; i < 3; i++ )
267  {
268  if ( ps[i] != NULL )
269  iStr++;
270  }
271 
272  // Check the event source has been registered and if
273  // not then register it now
274  if ( !m_hEventSource )
275  {
276  m_hEventSource = ::RegisterEventSource( NULL, // local machine
277  m_szServiceName ); // source name
278  }
279 
280  if ( m_hEventSource )
281  {
282  ::ReportEvent( m_hEventSource, wType, 0, dwID,
283  NULL, // sid
284  iStr, 0, ps, NULL );
285  }
286 }
287 
289 // Service startup and registration
290 
292 {
293  SERVICE_TABLE_ENTRY st[] = {{m_szServiceName, ServiceMain}, {NULL, NULL}};
294 
295  DebugMsg( "Calling StartServiceCtrlDispatcher()" );
296  BOOL b = ::StartServiceCtrlDispatcher( st );
297  if ( !b )
298  {
299  DWORD rc = GetLastError();
300  DebugMsg( "StartServiceCtrlDispatcher returned false: %d", rc );
301  }
302  DebugMsg( "Returned from StartServiceCtrlDispatcher()" );
303  return b;
304 }
305 
306 // static member function (callback)
307 void CNTService::ServiceMain( DWORD /*dwArgc*/, LPTSTR* /*lpszArgv*/ )
308 {
309  // Get a pointer to the C++ object
310  CNTService* pService = m_pThis;
311 
312  pService->DebugMsg( "Entering CNTService::ServiceMain()" );
313  // Register the control request handler
314  pService->m_Status.dwCurrentState = SERVICE_START_PENDING;
315  pService->m_hServiceStatus = RegisterServiceCtrlHandler( pService->m_szServiceName, Handler );
316  if ( pService->m_hServiceStatus == NULL )
317  {
318  pService->LogEvent( EVENTLOG_ERROR_TYPE, EVMSG_CTRLHANDLERNOTINSTALLED );
319  return;
320  }
321 
322  // Start the initialisation
323  if ( pService->Initialize() )
324  {
325  // Do the real work.
326  // When the Run function returns, the service has stopped.
327  pService->m_bIsRunning = TRUE;
328  pService->m_Status.dwWin32ExitCode = 0;
329  pService->m_Status.dwCheckPoint = 0;
330  pService->m_Status.dwWaitHint = 0;
331  pService->Run();
332  }
333 
334  // Tell the service manager we are stopped
335  pService->SetStatus( SERVICE_STOPPED );
336 
337  pService->DebugMsg( "Leaving CNTService::ServiceMain()" );
338 }
339 
341 // status functions
342 
344 {
345  DebugMsg( "CNTService::SetStatus(%lu, %lu)", m_hServiceStatus, dwState );
346  m_Status.dwCurrentState = dwState;
347  ::SetServiceStatus( m_hServiceStatus, &m_Status );
348 }
349 
351 // Service initialization
352 
354 {
355  DebugMsg( "Entering CNTService::Initialize()" );
356 
357  // Start the initialization
358  SetStatus( SERVICE_START_PENDING );
359 
360  // Perform the actual initialization
361  BOOL bResult = OnInit();
362 
363  // Set final state
364  m_Status.dwWin32ExitCode = GetLastError();
365  m_Status.dwCheckPoint = 0;
366  m_Status.dwWaitHint = 0;
367  if ( !bResult )
368  {
369  LogEvent( EVENTLOG_ERROR_TYPE, EVMSG_FAILEDINIT );
370  SetStatus( SERVICE_STOPPED );
371  return FALSE;
372  }
373 
374  LogEvent( EVENTLOG_INFORMATION_TYPE, EVMSG_STARTED );
375  SetStatus( SERVICE_RUNNING );
376 
377  DebugMsg( "Leaving CNTService::Initialize()" );
378  return TRUE;
379 }
380 
382 // main function to do the real work of the service
383 
384 // This function performs the main work of the service.
385 // When this function returns the service has stopped.
387 {
388  DebugMsg( "Entering CNTService::Run()" );
389 
390  while ( m_bIsRunning )
391  {
392  DebugMsg( "Sleeping..." );
393  Sleep( 5000 );
394  }
395 
396  // nothing more to do
397  DebugMsg( "Leaving CNTService::Run()" );
398 }
399 
401 // Control request handlers
402 
403 // static member function (callback) to handle commands from the
404 // service control manager
405 void CNTService::Handler( DWORD dwOpcode )
406 {
407  // Get a pointer to the object
408  CNTService* pService = m_pThis;
409 
410  pService->DebugMsg( "CNTService::Handler(%lu)", dwOpcode );
411  switch ( dwOpcode )
412  {
413  case SERVICE_CONTROL_STOP: // 1
414  pService->SetStatus( SERVICE_STOP_PENDING );
415  pService->OnStop();
416  pService->m_bIsRunning = FALSE;
417  pService->LogEvent( EVENTLOG_INFORMATION_TYPE, EVMSG_STOPPED );
418  break;
419 
420  case SERVICE_CONTROL_PAUSE: // 2
421  pService->OnPause();
422  break;
423 
424  case SERVICE_CONTROL_CONTINUE: // 3
425  pService->OnContinue();
426  break;
427 
428  case SERVICE_CONTROL_INTERROGATE: // 4
429  pService->OnInterrogate();
430  break;
431 
432  case SERVICE_CONTROL_SHUTDOWN: // 5
433  pService->OnShutdown();
434  break;
435 
436  default:
437  if ( dwOpcode >= SERVICE_CONTROL_USER )
438  {
439  if ( !pService->OnUserControl( dwOpcode ) )
440  {
441  pService->LogEvent( EVENTLOG_ERROR_TYPE, EVMSG_BADREQUEST );
442  }
443  }
444  else
445  {
446  pService->LogEvent( EVENTLOG_ERROR_TYPE, EVMSG_BADREQUEST );
447  }
448  break;
449  }
450 
451  // Report current status
452  pService->DebugMsg( "Updating status (%lu, %lu)", pService->m_hServiceStatus,
453  pService->m_Status.dwCurrentState );
454  ::SetServiceStatus( pService->m_hServiceStatus, &pService->m_Status );
455 }
456 
457 // Called when the service is first initialized
459 {
460  DebugMsg( "CNTService::OnInit()" );
461  return TRUE;
462 }
463 
464 // Called when the service control manager wants to stop the service
466 {
467  DebugMsg( "CNTService::OnStop()" );
468 }
469 
470 // called when the service is interrogated
472 {
473  DebugMsg( "CNTService::OnInterrogate()" );
474 }
475 
476 // called when the service is paused
478 {
479  DebugMsg( "CNTService::OnPause()" );
480 }
481 
482 // called when the service is continued
484 {
485  DebugMsg( "CNTService::OnContinue()" );
486 }
487 
488 // called when the service is shut down
490 {
491  DebugMsg( "CNTService::OnShutdown()" );
492 }
493 
494 // called when the service gets a user control message
496 {
497  CNTService* pService = m_pThis;
498 
499  switch ( dwOpcode )
500  {
501  case SERVICE_CONTROL_BEEP: // 1200
502  Beep( 1200, 200 );
503  break;
504  case SERVICE_CONTROL_STOP_POL: // 1201
505  pService->SetStatus( SERVICE_STOP_PENDING );
506  pService->OnStop();
507  pService->m_bIsRunning = FALSE;
508  pService->LogEvent( EVENTLOG_INFORMATION_TYPE, EVMSG_STOPPED );
509  break;
510  default:
511  DebugMsg( "CNTService::OnUserControl(%8.8lXH)", dwOpcode );
512  return FALSE; // say not handled
513  break;
514  }
515 
516  // if it makes it this far, something's wrong anyway.
517  // left in for contrivance
518  DebugMsg( "CNTService::OnUserControl(%8.8lXH)", dwOpcode );
519  return FALSE; // say not handled
520 }
521 
522 
524 // Debugging support
525 
526 void CNTService::DebugMsg( const char* pszFormat, ... )
527 {
528  char buf[1024];
529  sprintf( buf, "[%s](%lu): ", m_szServiceName, GetCurrentThreadId() );
530  va_list arglist;
531  va_start( arglist, pszFormat );
532  vsprintf( &buf[strlen( buf )], pszFormat, arglist );
533  va_end( arglist );
534  strcat( buf, "\n" );
535  OutputDebugString( buf );
536 }
537 }
538 }
char m_szServiceName[64]
Definition: NTService.h:48
SERVICE_STATUS m_Status
Definition: NTService.h:52
virtual void Run()
Definition: NTService.cpp:386
virtual void OnInterrogate()
Definition: NTService.cpp:471
BOOL ParseStandardArgs(int argc, char *argv[])
Definition: NTService.cpp:62
#define EVMSG_STOPPED
Definition: ntservmsg.h:116
virtual void OnPause()
Definition: NTService.cpp:477
typedef DWORD(WINAPI *__SymGetOptions)(VOID)
void SetStatus(DWORD dwState)
Definition: NTService.cpp:343
#define EVMSG_STARTED
Definition: ntservmsg.h:89
#define EVMSG_REMOVED
Definition: ntservmsg.h:53
virtual void OnContinue()
Definition: NTService.cpp:483
#define EVMSG_FAILEDINIT
Definition: ntservmsg.h:80
void DebugMsg(const char *pszFormat,...)
Definition: NTService.cpp:526
#define EVMSG_BADREQUEST
Definition: ntservmsg.h:98
virtual void OnStop()
Definition: NTService.cpp:465
virtual BOOL OnInit()
Definition: NTService.cpp:458
typedef BOOL(WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess
SERVICE_STATUS_HANDLE m_hServiceStatus
Definition: NTService.h:51
void LogEvent(WORD wType, DWORD dwID, const char *pszS1=NULL, const char *pszS2=NULL, const char *pszS3=NULL)
Definition: NTService.cpp:257
#define EVMSG_NOTREMOVED
Definition: ntservmsg.h:62
static CNTService * m_pThis
Definition: NTService.h:56
CNTService(const char *szServiceName)
Definition: NTService.cpp:23
static void WINAPI Handler(DWORD dwOpcode)
Definition: NTService.cpp:405
#define SERVICE_CONTROL_USER
Definition: NTService.h:14
static void WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
Definition: NTService.cpp:307
virtual BOOL OnUserControl(DWORD dwOpcode)
Definition: NTService.cpp:495
#define SERVICE_CONTROL_BEEP
Definition: NTService.cpp:14
virtual void OnShutdown()
Definition: NTService.cpp:489
#define EVMSG_INSTALLED
Definition: ntservmsg.h:44
#define EVMSG_CTRLHANDLERNOTINSTALLED
Definition: ntservmsg.h:71
Definition: berror.cpp:12
#define SERVICE_CONTROL_STOP_POL
Definition: NTService.cpp:15