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.

1012 lines
30 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. status.cxx
  5. Abstract:
  6. This file contains functions that are involved with setting the
  7. status for a service in the service controller.
  8. RSetServiceStatus
  9. RemovalThread
  10. RI_ScSetServiceBitsA
  11. RI_ScSetServiceBitsW
  12. ScRemoveServiceBits
  13. ScInitServerAnnounceFcn
  14. Author:
  15. Dan Lafferty (danl) 20-Mar-1991
  16. Environment:
  17. User Mode -Win32
  18. Revision History:
  19. 10-Mar-1998 jschwart
  20. Add code to RSetServiceStatus to notify Plug-and-Play when a service
  21. registers/deregisters for hardware profile change notifications.
  22. 08-Jan-1997 anirudhs
  23. RSetServiceStatus: Fix obscure locking bugs found by the new locking
  24. scheme. When a service stops, we sometimes need more restrictive
  25. locks than was previously assumed.
  26. 11-Apr-1996 anirudhs
  27. RSetServiceStatus: Notify NDIS when a service that belongs to a
  28. group NDIS is interested in starts running.
  29. 21-Nov-1995 anirudhs
  30. RI_ScSetServiceBitsW: Catch access violations caused if the
  31. hServiceStatus parameter is invalid.
  32. 23-Mar-1994 danl
  33. RSetServiceStatus: Only set the PopupStartFail flag when we have
  34. actually logged an event. This means that now an auto-started service
  35. can quietly stop itself without reporting an exit code, and we will not
  36. log an event or put up a popup.
  37. However, we will still put up a popup if a service stops itself during
  38. auto-start, and it provides an exit code.
  39. 20-Oct-1993 danl
  40. RSetServiceStatus: Only update the status if the service process is
  41. still running. It is possible that the status could have been blocked
  42. when the process unexpectedly terminated, and updated the status to
  43. stopped. In this case, the status that was blocked contains
  44. out-of-date information.
  45. 10-Dec-1992 danl
  46. RI_ScSetServiceBitsW & ScRemoveServiceBits no longer hold locks when
  47. calling ScNetServerSetServiceBits.
  48. 03-Nov-1992 danl
  49. RSetServiceStatus: Remove code that sets ExitCode to ERROR_GEN_FAILURE
  50. when a service transitions directly from START_PENDING to STOPPED with
  51. out an exit code of its own.
  52. 25-Aug-1992 danl
  53. RSetServiceStatus: Allow dirty checkpoint and exitcode fields.
  54. Force them clean.
  55. 19-Jun-1991 danl
  56. Allow ExitCodes to be specified for the SERVICE_STOP_PENDING state.
  57. Prior to this they were only allowed for the SERVICE_STOP state.
  58. 20-Mar-1991 danl
  59. created
  60. --*/
  61. //
  62. // INCLUDES
  63. //
  64. #include "precomp.hxx"
  65. #include <tstr.h> // Unicode string macros
  66. #include "valid.h" // ScCurrentStateInvalid
  67. #include "depend.h" // ScNotifyChangeState
  68. #include "driver.h" // ScNotifyNdis
  69. #include <lmcons.h> // NET_API_STATUS
  70. #include <lmerr.h> // NERR_Success
  71. #include <lmsname.h> // contains service name
  72. #include <lmserver.h> // SV_TYPE_NT (server announcement bits)
  73. #include <srvann.h> // I_NetServerSetServiceBits
  74. #include "control.h" // SERVICE_SET_STATUS
  75. extern "C" {
  76. #include <cfgmgr32.h>
  77. #include "cfgmgrp.h"
  78. }
  79. //
  80. // GLOBALS
  81. //
  82. //
  83. // This is a special storage place for the OR'd server announcement
  84. // bit masks. NOTE: This is only read or written to when the
  85. // service database exclusive lock is held.
  86. //
  87. DWORD GlobalServerAnnounce = SV_TYPE_NT;
  88. //
  89. // The following ServerHandle is the handle returned from the
  90. // LoadLibrary call which loaded netapi.dll. The entrypoint for
  91. // I_NetServerSetServiceBits is then found and stored in the
  92. // global location described below.
  93. //
  94. HMODULE ScGlobalServerHandle;
  95. extern "C" typedef NET_API_STATUS (*SETSBPROC) (
  96. IN LPTSTR servername,
  97. IN LPTSTR transport OPTIONAL,
  98. IN DWORD servicebits,
  99. IN DWORD updateimmediately
  100. );
  101. SETSBPROC ScNetServerSetServiceBits = NULL;
  102. //
  103. // Function Prototypes (local functions)
  104. //
  105. DWORD
  106. RemovalThread(
  107. IN LPSERVICE_RECORD ServiceRecord
  108. );
  109. DWORD
  110. RSetServiceStatus(
  111. IN SC_RPC_HANDLE hService,
  112. IN LPSERVICE_STATUS lpServiceStatus
  113. )
  114. /*++
  115. Routine Description:
  116. This function is called by services when they need to inform the
  117. service controller of a change in state.
  118. Arguments:
  119. hService - A service handle that has been given to the service
  120. with SERVICE_SET_STATUS access granted.
  121. lpServiceStatus - A pointer to a SERVICE_STATUS structure. This
  122. reflects the latest status of the calling service.
  123. Return Value:
  124. ERROR_INVALID_HANDLE - The specified handle is invalid.
  125. ERROR_INVALID_SERVICE_STATUS - The specified service status is invalid.
  126. Note:
  127. --*/
  128. {
  129. DWORD status = NO_ERROR;
  130. LPSERVICE_RECORD serviceRecord;
  131. LPSERVICE_RECORD hServiceStatus;
  132. DWORD threadId;
  133. HANDLE threadHandle;
  134. DWORD oldState;
  135. DWORD oldType;
  136. BOOL groupListLocked = FALSE;
  137. SC_LOG(TRACE,"In RSetServiceStatus routine\n",0);
  138. //
  139. // Check the handle.
  140. //
  141. if (!ScIsValidServiceHandle(hService))
  142. {
  143. return ERROR_INVALID_HANDLE;
  144. }
  145. if (((LPSC_HANDLE_STRUCT)hService)->AccessGranted != SERVICE_SET_STATUS)
  146. {
  147. return(ERROR_INVALID_HANDLE);
  148. }
  149. hServiceStatus = ((LPSC_HANDLE_STRUCT) hService)->Type.ScServiceObject.ServiceRecord;
  150. //
  151. // Validate the fields in the service status structure.
  152. //
  153. if (ScCurrentStateInvalid(lpServiceStatus->dwCurrentState))
  154. {
  155. SC_LOG2(ERROR, "RSetServiceStatus: " FORMAT_LPWSTR " set invalid "
  156. " dwCurrentState x%08lx\n",
  157. ((LPSERVICE_RECORD) hServiceStatus)->DisplayName,
  158. lpServiceStatus->dwCurrentState);
  159. ScLogEvent(
  160. NEVENT_BAD_SERVICE_STATE,
  161. ((LPSERVICE_RECORD) hServiceStatus)->DisplayName,
  162. lpServiceStatus->dwCurrentState
  163. );
  164. return(ERROR_INVALID_DATA);
  165. }
  166. if( (SERVICE_STATUS_TYPE_INVALID(lpServiceStatus->dwServiceType)) ||
  167. (CONTROLS_ACCEPTED_INVALID(lpServiceStatus->dwControlsAccepted)) )
  168. {
  169. SC_LOG3(ERROR,
  170. "RSetServiceStatus: Error in one of the following for service %ws\n"
  171. "\tServiceType %#x\n"
  172. "\tControlsAccepted %#x\n",
  173. ((LPSERVICE_RECORD) hServiceStatus)->DisplayName,
  174. lpServiceStatus->dwServiceType,
  175. lpServiceStatus->dwControlsAccepted);
  176. return(ERROR_INVALID_DATA);
  177. }
  178. //
  179. // If the service is not in the stopped or stop-pending state, then the
  180. // exit code fields should be 0.
  181. //
  182. if (((lpServiceStatus->dwCurrentState != SERVICE_STOPPED) &&
  183. (lpServiceStatus->dwCurrentState != SERVICE_STOP_PENDING))
  184. &&
  185. ((lpServiceStatus->dwWin32ExitCode != 0) ||
  186. (lpServiceStatus->dwServiceSpecificExitCode != 0)) )
  187. {
  188. SC_LOG(TRACE,"RSetServiceStatus: ExitCode fields not cleaned up "
  189. "when state indicates SERVICE_STOPPED\n",0);
  190. lpServiceStatus->dwWin32ExitCode = 0;
  191. lpServiceStatus->dwServiceSpecificExitCode = 0;
  192. }
  193. //
  194. // If the service is not in a pending state, then the waitHint and
  195. // checkPoint fields should be 0.
  196. //
  197. if ( ( (lpServiceStatus->dwCurrentState == SERVICE_STOPPED) ||
  198. (lpServiceStatus->dwCurrentState == SERVICE_RUNNING) ||
  199. (lpServiceStatus->dwCurrentState == SERVICE_PAUSED) )
  200. &&
  201. ( (lpServiceStatus->dwCheckPoint != 0) ||
  202. (lpServiceStatus->dwWaitHint != 0) ) )
  203. {
  204. SC_LOG(TRACE,"RSetServiceStatus: Dirty Checkpoint and WaitHint fields\n",0);
  205. lpServiceStatus->dwCheckPoint = 0;
  206. lpServiceStatus->dwWaitHint = 0;
  207. }
  208. //
  209. // If the service has stopped, ScRemoveServiceBits needs the service
  210. // list lock with shared access.
  211. //
  212. if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED)
  213. {
  214. ScServiceListLock.GetShared();
  215. }
  216. //
  217. // Update the service record. Exclusive locks are required for this.
  218. //
  219. // NOTICE that we don't destroy the ServiceType information that was
  220. // in the service record.
  221. //
  222. serviceRecord = (LPSERVICE_RECORD)hServiceStatus;
  223. SC_LOG(TRACE,"RSetServiceStatus: Status field accepted, service %ws\n",
  224. serviceRecord->ServiceName);
  225. ScServiceRecordLock.GetExclusive();
  226. //
  227. // If the service stopped, and its update flag is set (its config was
  228. // changed while it was running) we may need the group list lock in
  229. // ScRemoveService. So release the locks and reacquire them after
  230. // getting the group list lock. This is a rare occurrence, hence not
  231. // optimized.
  232. //
  233. if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED &&
  234. UPDATE_FLAG_IS_SET(serviceRecord))
  235. {
  236. ScServiceRecordLock.Release();
  237. ScServiceListLock.Release();
  238. ScGroupListLock.GetExclusive();
  239. ScServiceListLock.GetShared();
  240. ScServiceRecordLock.GetExclusive();
  241. groupListLocked = TRUE;
  242. }
  243. oldState = serviceRecord->ServiceStatus.dwCurrentState;
  244. oldType = serviceRecord->ServiceStatus.dwServiceType;
  245. //
  246. // It is possible that while we were blocked waiting for the lock,
  247. // that a running service could have terminated (Due to the process
  248. // terminating). So we need to look for "late" status updates, and
  249. // filter them out. If the ImageRecord pointer is NULL, then the
  250. // Service has Terminated. Otherwise update the status.
  251. //
  252. if (serviceRecord->ImageRecord != NULL)
  253. {
  254. //
  255. // Don't bother notifying PnP if the system is shutting down. This
  256. // prevents a deadlock where we can get stuck calling PnP, which is
  257. // stuck calling into the Eventlog, which is stuck calling into us.
  258. //
  259. if (!ScShutdownInProgress)
  260. {
  261. DWORD dwControlFlags;
  262. DWORD dwBitMask;
  263. dwControlFlags = serviceRecord->ServiceStatus.dwControlsAccepted
  264. &
  265. (SERVICE_ACCEPT_HARDWAREPROFILECHANGE |
  266. SERVICE_ACCEPT_POWEREVENT);
  267. dwBitMask = lpServiceStatus->dwControlsAccepted ^ dwControlFlags;
  268. if (dwBitMask
  269. ||
  270. ((lpServiceStatus->dwCurrentState == SERVICE_STOPPED) && dwControlFlags))
  271. {
  272. DWORD dwRetVal;
  273. //
  274. // The service is either changing its registration status for
  275. // hardware profile change notifications or power OR is stopping,
  276. // so inform PnP of this. On service stop, deregister the service
  277. // if it accepts power or hardware profile change notifications.
  278. //
  279. dwRetVal = RegisterServiceNotification(
  280. (SERVICE_STATUS_HANDLE)hServiceStatus,
  281. serviceRecord->ServiceName,
  282. lpServiceStatus->dwCurrentState != SERVICE_STOPPED ?
  283. lpServiceStatus->dwControlsAccepted : 0,
  284. (lpServiceStatus->dwCurrentState == SERVICE_STOPPED));
  285. if (dwRetVal != CR_SUCCESS)
  286. {
  287. SC_LOG3(ERROR,
  288. "Hardware profile change and power %sregistration failed "
  289. "for service %ws with config manager error %d\n",
  290. (lpServiceStatus->dwControlsAccepted &
  291. (SERVICE_ACCEPT_HARDWAREPROFILECHANGE |
  292. SERVICE_ACCEPT_POWEREVENT)) ?
  293. "" :
  294. "de",
  295. serviceRecord->ServiceName,
  296. dwRetVal);
  297. }
  298. }
  299. }
  300. //
  301. // Update to the new status
  302. //
  303. RtlCopyMemory(&(serviceRecord->ServiceStatus),
  304. lpServiceStatus,
  305. sizeof(SERVICE_STATUS));
  306. serviceRecord->ServiceStatus.dwServiceType = oldType;
  307. }
  308. //
  309. // For dependency handling
  310. //
  311. if ((serviceRecord->ServiceStatus.dwCurrentState == SERVICE_RUNNING ||
  312. serviceRecord->ServiceStatus.dwCurrentState == SERVICE_STOPPED ||
  313. serviceRecord->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING) &&
  314. oldState == SERVICE_START_PENDING)
  315. {
  316. if (serviceRecord->ServiceStatus.dwCurrentState == SERVICE_STOPPED ||
  317. serviceRecord->ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
  318. {
  319. serviceRecord->StartState = SC_START_FAIL;
  320. SC_LOG(DEPEND, "%ws START_PENDING -> FAIL\n", serviceRecord->ServiceName);
  321. }
  322. else if (serviceRecord->ServiceStatus.dwCurrentState == SERVICE_RUNNING)
  323. {
  324. serviceRecord->StartState = SC_START_SUCCESS;
  325. SC_LOG(DEPEND, "%ws START_PENDING -> RUNNING\n", serviceRecord->ServiceName);
  326. #ifdef TIMING_TEST
  327. DbgPrint("[SC_TIMING] TickCount for RUNNING service \t%ws\t%d\n",
  328. serviceRecord->ServiceName, GetTickCount());
  329. #endif // TIMING_TEST
  330. }
  331. //
  332. // Tell the dependency handling code that a start-pending
  333. // service is now running or stopped.
  334. //
  335. ScNotifyChangeState();
  336. }
  337. //
  338. // Note: We no longer need an exclusive lock on the service records,
  339. // but we still need at least a shared lock, since we read the
  340. // DisplayName and ErrorControl fields below. (If we didn't have a
  341. // shared lock, ChangeServiceConfig could change the fields under
  342. // us.) Later, we call ScRemoveServiceBits and ScRemoveService,
  343. // which acquire an exclusive lock, which is problematic if we
  344. // already have a shared lock.
  345. // To keep things simple, we just hold onto the exclusive lock.
  346. //
  347. //
  348. // Log an event about the service's new state if appropriate. Don't
  349. // do this during auto-start to avoid hurting boot performance and
  350. // filling the log with a start event for every auto-start service.
  351. //
  352. if (!ScAutoStartInProgress
  353. &&
  354. lpServiceStatus->dwCurrentState != oldState
  355. &&
  356. IS_STATUS_LOGGABLE(lpServiceStatus->dwCurrentState))
  357. {
  358. ScLogControlEvent(NEVENT_SERVICE_STATUS_SUCCESS,
  359. serviceRecord->DisplayName,
  360. lpServiceStatus->dwCurrentState);
  361. }
  362. //
  363. // If the new status indicates that the service has just started,
  364. // tell NDIS to issue the PNP notifications about this service's arrival,
  365. // if it belongs to one of the groups NDIS is interested in.
  366. //
  367. if ((lpServiceStatus->dwCurrentState == SERVICE_RUNNING) &&
  368. (oldState != SERVICE_RUNNING))
  369. {
  370. ScNotifyNdis(serviceRecord);
  371. }
  372. //
  373. // If the new status indicates that the service has just stopped,
  374. // we need to check to see if there are any other services running
  375. // in the service process. If not, then we can ask the service to
  376. // terminate. Another thread is spawned to handle this since we need
  377. // to return from this call in order to allow the service to complete
  378. // its shutdown.
  379. //
  380. else if ((lpServiceStatus->dwCurrentState == SERVICE_STOPPED) &&
  381. (oldState != SERVICE_STOPPED))
  382. {
  383. if (lpServiceStatus->dwWin32ExitCode != NO_ERROR)
  384. {
  385. if (lpServiceStatus->dwWin32ExitCode != ERROR_SERVICE_SPECIFIC_ERROR)
  386. {
  387. ScLogEvent(
  388. NEVENT_SERVICE_EXIT_FAILED,
  389. serviceRecord->DisplayName,
  390. lpServiceStatus->dwWin32ExitCode
  391. );
  392. }
  393. else
  394. {
  395. ScLogEvent(
  396. NEVENT_SERVICE_EXIT_FAILED_SPECIFIC,
  397. serviceRecord->DisplayName,
  398. lpServiceStatus->dwServiceSpecificExitCode
  399. );
  400. }
  401. //
  402. // For popup after user has logged on to indicate that some service
  403. // started at boot has failed.
  404. //
  405. if (serviceRecord->ErrorControl == SERVICE_ERROR_NORMAL ||
  406. serviceRecord->ErrorControl == SERVICE_ERROR_SEVERE ||
  407. serviceRecord->ErrorControl == SERVICE_ERROR_CRITICAL)
  408. {
  409. ScPopupStartFail = TRUE;
  410. }
  411. }
  412. //
  413. // Clear the server announcement bits in the global location
  414. // for this service.
  415. //
  416. ScRemoveServiceBits(serviceRecord);
  417. //
  418. // If this is the last service in the process, then delete the
  419. // process handle from the ProcessWatcher list.
  420. //
  421. if ((serviceRecord->ImageRecord != NULL) &&
  422. (serviceRecord->ImageRecord->ServiceCount == 1))
  423. {
  424. NTSTATUS ntStatus;
  425. //
  426. // Check vs. NULL in case the work item registration failed.
  427. // Deregister here so the process cleanup routine doesn't get
  428. // called if the service process exits between now and when we
  429. // call ScRemoveService.
  430. //
  431. if (serviceRecord->ImageRecord->ObjectWaitHandle != NULL) {
  432. ntStatus = RtlDeregisterWait(serviceRecord->ImageRecord->ObjectWaitHandle);
  433. if (NT_SUCCESS(ntStatus)) {
  434. serviceRecord->ImageRecord->ObjectWaitHandle = NULL;
  435. }
  436. else {
  437. SC_LOG1(ERROR,
  438. "RSetServiceStatus: RtlDeregisterWait failed 0x%x\n",
  439. ntStatus);
  440. }
  441. }
  442. }
  443. //
  444. // Even though the service said it stopped, save a status of
  445. // STOP_PENDING. ScRemoveService will set it to STOPPED. This
  446. // is to prevent anyone else from trying to restart the service
  447. // between the time that our thread releases the locks here and
  448. // the time that ScRemoveService (in the thread we are about to
  449. // create) acquires the locks. ScRemoveService must get to the
  450. // service first, because it may need to process an UPDATE_FLAG
  451. // set on the service before it's OK to restart it.
  452. //
  453. serviceRecord->ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
  454. SC_LOG(TRACE,
  455. "RSetServiceStatus:Create a thread to run ScRemoveService\n",0);
  456. threadHandle = CreateThread (
  457. NULL, // Thread Attributes.
  458. 0L, // Stack Size
  459. (LPTHREAD_START_ROUTINE)RemovalThread, // lpStartAddress
  460. (LPVOID)serviceRecord, // lpParameter
  461. 0L, // Creation Flags
  462. &threadId); // lpThreadId
  463. if (threadHandle == (HANDLE) NULL)
  464. {
  465. SC_LOG(ERROR,"RSetServiceStatus:CreateThread failed %d\n",
  466. GetLastError());
  467. //
  468. // If a thread couldn't be created to remove the service, it is removed
  469. // in the context of this thread. The result of this is a somewhat
  470. // dirty termination. The service record will be removed from the
  471. // installed database. If this was the last service in the process,
  472. // the process will terminate before we return to the thread. Note that
  473. // we must release the locks before calling ScRemoveService since the
  474. // first thing it does is to acquire the list lock -- not releasing here
  475. // will cause deadlock (bug #103102). The GroupListLock is not released
  476. // since ScRemoveService will acquire it in the same thread -- instead
  477. // of just releasing and then immediately reacquiring, release afterwards.
  478. //
  479. ScServiceRecordLock.Release();
  480. ScServiceListLock.Release();
  481. SC_LOG0(TRACE,"Attempting an in-thread removal in RSetServiceStatus\n");
  482. status = ScRemoveService(serviceRecord);
  483. if (groupListLocked)
  484. {
  485. ScGroupListLock.Release();
  486. }
  487. return status;
  488. }
  489. else
  490. {
  491. //
  492. // The Thread Creation was successful.
  493. //
  494. SC_LOG(TRACE,"Thread Creation Success, thread id = %#lx\n",threadId);
  495. CloseHandle(threadHandle);
  496. }
  497. }
  498. //
  499. // Release the locks we got earlier
  500. //
  501. ScServiceRecordLock.Release();
  502. if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED)
  503. {
  504. ScServiceListLock.Release();
  505. if (groupListLocked)
  506. {
  507. ScGroupListLock.Release();
  508. }
  509. }
  510. SC_LOG(TRACE,"Return from RSetServiceStatus\n",0);
  511. return(status);
  512. }
  513. DWORD
  514. RemovalThread(
  515. IN LPSERVICE_RECORD ServiceRecord
  516. )
  517. /*++
  518. Routine Description:
  519. This thread is used by RSetServiceStatus to remove a service from the
  520. Service Controller's database, and also - if necessary - shut down
  521. the service process. The later step is only done if this was the last
  522. service running in that process.
  523. The use of this thread allows RSetServiceStatus to return to the service
  524. so that the service can then continue to terminate itself.
  525. We know that the service record will not go away before this thread
  526. acquires an exclusive lock on the database, because the service record's
  527. use count is not zero.
  528. Arguments:
  529. ServiceRecord - This is a pointer to the service record that is being
  530. removed.
  531. Return Value:
  532. Same as return values for RemoveService().
  533. --*/
  534. {
  535. ScRemoveService (ServiceRecord);
  536. return(0);
  537. }
  538. DWORD
  539. RI_ScSetServiceBitsA(
  540. IN SC_RPC_HANDLE hService,
  541. IN DWORD dwServiceBits,
  542. IN DWORD bSetBitsOn,
  543. IN DWORD bUpdateImmediately,
  544. IN LPSTR pszReserved
  545. )
  546. /*++
  547. Routine Description:
  548. This function Or's the Service Bits that are passed in - into a
  549. global bitmask maintained by the service controller. Everytime this
  550. function is called, we check to see if the server service is running.
  551. If it is, then we call an internal entry point in the server service
  552. to pass in the complete bitmask.
  553. This function also Or's the Service Bits into the ServerAnnounce
  554. element in the service's ServiceRecord.
  555. NOTE: The exclusive database lock is obtained and held while the
  556. service record is being read, and while the GlobalServerAnnounce
  557. bits are set.
  558. Arguments:
  559. Return Value:
  560. NO_ERROR - The operation was completely successful. The information
  561. may or may not be delivered to the Server depending on if it is
  562. running or not.
  563. or any error returned from the server service I_NetServerSetServiceBits
  564. function.
  565. Note:
  566. --*/
  567. {
  568. if (ScShutdownInProgress) {
  569. return(ERROR_SHUTDOWN_IN_PROGRESS);
  570. }
  571. if (pszReserved != NULL) {
  572. return (ERROR_INVALID_PARAMETER);
  573. }
  574. return RI_ScSetServiceBitsW((SC_RPC_HANDLE)hService,
  575. dwServiceBits,
  576. bSetBitsOn,
  577. bUpdateImmediately,
  578. NULL);
  579. }
  580. DWORD
  581. RI_ScSetServiceBitsW(
  582. IN SC_RPC_HANDLE hService,
  583. IN DWORD dwServiceBits,
  584. IN DWORD bSetBitsOn,
  585. IN DWORD bUpdateImmediately,
  586. IN LPWSTR pszReserved
  587. )
  588. /*++
  589. Routine Description:
  590. This function Or's the Service Bits that are passed in - into a
  591. global bitmask maintained by the service controller. Everytime this
  592. function is called, we check to see if the server service is running.
  593. If it is, then we call an internal entry point in the server service
  594. to pass in the complete bitmask.
  595. This function also Or's the Service Bits into the ServerAnnounce
  596. element in the service's ServiceRecord.
  597. NOTE: The exclusive database lock is obtained and held while the
  598. service record is being read, and while the GlobalServerAnnounce
  599. bits are set.
  600. Arguments:
  601. Return Value:
  602. NO_ERROR - The operation was completely successful.
  603. ERROR_GEN_FAILURE - The server service is there, but the call to
  604. update it failed.
  605. Note:
  606. --*/
  607. {
  608. DWORD status = NO_ERROR;
  609. LPSERVICE_RECORD serviceRecord;
  610. LPWSTR serverServiceName;
  611. DWORD serviceState;
  612. if (ScShutdownInProgress) {
  613. return(ERROR_SHUTDOWN_IN_PROGRESS);
  614. }
  615. if (pszReserved != NULL) {
  616. return (ERROR_INVALID_PARAMETER);
  617. }
  618. //
  619. // Check the handle.
  620. //
  621. if (!ScIsValidServiceHandle(hService))
  622. {
  623. return ERROR_INVALID_HANDLE;
  624. }
  625. if (((LPSC_HANDLE_STRUCT)hService)->AccessGranted != SERVICE_SET_STATUS) {
  626. return(ERROR_INVALID_HANDLE);
  627. }
  628. if (ScNetServerSetServiceBits == (SETSBPROC)NULL) {
  629. if (! ScInitServerAnnounceFcn()) {
  630. return(ERROR_NO_NETWORK);
  631. }
  632. }
  633. serverServiceName = SERVICE_SERVER;
  634. CServiceListSharedLock LLock;
  635. CServiceRecordExclusiveLock RLock;
  636. serviceRecord = ((LPSC_HANDLE_STRUCT) hService)->Type.ScServiceObject.ServiceRecord;
  637. if (bSetBitsOn) {
  638. //
  639. // Set the bits in the global location.
  640. //
  641. GlobalServerAnnounce |= dwServiceBits;
  642. //
  643. // Set the bits in the service record.
  644. //
  645. serviceRecord->ServerAnnounce |= dwServiceBits;
  646. }
  647. else {
  648. //
  649. // Clear the bits in the global location.
  650. //
  651. GlobalServerAnnounce &= ~dwServiceBits;
  652. //
  653. // Clear the bits in the service record.
  654. //
  655. serviceRecord->ServerAnnounce &= ~dwServiceBits;
  656. }
  657. //
  658. // If the server service is running, then send the Global mask to
  659. // the server service.
  660. //
  661. status = ScGetNamedServiceRecord(
  662. serverServiceName,
  663. &serviceRecord);
  664. if (status == NO_ERROR) {
  665. serviceState = serviceRecord->ServiceStatus.dwCurrentState;
  666. if (serviceState == SERVICE_RUNNING) {
  667. status = ScNetServerSetServiceBits(
  668. NULL, // ServerName
  669. NULL, // TransportName
  670. GlobalServerAnnounce,
  671. bUpdateImmediately);
  672. if (status != NERR_Success) {
  673. SC_LOG(ERROR,"I_ScSetServiceBits: I_NetServerSetServiceBits failed %lu\n",
  674. status);
  675. }
  676. else {
  677. SC_LOG(TRACE,"I_ScSetServiceBits: I_NetServerSetServiceBits success\n",0);
  678. }
  679. }
  680. }
  681. else {
  682. status = NO_ERROR;
  683. }
  684. SC_LOG(TRACE,"I_ScSetServiceBits: GlobalServerAnnounce = 0x%lx\n",
  685. GlobalServerAnnounce);
  686. return(status);
  687. }
  688. DWORD
  689. ScRemoveServiceBits(
  690. IN LPSERVICE_RECORD ServiceRecord
  691. )
  692. /*++
  693. Routine Description:
  694. This function is called when a service stops running. It looks in
  695. the service record for any server announcement bits that are set
  696. and turns them off in GlobalServerAnnounce. The ServerAnnounce
  697. element in the service record is set to 0.
  698. Arguments:
  699. ServiceRecord - This is a pointer to the service record that
  700. has changed to the stopped state.
  701. Return Value:
  702. The status returned from I_NetServerSetServiceBits.
  703. --*/
  704. {
  705. DWORD status = NO_ERROR;
  706. LPSERVICE_RECORD serverServiceRecord;
  707. DWORD serviceState;
  708. if (ScNetServerSetServiceBits == (SETSBPROC)NULL) {
  709. if (! ScInitServerAnnounceFcn()) {
  710. return(ERROR_NO_NETWORK);
  711. }
  712. }
  713. if (ServiceRecord->ServerAnnounce != 0) {
  714. CServiceRecordExclusiveLock RLock;
  715. //
  716. // Clear the bits in the global location.
  717. //
  718. GlobalServerAnnounce &= ~(ServiceRecord->ServerAnnounce);
  719. //
  720. // Clear the bits in the service record.
  721. //
  722. ServiceRecord->ServerAnnounce = 0;
  723. SC_LOG1(TRACE,"RemoveServiceBits: New GlobalServerAnnounce = 0x%lx\n",
  724. GlobalServerAnnounce);
  725. //
  726. // If the server service is running, then send the Global mask to
  727. // the server service.
  728. //
  729. status = ScGetNamedServiceRecord(
  730. SERVICE_SERVER,
  731. &serverServiceRecord);
  732. if (status == NO_ERROR) {
  733. serviceState = serverServiceRecord->ServiceStatus.dwCurrentState;
  734. if ( serviceState == SERVICE_RUNNING) {
  735. status = ScNetServerSetServiceBits(
  736. NULL, // ServerName
  737. NULL, // Transport name
  738. GlobalServerAnnounce,
  739. TRUE); // Update immediately.
  740. if (status != NERR_Success) {
  741. SC_LOG(ERROR,"ScRemoveServiceBits: I_NetServerSetServiceBits failed %d\n",
  742. status);
  743. }
  744. }
  745. }
  746. }
  747. return(status);
  748. }
  749. BOOL
  750. ScInitServerAnnounceFcn(
  751. VOID
  752. )
  753. /*++
  754. Routine Description:
  755. Arguments:
  756. Return Value:
  757. --*/
  758. {
  759. ScGlobalServerHandle = LoadLibraryW(L"netapi32.dll");
  760. if (ScGlobalServerHandle == NULL) {
  761. SC_LOG(ERROR,"ScInitServerAnnouncFcn: LoadLibrary failed %d\n",
  762. GetLastError());
  763. return(FALSE);
  764. }
  765. //
  766. // Use I_NetServerSetServiceBits rather than the Ex version since
  767. // it uses a special flag to prevent the RPC failure path from
  768. // calling back into services.exe and potentially causing a deadlock.
  769. //
  770. ScNetServerSetServiceBits = (SETSBPROC)GetProcAddress(
  771. ScGlobalServerHandle,
  772. "I_NetServerSetServiceBits");
  773. if (ScNetServerSetServiceBits == (SETSBPROC)NULL) {
  774. SC_LOG(ERROR,"ScInitServerAnnouncFcn: GetProcAddress failed %d\n",
  775. GetLastError());
  776. return(FALSE);
  777. }
  778. return TRUE;
  779. }