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.

1299 lines
31 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. All rights reserved.
  4. Module Name:
  5. spooler.c
  6. Abstract:
  7. Handle spooler interaction via RPC.
  8. Public functions from this module:
  9. SpoolerOnline
  10. SpoolerOffline
  11. SpoolerStart
  12. SpoolerStop
  13. SpoolerIsAlive
  14. SpoolerLooksAlive
  15. There is a little interface bleed here--this module is aware
  16. of the cluster SetResourceStatus callback.
  17. Author:
  18. Albert Ting (AlbertT) 23-Sept-96
  19. Revision History:
  20. Khaled Sedky (KhaledS) 1998-2001
  21. --*/
  22. #include "precomp.hxx"
  23. #pragma hdrstop
  24. #include "splsvc.hxx"
  25. #include "clusinfo.hxx"
  26. #include "spooler.hxx"
  27. #include "winsprlp.h"
  28. #define TERMINATETHREADONCHECK \
  29. if ( ClusWorkerCheckTerminate( Worker )) \
  30. { \
  31. goto Done; \
  32. }
  33. /********************************************************************
  34. Conditional compile defines:
  35. USE_ISALIVE_THREAD
  36. Causes the resource DLL to wait on a thread each time the
  37. IsAlive all is made. This thread wait for ISALIVE_WAIT_TIME
  38. to see if the RPC call to the spooler successfully completes.
  39. If it does not, then we assume that the spooler is deadlocked.
  40. This is off because it kills the spooler if it's slow or
  41. being debugged. There are no scenarios where we should
  42. be deadlocked (or hold the critical section while doing a
  43. slow operation like hitting the net). During stress, however,
  44. we may appear deadlocked.
  45. USE_OFFLINE_HACK
  46. Causes an Offline call to terminate and restart the spooler.
  47. This will be turned on until we have clean shutdown code.
  48. USE_STUBS
  49. Causes us to use stubbed out winspool.drv calls. Usefule if
  50. the new winspool.drv isn't available and you want to compile
  51. a DLL which simulates talking to the spooler.
  52. ********************************************************************/
  53. //#define USE_ISALIVE_THREAD
  54. //#define USE_OFFLINE_HACK
  55. //#define USE_STUBS
  56. #define POLL_SLEEP_TIME 500 // Service control poll time.
  57. #define STATUS_SLEEP_TIME 1500
  58. #define ISALIVE_WAIT_TIME 2000 // Time before spooler is deadlocked.
  59. LPCTSTR gszSpooler = TEXT( "Spooler" );
  60. SC_HANDLE ghSpoolerService;
  61. SC_HANDLE ghSC;
  62. /********************************************************************
  63. Stubs
  64. ********************************************************************/
  65. #ifdef USE_STUBS
  66. BOOL
  67. ClusterSplOpen(
  68. LPCTSTR pszServer,
  69. LPCTSTR pszResource,
  70. PHANDLE phSpooler,
  71. LPCTSTR pszName,
  72. LPCTSTR pszAddress
  73. )
  74. {
  75. UNREFERENCED_PARAMETER( pszServer );
  76. UNREFERENCED_PARAMETER( pszResource );
  77. UNREFERENCED_PARAMETER( pszName );
  78. UNREFERENCED_PARAMETER( pszAddress );
  79. *phSpooler = (HANDLE)31;
  80. Sleep( 3200 );
  81. return TRUE;
  82. }
  83. BOOL
  84. ClusterSplClose(
  85. HANDLE hSpooler
  86. )
  87. {
  88. UNREFERENCED_PARAMETER( hSpooler );
  89. SPLASSERT( hSpooler==(HANDLE)31 );
  90. Sleep( 6000 );
  91. return TRUE;
  92. }
  93. BOOL
  94. ClusterSplIsAlive(
  95. HANDLE hSpooler
  96. )
  97. {
  98. UNREFERENCED_PARAMETER( hSpooler );
  99. Sleep( 500 );
  100. return TRUE;
  101. }
  102. #endif
  103. /********************************************************************
  104. Utility functions
  105. ********************************************************************/
  106. BOOL
  107. QuerySpoolerState(
  108. OUT PDWORD pdwState
  109. )
  110. /*++
  111. Routine Description:
  112. Checks the current state of the spooler service.
  113. Arguments:
  114. pdwState - Receives the state of the spooler.
  115. Return Value:
  116. TRUE - success
  117. FALSE - failure. *pdwState set to SERVICE_STOPPED
  118. --*/
  119. {
  120. SERVICE_STATUS ServiceStatus;
  121. SPLASSERT( ghSpoolerService );
  122. if( !QueryServiceStatus( ghSpoolerService,
  123. &ServiceStatus)) {
  124. DBGMSG( DBG_WARN,
  125. ( "SpoolerStatus: QueryServiceStatus failed %d\n",
  126. GetLastError() ));
  127. *pdwState = SERVICE_STOPPED;
  128. return FALSE;
  129. }
  130. *pdwState = ServiceStatus.dwCurrentState;
  131. return TRUE;
  132. }
  133. DWORD
  134. WINAPI
  135. SpoolerStatusReportThread(
  136. PCLUS_WORKER Worker,
  137. PVOID pStatusThreadInfo
  138. )
  139. {
  140. HANDLE hStatusEvent = ((STATUSTHREAD_INFO *)pStatusThreadInfo)->hStatusEvent;
  141. PSPOOLER_INFORMATION pSpoolerInfo = ((STATUSTHREAD_INFO *)pStatusThreadInfo)->pSpoolerInfo;
  142. PRESOURCE_STATUS pResourceStatus = ((STATUSTHREAD_INFO *)pStatusThreadInfo)->pResourceStatus;
  143. while(WaitForSingleObject(hStatusEvent,STATUS_SLEEP_TIME) == WAIT_TIMEOUT)
  144. {
  145. pResourceStatus->CheckPoint++;
  146. (pSpoolerInfo->pfnSetResourceStatus)(pSpoolerInfo->ResourceHandle,
  147. pResourceStatus);
  148. }
  149. return (0);
  150. }
  151. /********************************************************************
  152. Worker threads for SpoolerOnline/Offline.
  153. ********************************************************************/
  154. #ifdef USE_ISALIVE_THREAD
  155. DWORD
  156. WINAPI
  157. SpoolerIsAliveThread(
  158. PVOID pSpoolerInfo_
  159. )
  160. /*++
  161. Routine Description:
  162. Async thread to online the resource instance.
  163. Assumes vAddRef has been called already; we will call vDecRef
  164. when we are done.
  165. Arguments:
  166. Return Value:
  167. ERRROR_SUCCESS - Spooler still alive.
  168. dwError - Spooler dead.
  169. --*/
  170. {
  171. PSPOOLER_INFORMATION pSpoolerInfo = (PSPOOLER_INFORMATION)pSpoolerInfo_;
  172. HANDLE hSpooler = pSpoolerInfo->hSpooler;
  173. BOOL bIsAlive;
  174. //
  175. // We've stored all the information we need from pSpoolerInfo;
  176. // decrement the refcount.
  177. //
  178. vDecRef( pSpoolerInfo );
  179. //
  180. // RPC to spooler.
  181. //
  182. SPLASSERT( hSpooler );
  183. bIsAlive = ClusterSplIsAlive( hSpooler );
  184. DBGMSG( DBG_TRACE,
  185. ( "SpoolerIsAliveThread: return status: h=%x s=%x,%d\n",
  186. hSpooler, bIsAlive, GetLastError() ));
  187. if( bIsAlive ){
  188. return ERROR_SUCCESS;
  189. }
  190. //
  191. // Spooler is dead--return some error code.
  192. //
  193. return ERROR_INVALID_PARAMETER;
  194. }
  195. #endif
  196. DWORD
  197. SpoolerOnlineThread(
  198. IN PCLUS_WORKER Worker,
  199. IN PVOID pSpoolerInfo_
  200. )
  201. /*++
  202. Routine Description:
  203. Async thread to online the resource instance.
  204. Assumes vAddRef has been called already; we will call vDecRef
  205. when we are done.
  206. Arguments:
  207. Return Value:
  208. --*/
  209. {
  210. DWORD dwState;
  211. DWORD dwStatus;
  212. BOOL bStatus = FALSE;
  213. HANDLE hStatusEvent = NULL;
  214. RESOURCE_STATUS ResourceStatus;
  215. STATUSTHREAD_INFO StatusThreadInfo;
  216. PSPOOLER_INFORMATION pSpoolerInfo = (PSPOOLER_INFORMATION)pSpoolerInfo_;
  217. ResUtilInitializeResourceStatus( &ResourceStatus );
  218. ResourceStatus.ResourceState = ClusterResourceOnlinePending;
  219. ResourceStatus.CheckPoint = 1;
  220. (pSpoolerInfo->pfnSetResourceStatus)( pSpoolerInfo->ResourceHandle,
  221. &ResourceStatus );
  222. TERMINATETHREADONCHECK
  223. //
  224. // Get needed information about net name and tcpip address.
  225. //
  226. if( !bGetClusterNameInfo( pSpoolerInfo->pszResource,
  227. &pSpoolerInfo->pszName,
  228. &pSpoolerInfo->pszAddress )){
  229. (pSpoolerInfo->pfnLogEvent)(
  230. pSpoolerInfo->ResourceHandle,
  231. LOG_ERROR,
  232. L"Unable to retrieve Name and TcpIp address.\n" );
  233. DBGMSG( DBG_ERROR, ( "SplSvcOpen: Couldn't retrieve name/tcpip addr\n" ));
  234. goto Done;
  235. }
  236. TERMINATETHREADONCHECK
  237. //
  238. // Ensure the spooler is started.
  239. //
  240. bStatus = SpoolerStart( pSpoolerInfo );
  241. if( !bStatus ){
  242. DBGMSG( DBG_WARN, ( "SpoolerOnlineThread: SpoolerStart failed\n" ));
  243. goto Done;
  244. }
  245. while (TRUE) {
  246. if( !QuerySpoolerState( &dwState )){
  247. dwStatus = GetLastError();
  248. (pSpoolerInfo->pfnLogEvent)(
  249. pSpoolerInfo->ResourceHandle,
  250. LOG_ERROR,
  251. L"Query Service Status failed %1!u!.\n",
  252. dwStatus);
  253. goto Done;
  254. }
  255. if( dwState != SERVICE_START_PENDING ){
  256. break;
  257. }
  258. else
  259. {
  260. ResourceStatus.CheckPoint ++;
  261. (pSpoolerInfo->pfnSetResourceStatus)( pSpoolerInfo->ResourceHandle,
  262. &ResourceStatus );
  263. TERMINATETHREADONCHECK
  264. }
  265. Sleep( POLL_SLEEP_TIME );
  266. }
  267. if( dwState != SERVICE_RUNNING) {
  268. (pSpoolerInfo->pfnLogEvent)(
  269. pSpoolerInfo->ResourceHandle,
  270. LOG_ERROR,
  271. L"Failed to start service. Error: %1!u!.\n",
  272. ERROR_SERVICE_NEVER_STARTED);
  273. dwStatus = ERROR_SERVICE_NEVER_STARTED;
  274. goto Done;
  275. }
  276. //
  277. // Since we have to report the status of being online pending
  278. // to the cluster everywhile in order not to be considered failing
  279. // and because ClusterSplOpen takes a while and it is a synchronous
  280. // call , we hae to create this Status Thread which would keep
  281. // reporting the status to the cluster in the back ground.
  282. //
  283. hStatusEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  284. if(hStatusEvent)
  285. {
  286. StatusThreadInfo.pResourceStatus = &ResourceStatus;
  287. StatusThreadInfo.hStatusEvent = hStatusEvent;
  288. StatusThreadInfo.pSpoolerInfo = pSpoolerInfo;
  289. dwStatus = ClusWorkerCreate(&pSpoolerInfo->OnLineStatusThread,
  290. SpoolerStatusReportThread,
  291. (PVOID)&StatusThreadInfo);
  292. if( dwStatus != ERROR_SUCCESS )
  293. {
  294. //
  295. // In this case we will unfortunatly fall out of the Reporting thread and
  296. // behave the same as previously , which might cause the resource to fail
  297. // since it is not reporing the status properly (not likely to happen)
  298. //
  299. DBGMSG( DBG_WARN,
  300. ( "SpoolerOnlineThread : ClusWorkerCreate(SpoolerStatusReportThread) failed %d\n", dwStatus ));
  301. }
  302. }
  303. else
  304. {
  305. //
  306. // In this case we will unfortunatly fall out of the Reporting thread and
  307. // behave the same as previously , which might cause the resource to fail
  308. // since it is not reporing the status properly (not likely to happen)
  309. //
  310. DBGMSG( DBG_WARN,
  311. ( "SpoolerOnlineThread: Create StatusEvent failed %d\n", GetLastError() ));
  312. }
  313. //
  314. // RPC to spooler.
  315. //
  316. bStatus = ClusterSplOpen( NULL,
  317. pSpoolerInfo->pszResource,
  318. &pSpoolerInfo->hSpooler,
  319. pSpoolerInfo->pszName,
  320. pSpoolerInfo->pszAddress );
  321. if(hStatusEvent && pSpoolerInfo->OnLineStatusThread.hThread)
  322. {
  323. SetEvent(hStatusEvent);
  324. }
  325. SPLASSERT( bStatus || !pSpoolerInfo->hSpooler );
  326. DBGMSG( DBG_TRACE,
  327. ( "SpoolerOnlineThread: "TSTR" "TSTR" "TSTR" h=%x s=%x,d\n",
  328. DBGSTR( pSpoolerInfo->pszResource ),
  329. DBGSTR( pSpoolerInfo->pszName ),
  330. DBGSTR( pSpoolerInfo->pszAddress ),
  331. pSpoolerInfo->hSpooler,
  332. bStatus,
  333. GetLastError() ));
  334. Done:
  335. //
  336. // If we are terminating, then we should not set any state
  337. // and avoid calling SetResourceStatus since clustering doesn't
  338. // think we are pending online anymore.
  339. //
  340. if( pSpoolerInfo->eState != kTerminate ){
  341. if( bStatus ){
  342. //
  343. // Spooler successfully onlined.
  344. //
  345. pSpoolerInfo->eState = kOnline;
  346. ResourceStatus.ResourceState = ClusterResourceOnline;
  347. }
  348. else
  349. {
  350. ResourceStatus.ResourceState = ClusterResourceFailed;
  351. }
  352. ResourceStatus.CheckPoint++;
  353. (pSpoolerInfo->pfnSetResourceStatus)( pSpoolerInfo->ResourceHandle,
  354. &ResourceStatus );
  355. }
  356. vDecRef( pSpoolerInfo );
  357. if(hStatusEvent)
  358. {
  359. CloseHandle(hStatusEvent);
  360. }
  361. ClusWorkerTerminate(&(pSpoolerInfo->OnLineStatusThread));
  362. return 0;
  363. }
  364. DWORD
  365. SpoolerClose(
  366. PSPOOLER_INFORMATION pSpoolerInfo,
  367. EShutDownMethod ShutDownMethod
  368. )
  369. {
  370. CLUSTER_RESOURCE_STATE ClusterResourceState;
  371. STATUSTHREAD_INFO StatusThreadInfo;
  372. RESOURCE_STATUS ResourceStatus;
  373. BOOL bStatus = TRUE;
  374. HANDLE hStatusEvent = NULL;
  375. HANDLE hSpooler = NULL;
  376. DWORD dwStatus = ERROR_SUCCESS;
  377. ResUtilInitializeResourceStatus( &ResourceStatus );
  378. ResourceStatus.CheckPoint = 1;
  379. ResourceStatus.ResourceState = ClusterResourceOfflinePending;
  380. (pSpoolerInfo->pfnSetResourceStatus)( pSpoolerInfo->ResourceHandle,
  381. &ResourceStatus );
  382. vEnterSem();
  383. {
  384. ClusterResourceState = pSpoolerInfo->ClusterResourceState;
  385. if( pSpoolerInfo->hSpooler )
  386. {
  387. hSpooler = pSpoolerInfo->hSpooler;
  388. pSpoolerInfo->hSpooler = NULL;
  389. }
  390. }
  391. vLeaveSem();
  392. if( hSpooler )
  393. {
  394. //
  395. // Since we have to report the status of being offline pending
  396. // to the cluster everywhile in order not to be considered failing
  397. // and because ClusterSplClose takes a while and it is a synchronous
  398. // call , we have to create this Status Thread which would keep
  399. // reporting the status to the cluster in the back ground.
  400. //
  401. hStatusEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  402. if(hStatusEvent)
  403. {
  404. StatusThreadInfo.pResourceStatus = &ResourceStatus;
  405. StatusThreadInfo.hStatusEvent = hStatusEvent;
  406. StatusThreadInfo.pSpoolerInfo = pSpoolerInfo;
  407. dwStatus = ClusWorkerCreate((ShutDownMethod == kTerminateShutDown) ?
  408. &pSpoolerInfo->TerminateStatusThread :
  409. &pSpoolerInfo->OffLineStatusThread,
  410. SpoolerStatusReportThread,
  411. (PVOID)&StatusThreadInfo);
  412. if( dwStatus != ERROR_SUCCESS )
  413. {
  414. //
  415. // In this case we will unfortunatly fall out of the Reporting thread and
  416. // behave the same as previously , which might cause the resource to fail
  417. // since it is not reporing the status properly (not likely to happen)
  418. //
  419. DBGMSG( DBG_WARN,
  420. ( "SpoolerClose : ClusWorkerCreate(SpoolerStatusReportThread) failed %d\n", dwStatus ));
  421. dwStatus = ERROR_SUCCESS;
  422. }
  423. }
  424. else
  425. {
  426. //
  427. // In this case we will unfortunatly fall out of the Reporting thread and
  428. // behave the same as previously , which might cause the resource to fail
  429. // since it is not reporing the status properly (not likely to happen)
  430. //
  431. dwStatus = GetLastError();
  432. DBGMSG( DBG_WARN,
  433. ( "SpoolerClose: Create StatusEvent failed %d\n", GetLastError() ));
  434. }
  435. if(ShutDownMethod == kOffLineShutDown)
  436. {
  437. ClusWorkerTerminate(&(pSpoolerInfo->OnlineThread));
  438. }
  439. //
  440. // RPC to Terminate
  441. //
  442. bStatus = ClusterSplClose( hSpooler );
  443. if(hStatusEvent &&
  444. (ShutDownMethod == kTerminateShutDown) ?
  445. pSpoolerInfo->TerminateStatusThread.hThread :
  446. pSpoolerInfo->OffLineStatusThread.hThread)
  447. {
  448. SetEvent(hStatusEvent);
  449. }
  450. DBGMSG( DBG_TRACE,
  451. ( "SpoolerClose: h=%x, s=%x,%d\n",
  452. hSpooler, bStatus, GetLastError() ));
  453. }
  454. if( bStatus ){
  455. ResourceStatus.ResourceState = ClusterResourceState;
  456. pSpoolerInfo->hSpooler = NULL;
  457. }
  458. else
  459. {
  460. ResourceStatus.ResourceState = ClusterResourceFailed;
  461. dwStatus = ERROR_FUNCTION_FAILED;
  462. }
  463. ClusWorkerTerminate((ShutDownMethod == kTerminateShutDown) ?
  464. &(pSpoolerInfo->TerminateStatusThread) :
  465. &(pSpoolerInfo->OffLineStatusThread));
  466. //
  467. // If we are terminating, don't call SetResourceStatus since
  468. // clustering doesn't expect this after a terminate call.
  469. //
  470. if( pSpoolerInfo->eState != kTerminate )
  471. {
  472. ResourceStatus.CheckPoint++;
  473. (pSpoolerInfo->pfnSetResourceStatus)( pSpoolerInfo->ResourceHandle,
  474. &ResourceStatus );
  475. pSpoolerInfo->eState = kOffline;
  476. }
  477. if(hStatusEvent)
  478. {
  479. CloseHandle(hStatusEvent);
  480. }
  481. return dwStatus;
  482. }
  483. DWORD
  484. WINAPI
  485. SpoolerOfflineThread(
  486. IN PCLUS_WORKER Worker,
  487. IN PVOID pSpoolerInfo_
  488. )
  489. {
  490. PSPOOLER_INFORMATION pSpoolerInfo = (PSPOOLER_INFORMATION)pSpoolerInfo_;
  491. SpoolerClose(pSpoolerInfo,kOffLineShutDown);
  492. vDecRef( pSpoolerInfo );
  493. return 0;
  494. }
  495. DWORD
  496. WINAPI
  497. SpoolerTerminateSync(
  498. IN PSPOOLER_INFORMATION pSpoolerInfo
  499. )
  500. {
  501. return SpoolerClose(pSpoolerInfo,kTerminateShutDown);
  502. }
  503. /********************************************************************
  504. Spooler routines.
  505. ********************************************************************/
  506. BOOL
  507. SpoolerOnline(
  508. PSPOOLER_INFORMATION pSpoolerInfo
  509. )
  510. /*++
  511. Routine Description:
  512. Put the spooler service online. This call completes asynchronously;
  513. it will use the callback in pSpoolerInfo to update the status.
  514. Arguments:
  515. Return Value:
  516. --*/
  517. {
  518. DWORD status=ERROR_SUCCESS;
  519. pSpoolerInfo->hSpooler = NULL;
  520. pSpoolerInfo->eState = kOnlinePending;
  521. vAddRef( pSpoolerInfo );
  522. //
  523. // Create a worker thread to start the spooler and poll.
  524. //
  525. status = ClusWorkerCreate(&pSpoolerInfo->OnlineThread,
  526. SpoolerOnlineThread,
  527. (PVOID)pSpoolerInfo
  528. );
  529. if( status != ERROR_SUCCESS ){
  530. DBGMSG( DBG_WARN,
  531. ( "SpoolerOnline: ClusWorkerCreate failed %d\n", status ));
  532. vDecRef( pSpoolerInfo );
  533. return FALSE;
  534. }
  535. return TRUE;
  536. }
  537. DWORD
  538. SpoolerOffline(
  539. PSPOOLER_INFORMATION pSpoolerInfo
  540. )
  541. /*++
  542. Routine Description:
  543. Put the spooler service offline. This call completes asynchronously;
  544. it will use the callback in pSpoolerInfo to update the status.
  545. Arguments:
  546. Return Value:
  547. --*/
  548. {
  549. DWORD status = ERROR_SUCCESS;
  550. DBGMSG( DBG_WARN, ( ">>> SpoolerOffline: called %x\n", Resid ));
  551. vAddRef( pSpoolerInfo );
  552. pSpoolerInfo->eState = kOfflinePending;
  553. //
  554. // Create a worker thread to stop the spooler.
  555. //
  556. if((status = ClusWorkerCreate(&pSpoolerInfo->OfflineThread,
  557. SpoolerOfflineThread,
  558. (PVOID)pSpoolerInfo
  559. ))!=ERROR_SUCCESS)
  560. {
  561. DBGMSG( DBG_WARN,
  562. ( "SpoolerOffline: ClusWorkerCreate failed %d\n", status ));
  563. DBGMSG( DBG_ERROR,
  564. ( "SpoolerOffline: Unable to offline spooler\n" ));
  565. SPLASSERT(status == ERROR_SUCCESS)
  566. vDecRef( pSpoolerInfo );
  567. }
  568. else
  569. {
  570. status = ERROR_IO_PENDING;
  571. }
  572. return status;
  573. }
  574. VOID
  575. SpoolerTerminate(
  576. PSPOOLER_INFORMATION pSpoolerInfo
  577. )
  578. /*++
  579. Routine Description:
  580. Terminates the spooler process. This call completes asynchronously;
  581. it will use the callback in pSpoolerInfo to update the status.
  582. Arguments:
  583. Return Value:
  584. --*/
  585. {
  586. DWORD status = ERROR_SUCCESS;
  587. DBGMSG( DBG_WARN, ( ">>> SpoolerTerminate: called %x\n", Resid ));
  588. vAddRef( pSpoolerInfo );
  589. {
  590. ClusWorkerTerminate(&(pSpoolerInfo->OnlineThread));
  591. ClusWorkerTerminate(&(pSpoolerInfo->OfflineThread));
  592. pSpoolerInfo->eState = kOfflinePending;
  593. //
  594. // Create a worker thread to stop the spooler.
  595. //
  596. if((status = SpoolerTerminateSync(pSpoolerInfo))!=ERROR_SUCCESS)
  597. {
  598. DBGMSG( DBG_WARN,
  599. ( "SpoolerTerminate: ClusWorkerCreate failed %d\n", status ));
  600. DBGMSG( DBG_ERROR,
  601. ( "SpoolerTerminate: Unable to offline spooler\n" ));
  602. SPLASSERT(status == ERROR_SUCCESS)
  603. }
  604. }
  605. vDecRef( pSpoolerInfo );
  606. }
  607. BOOL
  608. SpoolerStart(
  609. PSPOOLER_INFORMATION pSpoolerInfo
  610. )
  611. /*++
  612. Routine Description:
  613. Start the spooler.
  614. Arguments:
  615. Return Value:
  616. --*/
  617. {
  618. BOOL bStatus = TRUE;
  619. UNREFERENCED_PARAMETER( pSpoolerInfo );
  620. vEnterSem();
  621. if( !ghSC ){
  622. ghSC = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
  623. }
  624. if( ghSC ){
  625. if( !ghSpoolerService ){
  626. ghSpoolerService = OpenService( ghSC,
  627. gszSpooler,
  628. SERVICE_ALL_ACCESS );
  629. }
  630. if( !ghSpoolerService ){
  631. DBGMSG( DBG_WARN,
  632. ( "SpoolerStart: Failed to open spooler service %d\n ",
  633. GetLastError() ));
  634. bStatus = FALSE;
  635. goto Done;
  636. }
  637. if( !StartService( ghSpoolerService, 0, NULL )){
  638. DWORD dwStatus;
  639. dwStatus = GetLastError();
  640. if( dwStatus != ERROR_SERVICE_ALREADY_RUNNING ){
  641. DBGMSG( DBG_WARN,
  642. ( "SpoolerStart: StartService failed %d\n",
  643. dwStatus ));
  644. bStatus = FALSE;
  645. }
  646. }
  647. }
  648. Done:
  649. vLeaveSem();
  650. return bStatus;
  651. }
  652. BOOL
  653. SpoolerStop(
  654. PSPOOLER_INFORMATION pSpoolerInfo
  655. )
  656. /*++
  657. Routine Description:
  658. Stop the spooler.
  659. Arguments:
  660. Return Value:
  661. --*/
  662. {
  663. BOOL bStatus;
  664. SERVICE_STATUS ServiceStatus;
  665. vEnterSem();
  666. bStatus = ControlService( ghSpoolerService,
  667. SERVICE_CONTROL_STOP,
  668. &ServiceStatus );
  669. if( !bStatus ){
  670. DBGMSG( DBG_WARN,
  671. ( "SpoolerStop: ControlService failed %d\n", GetLastError() ));
  672. (pSpoolerInfo->pfnLogEvent)(
  673. pSpoolerInfo->ResourceHandle,
  674. LOG_ERROR,
  675. L"Stop service failed, Error %1!u!.\n",
  676. GetLastError() );
  677. }
  678. CloseServiceHandle( ghSpoolerService );
  679. ghSpoolerService = NULL;
  680. vLeaveSem();
  681. return TRUE;
  682. }
  683. BOOL
  684. SpoolerIsAlive(
  685. PSPOOLER_INFORMATION pSpoolerInfo
  686. )
  687. /*++
  688. Routine Description:
  689. Expensive check to see if the spooler is still alive.
  690. Arguments:
  691. Return Value:
  692. TRUE - Spooler is alive, and critical section successfully acquired.
  693. FALSE - Spooler is dead.
  694. --*/
  695. {
  696. #ifdef USE_ISALIVE_THREAD
  697. HANDLE hThread;
  698. DWORD dwThreadId;
  699. DWORD dwExitCode;
  700. //
  701. // RPC to spooler.
  702. //
  703. SPLASSERT( pSpoolerInfo->hSpooler );
  704. vAddRef( pSpoolerInfo );
  705. //
  706. // Create a worker thread to start the spooler and poll.
  707. //
  708. hThread = CreateThread( NULL,
  709. 0,
  710. SpoolerIsAliveThread,
  711. (PVOID)pSpoolerInfo,
  712. 0,
  713. &dwThreadId );
  714. if( !hThread ){
  715. DBGMSG( DBG_WARN,
  716. ( "SpoolerOnline: CreateThread failed %d\n", GetLastError() ));
  717. vDecRef( pSpoolerInfo );
  718. return FALSE;
  719. }
  720. WaitForSingleObject( hThread, ISALIVE_WAIT_TIME );
  721. if( !GetExitCodeThread( hThread, &dwExitCode )){
  722. dwExitCode = GetLastError();
  723. }
  724. CloseHandle( hThread );
  725. DBGMSG( DBG_TRACE,
  726. ( "SpoolerIsAlive: h=%x s=%d\n",
  727. pSpoolerInfo->hSpooler, dwExitCode ));
  728. return dwExitCode == ERROR_SUCCESS;
  729. #else // Don't use thread.
  730. BOOL bIsAlive;
  731. //
  732. // RPC to spooler.
  733. //
  734. SPLASSERT( pSpoolerInfo->hSpooler );
  735. bIsAlive = ClusterSplIsAlive( pSpoolerInfo->hSpooler );
  736. DBGMSG( DBG_TRACE,
  737. ( "SpoolerIsAlive: h=%x s=%x,%d\n",
  738. pSpoolerInfo->hSpooler, bIsAlive, GetLastError() ));
  739. return bIsAlive;
  740. #endif
  741. }
  742. BOOL
  743. SpoolerLooksAlive(
  744. PSPOOLER_INFORMATION pSpoolerInfo
  745. )
  746. /*++
  747. Routine Description:
  748. Quick check to see if the spooler is still alive.
  749. Arguments:
  750. Return Value:
  751. TRUE - Looks alive.
  752. FALSE - Looks dead.
  753. --*/
  754. {
  755. DWORD dwState;
  756. if( !QuerySpoolerState( &dwState )){
  757. DBGMSG( DBG_WARN,
  758. ( "SpoolerLooksAlive: SpoolerStatus failed %d\n",
  759. GetLastError() ));
  760. (pSpoolerInfo->pfnLogEvent)(
  761. pSpoolerInfo->ResourceHandle,
  762. LOG_ERROR,
  763. L"Query Service Status failed %1!u!.\n",
  764. GetLastError());
  765. return FALSE;
  766. }
  767. //
  768. // Now check the status of the service
  769. //
  770. if(( dwState != SERVICE_RUNNING ) &&
  771. ( dwState != SERVICE_START_PENDING )){
  772. DBGMSG( DBG_WARN,
  773. ( "SpoolerLooksAlive: QueryServiceStatus bad state %d\n",
  774. dwState ));
  775. (pSpoolerInfo->pfnLogEvent)(
  776. pSpoolerInfo->ResourceHandle,
  777. LOG_ERROR,
  778. L"Failed the IsAlive test. Current State is %1!u!.\n",
  779. dwState );
  780. return FALSE;
  781. }
  782. return TRUE;
  783. }
  784. /*++
  785. Routine Name
  786. SpoolerWriteClusterUpgradedKey
  787. Routine Description:
  788. After the first reboot following an upgrade of a node, the cluster
  789. service informs the resdll that a version change occured. At this
  790. time out spooler resource may be running on another node or may
  791. not be actie at all. Thus we write a value in the local registry.
  792. When the cluster spooler resource fails over on this machine it
  793. will query for that value to know if it needs to preform post
  794. upgrade operations, like upgrading the printer drivers.
  795. Arguments:
  796. pszResourceID - string representation of the GUID of the resoruce
  797. Return Value:
  798. Win32 error code
  799. --*/
  800. DWORD
  801. SpoolerWriteClusterUpgradedKey(
  802. IN LPCWSTR pszResourceID
  803. )
  804. {
  805. DWORD dwError = ERROR_INVALID_PARAMETER;
  806. HKEY hRootKey = NULL;
  807. HKEY hUpgradeKey = NULL;
  808. if (pszResourceID &&
  809. (dwError = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
  810. SPLREG_CLUSTER_LOCAL_ROOT_KEY,
  811. 0,
  812. NULL,
  813. 0,
  814. KEY_WRITE,
  815. NULL,
  816. &hRootKey,
  817. NULL)) == ERROR_SUCCESS &&
  818. (dwError = RegCreateKeyEx(hRootKey,
  819. SPLREG_CLUSTER_UPGRADE_KEY,
  820. 0,
  821. NULL,
  822. 0,
  823. KEY_WRITE,
  824. NULL,
  825. &hUpgradeKey,
  826. NULL)) == ERROR_SUCCESS)
  827. {
  828. DWORD dwValue = 1;
  829. dwError = RegSetValueEx(hUpgradeKey, pszResourceID, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
  830. }
  831. if (hUpgradeKey) RegCloseKey(hUpgradeKey);
  832. if (hRootKey) RegCloseKey(hRootKey);
  833. return dwError;
  834. }
  835. /*++
  836. Routine Name
  837. WipeDirectory
  838. Routine Description:
  839. Remove a directory tree.
  840. Arguments:
  841. pszDir - directory to remove
  842. Return Value:
  843. None
  844. --*/
  845. VOID
  846. WipeDirectory(
  847. IN LPCWSTR pszDir
  848. )
  849. {
  850. WCHAR szBuf[MAX_PATH];
  851. WIN32_FIND_DATA FindData;
  852. HANDLE hFind;
  853. LPCWSTR pszAllFiles = L"\\*.*";
  854. if (pszDir && *pszDir)
  855. {
  856. //
  857. // Sanity check. The string that we are going to construct must
  858. // fit in the buffer.
  859. //
  860. if (wcslen(pszDir) + wcslen(pszAllFiles) + 1 <= MAX_PATH)
  861. {
  862. wcscpy(szBuf, pszDir);
  863. wcscat(szBuf, L"\\*.*");
  864. //
  865. // Find first file that meets the criteria
  866. //
  867. if ((hFind = FindFirstFile(szBuf, &FindData)) != INVALID_HANDLE_VALUE)
  868. {
  869. do
  870. {
  871. //
  872. // Sanity check to prevent buffer overrun
  873. // length(pszdir) + "\" + length(cFileName) + NULL <= MAX_PATH
  874. //
  875. if (wcslen(pszDir) + 1 + wcslen(FindData.cFileName) + 1 <= MAX_PATH)
  876. {
  877. wcscpy(szBuf, pszDir);
  878. wcscat(szBuf, L"\\");
  879. wcscat(szBuf, FindData.cFileName);
  880. //
  881. // Search for the rest of the files. We are interested in files
  882. // which are not directories
  883. //
  884. if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  885. {
  886. if (FindData.cFileName[0] != L'.')
  887. {
  888. WipeDirectory(szBuf);
  889. }
  890. }
  891. else
  892. {
  893. DeleteFile(szBuf);
  894. }
  895. }
  896. } while (FindNextFile(hFind, &FindData));
  897. FindClose(hFind);
  898. }
  899. }
  900. RemoveDirectory(pszDir);
  901. }
  902. }
  903. /*++
  904. Routine Name
  905. SpoolerCleanDriverDirectory
  906. Routine Description:
  907. Thie routine is called when the spooler resource is deleted.
  908. It will remove from the cluster disk the directory where the
  909. spooler keeps the pirnter drivers.
  910. Arguments:
  911. hKey - handle to the Parameters key of the spooler resource
  912. Return Value:
  913. Win32 error code
  914. --*/
  915. DWORD
  916. SpoolerCleanDriverDirectory(
  917. IN HKEY hKey
  918. )
  919. {
  920. DWORD dwError;
  921. DWORD dwType;
  922. DWORD cbNeeded = 0;
  923. //
  924. // We read the string value (private property) that was written
  925. // by the spooler. This is the directory to delete.
  926. // Note that ClusterRegQueryValue has a bizzare behaviour. If
  927. // you pass NULL for the buffer and the value exists, then it
  928. // doesn't return ERROR_MORE_DATA, but ERROR_SUCCESS
  929. // Should the reg type not be reg_Sz, then we can't do anything
  930. // in this function.
  931. //
  932. if ((dwError = ClusterRegQueryValue(hKey,
  933. SPLREG_CLUSTER_DRIVER_DIRECTORY,
  934. &dwType,
  935. NULL,
  936. &cbNeeded)) == ERROR_SUCCESS &&
  937. dwType == REG_SZ)
  938. {
  939. LPWSTR pszDir;
  940. if (pszDir = (LPWSTR)LocalAlloc(LMEM_FIXED, cbNeeded))
  941. {
  942. if ((dwError = ClusterRegQueryValue(hKey,
  943. SPLREG_CLUSTER_DRIVER_DIRECTORY,
  944. &dwType,
  945. (LPBYTE)pszDir,
  946. &cbNeeded)) == ERROR_SUCCESS)
  947. {
  948. //
  949. // Remove the directory
  950. //
  951. WipeDirectory(pszDir);
  952. }
  953. LocalFree(pszDir);
  954. }
  955. else
  956. {
  957. dwError = GetLastError();
  958. }
  959. }
  960. return dwError;
  961. }