ntservice.cpp 18 KB

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