Source code of Windows XP (NT5)
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.

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