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.

2541 lines
74 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. dataman.cxx
  5. Abstract:
  6. Contains code for the Service Control Database manager. This includes
  7. all the linked list routines. This file contains the following
  8. functions:
  9. ScGetOrderGroupList
  10. ScGetStandaloneGroupList
  11. ScGetServiceDatabase
  12. ScGetUnresolvedDependList
  13. ScActivateServiceRecord
  14. ScCreateImageRecord
  15. ScCreateServiceRecord
  16. ScFreeServiceRecord
  17. ScAddConfigInfoServiceRecord
  18. ScDecrementUseCountAndDelete
  19. ScProcessDeferredList
  20. ScFindEnumStart
  21. ScGetNamedImageRecord
  22. ScGetNamedServiceRecord
  23. ScGetDisplayNamedServiceRecord
  24. ScGetTotalNumberOfRecords
  25. ScInitDatabase
  26. ScProcessCleanup
  27. ScDeleteMarkedServices
  28. ScRemoveService
  29. ScDeleteImageRecord (internal only)
  30. ScDeactivateServiceRecord
  31. ScTerminateServiceProcess (internal only)
  32. ScUpdateServiceRecordConfig
  33. ScNotifyServiceObject
  34. Author:
  35. Dan Lafferty (danl) 04-Feb-1992
  36. Environment:
  37. User Mode -Win32
  38. Revision History:
  39. 22-Oct-1998 jschwart
  40. Convert SCM to use NT thread pool APIs
  41. 22-Oct-1997 JSchwart (after AnirudhS in _CAIRO_ 12-Apr-1995)
  42. Enabled changes made in ifdef _CAIRO_ on 12-Apr-1995.
  43. Removed ScNotifyServiceObject.
  44. 08-Jan-1997 AnirudhS
  45. Replaced ScDatabaseLockFcn and ScGroupListLock with the new
  46. locking scheme in lock.cxx. Rewrote ScDeferredList processing.
  47. 04-Dec-1996 AnirudhS
  48. Added calls to service crash recovery code.
  49. 12-Jul-1996 AnirudhS
  50. ScDecrementUseCountAndDelete: Don't actually process the deferred
  51. list in this routine, because a number of calling routines assume
  52. that the service record does NOT go away when they call this routine.
  53. Instead, process it when a database lock is released.
  54. 25-Jun-1996 AnirudhS
  55. ScProcessCleanup: Fix the use of a freed service record. Don't
  56. try to upgrade shared lock to exclusive, as it can deadlock.
  57. 25-Oct-1995 AnirudhS
  58. ScAddConfigInfoServiceRecord: Fix heap corruption bug caused by
  59. security descriptor being freed twice, the second time by
  60. ScProcessDeferredList.
  61. 20-Sep-1995 AnirudhS
  62. ScDeleteMarkedServices: Fix heap corruption bug caused by service
  63. record being deleted using LocalFree instead of HeapFree.
  64. 26-Jun-1995 AnirudhS
  65. Added ScNotifyServiceObject.
  66. 12-Apr-1995 AnirudhS
  67. Added AccountName field to image record.
  68. 21-Jan-1994 Danl
  69. ScAddConfigInfoServiceRecord: If no DisplayName, or the DisplayName
  70. is an empty string, or the DisplayName is the same as the
  71. ServiceName, then just point to the ServiceName for the DisplayName.
  72. 22-Oct-1993 Danl
  73. Moved Group and Dependency function into groupman.c.
  74. 16-Sept-1993 Danl
  75. ScProcessCleanup: Get the shared lock prior to walking through the
  76. database looking for the one to cleanup. Then get the exclusive
  77. lock to modify it. Remove assert.
  78. 12-Feb-1993 Danl
  79. ScActivateServiceRecord now increments the UseCount. This is to
  80. balance the fact that we decrement the UseCount when we
  81. deactivate the service record.
  82. 28-Aug-1992 Danl
  83. Re-Added ScGetTotalNumberOfRecords function. This is needed
  84. by the ScShutdownAllServices function.
  85. 14-Apr-1992 JohnRo
  86. Use SC_ASSERT() macro.
  87. Made changes suggested by PC-LINT.
  88. 10-Apr-1992 JohnRo
  89. Use ScImagePathsMatch() to allow mixed-case image names.
  90. Make sure DeleteFlag gets a value when service record is created.
  91. Added some assertion checks.
  92. 04-Feb-1992 Danl
  93. created
  94. --*/
  95. //
  96. // INCLUDES
  97. //
  98. #include "precomp.hxx"
  99. #include <userenv.h> // UnloadUserProfile
  100. #include <stdlib.h> // wide character c runtimes.
  101. #include <ntrpcp.h> // MIDL_user_allocate
  102. #include <control.h> // SendControl
  103. #include "scconfig.h" // ScGenerateServiceDB,ScInitSecurityProcess
  104. #include "scsec.h" // ScCreateScServiceObject
  105. #include "account.h" // ScRemoveAccount
  106. #include <sclib.h> // ScImagePathsMatch().
  107. #include "bootcfg.h" // ScDeleteRegTree().
  108. #include <strarray.h> // ScWStrArraySize
  109. extern "C" {
  110. #include <cfgmgr32.h>
  111. #include "cfgmgrp.h" // RegisterServiceNotification
  112. }
  113. //
  114. // Macros
  115. //
  116. // for every service record in the database...
  117. // (a slightly more efficient definition of this macro for use within
  118. // this file)
  119. //
  120. #undef FOR_ALL_SERVICES
  121. #define FOR_ALL_SERVICES(SR) \
  122. SC_ASSERT(ScServiceListLock.Have()); \
  123. for (LPSERVICE_RECORD SR = ServiceDatabase.Next; \
  124. SR != NULL; \
  125. SR = SR->Next)
  126. //
  127. // Defines and Typedefs
  128. //
  129. class DEFER_LIST
  130. {
  131. public:
  132. VOID Add (LPSERVICE_RECORD ServiceRecord);
  133. VOID Process();
  134. private:
  135. DWORD TotalElements; // size of ServiceRecPtr array
  136. DWORD NumElements; // numElements in array
  137. LPSERVICE_RECORD * ServiceRecordPtr; // pointer to array
  138. };
  139. //
  140. // Globals
  141. //
  142. //
  143. // These are the linked list heads for each of the databases
  144. // that are maintained.
  145. //
  146. IMAGE_RECORD ImageDatabase;
  147. SERVICE_RECORD ServiceDatabase;
  148. DWORD ScTotalNumServiceRecs;// number of services
  149. //
  150. // Service Record index number. This allows enumeration to be broken
  151. // up into several calls.
  152. //
  153. DWORD ResumeNumber;
  154. //
  155. // The ScGlobalDeferredList points to a structure that contains an
  156. // array of pointers to service records. The first two elements in
  157. // the structure contain the size and number of element information
  158. // about the array.
  159. // Access to the list is guarded by ScServiceRecordLock.
  160. //
  161. DEFER_LIST ScDeferredList;
  162. //
  163. // ServiceRecord Heap Information
  164. //
  165. // ServiceRecord Heap - is where all the service records are allocated
  166. // from.
  167. // OrderedHash Heap - Service Names can be found via a (very simple) hash
  168. // table. There is an array of pointers (one for each letter of
  169. // alphabet), where each pointer points to the top of an array of
  170. // pointers to service records. All the service records in that array
  171. // will have names beginning with the same letter. The service record
  172. // pointers will be ordered as to the frequency of access.
  173. //
  174. HANDLE ServiceRecordHeap = NULL;
  175. HANDLE OrderedHashHeap = NULL;
  176. //
  177. // Local Function Prototypes
  178. //
  179. VOID
  180. ScDeferredListWorkItem(
  181. IN PVOID pContext
  182. );
  183. //****************************************************************************/
  184. // Miscellaneous Short Functions
  185. //****************************************************************************/
  186. LPSERVICE_RECORD
  187. ScGetServiceDatabase(
  188. VOID
  189. )
  190. {
  191. return ServiceDatabase.Next;
  192. }
  193. /****************************************************************************/
  194. VOID
  195. ScActivateServiceRecord (
  196. IN LPSERVICE_RECORD ServiceRecord,
  197. IN LPIMAGE_RECORD ImageRecord
  198. )
  199. /*++
  200. Routine Description:
  201. This function can be called with or without a pointer to an ImageRecord.
  202. If it is called without the pointer to the ImageRecord, just the
  203. ServiceRecord is initialized to the START_PENDING state, and the UseCount
  204. is incremented.
  205. If it is called with the pointer to the ImageRecord, then the ImageRecord
  206. pointer is added to the ServiceRecord, and the ImageUseCount
  207. is incremented.
  208. Arguments:
  209. ServiceRecord - This is a pointer to the ServiceRecord that is to be
  210. activated.
  211. ImageRecord - This is a pointer to the ImageRecord that the service
  212. record will point to.
  213. Notes:
  214. This routine assumes that the Exclusive database lock has already
  215. been obtained.
  216. Return Value:
  217. returns 0. (It used to return a service count - but it wasn't used
  218. anywhere).
  219. --*/
  220. {
  221. SC_ASSERT(ScServiceRecordLock.HaveExclusive());
  222. if (ImageRecord == NULL)
  223. {
  224. ServiceRecord->ImageRecord = NULL;
  225. ServiceRecord->ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
  226. ServiceRecord->ServiceStatus.dwControlsAccepted = 0;
  227. ServiceRecord->ServiceStatus.dwWin32ExitCode = NO_ERROR;
  228. ServiceRecord->ServiceStatus.dwServiceSpecificExitCode = 0;
  229. ServiceRecord->ServiceStatus.dwCheckPoint = 0;
  230. ServiceRecord->ServiceStatus.dwWaitHint = 2000;
  231. ServiceRecord->UseCount++;
  232. SC_LOG2(USECOUNT, "ScActivateServiceRecord: " FORMAT_LPWSTR
  233. " increment USECOUNT=%lu\n",
  234. ServiceRecord->ServiceName,
  235. ServiceRecord->UseCount);
  236. }
  237. else
  238. {
  239. //
  240. // Increment the service count in the image record.
  241. //
  242. ServiceRecord->ImageRecord = ImageRecord;
  243. ServiceRecord->ImageRecord->ServiceCount++;
  244. }
  245. return;
  246. }
  247. /****************************************************************************/
  248. DWORD
  249. ScCreateImageRecord (
  250. OUT LPIMAGE_RECORD *ImageRecordPtr,
  251. IN LPWSTR ImageName,
  252. IN LPWSTR AccountName,
  253. IN DWORD Pid,
  254. IN HANDLE PipeHandle,
  255. IN HANDLE ProcessHandle,
  256. IN HANDLE TokenHandle,
  257. IN HANDLE ProfileHandle,
  258. IN DWORD ImageFlags
  259. )
  260. /*++
  261. Routine Description:
  262. This function allocates storage for a new Image Record, and links
  263. it into the Image Record Database. It also initializes all fields
  264. in the record with the passed in information.
  265. Arguments:
  266. ImageRecordPtr - This is a pointer to where the image record pointer
  267. is to be placed.
  268. ImageName - This is a pointer to a NUL terminated string containing
  269. the name of the image file.
  270. AccountName - This is either NULL (to represent the LocalSystem account)
  271. or a pointer to a NUL terminated string containing the name of the
  272. account under which the image was started.
  273. Pid - This is the Process ID for that the image is running in.
  274. PipeHandle - This is a handle to the pipe that is used to communicat
  275. with the image process.
  276. ProcessHandle - This is a handle to the image process object.
  277. TokenHandle - This is a handle to the process's logon token. It
  278. is NULL if the process runs in the LocalSystem context.
  279. ProfileHandle -
  280. ImageFlags -
  281. Return Value:
  282. NO_ERROR - The operation was successful.
  283. ERROR_NOT_ENOUGH_MEMORY - Unable to allocate buffer for the image
  284. record.
  285. ERROR_LOCKED - Exclusive access to the database could
  286. not be obtained.
  287. Note:
  288. This routine temporarily acquires the exclusive database lock.
  289. --*/
  290. {
  291. LPIMAGE_RECORD imageRecord; // The new image record pointer
  292. LPIMAGE_RECORD record ; // Temporary pointer
  293. LPWSTR stringArea; // String area in allocated buffer.
  294. TOKEN_STATISTICS TokenStats;
  295. DWORD dwError;
  296. //
  297. // Allocate space for the new record (including the string)
  298. //
  299. imageRecord = (LPIMAGE_RECORD)LocalAlloc(LMEM_ZEROINIT,
  300. sizeof(IMAGE_RECORD)
  301. + WCSSIZE(ImageName)
  302. + (AccountName ? WCSSIZE(AccountName) : 0)
  303. );
  304. if (imageRecord == NULL)
  305. {
  306. SC_LOG(TRACE,"CreateImageRecord: Local Alloc failure rc=%ld\n",
  307. GetLastError());
  308. return(ERROR_NOT_ENOUGH_MEMORY);
  309. }
  310. //
  311. // Copy the strings into the new buffer space.
  312. //
  313. stringArea = (LPWSTR)(imageRecord + 1);
  314. (VOID) wcscpy (stringArea, ImageName);
  315. imageRecord->ImageName = stringArea;
  316. if (AccountName)
  317. {
  318. stringArea += (wcslen(stringArea) + 1);
  319. (VOID) wcscpy (stringArea, AccountName);
  320. imageRecord->AccountName = stringArea;
  321. }
  322. else
  323. {
  324. imageRecord->AccountName = NULL;
  325. }
  326. //
  327. // Update the rest of the fields in the Image Record
  328. //
  329. imageRecord->Next = NULL;
  330. imageRecord->Pid = Pid;
  331. imageRecord->PipeHandle = PipeHandle;
  332. imageRecord->ProcessHandle = ProcessHandle;
  333. imageRecord->ServiceCount = 0;
  334. imageRecord->TokenHandle = TokenHandle;
  335. imageRecord->ProfileHandle = ProfileHandle;
  336. imageRecord->ObjectWaitHandle = NULL;
  337. imageRecord->ImageFlags = ImageFlags;
  338. if (TokenHandle == NULL)
  339. {
  340. LUID SystemLuid = SYSTEM_LUID;
  341. RtlCopyLuid(&imageRecord->AccountLuid, &SystemLuid);
  342. }
  343. else
  344. {
  345. //
  346. // Get the unique session ID for this process
  347. //
  348. if (!GetTokenInformation(TokenHandle,
  349. TokenStatistics, // Information wanted
  350. &TokenStats,
  351. sizeof(TokenStats), // Buffer size
  352. &dwError)) // Size required
  353. {
  354. dwError = GetLastError();
  355. SC_LOG1(ERROR,
  356. "ScCreateImageRecord: GetTokenInformation FAILED %d\n",
  357. dwError);
  358. LocalFree(imageRecord);
  359. return dwError;
  360. }
  361. imageRecord->AccountLuid.LowPart = TokenStats.AuthenticationId.LowPart;
  362. imageRecord->AccountLuid.HighPart = TokenStats.AuthenticationId.HighPart;
  363. }
  364. //
  365. // Add record to the Image Database linked list.
  366. //
  367. CServiceRecordExclusiveLock Lock;
  368. record = &ImageDatabase;
  369. ADD_TO_LIST(record, imageRecord);
  370. *ImageRecordPtr = imageRecord;
  371. return(NO_ERROR);
  372. }
  373. /****************************************************************************/
  374. DWORD
  375. ScCreateServiceRecord(
  376. IN LPWSTR ServiceName,
  377. OUT LPSERVICE_RECORD *ServiceRecord
  378. )
  379. /*++
  380. Routine Description:
  381. This function creates a new "inactive" service record and adds it to
  382. the service record list. A resume number is assigned so that it
  383. can be used as a key in enumeration searches.
  384. To initialize the service record with the fields from the registry,
  385. call ScAddConfigInfoServiceRecord.
  386. Arguments:
  387. ServiceName - This is a pointer to the NUL terminated service name
  388. string.
  389. ServiceRecord - Receives a pointer to the service record created and
  390. inserted into the service record list.
  391. Return Value:
  392. NO_ERROR - The operation was successful.
  393. ERROR_NOT_ENOUGH_MEMORY - The call to allocate memory for a new
  394. service record failed.
  395. Note:
  396. This routine assumes that the caller has exclusively acquired the
  397. database lock.
  398. --*/
  399. {
  400. DWORD status = NO_ERROR;
  401. LPSERVICE_RECORD record; // Temporary pointer
  402. LPWSTR nameArea; // NameString area in allocated buffer.
  403. DWORD nameSize; // num bytes in service name.
  404. SC_ASSERT(ScServiceListLock.HaveExclusive());
  405. // (The service record lock is not needed here since we don't touch
  406. // the service record after adding it to the service list)
  407. //
  408. // Allocate the new service record.
  409. //
  410. nameSize = (DWORD) WCSSIZE(ServiceName);
  411. (*ServiceRecord) = (LPSERVICE_RECORD)HeapAlloc(
  412. ServiceRecordHeap,
  413. HEAP_ZERO_MEMORY,
  414. nameSize + sizeof(SERVICE_RECORD)
  415. );
  416. if ((*ServiceRecord) == NULL)
  417. {
  418. SC_LOG0(ERROR,"CreateServiceRecord: HeapAlloc failure\n");
  419. return ERROR_NOT_ENOUGH_MEMORY;
  420. }
  421. //
  422. // Copy the ServiceName into the new buffer space.
  423. //
  424. nameArea = (LPWSTR)((LPBYTE)(*ServiceRecord) + sizeof(SERVICE_RECORD));
  425. (VOID) wcscpy (nameArea, ServiceName);
  426. //
  427. // At this point we have the space for a service record, and it
  428. // contains the name of the service.
  429. //
  430. //
  431. // Fill in all the fields that need to be non-zero.
  432. // Note: The display name is initialized to point to the service name.
  433. //
  434. (*ServiceRecord)->ServiceName = nameArea;
  435. (*ServiceRecord)->DisplayName = nameArea;
  436. (*ServiceRecord)->ResumeNum = ResumeNumber++;
  437. (*ServiceRecord)->Signature = SERVICE_SIGNATURE;
  438. (*ServiceRecord)->ImageRecord = NULL;
  439. (*ServiceRecord)->StartDepend = NULL;
  440. (*ServiceRecord)->StopDepend = NULL;
  441. (*ServiceRecord)->ErrorControl = SERVICE_ERROR_NORMAL;
  442. (*ServiceRecord)->StatusFlag = 0;
  443. (*ServiceRecord)->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
  444. (*ServiceRecord)->ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
  445. (*ServiceRecord)->StartState = SC_NEVER_STARTED;
  446. //
  447. // Add the service to the service record linked list.
  448. //
  449. record = &ServiceDatabase;
  450. ADD_TO_LIST(record, (*ServiceRecord));
  451. ScTotalNumServiceRecs++;
  452. return(status);
  453. }
  454. /****************************************************************************/
  455. VOID
  456. ScFreeServiceRecord(
  457. IN LPSERVICE_RECORD ServiceRecord
  458. )
  459. /*++
  460. Routine Description:
  461. This function frees up a service record that has already been removed
  462. from the service record list.
  463. Arguments:
  464. ServiceRecord - Receives a pointer to the service record to free.
  465. --*/
  466. {
  467. if (!HeapFree(ServiceRecordHeap, 0, ServiceRecord))
  468. {
  469. SC_LOG2(ERROR,
  470. "ScFreeServiceRecord: HeapFree for %ws service failed %d\n",
  471. ServiceRecord->ServiceName,
  472. GetLastError());
  473. }
  474. return;
  475. }
  476. /****************************************************************************/
  477. DWORD
  478. ScAddConfigInfoServiceRecord(
  479. IN LPSERVICE_RECORD ServiceRecord,
  480. IN DWORD ServiceType,
  481. IN DWORD StartType,
  482. IN DWORD ErrorControl,
  483. IN LPWSTR Group OPTIONAL,
  484. IN DWORD Tag,
  485. IN LPWSTR Dependencies OPTIONAL,
  486. IN LPWSTR DisplayName OPTIONAL,
  487. IN PSECURITY_DESCRIPTOR Sd OPTIONAL
  488. )
  489. /*++
  490. Routine Description:
  491. This function adds the configuration information to the service
  492. record.
  493. NOTE: This function is called when the service controller is
  494. reading service entries from the registry at startup, as well as
  495. from RCreateServiceW.
  496. Arguments:
  497. ServiceRecord - Pointer to the service record to modify.
  498. DisplayName - A string that is the displayable name for the service.
  499. ServiceType - Indicates whether the ServiceRecord is for a win32 service
  500. or a device driver.
  501. StartType - Specifies when to start the service: automatically at boot or
  502. on demand.
  503. ErrorControl - Specifies the severity of the error if the service fails
  504. to start.
  505. Tag - DWORD identifier for the service. 0 means no tag.
  506. Group - Name of the load order group this service is a member of.
  507. Dependencies - Names of services separated by colon which this service
  508. require to be started before it can run.
  509. Sd - Security descriptor for the service object. If NULL, i.e. could
  510. not read from registry, create a default one.
  511. Return Value:
  512. NO_ERROR - The operation was successful.
  513. ERROR_NOT_ENOUGH_MEMORY - The call to allocate memory for a new
  514. service record or display name failed.
  515. Note:
  516. This routine assumes that the caller has exclusively acquired both the
  517. database lock and the GroupListLock.
  518. If this call is successful, do the following before freeing the memory
  519. of the service record in ScDecrementUseCountAndDelete:
  520. ScDeleteStartDependencies(ServiceRecord);
  521. ScDeleteGroupMembership(ServiceRecord);
  522. ScDeleteRegistryGroupPointer(ServiceRecord);
  523. --*/
  524. {
  525. DWORD status;
  526. SC_ASSERT(ScGroupListLock.HaveExclusive());
  527. SC_ASSERT(ScServiceRecordLock.HaveExclusive());
  528. //
  529. // Fill in the service record.
  530. //
  531. ServiceRecord->StartType = StartType;
  532. ServiceRecord->ServiceStatus.dwServiceType = ServiceType;
  533. ServiceRecord->ErrorControl = ErrorControl;
  534. ServiceRecord->Tag = Tag;
  535. //
  536. // The display name in the service record already points to the
  537. // ServiceName string. If the DisplayName is present and different
  538. // from the ServiceName, then allocate storage for it and copy the
  539. // string there.
  540. //
  541. SC_LOG0(SECURITY,"ScAddConfigInfoServiceRecord: Allocate for display name\n");
  542. if ((DisplayName != NULL) && (*DisplayName != L'\0') &&
  543. (_wcsicmp(DisplayName,ServiceRecord->ServiceName) != 0))
  544. {
  545. ServiceRecord->DisplayName = (LPWSTR)LocalAlloc(
  546. LMEM_FIXED,
  547. WCSSIZE(DisplayName));
  548. if (ServiceRecord->DisplayName == NULL)
  549. {
  550. SC_LOG(TRACE,"ScAddConfigInfoServiceRecord: LocalAlloc failure rc=%ld\n",
  551. GetLastError());
  552. return ERROR_NOT_ENOUGH_MEMORY;
  553. }
  554. wcscpy(ServiceRecord->DisplayName,DisplayName);
  555. }
  556. //
  557. // Create a default security descriptor for the service.
  558. //
  559. if (! ARGUMENT_PRESENT(Sd))
  560. {
  561. SC_LOG0(SECURITY,"ScAddConfigInfoServiceRecord: create service obj\n");
  562. if ((status = ScCreateScServiceObject(
  563. &ServiceRecord->ServiceSd
  564. )) != NO_ERROR)
  565. {
  566. goto ErrorExit;
  567. }
  568. }
  569. else
  570. {
  571. SC_LOG1(SECURITY,
  572. "ScAddConfigInfoServiceRecord: Using " FORMAT_LPWSTR
  573. " descriptor from registry\n", ServiceRecord->ServiceName);
  574. ServiceRecord->ServiceSd = Sd;
  575. }
  576. SC_LOG0(SECURITY,"ScAddConfigInfoServiceRecord: Get Group List Lock\n");
  577. //
  578. // Save the group membership information.
  579. //
  580. SC_LOG0(SECURITY,"ScAddConfigInfoServiceRecord: create group memebership\n");
  581. if ((status = ScCreateGroupMembership(
  582. ServiceRecord,
  583. Group
  584. )) != NO_ERROR)
  585. {
  586. goto ErrorExit;
  587. }
  588. SC_LOG0(SECURITY,"ScAddConfigInfoServiceRecord: create Reg Grp Ptr\n");
  589. if ((status = ScCreateRegistryGroupPointer(
  590. ServiceRecord,
  591. Group
  592. )) != NO_ERROR)
  593. {
  594. ScDeleteGroupMembership(ServiceRecord);
  595. goto ErrorExit;
  596. }
  597. //
  598. // Don't create dependencies list yet. Just save the string in
  599. // the service record.
  600. //
  601. if ((Dependencies != NULL) && (*Dependencies != 0))
  602. {
  603. //
  604. // If StartType is BOOT_START or SYSTEM_START, it is invalid
  605. // for the service to be dependent on another service. It can
  606. // only be dependent on a group.
  607. //
  608. // N.B. This check has been removed. There doesn't seem to be
  609. // any point in enforcing it, and remote boot needs to
  610. // allow NetBT to be dependent Tcpip.
  611. //
  612. DWORD DependenciesSize = 0;
  613. DWORD EntryByteCount;
  614. LPWSTR Entry = Dependencies;
  615. while (*Entry != 0)
  616. {
  617. #if 0
  618. if (StartType == SERVICE_BOOT_START ||
  619. StartType == SERVICE_SYSTEM_START)
  620. {
  621. if (*Entry != SC_GROUP_IDENTIFIERW)
  622. {
  623. SC_LOG1(ERROR, "ScAddConfigInfoServiceRecord: Boot or System "
  624. "start driver " FORMAT_LPWSTR " must depend on a group\n",
  625. ServiceRecord->DisplayName);
  626. ScEvent(
  627. EVENT_INVALID_DRIVER_DEPENDENCY,
  628. ServiceRecord->DisplayName
  629. );
  630. status = ERROR_INVALID_PARAMETER;
  631. ScDeleteGroupMembership(ServiceRecord);
  632. ScDeleteRegistryGroupPointer(ServiceRecord);
  633. goto ErrorExit;
  634. }
  635. }
  636. #endif
  637. EntryByteCount = (DWORD) WCSSIZE(Entry); // This entry and its null.
  638. DependenciesSize += EntryByteCount;
  639. Entry = (LPWSTR) ((DWORD_PTR) Entry + EntryByteCount);
  640. }
  641. DependenciesSize += sizeof(WCHAR);
  642. ServiceRecord->Dependencies = (LPWSTR)LocalAlloc(
  643. 0,
  644. DependenciesSize
  645. );
  646. if (ServiceRecord->Dependencies == NULL)
  647. {
  648. ScDeleteGroupMembership(ServiceRecord);
  649. ScDeleteRegistryGroupPointer(ServiceRecord);
  650. goto ErrorExit;
  651. }
  652. RtlCopyMemory(ServiceRecord->Dependencies, Dependencies, DependenciesSize);
  653. }
  654. return NO_ERROR;
  655. ErrorExit:
  656. if (DisplayName != NULL)
  657. {
  658. //
  659. // Prevent ScProcessDeferredList from trying to free the same heap block
  660. // again later
  661. //
  662. LocalFree(ServiceRecord->DisplayName);
  663. ServiceRecord->DisplayName = NULL;
  664. }
  665. RtlDeleteSecurityObject(&ServiceRecord->ServiceSd);
  666. //
  667. // Prevent ScProcessDeferredList from trying to free the same heap block
  668. // again later
  669. //
  670. ServiceRecord->ServiceSd = NULL;
  671. return status;
  672. }
  673. /****************************************************************************/
  674. VOID
  675. ScDecrementUseCountAndDelete(
  676. LPSERVICE_RECORD ServiceRecord
  677. )
  678. /*++
  679. Routine Description:
  680. This function decrements the UseCount for a service, and it the
  681. UseCount reaches zero and the service is marked for deletion,
  682. it puts a pointer to the service record into an array of
  683. such pointers that is stored in a deferred list structure. The
  684. pointer to this structure is stored at the global location called
  685. ScDeferredList. Access to this list is synchronized by use of the
  686. ScServiceRecordLock.
  687. A large number of routines in the service controller walk the
  688. service database and sometimes call this function, either directly
  689. or indirectly. It would complicate the programming of all those
  690. routines if they all had to assume that service records could get
  691. deleted under them. Therefore, this function never actually deletes
  692. any service record. Instead, it starts a separate thread that first
  693. acquires all three locks exclusively and then processes the deferred
  694. list.
  695. Note: By jumping through hoops, it is possible to avoid creating
  696. that thread in certain cases, but since service deletion is a rather
  697. rare operation, it was not considered worthwhile to create very-
  698. difficult-to-maintain code to optimize it.
  699. NOTE: The caller is expected to hold the Exclusive DatabaseLock
  700. prior to calling this function.
  701. The following functions call this routine:
  702. ScDeactivateServiceRecord
  703. RCloseServiceHandle
  704. ScGetDriverStatus
  705. ScStartServiceAndDependencies
  706. Arguments:
  707. ServiceRecord - This is a pointer to the service record that is having
  708. its use count deleted.
  709. Return Value:
  710. none.
  711. --*/
  712. {
  713. SC_ASSERT(ScServiceRecordLock.HaveExclusive());
  714. if (ServiceRecord->UseCount == 0)
  715. {
  716. //
  717. // The use count should not ever be zero when we enter this routine.
  718. //
  719. SC_LOG1(ERROR,"ScDecrementUseCountAndDelete: Attempt to decrement UseCount beyond zero.\n"
  720. "\t"FORMAT_LPWSTR" \n", ServiceRecord->ServiceName);
  721. SC_ASSERT(FALSE);
  722. }
  723. else
  724. {
  725. if ((ServiceRecord->UseCount == 1) &&
  726. (DELETE_FLAG_IS_SET(ServiceRecord)))
  727. {
  728. //
  729. // If the use count is one, we have a special case. We want
  730. // to postpone decrementing this last time until we have the
  731. // group list lock.
  732. //
  733. //
  734. // Put the service record pointer in the list, and start a
  735. // separate thread to process the list.
  736. //
  737. ScDeferredList.Add(ServiceRecord);
  738. }
  739. else
  740. {
  741. //
  742. // If the use count is greater than one, or the service is
  743. // NOT marked for delete, then we want to decrement the use
  744. // count and that is all.
  745. //
  746. ServiceRecord->UseCount--;
  747. SC_LOG2(USECOUNT, "ScDecrementUseCountAndDelete: " FORMAT_LPWSTR
  748. " decrement USECOUNT=%lu\n", ServiceRecord->ServiceName, ServiceRecord->UseCount);
  749. }
  750. }
  751. return;
  752. }
  753. /****************************************************************************/
  754. VOID
  755. DEFER_LIST::Add(
  756. LPSERVICE_RECORD ServiceRecord
  757. )
  758. /*++
  759. Routine Description:
  760. This function adds a service record to the list of service records to
  761. be deleted. It then starts a thread that will acquire all the locks
  762. and perform the actual deletion, if such a thread hasn't already been
  763. started.
  764. ScServiceRecordLock is used to ensure that only one thread accesses
  765. the deferred list at a time.
  766. The following functions call this routine:
  767. ScDecrementUseCountAndDelete
  768. --*/
  769. {
  770. SC_ASSERT(ScServiceRecordLock.HaveExclusive());
  771. if (NumElements + 1 > TotalElements)
  772. {
  773. //
  774. // Reallocate the array with a bit more room
  775. //
  776. DWORD NewTotalElements = TotalElements + 4;
  777. LPSERVICE_RECORD * NewArray = (LPSERVICE_RECORD *)
  778. LocalAlloc(0, NewTotalElements * sizeof(LPSERVICE_RECORD));
  779. if (NewArray == NULL)
  780. {
  781. SC_LOG(ERROR, "DEFER_LIST::Add: LocalAlloc FAILED %lu\n", GetLastError());
  782. return;
  783. }
  784. if (ServiceRecordPtr != NULL)
  785. {
  786. RtlCopyMemory(NewArray, ServiceRecordPtr,
  787. NumElements * sizeof(LPSERVICE_RECORD));
  788. LocalFree(ServiceRecordPtr);
  789. }
  790. TotalElements = NewTotalElements;
  791. ServiceRecordPtr = NewArray;
  792. }
  793. //
  794. // At this point we have a deferred list that can hold the new element.
  795. //
  796. ServiceRecordPtr[NumElements] = ServiceRecord;
  797. NumElements++;
  798. SC_LOG(LOCKS, "Added %ws service to deferred list\n",
  799. ServiceRecord->ServiceName);
  800. //
  801. // If we created the deferred list, queue a workitem (start a
  802. // thread) to process it.
  803. //
  804. if (NumElements == 1)
  805. {
  806. NTSTATUS ntStatus;
  807. ntStatus = RtlQueueWorkItem(ScDeferredListWorkItem, // callback function
  808. NULL, // context
  809. WT_EXECUTEONLYONCE); // flags
  810. if (!NT_SUCCESS(ntStatus))
  811. {
  812. SC_LOG(ERROR,"Couldn't add DeferredListWorkItem, 0x%x\n",
  813. ntStatus);
  814. }
  815. else
  816. {
  817. SC_LOG0(LOCKS,"Work item will process deferred list\n");
  818. }
  819. }
  820. }
  821. /****************************************************************************/
  822. VOID
  823. DEFER_LIST::Process(
  824. VOID
  825. )
  826. /*++
  827. Routine Description:
  828. This function loops through each service record pointer in the
  829. ScDeferredList, and decrements the UseCount for that ServiceRecord.
  830. If that count becomes zero, and if the ServiceRecord is marked
  831. for deletion, This routine will delete the service record and
  832. the registry entry for that service.
  833. This function frees the memory pointed to by ScDeferredList, when
  834. it is done processing the list.
  835. This routine acquires all 3 database locks.
  836. The following functions call this routine:
  837. CGroupListLock::Release
  838. Arguments:
  839. none.
  840. Return Value:
  841. none.
  842. --*/
  843. {
  844. //
  845. // Wait until we have acquired all 3 locks in the proper order
  846. //
  847. SC_LOG0(LOCKS, "In ScProcessDeferredList, waiting for locks\n");
  848. CGroupListExclusiveLock GLock;
  849. CServiceListExclusiveLock LLock;
  850. CServiceRecordExclusiveLock RLock;
  851. //
  852. // For each element in the list, delete the service information, and
  853. // free up its associated resources.
  854. //
  855. for (DWORD i=0; i<NumElements; i++)
  856. {
  857. LPSERVICE_RECORD ServiceRecord = ServiceRecordPtr[i];
  858. if (ServiceRecord->UseCount == 0)
  859. {
  860. SC_LOG1(ERROR,"ScProcessDeferredList: Attempt to decrement UseCount beyond zero.\n"
  861. "\t"FORMAT_LPWSTR" \n", ServiceRecord->ServiceName);
  862. SC_ASSERT(FALSE);
  863. }
  864. else
  865. {
  866. //
  867. // The use count is not zero, so we want to decrement it.
  868. // NOTE that even though the count was 1 when we put it in
  869. // the deferred list, it may have been incremented in the
  870. // mean-time.
  871. // CODEWORK: Why doesn't ScDecrementUseCountAndDelete just
  872. // decrement the UseCount to zero itself? If it did, we
  873. // wouldn't need RLock at all here, just LLock.
  874. //
  875. ServiceRecord->UseCount--;
  876. SC_LOG2(USECOUNT, "ScProcessDeferredList: " FORMAT_LPWSTR
  877. " decrement USECOUNT=%lu\n", ServiceRecord->ServiceName, ServiceRecord->UseCount);
  878. }
  879. if ((ServiceRecord->UseCount == 0) &&
  880. (DELETE_FLAG_IS_SET(ServiceRecord)))
  881. {
  882. SC_LOG1(USECOUNT,"ScProcessDeferredList:DELETING THE ("FORMAT_LPWSTR") SERVICE\n",
  883. ServiceRecord->ServiceName);
  884. //
  885. // Check to see if there is an LSA secret object to delete
  886. //
  887. if (ServiceRecord->ServiceStatus.dwServiceType & SERVICE_WIN32_OWN_PROCESS)
  888. {
  889. HKEY ServiceNameKey;
  890. LPWSTR AccountName;
  891. //
  892. // Open the service name key.
  893. //
  894. if (ScOpenServiceConfigKey(
  895. ServiceRecord->ServiceName,
  896. KEY_READ,
  897. FALSE, // Create if missing
  898. &ServiceNameKey
  899. ) == NO_ERROR)
  900. {
  901. //
  902. // Read the account name from the registry.
  903. //
  904. if (ScReadStartName(
  905. ServiceNameKey,
  906. &AccountName
  907. ) == NO_ERROR)
  908. {
  909. if (AccountName != NULL
  910. &&
  911. _wcsicmp(AccountName, SC_LOCAL_SYSTEM_USER_NAME) != 0)
  912. {
  913. ScRemoveAccount(ServiceRecord->ServiceName);
  914. }
  915. LocalFree(AccountName);
  916. } // Got the StartName
  917. ScRegCloseKey(ServiceNameKey);
  918. }
  919. } // endif SERVICE_WIN32_OWN_PROCESS
  920. LocalFree(ServiceRecord->Dependencies);
  921. //
  922. // Free up the DisplayName space.
  923. //
  924. if (ServiceRecord->DisplayName != ServiceRecord->ServiceName)
  925. {
  926. LocalFree(ServiceRecord->DisplayName);
  927. }
  928. ScDeleteGroupMembership(ServiceRecord);
  929. ScDeleteRegistryGroupPointer(ServiceRecord);
  930. ScDeleteStartDependencies(ServiceRecord);
  931. ScDeleteStopDependencies(ServiceRecord);
  932. if (ServiceRecord->ServiceSd != NULL)
  933. {
  934. RtlDeleteSecurityObject(&ServiceRecord->ServiceSd);
  935. }
  936. delete ServiceRecord->CrashRecord;
  937. //*******************************
  938. // Delete the registry node for
  939. // This service.
  940. //*******************************
  941. DeleteServicePlugPlayRegKeys(ServiceRecord->ServiceName);
  942. ScDeleteRegServiceEntry(ServiceRecord->ServiceName);
  943. REMOVE_FROM_LIST(ServiceRecord);
  944. ScFreeServiceRecord(ServiceRecord);
  945. } // End If service can be deleted.
  946. } // End for each element in the list.
  947. //
  948. // The deferred list is no longer needed free it.
  949. //
  950. LocalFree(ServiceRecordPtr);
  951. ServiceRecordPtr = NULL;
  952. TotalElements = 0;
  953. NumElements = 0;
  954. SC_LOG0(LOCKS, "Returning from ScProcessDeferredList\n");
  955. }
  956. /****************************************************************************/
  957. BOOL
  958. ScFindEnumStart(
  959. IN DWORD ResumeIndex,
  960. OUT LPSERVICE_RECORD *ServiceRecordPtr
  961. )
  962. /*++
  963. Routine Description:
  964. This function finds the first service record to begin the enumeration
  965. search with by finding the next service record folloing the resumeIndex.
  966. Service records are indexed by a ResumeNum value that is stored in
  967. each service record. The numbers increment as the linked list is
  968. walked.
  969. Arguments:
  970. ResumeIndex - This index is compared against the ResumeNum in the
  971. services records. The pointer to the next service record beyond
  972. the ResumeIndex is returned.
  973. ServiceRecordPtr - This is a pointer to a location where the pointer
  974. to the returned service record is to be placed.
  975. Return Value:
  976. TRUE - Indicates that there are service records beyond the resume index.
  977. FALSE - Indicates that there are no service records beyond the resume
  978. index.
  979. Note:
  980. --*/
  981. {
  982. FOR_SERVICES_THAT(serviceRecord, serviceRecord->ResumeNum > ResumeIndex)
  983. {
  984. *ServiceRecordPtr = serviceRecord;
  985. return TRUE;
  986. }
  987. return FALSE;
  988. }
  989. /****************************************************************************/
  990. BOOL
  991. ScGetNamedImageRecord (
  992. IN LPWSTR ImageName,
  993. OUT LPIMAGE_RECORD *ImageRecordPtr
  994. )
  995. /*++
  996. Routine Description:
  997. This function searches for an Image Record that has a name matching
  998. that which is passed in.
  999. NOTE: If this function is called, it is to find a shareable Image Record
  1000. of the given name.
  1001. Arguments:
  1002. ImageName - This is a pointer to a NUL terminated image name string.
  1003. This may be in mixed case.
  1004. ImageRecordPtr - This is a pointer to a location where the pointer to
  1005. the Image Record is to be placed.
  1006. Note:
  1007. The Database Lock must be held with at least shared access prior to
  1008. calling this routine.
  1009. Return Value:
  1010. TRUE - if the record was found.
  1011. FALSE - if the record was not found.
  1012. --*/
  1013. {
  1014. if (ImageName == NULL)
  1015. {
  1016. SC_LOG(TRACE,"GetNamedImageRecord: Name was NULL\n",0);
  1017. return (FALSE);
  1018. }
  1019. SC_ASSERT(ScServiceRecordLock.Have());
  1020. //
  1021. // Check the database of running images
  1022. //
  1023. for (PIMAGE_RECORD imageRecord = ImageDatabase.Next;
  1024. imageRecord != NULL;
  1025. imageRecord = imageRecord->Next)
  1026. {
  1027. //
  1028. // We need a shareable Image Record, so check the ImageFlags
  1029. //
  1030. if (ScImagePathsMatch(imageRecord->ImageName, ImageName) &&
  1031. (imageRecord->ImageFlags & CANSHARE_FLAG))
  1032. {
  1033. *ImageRecordPtr = imageRecord;
  1034. return TRUE;
  1035. }
  1036. }
  1037. return FALSE;
  1038. }
  1039. /****************************************************************************/
  1040. DWORD
  1041. ScGetNamedServiceRecord (
  1042. IN LPWSTR ServiceName,
  1043. OUT LPSERVICE_RECORD *ServiceRecordPtr
  1044. )
  1045. /*++
  1046. Routine Description:
  1047. Uses the service name to look through the service and device linked
  1048. lists until it finds a match. Inactive services can be identified by
  1049. finding CurrentState = SERVICE_STOPPED.
  1050. Arguments:
  1051. ServiceName - This is a pointer to a NUL terminated service name string.
  1052. ServiceRecordPtr - This is a pointer to a location where the pointer to
  1053. the Service Record is to be placed.
  1054. Return Value:
  1055. NO_ERROR - if the record was found.
  1056. ERROR_SERVICE_DOES_NOT_EXIST - if the service record was not found in
  1057. the linked list.
  1058. ERROR_INVALID_NAME - if the service name was NULL.
  1059. Note:
  1060. The caller is expected to grab the lock before calling this routine.
  1061. --*/
  1062. {
  1063. if (ServiceName == NULL)
  1064. {
  1065. SC_LOG0(TRACE,"GetNamedServiceRecord: Name was NULL\n");
  1066. return ERROR_INVALID_NAME;
  1067. }
  1068. //
  1069. // Check the database of running services
  1070. //
  1071. FOR_SERVICES_THAT(serviceRecord,
  1072. _wcsicmp(serviceRecord->ServiceName, ServiceName)== 0)
  1073. {
  1074. *ServiceRecordPtr = serviceRecord;
  1075. return NO_ERROR;
  1076. }
  1077. return ERROR_SERVICE_DOES_NOT_EXIST;
  1078. }
  1079. /****************************************************************************/
  1080. DWORD
  1081. ScGetDisplayNamedServiceRecord (
  1082. IN LPWSTR ServiceDisplayName,
  1083. OUT LPSERVICE_RECORD *ServiceRecordPtr
  1084. )
  1085. /*++
  1086. Routine Description:
  1087. Uses the service display name to look through the service and device
  1088. linked lists until it finds a match.
  1089. Arguments:
  1090. ServiceDisplayName - This is a pointer to a NUL terminated service
  1091. display name string.
  1092. ServiceRecordPtr - This is a pointer to a location where the pointer to
  1093. the Service Record is to be placed.
  1094. Return Value:
  1095. NO_ERROR - if the record was found.
  1096. ERROR_SERVICE_DOES_NOT_EXIST - if the service record was not found in
  1097. the linked list.
  1098. ERROR_INVALID_NAME - if the service display name was NULL.
  1099. Note:
  1100. The caller is expected to grab the lock before calling this routine.
  1101. --*/
  1102. {
  1103. if (ServiceDisplayName == NULL)
  1104. {
  1105. SC_LOG0(TRACE,"GetDisplayNamedServiceRecord: Name was NULL\n");
  1106. return ERROR_INVALID_NAME;
  1107. }
  1108. //
  1109. // Check the database of running services
  1110. //
  1111. SC_ASSERT(ScServiceRecordLock.Have());
  1112. FOR_SERVICES_THAT(serviceRecord,
  1113. _wcsicmp(serviceRecord->DisplayName, ServiceDisplayName)== 0)
  1114. {
  1115. *ServiceRecordPtr = serviceRecord;
  1116. return NO_ERROR;
  1117. }
  1118. return ERROR_SERVICE_DOES_NOT_EXIST;
  1119. }
  1120. /****************************************************************************/
  1121. DWORD
  1122. ScGetTotalNumberOfRecords (VOID)
  1123. /*++
  1124. Routine Description:
  1125. Finds the total number of installed Service Records in the database.
  1126. This is used in the Enum case where only the installed services are
  1127. enumerated.
  1128. Arguments:
  1129. none
  1130. Return Value:
  1131. TotalNumberOfRecords
  1132. --*/
  1133. {
  1134. return(ScTotalNumServiceRecs);
  1135. }
  1136. /****************************************************************************/
  1137. BOOL
  1138. ScInitDatabase (VOID)
  1139. /*++
  1140. Routine Description:
  1141. This function initializes the Service Controllers database.
  1142. Arguments:
  1143. none
  1144. Return Value:
  1145. TRUE - Initialization was successful
  1146. FALSE - Initialization failed
  1147. --*/
  1148. {
  1149. ScTotalNumServiceRecs = 0;
  1150. ImageDatabase.Next = NULL;
  1151. ImageDatabase.Prev = NULL;
  1152. ServiceDatabase.Next = NULL;
  1153. ServiceDatabase.Prev = NULL;
  1154. ScInitGroupDatabase();
  1155. ResumeNumber = 1;
  1156. //
  1157. // Create the database lock.
  1158. // NOTE: This is never deleted. It is assumed it will be deleted
  1159. // when the process goes away.
  1160. //
  1161. ScServiceRecordLock.Initialize(" R", "ServiceRecord");
  1162. ScServiceListLock.Initialize(" L ", "ServiceList");
  1163. //
  1164. // Initialize the group list lock used for protecting the
  1165. // OrderGroupList and StandaloneGroupList
  1166. //
  1167. ScGroupListLock.Initialize("G ", "GroupList");
  1168. //
  1169. // This routine does the following:
  1170. // - Read the load order group information from the registry.
  1171. // - Generate the database of service records from the information
  1172. // stored in the registry.
  1173. //
  1174. if (!ScGenerateServiceDB())
  1175. {
  1176. return(FALSE);
  1177. }
  1178. return(TRUE);
  1179. }
  1180. /****************************************************************************/
  1181. VOID
  1182. ScProcessCleanup(
  1183. HANDLE ProcessHandle
  1184. )
  1185. /*++
  1186. Routine Description:
  1187. This function is called when a process has died, and the service
  1188. record in the database needs cleaning up. This function will
  1189. use the ProcessHandle as a key when scanning the ServiceRecord
  1190. database. All of the service records referencing that handle
  1191. are cleaned up, and then the image record that they reference
  1192. is deleted.
  1193. In cleaning up a service record, CurrentState is set to
  1194. SERVICE_STOPPED, and the ExitCode is set to a unique value that
  1195. indicates that the service died unexpectedly and without warning.
  1196. Arguments:
  1197. ProcessHandle - This is the handle of the process that died
  1198. unexpectedly.
  1199. Return Value:
  1200. none.
  1201. --*/
  1202. {
  1203. PIMAGE_RECORD imageRecord;
  1204. {
  1205. //
  1206. // Get exclusive use of database so that it can be modified.
  1207. //
  1208. // If the service record's update flag is set, we may have to
  1209. // modify the group list (within ScDeactivateServiceRecord),
  1210. // so lock the group list too.
  1211. //
  1212. CGroupListExclusiveLock GLock;
  1213. CServiceListSharedLock LLock;
  1214. CServiceRecordExclusiveLock RLock;
  1215. //
  1216. // Find the image record that has this ProcessHandle.
  1217. //
  1218. for (imageRecord = ImageDatabase.Next;
  1219. imageRecord != NULL;
  1220. imageRecord = imageRecord->Next)
  1221. {
  1222. if (ProcessHandle == imageRecord->ProcessHandle)
  1223. {
  1224. break;
  1225. }
  1226. }
  1227. if (imageRecord == NULL)
  1228. {
  1229. SC_LOG(ERROR, "ScProcessCleanup: No image record has handle %#lx!\n",
  1230. ProcessHandle);
  1231. return;
  1232. }
  1233. SC_LOG2(ERROR, "Service process %ld (%ws) died\n", imageRecord->Pid,
  1234. imageRecord->ImageName);
  1235. //
  1236. // Deregister the wait. Note that this must be done
  1237. // even if the WT_EXECUTEONLYONCE flag was specified)
  1238. //
  1239. if (imageRecord->ObjectWaitHandle != NULL)
  1240. {
  1241. NTSTATUS ntStatus = RtlDeregisterWait(imageRecord->ObjectWaitHandle);
  1242. if (!NT_SUCCESS(ntStatus))
  1243. {
  1244. SC_LOG1(ERROR,
  1245. "ScProcessCleanup: RtlDeregisterWait FAILED %#x\n",
  1246. ntStatus);
  1247. }
  1248. }
  1249. DWORD serviceCount = imageRecord->ServiceCount;
  1250. //
  1251. // The image record's service count must include at least this service
  1252. //
  1253. if (serviceCount == 0)
  1254. {
  1255. SC_ASSERT(0);
  1256. // Do something sensible if this ever does happen
  1257. serviceCount = imageRecord->ServiceCount = 1;
  1258. }
  1259. //
  1260. // The Image may have several services running in it.
  1261. // Find the service records for all running services in this
  1262. // image.
  1263. //
  1264. // NOTE: If the service is typed as a SERVICE_WIN32_OWN_PROCESS, this
  1265. // means that only one service can exist in the process that
  1266. // went down. However, the serviceCount should correctly
  1267. // indicate as such in that case.
  1268. //
  1269. FOR_SERVICES_THAT(serviceRecord,
  1270. (serviceRecord->ImageRecord == imageRecord)
  1271. &&
  1272. (serviceRecord->ServiceStatus.dwCurrentState != SERVICE_STOPPED))
  1273. {
  1274. SC_LOG2(ERROR, "Dead process contained %ws service in state %#lx\n",
  1275. serviceRecord->ServiceName,
  1276. serviceRecord->ServiceStatus.dwCurrentState);
  1277. //
  1278. // Don't bother notifying PnP if the system is shutting down. This
  1279. // prevents a deadlock where we can get stuck calling PnP, which is
  1280. // stuck calling into the Eventlog, which is stuck calling into us.
  1281. //
  1282. if (!ScShutdownInProgress)
  1283. {
  1284. if (serviceRecord->ServiceStatus.dwControlsAccepted
  1285. &
  1286. (SERVICE_ACCEPT_POWEREVENT | SERVICE_ACCEPT_HARDWAREPROFILECHANGE))
  1287. {
  1288. //
  1289. // Tell PnP not to send controls to this service any more
  1290. //
  1291. RegisterServiceNotification((SERVICE_STATUS_HANDLE)serviceRecord,
  1292. serviceRecord->ServiceName,
  1293. 0,
  1294. TRUE);
  1295. }
  1296. //
  1297. // Increment the service's crash count and perform the configured
  1298. // recovery action. Don't bother if the system is shutting down.
  1299. //
  1300. ScQueueRecoveryAction(serviceRecord);
  1301. }
  1302. serviceRecord->StartError = ERROR_PROCESS_ABORTED;
  1303. serviceRecord->StartState = SC_START_FAIL;
  1304. serviceRecord->ServiceStatus.dwWin32ExitCode = ERROR_PROCESS_ABORTED;
  1305. //
  1306. // Clear the server announcement bits in the global location
  1307. // for this service.
  1308. //
  1309. ScRemoveServiceBits(serviceRecord);
  1310. serviceCount = ScDeactivateServiceRecord(serviceRecord);
  1311. if (serviceCount == 0)
  1312. {
  1313. // No need to continue
  1314. break;
  1315. }
  1316. }
  1317. // (If we hit this assert it means that the service database was corrupt:
  1318. // the number of service records pointing to this image record was less
  1319. // than the service count in the image record. Not much we can do now.)
  1320. SC_ASSERT(serviceCount == 0);
  1321. //
  1322. // Remove the ImageRecord from the list and delete it
  1323. //
  1324. REMOVE_FROM_LIST(imageRecord);
  1325. }
  1326. ScDeleteImageRecord(imageRecord);
  1327. return;
  1328. }
  1329. VOID
  1330. ScDeleteMarkedServices(
  1331. VOID
  1332. )
  1333. /*++
  1334. Routine Description:
  1335. This function looks through the service record database for any entries
  1336. marked for deletion. If one is found, it is removed from the registry
  1337. and its entry is deleted from the service record database.
  1338. WARNING:
  1339. This function is to be called during initialization only. It
  1340. is assumed that no services are running when this function is called.
  1341. Therefore, no locks are held during this operation.
  1342. Arguments:
  1343. none
  1344. Return Value:
  1345. none
  1346. --*/
  1347. {
  1348. HKEY ServiceNameKey;
  1349. LPWSTR AccountName;
  1350. FOR_SERVICES_THAT(serviceRecord, DELETE_FLAG_IS_SET(serviceRecord))
  1351. {
  1352. SC_LOG(TRACE,"ScDeleteMarkedServices: %ws is being deleted\n",
  1353. serviceRecord->ServiceName);
  1354. //
  1355. // Open the service name key.
  1356. //
  1357. if (ScOpenServiceConfigKey(
  1358. serviceRecord->ServiceName,
  1359. KEY_READ,
  1360. FALSE, // Create if missing
  1361. &ServiceNameKey) == NO_ERROR)
  1362. {
  1363. //
  1364. // Read the account name from the registry.
  1365. // If this fails, we still want to delete the registry entry.
  1366. //
  1367. if (ScReadStartName(ServiceNameKey, &AccountName) == NO_ERROR
  1368. &&
  1369. AccountName != NULL)
  1370. {
  1371. if (_wcsicmp(AccountName, SC_LOCAL_SYSTEM_USER_NAME) != 0)
  1372. {
  1373. ScRemoveAccount(serviceRecord->ServiceName);
  1374. }
  1375. LocalFree(AccountName);
  1376. } // Got the StartName
  1377. ScRegCloseKey(ServiceNameKey);
  1378. //
  1379. // Delete the entry from the registry
  1380. //
  1381. ScDeleteRegServiceEntry(serviceRecord->ServiceName);
  1382. //
  1383. // Free memory for the DisplayName.
  1384. //
  1385. if (serviceRecord->DisplayName != serviceRecord->ServiceName)
  1386. {
  1387. LocalFree(serviceRecord->DisplayName);
  1388. }
  1389. //
  1390. // Remove the service record from the database
  1391. //
  1392. LPSERVICE_RECORD saveRecord = serviceRecord->Prev;
  1393. REMOVE_FROM_LIST(serviceRecord);
  1394. ScFreeServiceRecord(serviceRecord);
  1395. serviceRecord = saveRecord;
  1396. }
  1397. }
  1398. }
  1399. /****************************************************************************/
  1400. DWORD
  1401. ScRemoveService (
  1402. IN LPSERVICE_RECORD ServiceRecord
  1403. )
  1404. /*++
  1405. Routine Description:
  1406. This should be used to deactivate a service record and shut down
  1407. the process only if it is the last service in the process. It
  1408. will be used for polite shut-down when a service terminates as
  1409. normal. It will always be called by the status routine. If the
  1410. service controller believes that no other services are running in
  1411. the process, it can force termination of the process if it does
  1412. not respond to the process shutdown request.
  1413. This function deactivates the service record (ScDeactivateServiceRecord)
  1414. and checks to see if the ServiceCount in the image record has gone to
  1415. zero. If it has, then it terminates the service process. When that
  1416. is complete, it calls ScDeleteImageRecord to remove the remaining
  1417. evidence.
  1418. Even if an error occurs, and we are unable to terminate the process,
  1419. we delete the image record any - just as we would in the case
  1420. where it goes away.
  1421. Arguments:
  1422. ServiceRecord - This is a pointer to the service record that is to
  1423. be removed.
  1424. Return Value:
  1425. NO_ERROR - The operation was successful.
  1426. NERR_ServiceKillProc - The service process had to be killed because
  1427. it wouldn't terminate when requested. If the process did not
  1428. go away - even after being killed (TerminateProcess), this error
  1429. message is still returned.
  1430. Note:
  1431. Uses Exclusive Locks.
  1432. --*/
  1433. {
  1434. DWORD serviceCount = 0;
  1435. LPIMAGE_RECORD ImageRecord;
  1436. {
  1437. //
  1438. // Get exclusive use of database so that it can be modified.
  1439. // If the service record's update flag is set, we may have to
  1440. // modify the group list, so lock the group list too.
  1441. //
  1442. CGroupListExclusiveLock GLock;
  1443. CServiceListSharedLock LLock; // (needed if update flag set and service has dependencies)
  1444. CServiceRecordExclusiveLock RLock;
  1445. ImageRecord = ServiceRecord->ImageRecord;
  1446. //
  1447. // ImageRecord may be NULL if it had been cleaned up earlier
  1448. //
  1449. if (ImageRecord != NULL)
  1450. {
  1451. //
  1452. // Deactivate the service record.
  1453. //
  1454. serviceCount = ScDeactivateServiceRecord(ServiceRecord);
  1455. //
  1456. // Do as little as possible while holding the locks.
  1457. // Otherwise, we can cause a deadlock or a bottleneck
  1458. // on system shutdown due to contention for the
  1459. // exclusive locks
  1460. //
  1461. if (serviceCount == 0)
  1462. {
  1463. //
  1464. // Remove the Image record from linked list.
  1465. //
  1466. REMOVE_FROM_LIST(ImageRecord);
  1467. }
  1468. }
  1469. }
  1470. //
  1471. // Done with modifications - now allow other threads database access.
  1472. //
  1473. if (ImageRecord != NULL && serviceCount == 0)
  1474. {
  1475. //
  1476. // Now we must terminate the Service Process. The return status
  1477. // from this call is not very interesting. The calling application
  1478. // probably doesn't care how the process died. (whether it died
  1479. // cleanly, or had to be killed).
  1480. //
  1481. ScTerminateServiceProcess(ImageRecord);
  1482. ScDeleteImageRecord (ImageRecord);
  1483. }
  1484. return(NO_ERROR);
  1485. }
  1486. /****************************************************************************/
  1487. VOID
  1488. ScDeleteImageRecord (
  1489. IN LPIMAGE_RECORD ImageRecord
  1490. )
  1491. /*++
  1492. Routine Description:
  1493. This function deletes an ImageRecord from the database by removing it
  1494. from the linked list, and freeing its associated memory. Prior to
  1495. doing this however, it closes the PipeHandle and the ProcessHandle
  1496. in the record.
  1497. Arguments:
  1498. ImageRecord - This is a pointer to the ImageRecord that is being deleted.
  1499. Return Value:
  1500. nothing
  1501. Notes:
  1502. This routine assumes that the image record has already been removed
  1503. from the list before it is called via the REMOVE_FROM_LIST macro. This
  1504. is to allow us to call ScDeleteImageRecord without holding any exclusive
  1505. locks
  1506. --*/
  1507. {
  1508. HANDLE status; // return status from LocalFree
  1509. SC_ASSERT( ImageRecord != NULL );
  1510. //
  1511. // What else can we do except note the errors in debug mode?
  1512. //
  1513. if (CloseHandle(ImageRecord->PipeHandle) == FALSE)
  1514. {
  1515. SC_LOG(TRACE,"DeleteImageRecord: ClosePipeHandle Failed %lu\n",
  1516. GetLastError());
  1517. }
  1518. if (CloseHandle(ImageRecord->ProcessHandle) == FALSE)
  1519. {
  1520. SC_LOG(TRACE,"DeleteImageRecord: CloseProcessHandle Failed %lu\n",
  1521. GetLastError());
  1522. }
  1523. if (ImageRecord->ProfileHandle != (HANDLE) NULL)
  1524. {
  1525. if (UnloadUserProfile(ImageRecord->TokenHandle,
  1526. ImageRecord->ProfileHandle) == FALSE)
  1527. {
  1528. SC_LOG1(ERROR,"DeleteImageRecord: UnloadUserProfile Failed %lu\n",
  1529. GetLastError());
  1530. }
  1531. }
  1532. if (ImageRecord->TokenHandle != (HANDLE) NULL)
  1533. {
  1534. if (CloseHandle(ImageRecord->TokenHandle) == FALSE)
  1535. {
  1536. SC_LOG1(TRACE,"DeleteImageRecord: CloseTokenHandle Failed %lu\n",
  1537. GetLastError());
  1538. }
  1539. }
  1540. status = LocalFree(ImageRecord);
  1541. if (status != NULL)
  1542. {
  1543. SC_LOG(TRACE,"DeleteImageRecord: LocalFree Failed, rc = %d\n",
  1544. GetLastError());
  1545. }
  1546. return;
  1547. }
  1548. DWORD
  1549. ScDeactivateServiceRecord (
  1550. IN LPSERVICE_RECORD ServiceRecord
  1551. )
  1552. /*++
  1553. Routine Description:
  1554. This function deactivates a service record by updating the proper
  1555. GlobalCount data structure.
  1556. NOTE: Although the ServiceRecord does not go away, the pointer to
  1557. the ImageRecord is destroyed.
  1558. Arguments:
  1559. ServiceRecord - This is a pointer to the ServiceRecord that is to be
  1560. deleted (moved to uninstalled database).
  1561. Notes:
  1562. This routine assumes that the Exclusive database lock has already
  1563. been obtained. (ScRemoveService & ScProcessCleanup call this function).
  1564. If the service's update flag is set (its configuration was changed
  1565. while it was running), the exclusive group list lock must also have
  1566. been obtained.
  1567. Return Value:
  1568. ServiceCount - This indicates how many services in this service process
  1569. are actually installed.
  1570. --*/
  1571. {
  1572. DWORD serviceCount = 0;
  1573. DWORD status;
  1574. DWORD dwServiceType;
  1575. DWORD dwStartType;
  1576. DWORD dwErrorControl;
  1577. DWORD dwTagId;
  1578. LPWSTR lpLoadOrderGroup = NULL;
  1579. SC_LOG(TRACE,"In DeactivateServiceRecord\n",0);
  1580. SC_ASSERT(ScServiceRecordLock.HaveExclusive());
  1581. //
  1582. // Decrement the service count in the image record.
  1583. //
  1584. if (ServiceRecord->ImageRecord != NULL)
  1585. {
  1586. serviceCount = --(ServiceRecord->ImageRecord->ServiceCount);
  1587. }
  1588. ServiceRecord->ServiceStatus.dwCurrentState = SERVICE_STOPPED;
  1589. ServiceRecord->ServiceStatus.dwControlsAccepted = 0;
  1590. ServiceRecord->ServiceStatus.dwCheckPoint = 0;
  1591. ServiceRecord->ServiceStatus.dwWaitHint = 0;
  1592. ServiceRecord->ImageRecord = NULL;
  1593. //
  1594. // If the Update bit is set in the services status flag, we need
  1595. // to read the latest registry configuration information into the
  1596. // service record. If this fails, all we can do is log the error
  1597. // and press on with the existing data in the service record.
  1598. //
  1599. if (UPDATE_FLAG_IS_SET(ServiceRecord))
  1600. {
  1601. SC_ASSERT(ScGroupListLock.HaveExclusive());
  1602. status = ScReadConfigFromReg(
  1603. ServiceRecord,
  1604. &dwServiceType,
  1605. &dwStartType,
  1606. &dwErrorControl,
  1607. &dwTagId,
  1608. NULL, // Don't need dependencies -- they're updated dynamically
  1609. &lpLoadOrderGroup,
  1610. NULL); // Don't need display name -- it's updated dynamically
  1611. if (status == NO_ERROR)
  1612. {
  1613. //
  1614. // Dependencies are NULL since they're updated dynamically
  1615. //
  1616. status = ScUpdateServiceRecordConfig(
  1617. ServiceRecord,
  1618. dwServiceType,
  1619. dwStartType,
  1620. dwErrorControl,
  1621. lpLoadOrderGroup,
  1622. NULL);
  1623. }
  1624. if (status != NO_ERROR)
  1625. {
  1626. SC_LOG1(ERROR,"ScDeactivateServiceRecord:Attempt to update "
  1627. "configuration for stopped service failed\n",status);
  1628. //
  1629. // ERROR_LOG ErrorLog
  1630. //
  1631. }
  1632. LocalFree(lpLoadOrderGroup);
  1633. CLEAR_UPDATE_FLAG(ServiceRecord);
  1634. }
  1635. //
  1636. // Since the service is no longer running, and no longer has a handle
  1637. // to the service, we need to decrement the use count. If the
  1638. // count is decremented to zero, and the service is marked for
  1639. // deletion, it will get deleted.
  1640. //
  1641. ScDecrementUseCountAndDelete(ServiceRecord);
  1642. return(serviceCount);
  1643. }
  1644. /****************************************************************************/
  1645. DWORD
  1646. ScTerminateServiceProcess (
  1647. IN PIMAGE_RECORD ImageRecord
  1648. )
  1649. /*++
  1650. Routine Description:
  1651. This function sends an SERVICE_STOP control message to the target
  1652. ControlDispatcher. Then it uses the process handle to wait for the
  1653. service process to terminate.
  1654. If the service process fails to terminate with the polite request, it
  1655. will be abruptly killed. After killing the process, this routine will
  1656. wait on the process handle to make sure it enters the signaled state.
  1657. If it doesn't, and the wait times out, we return anyway having given
  1658. it our best shot.
  1659. Arguments:
  1660. ImageRecord - This is a pointer to the Image Record that stores
  1661. information about the service that is to be terminated.
  1662. Return Value:
  1663. NO_ERROR - The operation was successful.
  1664. NERR_ServiceKillProc - The service process had to be killed because
  1665. it wouldn't terminate when requested. If the process did not
  1666. go away - even after being killed (TerminateProcess), this error
  1667. message is still returned.
  1668. Note:
  1669. LOCKS:
  1670. This function always operates within an exclusive database lock.
  1671. It DOES NOT give up this lock when sending the control to the service
  1672. process. We would like all these operations to be atomic.
  1673. It must give this up temporarily when it does the pipe transact.
  1674. --*/
  1675. {
  1676. DWORD returnStatus;
  1677. DWORD status;
  1678. DWORD waitStatus;
  1679. returnStatus = NO_ERROR;
  1680. //
  1681. // Check vs. NULL in case the register failed or the work item
  1682. // was already deregistered in RSetServiceStatus
  1683. //
  1684. if (ImageRecord->ObjectWaitHandle != NULL) {
  1685. status = RtlDeregisterWait(ImageRecord->ObjectWaitHandle);
  1686. if (NT_SUCCESS(status)) {
  1687. ImageRecord->ObjectWaitHandle = NULL;
  1688. }
  1689. else {
  1690. SC_LOG1(ERROR,
  1691. "ScTerminateServiceProcess: RtlDeregisterWait failed 0x%x\n",
  1692. status);
  1693. }
  1694. }
  1695. //
  1696. // Send Uninstall message to the Service Process
  1697. // Note that the ServiceName is NULL when addressing
  1698. // the Service Process.
  1699. //
  1700. SC_LOG(TRACE,"TerminateServiceProcess, sending Control...\n",0);
  1701. //
  1702. // Stop the service's control dispatcher
  1703. //
  1704. status = ScSendControl(
  1705. L"", // no service name.
  1706. L"", // no display name.
  1707. ImageRecord->PipeHandle, // PipeHandle
  1708. SERVICE_STOP, // Opcode
  1709. NULL, // CmdArgs (pointer to vectors).
  1710. 0L, // NumArgs
  1711. NULL); // Ignore handler return value
  1712. if (status == NO_ERROR)
  1713. {
  1714. //
  1715. // Control Dispatcher accepted the request - now
  1716. // wait for it to shut down.
  1717. //
  1718. SC_LOG(TRACE,
  1719. "TerminateServiceProcess, waiting for process to terminate...\n",0);
  1720. waitStatus = WaitForSingleObject (
  1721. ImageRecord->ProcessHandle,
  1722. TERMINATE_TIMEOUT);
  1723. if (waitStatus == WAIT_TIMEOUT)
  1724. {
  1725. SC_LOG3(ERROR,"TerminateServiceProcess: Process %#lx (%ws) did not exit because of timeout: %#ld\n",
  1726. ImageRecord->Pid, ImageRecord->ImageName, WAIT_TIMEOUT);
  1727. //
  1728. // Process didn't terminate. So Now I have to kill it.
  1729. //
  1730. TerminateProcess(ImageRecord->ProcessHandle, 0);
  1731. waitStatus = WaitForSingleObject (
  1732. ImageRecord->ProcessHandle,
  1733. TERMINATE_TIMEOUT);
  1734. if (waitStatus == WAIT_TIMEOUT)
  1735. {
  1736. SC_LOG2(ERROR,"TerminateServiceProcess: Couldn't kill process %#lx because of timeout: %#ld\n",
  1737. ImageRecord->Pid, WAIT_TIMEOUT);
  1738. }
  1739. returnStatus = NO_ERROR;
  1740. }
  1741. }
  1742. else
  1743. {
  1744. //
  1745. // ScSendControl failed -- this can occur if the service calls ExitProcess
  1746. // while handling SERVICE_CONTROL_STOP or SERVICE_CONTROL_SHUTDOWN since
  1747. // the pipe for the image record is now broken (ERROR_BROKEN_PIPE).
  1748. //
  1749. SC_LOG3(ERROR,
  1750. "TerminateServiceProcess:SendControl to stop process %#lx (%ws) failed, %ld\n",
  1751. ImageRecord->Pid, ImageRecord->ImageName, status);
  1752. TerminateProcess(ImageRecord->ProcessHandle, 0);
  1753. waitStatus = WaitForSingleObject (
  1754. ImageRecord->ProcessHandle,
  1755. TERMINATE_TIMEOUT);
  1756. if (waitStatus == WAIT_TIMEOUT)
  1757. {
  1758. SC_LOG2(ERROR,"TerminateServiceProcess: Couldn't kill process because of timeout: %ld\n",
  1759. 0, WAIT_TIMEOUT);
  1760. }
  1761. returnStatus = NO_ERROR;
  1762. }
  1763. SC_LOG(TRACE,"TerminateServiceProcess, Done terminating Process!\n",0);
  1764. return(returnStatus);
  1765. }
  1766. VOID
  1767. ScDeferredListWorkItem(
  1768. IN PVOID pContext
  1769. )
  1770. /*++
  1771. Routine Description:
  1772. This function acquires all the database locks, and allows
  1773. the group list lock routine to process the deferred list.
  1774. --*/
  1775. {
  1776. ScDeferredList.Process();
  1777. }
  1778. DWORD
  1779. ScUpdateServiceRecordConfig(
  1780. IN LPSERVICE_RECORD ServiceRecord,
  1781. IN DWORD dwServiceType,
  1782. IN DWORD dwStartType,
  1783. IN DWORD dwErrorControl,
  1784. IN LPWSTR lpLoadOrderGroup,
  1785. IN LPBYTE lpDependencies
  1786. )
  1787. /*++
  1788. Routine Description:
  1789. This function updates the service record with the latest config
  1790. information (passed in).
  1791. It assumed that exclusive locks are held before calling this function.
  1792. Arguments:
  1793. Return Value:
  1794. Note:
  1795. --*/
  1796. #define SERVICE_TYPE_CHANGED 0x00000001
  1797. #define START_TYPE_CHANGED 0x00000002
  1798. #define ERROR_CONTROL_CHANGED 0x00000004
  1799. #define BINARY_PATH_CHANGED 0x00000008
  1800. #define LOAD_ORDER_CHANGED 0x00000010
  1801. #define TAG_ID_CHANGED 0x00000020
  1802. #define DEPENDENCIES_CHANGED 0x00000040
  1803. #define START_NAME_CHANGED 0x00000080
  1804. {
  1805. DWORD status;
  1806. DWORD backoutStatus;
  1807. LPWSTR OldLoadOrderGroup = NULL;
  1808. LPWSTR OldDependencies = NULL;
  1809. DWORD OldServiceType;
  1810. DWORD OldStartType;
  1811. DWORD OldErrorControl;
  1812. DWORD Progress = 0;
  1813. DWORD bufSize;
  1814. DWORD MaxDependSize = 0;
  1815. SC_ASSERT(ScGroupListLock.HaveExclusive());
  1816. SC_ASSERT(ScServiceRecordLock.HaveExclusive());
  1817. OldServiceType = ServiceRecord->ServiceStatus.dwServiceType;
  1818. OldStartType = ServiceRecord->StartType;
  1819. OldErrorControl= ServiceRecord->ErrorControl;
  1820. //==============================
  1821. // UPDATE DWORDs
  1822. //==============================
  1823. if (dwServiceType != SERVICE_NO_CHANGE)
  1824. {
  1825. ServiceRecord->ServiceStatus.dwServiceType = dwServiceType;
  1826. }
  1827. if (dwStartType != SERVICE_NO_CHANGE)
  1828. {
  1829. ServiceRecord->StartType = dwStartType;
  1830. }
  1831. if (dwErrorControl != SERVICE_NO_CHANGE)
  1832. {
  1833. ServiceRecord->ErrorControl = dwErrorControl;
  1834. }
  1835. Progress |= (SERVICE_TYPE_CHANGED |
  1836. START_TYPE_CHANGED |
  1837. ERROR_CONTROL_CHANGED );
  1838. //==============================
  1839. // UPDATE Dependencies
  1840. //==============================
  1841. if (lpDependencies != NULL)
  1842. {
  1843. //
  1844. // Generate the current (old) list of dependency strings.
  1845. //
  1846. ScGetDependencySize(ServiceRecord,&bufSize, &MaxDependSize);
  1847. if (bufSize > 0)
  1848. {
  1849. OldDependencies = (LPWSTR)LocalAlloc(LMEM_FIXED, bufSize);
  1850. if (OldDependencies == NULL)
  1851. {
  1852. status = GetLastError();
  1853. goto Cleanup;
  1854. }
  1855. status = ScGetDependencyString(
  1856. ServiceRecord,
  1857. MaxDependSize,
  1858. bufSize,
  1859. OldDependencies);
  1860. if (status != NO_ERROR)
  1861. {
  1862. goto Cleanup;
  1863. }
  1864. }
  1865. ScDeleteStartDependencies(ServiceRecord);
  1866. status = ScCreateDependencies(ServiceRecord, (LPWSTR) lpDependencies);
  1867. if (status != NO_ERROR)
  1868. {
  1869. goto Cleanup;
  1870. }
  1871. Progress |= DEPENDENCIES_CHANGED;
  1872. }
  1873. //==============================
  1874. // UPDATE LoadOrderGroup
  1875. //==============================
  1876. if (lpLoadOrderGroup != NULL)
  1877. {
  1878. if (*lpLoadOrderGroup != 0)
  1879. {
  1880. //
  1881. // The string in lpLoadOrderGroup should match that in the RegistryGroup
  1882. // in the service record. If a service monkeyed directly with its registry
  1883. // values, it's possible that the RegistryGroup pointer we have now is NULL.
  1884. //
  1885. if (ServiceRecord->RegistryGroup == NULL ||
  1886. _wcsicmp(lpLoadOrderGroup, ServiceRecord->RegistryGroup->GroupName) != 0)
  1887. {
  1888. SC_LOG2(ERROR,
  1889. "ScUpdateServiceRecordConfig: New group [%ws] doesn't match that stored "
  1890. "in the service database [%ws]\n",
  1891. lpLoadOrderGroup,
  1892. ServiceRecord->RegistryGroup ? ServiceRecord->RegistryGroup->GroupName : L"<null>");
  1893. status = ERROR_GEN_FAILURE;
  1894. goto Cleanup;
  1895. }
  1896. }
  1897. //
  1898. // Save Old MemberOfGroup name for error recovery
  1899. //
  1900. if (ServiceRecord->MemberOfGroup != NULL)
  1901. {
  1902. OldLoadOrderGroup = (LPWSTR)LocalAlloc(
  1903. LMEM_FIXED,
  1904. WCSSIZE(ServiceRecord->MemberOfGroup->GroupName));
  1905. //
  1906. // If this allocation fails, just pretend that it doesn't exist.
  1907. //
  1908. if (OldLoadOrderGroup != NULL)
  1909. {
  1910. wcscpy(OldLoadOrderGroup, ServiceRecord->MemberOfGroup->GroupName);
  1911. }
  1912. }
  1913. //
  1914. // Delete MemberOfGroup & Add RegistryGroup to MemberOfGroup so that
  1915. // they are the same.
  1916. // REMEMBER that RegistryGroup and lpLoadOrderGroup are the same!
  1917. //
  1918. ScDeleteGroupMembership(ServiceRecord);
  1919. status = ScCreateGroupMembership(ServiceRecord, lpLoadOrderGroup);
  1920. if (status != NO_ERROR)
  1921. {
  1922. ScDeleteGroupMembership(ServiceRecord);
  1923. if ((OldLoadOrderGroup != NULL) && (*OldLoadOrderGroup))
  1924. {
  1925. backoutStatus = ScCreateGroupMembership(
  1926. ServiceRecord,
  1927. OldLoadOrderGroup);
  1928. if (backoutStatus != NO_ERROR)
  1929. {
  1930. // Do what? - we may want to write to ERROR LOG?
  1931. }
  1932. }
  1933. goto Cleanup;
  1934. }
  1935. }
  1936. status = NO_ERROR;
  1937. Cleanup:
  1938. if (status != NO_ERROR)
  1939. {
  1940. ServiceRecord->ServiceStatus.dwServiceType = OldServiceType;
  1941. ServiceRecord->StartType = OldStartType;
  1942. ServiceRecord->ErrorControl = OldErrorControl;
  1943. if (Progress & DEPENDENCIES_CHANGED)
  1944. {
  1945. ScDeleteStartDependencies(ServiceRecord);
  1946. if ((OldDependencies != NULL) && (*OldDependencies != 0))
  1947. {
  1948. backoutStatus = ScCreateDependencies(
  1949. ServiceRecord,
  1950. OldDependencies);
  1951. if (backoutStatus != NO_ERROR)
  1952. {
  1953. // Do what? - we may want to write to ERROR LOG?
  1954. }
  1955. }
  1956. }
  1957. }
  1958. LocalFree(OldDependencies);
  1959. LocalFree(OldLoadOrderGroup);
  1960. return(status);
  1961. }
  1962. BOOL
  1963. ScAllocateSRHeap(
  1964. DWORD HeapSize
  1965. )
  1966. /*++
  1967. Routine Description:
  1968. Arguments:
  1969. Return Value:
  1970. --*/
  1971. {
  1972. ServiceRecordHeap = HeapCreate(0,HeapSize,0);
  1973. if (ServiceRecordHeap == NULL)
  1974. {
  1975. SC_LOG0(ERROR,"Could not allocate Heap for Service Database\n");
  1976. return(FALSE);
  1977. }
  1978. return(TRUE);
  1979. }