Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

677 lines
18 KiB

  1. /******************************************************************************
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. module.cpp
  5. Abstract:
  6. This file contains the implementation of the CServiceModule class, which is
  7. used to handling service-related routines.
  8. Revision History:
  9. Davide Massarenti (Dmassare) 03/14/2000
  10. created
  11. ******************************************************************************/
  12. #include "stdafx.h"
  13. /////////////////////////////////////////////////////////////////////////////
  14. DWORD dwTimeOut = 16*1000; // time for EXE to be idle before shutting down
  15. const DWORD dwPause = 1000; // time to wait for threads to finish up
  16. CServiceModule _Module;
  17. MPC::NTEvent g_NTEvents;
  18. MPC::FileLog g_ApplicationLog( /*fCacheHandle*/false, /*fUseUnicode*/false );
  19. /////////////////////////////////////////////////////////////////////////////
  20. #ifdef DEBUG
  21. #define DEBUG_REGKEY HC_REGISTRY_HELPSVC L"\\Debug"
  22. #define DEBUG_BREAKONSTART L"BREAKONSTART"
  23. #define DEBUG_TIMEOUT L"TIMEOUT"
  24. void CServiceModule::ReadDebugSettings()
  25. {
  26. __HCP_FUNC_ENTRY( "CServiceModule::ReadDebugSettings" );
  27. HRESULT hr;
  28. MPC::RegKey rkBase;
  29. bool fFound;
  30. __MPC_EXIT_IF_METHOD_FAILS(hr, rkBase.SetRoot( HKEY_LOCAL_MACHINE ));
  31. __MPC_EXIT_IF_METHOD_FAILS(hr, rkBase.Attach ( DEBUG_REGKEY ));
  32. __MPC_EXIT_IF_METHOD_FAILS(hr, rkBase.Exists ( fFound ));
  33. if(fFound)
  34. {
  35. CComVariant vValue;
  36. __MPC_EXIT_IF_METHOD_FAILS(hr, rkBase.get_Value( vValue, fFound, DEBUG_BREAKONSTART ));
  37. if(fFound && vValue.vt == VT_I4)
  38. {
  39. if(vValue.lVal == 1) DebugBreak();
  40. if(vValue.lVal == 2) while(vValue.lVal) ::Sleep( 100 );
  41. }
  42. __MPC_EXIT_IF_METHOD_FAILS(hr, rkBase.get_Value( vValue, fFound, DEBUG_TIMEOUT ));
  43. if(fFound && vValue.vt == VT_I4)
  44. {
  45. dwTimeOut = 1000 * vValue.lVal;
  46. }
  47. }
  48. __HCP_FUNC_CLEANUP;
  49. }
  50. #endif
  51. /////////////////////////////////////////////////////////////////////////////
  52. static const WCHAR s_SvcHost[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost";
  53. static const WCHAR s_Key [] = L"System\\CurrentControlSet\\Services\\%s";
  54. static const WCHAR s_Key2 [] = L"\\Parameters";
  55. static const WCHAR s_Name [] = L"ServiceDll";
  56. static const WCHAR s_Value[] = L"%WINDIR%\\PCHealth\\HelpCtr\\Binaries\\pchsvc.dll";
  57. static HRESULT ServiceHost_Install( LPCWSTR szName, LPCWSTR szGroup )
  58. {
  59. __HCP_FUNC_ENTRY( "ServiceHost_Install" );
  60. HRESULT hr;
  61. {
  62. WCHAR rgRegPath[_MAX_PATH];
  63. StringCchPrintfW( rgRegPath, ARRAYSIZE(rgRegPath), s_Key, szName );
  64. StringCchCatW( rgRegPath, ARRAYSIZE(rgRegPath), s_Key2 );
  65. __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::RegKey_Value_Write( s_Value, rgRegPath, s_Name, HKEY_LOCAL_MACHINE, true ));
  66. }
  67. {
  68. MPC::RegKey rk;
  69. MPC::WStringList lstValue;
  70. bool fFound;
  71. bool fGot = false;
  72. __MPC_EXIT_IF_METHOD_FAILS(hr, rk.SetRoot( HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS ));
  73. __MPC_EXIT_IF_METHOD_FAILS(hr, rk.Attach ( s_SvcHost ));
  74. __MPC_EXIT_IF_METHOD_FAILS(hr, rk.Create ( ));
  75. if(SUCCEEDED(rk.Read( lstValue, fFound, szGroup )))
  76. {
  77. for(MPC::WStringIterConst it = lstValue.begin(); it != lstValue.end(); it++)
  78. {
  79. if(!MPC::StrICmp( *it, szName ))
  80. {
  81. fGot = true;
  82. break;
  83. }
  84. }
  85. }
  86. if(fGot == false)
  87. {
  88. lstValue.push_back( szName );
  89. __MPC_EXIT_IF_METHOD_FAILS(hr, rk.Write( lstValue, szGroup ));
  90. }
  91. }
  92. hr = S_OK;
  93. __HCP_FUNC_CLEANUP;
  94. __HCP_FUNC_EXIT(hr);
  95. }
  96. static HRESULT ServiceHost_Uninstall( LPCWSTR szName, LPCWSTR szGroup )
  97. {
  98. __HCP_FUNC_ENTRY( "ServiceHost_Uninstall" );
  99. HRESULT hr;
  100. {
  101. WCHAR rgRegPath[_MAX_PATH]; StringCchPrintfW( rgRegPath, ARRAYSIZE(rgRegPath), s_Key, szName );
  102. MPC::RegKey rk;
  103. __MPC_EXIT_IF_METHOD_FAILS(hr, rk.SetRoot( HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS ));
  104. __MPC_EXIT_IF_METHOD_FAILS(hr, rk.Attach ( rgRegPath ));
  105. (void)rk.Delete( /*fDeep*/true );
  106. }
  107. {
  108. MPC::RegKey rk;
  109. MPC::WStringList lstValue;
  110. bool fFound;
  111. __MPC_EXIT_IF_METHOD_FAILS(hr, rk.SetRoot( HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS ));
  112. __MPC_EXIT_IF_METHOD_FAILS(hr, rk.Attach ( s_SvcHost ));
  113. if(SUCCEEDED(rk.Read( lstValue, fFound, szGroup )))
  114. {
  115. MPC::WStringIterConst it = lstValue.begin();
  116. bool fGot = false;
  117. while(it != lstValue.end())
  118. {
  119. MPC::WStringIterConst it2 = it++;
  120. if(!MPC::StrICmp( *it2, szName ))
  121. {
  122. lstValue.erase( it2 );
  123. fGot = true;
  124. }
  125. }
  126. if(fGot)
  127. {
  128. __MPC_EXIT_IF_METHOD_FAILS(hr, rk.Write( lstValue, szGroup ));
  129. }
  130. }
  131. }
  132. hr = S_OK;
  133. __HCP_FUNC_CLEANUP;
  134. __HCP_FUNC_EXIT(hr);
  135. }
  136. /////////////////////////////////////////////////////////////////////////////
  137. CServiceModule::CServiceModule()
  138. {
  139. m_hEventShutdown = NULL; // HANDLE m_hEventShutdown;
  140. m_dwThreadID = 0; // DWORD m_dwThreadID;
  141. m_hMonitor = NULL; // HANDLE m_hMonitor;
  142. m_bActivity = FALSE; // BOOL m_bActivity;
  143. //
  144. m_szServiceName = NULL; // LPCWSTR m_szServiceName;
  145. m_iDisplayName = 0; // UINT m_iDisplayName;
  146. m_iDescription = 0; // UINT m_iDescription;
  147. m_hServiceStatus = NULL; // SERVICE_STATUS_HANDLE m_hServiceStatus;
  148. // SERVICE_STATUS m_status;
  149. m_bService = FALSE; // BOOL m_bService;
  150. ::ZeroMemory( &m_status, sizeof( m_status ) );
  151. }
  152. CServiceModule::~CServiceModule()
  153. {
  154. if(m_hEventShutdown) ::CloseHandle( m_hEventShutdown );
  155. if(m_hMonitor ) ::CloseHandle( m_hMonitor );
  156. }
  157. /////////////////////////////////////////////////////////////////////////////
  158. LONG CServiceModule::Lock()
  159. {
  160. LONG lCount = CComModule::Lock();
  161. return lCount;
  162. }
  163. LONG CServiceModule::Unlock()
  164. {
  165. LONG lCount = CComModule::Unlock();
  166. if(lCount == 0)
  167. {
  168. m_bActivity = TRUE;
  169. if(m_hEventShutdown) ::SetEvent( m_hEventShutdown ); // tell monitor that we transitioned to zero
  170. }
  171. return lCount;
  172. }
  173. void CServiceModule::MonitorShutdown()
  174. {
  175. while(1)
  176. {
  177. DWORD dwWait;
  178. m_bActivity = FALSE;
  179. dwWait = ::WaitForSingleObject( m_hEventShutdown, dwTimeOut );
  180. if(dwWait == WAIT_OBJECT_0) continue; // We are alive...
  181. //
  182. // If no activity let's really bail.
  183. //
  184. if(m_bActivity == FALSE && m_nLockCnt <= 0)
  185. {
  186. ::CoSuspendClassObjects();
  187. if(m_bActivity == FALSE && m_nLockCnt <= 0) break;
  188. }
  189. }
  190. ForceShutdown();
  191. }
  192. void CServiceModule::ForceShutdown()
  193. {
  194. //
  195. // Tell process to exit.
  196. //
  197. ::PostThreadMessage( m_dwThreadID, WM_QUIT, 0, 0 );
  198. }
  199. BOOL CServiceModule::StartMonitor()
  200. {
  201. DWORD dwThreadID;
  202. m_hMonitor = ::CreateThread( NULL, 0, _Monitor, this, 0, &dwThreadID );
  203. if(m_hMonitor == NULL) return FALSE;
  204. return TRUE;
  205. }
  206. /////////////////////////////////////////////////////////////////////////////
  207. HRESULT CServiceModule::RegisterServer( BOOL bRegTypeLib, BOOL bService, LPCWSTR szSvcHostGroup )
  208. {
  209. HRESULT hr;
  210. // Remove any previous service since it may point to the incorrect file
  211. Uninstall( szSvcHostGroup );
  212. if(bService)
  213. {
  214. // Create service
  215. Install( szSvcHostGroup );
  216. }
  217. // Add object entries
  218. if(FAILED(hr = CComModule::RegisterServer( TRUE ))) return hr;
  219. if(FAILED(hr = _Module.UpdateRegistryFromResource( IDR_HELPSVC , TRUE ))) return hr;
  220. if(FAILED(hr = _Module.UpdateRegistryFromResource( IDR_HCUPDATE, TRUE ))) return hr;
  221. #ifndef NOJETBLUECOM
  222. if(FAILED(hr = _Module.UpdateRegistryFromResource( IDR_PCHDBSESSION, TRUE ))) return hr;
  223. #endif
  224. return S_OK;
  225. }
  226. HRESULT CServiceModule::UnregisterServer( LPCWSTR szSvcHostGroup )
  227. {
  228. HRESULT hr;
  229. // Remove service
  230. Uninstall( szSvcHostGroup );
  231. // Remove object entries
  232. if(FAILED(hr = CComModule::UnregisterServer( TRUE ))) return hr;
  233. if(FAILED(hr = _Module.UpdateRegistryFromResource( IDR_HELPSVC , FALSE ))) return hr;
  234. if(FAILED(hr = _Module.UpdateRegistryFromResource( IDR_HCUPDATE, FALSE ))) return hr;
  235. #ifndef NOJETBLUECOM
  236. if(FAILED(hr = _Module.UpdateRegistryFromResource( IDR_PCHDBSESSION, FALSE ))) return hr;
  237. #endif
  238. return S_OK;
  239. }
  240. void CServiceModule::Init( _ATL_OBJMAP_ENTRY* p, HINSTANCE h, LPCWSTR szServiceName, UINT iDisplayName, UINT iDescription, const GUID* plibid )
  241. {
  242. CComModule::Init( p, h, plibid );
  243. {
  244. MPC::wstring strLogFile( HC_ROOT_HELPSVC_LOGS L"\\" ); MPC::SubstituteEnvVariables( strLogFile );
  245. strLogFile += szServiceName;
  246. strLogFile += L".log";
  247. g_ApplicationLog.SetLocation( strLogFile.c_str() );
  248. }
  249. m_szServiceName = szServiceName;
  250. m_iDisplayName = iDisplayName;
  251. m_iDescription = iDescription;
  252. // set up the initial service status
  253. m_hServiceStatus = NULL;
  254. m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  255. m_status.dwCurrentState = SERVICE_STOPPED;
  256. m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  257. m_status.dwWin32ExitCode = 0;
  258. m_status.dwServiceSpecificExitCode = 0;
  259. m_status.dwCheckPoint = 0;
  260. m_status.dwWaitHint = 0;
  261. }
  262. BOOL CServiceModule::IsInstalled()
  263. {
  264. BOOL bResult = FALSE;
  265. SC_HANDLE hSCM = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  266. if((hSCM = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS )))
  267. {
  268. SC_HANDLE hService;
  269. if((hService = ::OpenServiceW( hSCM, m_szServiceName, SERVICE_QUERY_CONFIG )))
  270. {
  271. bResult = TRUE;
  272. ::CloseServiceHandle( hService );
  273. }
  274. ::CloseServiceHandle( hSCM );
  275. }
  276. return bResult;
  277. }
  278. BOOL CServiceModule::Install( LPCWSTR szSvcHostGroup )
  279. {
  280. BOOL bResult = FALSE;
  281. SC_HANDLE hSCM;
  282. if((hSCM = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS )))
  283. {
  284. MPC::wstring strDisplayName;
  285. MPC::wstring strDescription;
  286. WCHAR szFilePath[_MAX_PATH];
  287. SC_HANDLE hService;
  288. DWORD dwStartType;
  289. if(szSvcHostGroup)
  290. {
  291. dwStartType = SERVICE_WIN32_SHARE_PROCESS;
  292. StringCchPrintfW( szFilePath, ARRAYSIZE(szFilePath), L"%%SystemRoot%%\\System32\\svchost.exe -k %s", szSvcHostGroup );
  293. }
  294. else
  295. {
  296. dwStartType = SERVICE_WIN32_OWN_PROCESS;
  297. ::GetModuleFileNameW( NULL, szFilePath, _MAX_PATH );
  298. }
  299. if(FAILED( MPC::LocalizeString( m_iDisplayName, strDisplayName )))
  300. {
  301. strDisplayName = m_szServiceName;
  302. }
  303. (void)MPC::LocalizeString( m_iDescription, strDescription );
  304. hService = ::OpenServiceW( hSCM, m_szServiceName, SERVICE_QUERY_CONFIG );
  305. if(hService == NULL)
  306. {
  307. hService = ::CreateServiceW( hSCM ,
  308. m_szServiceName ,
  309. strDisplayName.c_str(),
  310. SERVICE_ALL_ACCESS ,
  311. dwStartType ,
  312. SERVICE_AUTO_START ,
  313. SERVICE_ERROR_NORMAL ,
  314. szFilePath ,
  315. NULL ,
  316. NULL ,
  317. L"RPCSS\0" ,
  318. NULL ,
  319. NULL );
  320. }
  321. if(hService)
  322. {
  323. if(strDescription.size())
  324. {
  325. SERVICE_DESCRIPTIONW desc; ::ZeroMemory( &desc , sizeof(desc ) );
  326. SERVICE_FAILURE_ACTIONSW recovery; ::ZeroMemory( &recovery, sizeof(recovery) );
  327. SC_ACTION actions[] =
  328. {
  329. { SC_ACTION_RESTART, 100 },
  330. { SC_ACTION_RESTART, 100 },
  331. { SC_ACTION_NONE , 100 },
  332. };
  333. desc.lpDescription = (LPWSTR)strDescription.c_str();
  334. recovery.dwResetPeriod = 24 * 60 * 60; // 1 day
  335. recovery.cActions = ARRAYSIZE(actions);
  336. recovery.lpsaActions = actions;
  337. ::ChangeServiceConfig2W( hService, SERVICE_CONFIG_DESCRIPTION , &desc );
  338. ::ChangeServiceConfig2W( hService, SERVICE_CONFIG_FAILURE_ACTIONS, &recovery );
  339. }
  340. if(szSvcHostGroup)
  341. {
  342. if(SUCCEEDED(ServiceHost_Install( m_szServiceName, szSvcHostGroup )))
  343. {
  344. bResult = TRUE;
  345. }
  346. }
  347. else
  348. {
  349. bResult = TRUE;
  350. }
  351. ::CloseServiceHandle( hService );
  352. }
  353. if(bResult == FALSE)
  354. {
  355. (void)g_NTEvents.LogEvent( EVENTLOG_ERROR_TYPE, HELPSVC_ERR_CANNOTCREATESERVICE, NULL );
  356. }
  357. ::CloseServiceHandle( hSCM );
  358. }
  359. else
  360. {
  361. (void)g_NTEvents.LogEvent( EVENTLOG_ERROR_TYPE, HELPSVC_ERR_CANNOTOPENSCM, NULL );
  362. }
  363. return bResult;
  364. }
  365. BOOL CServiceModule::Uninstall( LPCWSTR szSvcHostGroup )
  366. {
  367. BOOL bResult = FALSE;
  368. SC_HANDLE hSCM;
  369. if((hSCM = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS )))
  370. {
  371. SC_HANDLE hService;
  372. if((hService = ::OpenServiceW( hSCM, m_szServiceName, SERVICE_STOP | DELETE )))
  373. {
  374. SERVICE_STATUS status;
  375. ::ControlService( hService, SERVICE_CONTROL_STOP, &status );
  376. bResult = ::DeleteService( hService );
  377. if(bResult)
  378. {
  379. ::Sleep( 2000 ); // Let the service stop down...
  380. if(szSvcHostGroup)
  381. {
  382. if(FAILED(ServiceHost_Uninstall( m_szServiceName, szSvcHostGroup )))
  383. {
  384. bResult = FALSE;
  385. }
  386. }
  387. }
  388. if(bResult == FALSE)
  389. {
  390. (void)g_NTEvents.LogEvent( EVENTLOG_ERROR_TYPE, HELPSVC_ERR_CANNOTDELETESERVICE, NULL );
  391. }
  392. ::CloseServiceHandle( hService );
  393. }
  394. ::CloseServiceHandle( hSCM );
  395. }
  396. else
  397. {
  398. (void)g_NTEvents.LogEvent( EVENTLOG_ERROR_TYPE, HELPSVC_ERR_CANNOTOPENSCM, NULL );
  399. }
  400. return bResult;
  401. }
  402. //////////////////////////////////////////////////////////////////////////////////////////////
  403. // Service startup and registration
  404. BOOL CServiceModule::Start( BOOL bService )
  405. {
  406. SERVICE_TABLE_ENTRYW st[] =
  407. {
  408. { (LPWSTR)m_szServiceName, _ServiceMain },
  409. { NULL , NULL }
  410. };
  411. m_hEventShutdown = ::CreateEvent( NULL, FALSE, FALSE, NULL );
  412. if(m_hEventShutdown == NULL) return FALSE;
  413. if((m_bService = bService) && !::StartServiceCtrlDispatcherW( st ))
  414. {
  415. DWORD dwRes = ::GetLastError();
  416. m_bService = FALSE;
  417. }
  418. if(m_bService == FALSE)
  419. {
  420. if(StartMonitor() == FALSE) return FALSE;
  421. if(FAILED(Run())) return FALSE;
  422. }
  423. return TRUE;
  424. }
  425. void CServiceModule::ServiceMain( DWORD dwArgc, LPWSTR lpszArgv[] )
  426. {
  427. // Register the control request handler
  428. m_status.dwCurrentState = SERVICE_START_PENDING;
  429. if((m_hServiceStatus = ::RegisterServiceCtrlHandlerW( m_szServiceName, _Handler )))
  430. {
  431. SetServiceStatus( SERVICE_START_PENDING );
  432. m_status.dwWin32ExitCode = S_OK;
  433. m_status.dwCheckPoint = 0;
  434. m_status.dwWaitHint = 0;
  435. // When the Run function returns, the service has stopped.
  436. Run();
  437. SetServiceStatus( SERVICE_STOPPED );
  438. }
  439. else
  440. {
  441. (void)g_NTEvents.LogEvent( EVENTLOG_ERROR_TYPE, HELPSVC_ERR_REGISTERHANDLER, NULL );
  442. }
  443. }
  444. void CServiceModule::Handler( DWORD dwOpcode )
  445. {
  446. switch(dwOpcode)
  447. {
  448. case SERVICE_CONTROL_STOP:
  449. SetServiceStatus( SERVICE_STOP_PENDING );
  450. ForceShutdown();
  451. break;
  452. case SERVICE_CONTROL_PAUSE:
  453. break;
  454. case SERVICE_CONTROL_CONTINUE:
  455. break;
  456. case SERVICE_CONTROL_INTERROGATE:
  457. break;
  458. case SERVICE_CONTROL_SHUTDOWN:
  459. break;
  460. default:
  461. (void)g_NTEvents.LogEvent( EVENTLOG_ERROR_TYPE, HELPSVC_ERR_BADSVCREQUEST, NULL );
  462. }
  463. }
  464. HRESULT CServiceModule::Run()
  465. {
  466. __HCP_FUNC_ENTRY( "CServiceModule::Run" );
  467. HRESULT hr;
  468. MSG msg;
  469. m_dwThreadID = ::GetCurrentThreadId();
  470. __MPC_EXIT_IF_METHOD_FAILS(hr, ::CoInitializeEx( NULL, COINIT_MULTITHREADED )); // We need to be a multi-threaded application.
  471. __MPC_EXIT_IF_METHOD_FAILS(hr, RegisterClassObjects( CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, REGCLS_MULTIPLEUSE ));
  472. if(m_bService)
  473. {
  474. SetServiceStatus( SERVICE_RUNNING );
  475. }
  476. while(::GetMessage( &msg, 0, 0, 0 ))
  477. {
  478. ::DispatchMessage( &msg );
  479. }
  480. hr = S_OK;
  481. __HCP_FUNC_CLEANUP;
  482. _Module.RevokeClassObjects();
  483. ::Sleep( dwPause ); //wait for any threads to finish
  484. __HCP_FUNC_EXIT(hr);
  485. }
  486. void CServiceModule::SetServiceStatus( DWORD dwState )
  487. {
  488. m_status.dwCurrentState = dwState;
  489. ::SetServiceStatus( m_hServiceStatus, &m_status );
  490. }
  491. ////////////////////////////////////////////////////////////////////////////////
  492. void WINAPI CServiceModule::_ServiceMain( DWORD dwArgc, LPWSTR* lpszArgv )
  493. {
  494. _Module.ServiceMain( dwArgc, lpszArgv );
  495. }
  496. void WINAPI CServiceModule::_Handler( DWORD dwOpcode )
  497. {
  498. _Module.Handler( dwOpcode );
  499. }
  500. DWORD WINAPI CServiceModule::_Monitor( void* pv )
  501. {
  502. ((CServiceModule*)pv)->MonitorShutdown();
  503. return 0;
  504. }