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.

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