ntservice.cpp 19 KB

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