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.

1626 lines
42 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. svcctrl.cxx
  5. Abstract:
  6. This is the main routine for the NT LAN Manager Service Controller.
  7. To use this as a template for another service, simply replace the string
  8. "svcctl" with the name of the new interface.
  9. Author:
  10. Dan Lafferty (danl) 20-Mar-1991
  11. Environment:
  12. User Mode - Win32
  13. Revision History:
  14. 04-Aug-1999 jschwart
  15. Added code to watch the list of network providers and write the list
  16. of providers enabled in this HW profile to the registry for mpr.dll
  17. 22-Oct-1998 jschwart
  18. Added an unhandled exception filter and converted SCM to use
  19. the NT thread pool APIs
  20. 22-Jun-1998 jschwart
  21. Added SetErrorMode call to prevent services from halting the process
  22. with a hard error popup
  23. 10-Mar-1998 jschwart
  24. Added RegisterScmCallback call to provide SCM support for passing
  25. PnP messages to services
  26. 12-Dec-1997 WesW
  27. Added support for safe boot
  28. 11-Jun-1996 AnirudhS
  29. Don't popup messages during setup. The most common cause of popups
  30. during upgrade is that a service runs in a domain account, and hence
  31. has a dependency on netlogon, which is disabled during upgrade.
  32. 26-Jun-1995 AnirudhS
  33. Added callouts to service object class code (ScNotifyServiceObject).
  34. 20-Oct-1993 Danl
  35. Added Globals for ScConnectedToSecProc and ScGlobalNetLogonName.
  36. 28-Oct-1992 Danl
  37. Removed ParseArgs and the NT event. Added Windows event for
  38. synchronizing service controller with the OpenSCManager client side.
  39. OpenScManager will now wait until the service controller event is
  40. set.
  41. 20-Mar-1991 danl
  42. created
  43. --*/
  44. //
  45. // INCLUDES
  46. //
  47. #include "precomp.hxx"
  48. #include <stdio.h> // printf
  49. #include <winuserp.h> // RegisterServicesProcess
  50. #include <lmcons.h> // needed by lmalert.h
  51. #include <lmalert.h> // NetAlertRaiseEx definitions
  52. #include <alertmsg.h> // ALERT_SC_IsLastKnownGood
  53. #ifdef _CAIRO_
  54. #include <wtypes.h> // HRESULT
  55. #include <scmso.h> // ScmCallSvcObject
  56. #endif
  57. #include <tstr.h> // Unicode string macros
  58. #include <ntrpcp.h> // Rpcp... function prototypes
  59. #include <sclib.h> // SC_INTERNAL_START_EVENT
  60. #include <svcslib.h> // CWorkItemContext
  61. #include "scsec.h" // Security object functions
  62. #include "scconfig.h" // ScInitSecurityProcess
  63. #include "depend.h" // ScAutoStartServices
  64. #include "bootcfg.h" // ScCheckLastKnownGood()
  65. #include "account.h" // ScInitServiceAccount
  66. #include "info.h" // ScGetBootAndSystemDriverState
  67. #include "control.h" // ScShutdownAllServices
  68. #include "lockapi.h" // ScLockDatabase
  69. #include "scbsm.h" // ScInitBSM
  70. #include <svcsp.h> // SVCS_RPC_PIPE, SVCS_LRPC_PROTOCOl, SVCS_LRPC_PORT
  71. #include <winsvcp.h> // SC_AUTOSTART_EVENT_NAME
  72. #include <sddl.h> // ConvertSidToStringSid
  73. #include "resource.h"
  74. extern "C" {
  75. #include <cfgmgr32.h>
  76. #include "cfgmgrp.h"
  77. }
  78. #include <scesrv.h>
  79. #include <crypstub.h> // StartCryptServiceStubs, StopCryptServiceStubs
  80. #include <trkstub.h> // StartTrkWksServiceStubs, StopTrkWksServiceStubs
  81. //
  82. // Macros:
  83. //
  84. // IsServer -- We're running on an NT server or DC
  85. // IsTerminalServer -- We're running Hydra
  86. //
  87. // Note that IsServer is not guaranteed to be accurate during GUI-mode setup since
  88. // the product type may be changing during an upgrade.
  89. //
  90. #define IsServer() (USER_SHARED_DATA->NtProductType != NtProductWinNt)
  91. #define IsTerminalServer() (BOOLEAN)(USER_SHARED_DATA->SuiteMask & (1 << TerminalServer))
  92. #define LENGTH(array) (sizeof(array)/sizeof((array)[0]))
  93. //
  94. // The following turns on code that captures time info & displays it.
  95. // To be used for performance analysis.
  96. //
  97. //#define TIMING_TEST 1
  98. //
  99. // Defines
  100. //
  101. #define SVCCTRL_SHUTDOWN_LEVEL 480
  102. #define SCREG_BASE_PRIORITY 9
  103. #define SERVICES_EXPANDPATH L"%SystemRoot%\\system32\\services.exe"
  104. #define SECURITY_EXPANDPATH L"%SystemRoot%\\system32\\lsass.exe"
  105. extern "C" typedef
  106. NET_API_STATUS (NET_API_FUNCTION * PF_NetAlertRaiseEx) (
  107. IN LPCWSTR AlertEventName,
  108. IN LPVOID VariableInfo,
  109. IN DWORD VariableInfoSize,
  110. IN LPCWSTR ServiceName
  111. );
  112. //===========================
  113. // Globals
  114. //===========================
  115. DWORD ScShutdownInProgress = FALSE;
  116. //
  117. // For determining if the service controller is still in its
  118. // initialization code.
  119. //
  120. BOOL ScStillInitializing = TRUE;
  121. //
  122. // For the service controller to put up a popup to notify the first
  123. // logged on user if any boot, system, or auto start services failed
  124. // to start.
  125. //
  126. BOOL ScPopupStartFail = FALSE;
  127. #ifndef _CAIRO_
  128. //
  129. // Flag indicating whether or not NetLogon has been created, and we
  130. // have successfully connected to the Security Process .
  131. // If it hasn't then we need to look for it when it is created so that
  132. // we can synchronize with lsass appropriately.
  133. //
  134. BOOL ScConnectedToSecProc = FALSE;
  135. #endif // _CAIRO_
  136. //
  137. // Linked list of names of boot or system start drivers which failed
  138. // to load. This list is logged to the eventlog.
  139. //
  140. LPFAILED_DRIVER ScFailedDrivers = NULL;
  141. DWORD ScTotalSizeFailedDrivers = 0;
  142. //
  143. // ScGlobalThisExePath gets initialized to the full path of where this
  144. // executable image is to be. This is later used to create an image
  145. // record for services that run in the context of this process.
  146. //
  147. LPWSTR ScGlobalThisExePath = NULL;
  148. //
  149. // ScGlobalSecurityExePath gets initialized to the full path of the
  150. // security process's executable image. This is later used to
  151. // determine if we need to initialize the security proc when starting
  152. // services (i.e., initialize if we start the first SecProc service)
  153. //
  154. LPWSTR ScGlobalSecurityExePath = NULL;
  155. //
  156. // ScGlobalProductType contains the product type for this machine.
  157. // Possiblilties are NtProductWinNt, NtProductLanManNt, NtProductServer.
  158. //
  159. NT_PRODUCT_TYPE ScGlobalProductType;
  160. //
  161. // Global variables used for safeboot support. g_szSafebootKey contains
  162. // the name of the safeboot key (minus the service name, which is filled
  163. // in by ScStartService for each service in start.cxx). g_dwSafebootLen
  164. // holds the length of the safeboot key name, minus the service name
  165. //
  166. WCHAR g_szSafebootKey[SAFEBOOT_BUFFER_LENGTH] = SAFEBOOT_KEY;
  167. DWORD g_dwSafebootLen;
  168. DWORD g_SafeBootEnabled;
  169. //
  170. // Key handle for MPR provider change notification. Do this in
  171. // the SCM so we can avoid loading 3 DLLs (needed for calling the
  172. // client side of the PNP HW profile APIs) into every process that
  173. // uses mpr.dll.
  174. //
  175. HKEY g_hProviderKey;
  176. //=================================
  177. // prototypes
  178. //=================================
  179. BOOL
  180. ScGetStartEvent(
  181. LPHANDLE pScStartEvent
  182. );
  183. VOID
  184. ScPopupThread(
  185. DWORD StartFailFlag
  186. );
  187. VOID
  188. ScDestroyFailedDriverList(
  189. VOID
  190. );
  191. DWORD
  192. ScMakeFailedDriversOneString(
  193. LPWSTR *DriverList
  194. );
  195. LONG
  196. WINAPI
  197. ScUnhandledExceptionFilter(
  198. struct _EXCEPTION_POINTERS *ExceptionInfo
  199. );
  200. NTSTATUS
  201. SvcStartRPCProxys(
  202. VOID
  203. );
  204. NTSTATUS
  205. SvcStopRPCProxys(
  206. VOID
  207. );
  208. NTSTATUS
  209. ScCreateRpcEndpointSD(
  210. PSECURITY_DESCRIPTOR *ppSD
  211. );
  212. VOID
  213. SvcctrlMain (
  214. int argc,
  215. PCHAR argv[]
  216. )
  217. /*++
  218. Routine Description:
  219. This is the main routine for the Service Controller. It sets up
  220. the RPC interface.
  221. Arguments:
  222. Return Value:
  223. Note:
  224. --*/
  225. {
  226. RPC_STATUS status;
  227. NTSTATUS ntStatus;
  228. DWORD dwStatus;
  229. HANDLE ScStartEvent;
  230. HANDLE ThreadHandle;
  231. DWORD ThreadId;
  232. SC_RPC_LOCK Lock=NULL;
  233. KPRIORITY NewBasePriority = SCREG_BASE_PRIORITY;
  234. HANDLE AutoStartHandle = NULL;
  235. HKEY hKeySafeBoot;
  236. HANDLE hProviderEvent = NULL;
  237. //
  238. // Save bitwise flags to indicate the amount of initialization
  239. // work done so that if we hit an error along the way, the
  240. // appropriate amount of shutdown can occur.
  241. //
  242. DWORD ScInitState = 0;
  243. SetUnhandledExceptionFilter(&ScUnhandledExceptionFilter);
  244. //
  245. // Prevent critical errors from raising hard error popups and
  246. // halting services.exe. The flag below will have the system
  247. // send the errors to the process instead.
  248. //
  249. SetErrorMode(SEM_FAILCRITICALERRORS);
  250. #ifdef TIMING_TEST
  251. DWORD TickCount1;
  252. DWORD TickCount2;
  253. DWORD TickCount3;
  254. TickCount1 = GetTickCount();
  255. #endif // TIMING_TEST
  256. //
  257. // Create event that Service Controller will set when done starting all
  258. // Auto-Start services (including async devices). If this call fails,
  259. // it typically means somebody tried to start up a second instance of
  260. // services.exe (which is running in the user's context, not LocalSystem).
  261. //
  262. AutoStartHandle = CreateEvent(
  263. NULL, // Event Attributes
  264. TRUE, // ManualReset
  265. FALSE, // Initial State (not-signaled)
  266. SC_AUTOSTART_EVENT_NAME); // Name
  267. if (AutoStartHandle == NULL)
  268. {
  269. SC_LOG2(ERROR,
  270. "SvcctrlMain: CreateEvent( \"%ws\" ) failed %ld\n",
  271. SC_AUTOSTART_EVENT_NAME,
  272. GetLastError());
  273. goto CleanExit;
  274. }
  275. //
  276. // Create a string containing the pathname for this executable image
  277. // and one containing the pathname for the security proc image
  278. //
  279. {
  280. DWORD NumChars = 0;
  281. DWORD CharsReturned = 0;
  282. WCHAR Temp[1];
  283. //
  284. // Create the string for this Exe
  285. //
  286. NumChars = ExpandEnvironmentStringsW(SERVICES_EXPANDPATH,Temp,1);
  287. if (NumChars > 1) {
  288. ScGlobalThisExePath = (LPWSTR)LocalAlloc(
  289. LPTR,
  290. NumChars * sizeof(WCHAR));
  291. if (ScGlobalThisExePath == NULL) {
  292. SC_LOG0(ERROR,"Couldn't allocate for ThisExePath\n");
  293. goto CleanExit;
  294. }
  295. CharsReturned = ExpandEnvironmentStringsW(
  296. SERVICES_EXPANDPATH,
  297. ScGlobalThisExePath,
  298. NumChars);
  299. if (CharsReturned > NumChars) {
  300. SC_LOG0(ERROR,"Couldn't expand ThisExePath\n");
  301. goto CleanExit;
  302. }
  303. }
  304. //
  305. // Create the string for the security image
  306. //
  307. NumChars = ExpandEnvironmentStringsW(SECURITY_EXPANDPATH, Temp, 1);
  308. if (NumChars > 1) {
  309. ScGlobalSecurityExePath = (LPWSTR)LocalAlloc(LPTR,
  310. NumChars * sizeof(WCHAR));
  311. if (ScGlobalSecurityExePath == NULL) {
  312. SC_LOG0(ERROR,"Couldn't allocate for SecurityExePath\n");
  313. goto CleanExit;
  314. }
  315. CharsReturned = ExpandEnvironmentStringsW(
  316. SECURITY_EXPANDPATH,
  317. ScGlobalSecurityExePath,
  318. NumChars);
  319. if (CharsReturned > NumChars) {
  320. SC_LOG0(ERROR,"Couldn't expand SecurityExePath\n");
  321. goto CleanExit;
  322. }
  323. }
  324. }
  325. //
  326. // Create well-known SIDs
  327. //
  328. if (! NT_SUCCESS(ntStatus = ScCreateWellKnownSids())) {
  329. SC_LOG1(ERROR, "ScCreateWellKnownSids failed: %08lx\n", ntStatus);
  330. goto CleanExit;
  331. }
  332. ScInitState |= WELL_KNOWN_SIDS_CREATED;
  333. //
  334. // Set up the provider information for mpr.dll
  335. //
  336. dwStatus = ScRegOpenKeyExW(HKEY_LOCAL_MACHINE,
  337. PROVIDER_KEY_BASE L"\\" PROVIDER_KEY_ORDER,
  338. REG_OPTION_NON_VOLATILE,
  339. KEY_ALL_ACCESS,
  340. &g_hProviderKey);
  341. if (dwStatus == NO_ERROR)
  342. {
  343. hProviderEvent = CreateEvent(NULL,
  344. TRUE, // Manual-reset
  345. FALSE, // Nonsignaled
  346. NULL);
  347. if (hProviderEvent != NULL)
  348. {
  349. ScHandleProviderChange(hProviderEvent, FALSE);
  350. }
  351. else
  352. {
  353. SC_LOG1(ERROR,
  354. "SvcctrlMain: CreateEvent for provider event FAILED %d\n",
  355. GetLastError());
  356. ScRegCloseKey(g_hProviderKey);
  357. g_hProviderKey = NULL;
  358. }
  359. }
  360. else
  361. {
  362. SC_LOG1(ERROR,
  363. "SvcctrlMain: Unable to open provider key %d\n",
  364. dwStatus);
  365. }
  366. //
  367. // Create the event that the OpenSCManager will use to wait on the
  368. // service controller with.
  369. //
  370. if (!ScGetStartEvent(&ScStartEvent)) {
  371. SC_LOG0(ERROR,"SvcctrlMain: ScGetStartEvent Failed\n");
  372. goto CleanExit;
  373. }
  374. ScInitState |= SC_NAMED_EVENT_CREATED;
  375. //
  376. // Create security descriptor for SC Manager object to protect
  377. // the SC Manager databases
  378. //
  379. if (ScCreateScManagerObject() != NO_ERROR) {
  380. SC_LOG0(ERROR, "ScCreateScManagerObject failed\n");
  381. goto CleanExit;
  382. }
  383. ScInitState |= SC_MANAGER_OBJECT_CREATED;
  384. //
  385. // Get the ProductType.
  386. //
  387. if (!RtlGetNtProductType(&ScGlobalProductType)) {
  388. SC_LOG0(ERROR, "GetNtProductType failed\n");
  389. goto CleanExit;
  390. }
  391. //
  392. // Check the Boot Configuration and assure that the LastKnownGood
  393. // ControlSet is safe, and pointers are correct.
  394. // This function initializes the ScGlobalLastKnownGood flag.
  395. //
  396. if (!ScCheckLastKnownGood()) {
  397. SC_LOG0(ERROR, "ScCheckLastKnownGood failed\n");
  398. goto CleanExit;
  399. }
  400. //
  401. // Initialize data structures required to remove a service account.
  402. // They will be cleaned up by ScEndServiceAccount.
  403. //
  404. // NOTE: ScGetComputerNameAndMutex must be called before call to
  405. // ScInitDatabase because ScInitDatabase may delete a service
  406. // entry that was marked for delete from a previous boot.
  407. //
  408. if (! ScGetComputerNameAndMutex()) {
  409. SC_LOG0(ERROR, "ScGetComputerName failed\n");
  410. goto CleanExit;
  411. }
  412. //
  413. // Read installed services into memory
  414. //
  415. if (! ScInitDatabase()) {
  416. SC_LOG0(ERROR, "ScInitDatabase failed\n");
  417. goto CleanExit;
  418. }
  419. ScInitState |= SC_DATABASE_INITIALIZED;
  420. //
  421. // Initialize accounts functionality.
  422. //
  423. if (! ScInitServiceAccount()) {
  424. SC_LOG0(ERROR, "ScInitServiceAccount failed\n");
  425. goto CleanExit;
  426. }
  427. //
  428. // Create critical sections
  429. //
  430. ScInitStartImage();
  431. ScInitTransactNamedPipe();
  432. ScInitState |= CRITICAL_SECTIONS_CREATED;
  433. if (!CWorkItemContext::Init()) {
  434. SC_LOG0(ERROR, "CWorkItemContext::Init failed\n");
  435. goto CleanExit;
  436. }
  437. //
  438. // look to see if we booted in safeboot mode
  439. //
  440. dwStatus = RegOpenKey(HKEY_LOCAL_MACHINE,
  441. L"system\\currentcontrolset\\control\\safeboot\\option",
  442. &hKeySafeBoot);
  443. if (dwStatus == ERROR_SUCCESS) {
  444. //
  445. // we did in fact boot under safeboot control
  446. //
  447. ThreadId = sizeof(DWORD);
  448. dwStatus = RegQueryValueEx(hKeySafeBoot,
  449. L"OptionValue",
  450. NULL,
  451. NULL,
  452. (LPBYTE)&g_SafeBootEnabled,
  453. &ThreadId);
  454. if (dwStatus != ERROR_SUCCESS) {
  455. g_SafeBootEnabled = 0;
  456. }
  457. RegCloseKey(hKeySafeBoot);
  458. if (g_SafeBootEnabled) {
  459. g_dwSafebootLen = (sizeof(SAFEBOOT_KEY) / sizeof(WCHAR)) - 1;
  460. switch (g_SafeBootEnabled) {
  461. case SAFEBOOT_MINIMAL:
  462. wcscpy(g_szSafebootKey + g_dwSafebootLen, SAFEBOOT_MINIMAL_STR_W);
  463. g_dwSafebootLen += (sizeof(SAFEBOOT_MINIMAL_STR_W) / sizeof(WCHAR)) - 1;
  464. break;
  465. case SAFEBOOT_NETWORK:
  466. wcscpy(g_szSafebootKey + g_dwSafebootLen, SAFEBOOT_NETWORK_STR_W);
  467. g_dwSafebootLen += (sizeof(SAFEBOOT_NETWORK_STR_W) / sizeof(WCHAR)) - 1;
  468. break;
  469. case SAFEBOOT_DSREPAIR:
  470. wcscpy(g_szSafebootKey + g_dwSafebootLen, SAFEBOOT_DSREPAIR_STR_W);
  471. g_dwSafebootLen += (sizeof(SAFEBOOT_DSREPAIR_STR_W) / sizeof(WCHAR)) - 1;
  472. break;
  473. default:
  474. SC_ASSERT(FALSE);
  475. break;
  476. }
  477. wcscpy(g_szSafebootKey + g_dwSafebootLen, L"\\");
  478. g_dwSafebootLen += 1;
  479. }
  480. }
  481. //
  482. // Perform initialization related to network drive arrival broadcasts.
  483. // (This addes another work item to the object watcher work list.)
  484. //
  485. ScInitBSM();
  486. //
  487. // Get the latest state of drivers started up by boot and system
  488. // init.
  489. //
  490. ScGetBootAndSystemDriverState();
  491. //
  492. // Create semaphores needed for handling start dependencies
  493. //
  494. if (! ScInitAutoStart()) {
  495. SC_LOG0(ERROR, "ScInitAutoStart failed\n");
  496. goto CleanExit;
  497. }
  498. ScInitState |= AUTO_START_INITIALIZED;
  499. //
  500. // Register this process with User32. This tells User32 to use the
  501. // value from HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control
  502. // \WaitToKillServiceTimeout (if it exists), rather than
  503. // HKEY_CURRENT_USER\Control Panel\Desktop\WaitToKillAppTimeout,
  504. // to decide how long to wait before killing us on shutdown.
  505. //
  506. if (! RegisterServicesProcess(GetCurrentProcessId())) {
  507. SC_LOG0(ERROR, "RegisterServicesProcess failed\n");
  508. }
  509. //
  510. // Lock the database until autostart is complete
  511. //
  512. status = ScLockDatabase(TRUE, SERVICES_ACTIVE_DATABASEW, &Lock);
  513. if (status != NO_ERROR) {
  514. SC_LOG1(ERROR, "ScLockDatabase failed during init %d\n",status);
  515. goto CleanExit;
  516. }
  517. //
  518. // Start the RPC server
  519. //
  520. SC_LOG0(TRACE, "Getting ready to start RPC server\n");
  521. //
  522. // Listen to common LRPC port.
  523. //
  524. PSECURITY_DESCRIPTOR pSD;
  525. status = ScCreateRpcEndpointSD(&pSD);
  526. if (!NT_SUCCESS(status))
  527. {
  528. status = RtlNtStatusToDosError(status);
  529. SC_LOG1(ERROR,
  530. "SvcctrlMain: ScCreateRpcEndpointSD failed %d\n",
  531. status);
  532. goto CleanExit;
  533. }
  534. status = RpcServerUseProtseqEp((unsigned short *)SVCS_LRPC_PROTOCOL,
  535. RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
  536. (unsigned short *)SVCS_LRPC_PORT,
  537. pSD);
  538. RtlDeleteSecurityObject(&pSD);
  539. pSD = NULL;
  540. if (status != RPC_S_OK)
  541. {
  542. SC_LOG1(ERROR, "RpcServerUseProtseqEp on LRPC failed: %d\n", status);
  543. goto CleanExit;
  544. }
  545. // Listen to named pipe endpoint and register interface.
  546. status = RpcpStartRpcServer(
  547. SVCS_RPC_PIPE,
  548. svcctl_ServerIfHandle);
  549. if (!NT_SUCCESS(status))
  550. {
  551. SC_LOG1(ERROR, "RpcpStartRpcServer: %lx\n",status);
  552. goto CleanExit;
  553. }
  554. ScInitState |= RPC_SERVER_STARTED;
  555. //
  556. // Signal the event that indicates that we are completely started.
  557. //
  558. if (!SetEvent(ScStartEvent))
  559. {
  560. SC_LOG1(ERROR, "Unable to set StartEvent: %d\n", GetLastError());
  561. }
  562. SC_LOG0(INFO,"Service Controller successfully initialized\n");
  563. //
  564. // Set up for proper shutdown.
  565. //
  566. if (!SetConsoleCtrlHandler(ScShutdownNotificationRoutine, TRUE))
  567. {
  568. SC_LOG1(ERROR, "SetConsoleCtrlHandler call failed %d\n",GetLastError());
  569. }
  570. if (!SetProcessShutdownParameters(SVCCTRL_SHUTDOWN_LEVEL, SHUTDOWN_NORETRY))
  571. {
  572. SC_LOG1(ERROR, "SetProcessShutdownParameters call failed %d\n",
  573. GetLastError());
  574. }
  575. SC_LOG0(TRACE,"** ** Service Controller can now accept shutdown system request\n");
  576. //
  577. // init SCE server. if it fails to intialize, the process will terminate
  578. //
  579. dwStatus = ScesrvInitializeServer(RpcpStartRpcServer);
  580. if (ERROR_SUCCESS != dwStatus)
  581. {
  582. //
  583. // event log is not up running yet.
  584. // just log a message to debugger
  585. // no need to shutdown services
  586. //
  587. SC_LOG(ERROR,"ScesrvInitializeServer failed to initialize! %lu\n", dwStatus);
  588. goto CleanExit;
  589. }
  590. //
  591. // Initialization is done, so give PnP a callback routine for them to call
  592. // when a service needs to receive a PnP event (callback in control.cxx) and
  593. // to validate a service calling RegisterDeviceNotification.
  594. //
  595. RegisterScmCallback(&ScSendPnPMessage, &ScValidatePnPService);
  596. SvcStartRPCProxys();
  597. //
  598. // Init the WMI events.
  599. //
  600. InitNCEvents();
  601. //
  602. // Auto-start services
  603. //
  604. dwStatus = ScAutoStartServices(&Lock);
  605. if (dwStatus != NO_ERROR)
  606. {
  607. SC_LOG1(ERROR,
  608. "SvcctrlMain: ScAutoStartServices failed %d\n",
  609. dwStatus);
  610. goto CleanExit;
  611. }
  612. //
  613. // Log event if any boot/system start drivers failed.
  614. //
  615. if (ScFailedDrivers != NULL)
  616. {
  617. LPWSTR DriverList;
  618. ScMakeFailedDriversOneString(&DriverList);
  619. ScLogEvent(
  620. NEVENT_BOOT_SYSTEM_DRIVERS_FAILED,
  621. DriverList
  622. );
  623. LocalFree(DriverList);
  624. ScDestroyFailedDriverList();
  625. }
  626. //
  627. // Spin a thread to put up popup if a service specified to start
  628. // automatically at boot has failed to start, or we are running the
  629. // last-known-good configuration.
  630. //
  631. // Only popup a message if we're running on a server -- Workstation
  632. // users get confused by the message and don't know where to look
  633. // to figure out what went wrong. Note that IsServer is not valid
  634. // during GUI-mode setup (the product type may be changing), but
  635. // the later call to SetupInProgress handles this.
  636. //
  637. // Don't popup any messages if we're booting into safe mode, since
  638. // several boot and system drivers are explicitly not started and
  639. // will result in a "false error" when the SCM notices they "failed"
  640. // to start.
  641. //
  642. // Don't popup any messages during setup/upgrade. (The most common
  643. // cause of messages during upgrade is dependence on netlogon, which
  644. // is disabled.)
  645. //
  646. if ((ScPopupStartFail || (ScGlobalLastKnownGood & REVERTED_TO_LKG))
  647. &&
  648. IsServer()
  649. &&
  650. !g_SafeBootEnabled
  651. &&
  652. (! SetupInProgress(NULL, NULL))) {
  653. //
  654. // Suppress the popups if NoPopupsOnBoot is indicated in the registry.
  655. //
  656. DWORD PopupStatus;
  657. BOOLEAN bPopups = TRUE; // FALSE means suppress popups on boot
  658. HKEY WindowsKey=NULL;
  659. PopupStatus = ScRegOpenKeyExW(
  660. HKEY_LOCAL_MACHINE,
  661. CONTROL_WINDOWS_KEY_W,
  662. REG_OPTION_NON_VOLATILE, // options
  663. KEY_READ, // desired access
  664. &WindowsKey
  665. );
  666. if (PopupStatus == ERROR_SUCCESS) {
  667. DWORD Type;
  668. DWORD Data;
  669. DWORD cbData = sizeof(Data);
  670. PopupStatus = ScRegQueryValueExW(
  671. WindowsKey,
  672. NOBOOTPOPUPS_VALUENAME_W,
  673. NULL,
  674. &Type,
  675. (LPBYTE) &Data,
  676. &cbData
  677. );
  678. //
  679. // Popups are suppressed if the NOBOOTPOPUPS_VALUENAME_W value is
  680. // present, is a REG_DWORD and is non-zero.
  681. //
  682. if (PopupStatus == ERROR_SUCCESS &&
  683. Type == REG_DWORD &&
  684. Data != 0) {
  685. bPopups = FALSE;
  686. }
  687. ScRegCloseKey(WindowsKey);
  688. }
  689. if (bPopups) {
  690. ThreadHandle = CreateThread(
  691. NULL,
  692. 0L,
  693. (LPTHREAD_START_ROUTINE) ScPopupThread,
  694. (LPVOID)(DWORD_PTR) ScPopupStartFail,
  695. 0L,
  696. &ThreadId
  697. );
  698. if (ThreadHandle == (HANDLE) NULL) {
  699. SC_LOG(TRACE,"CreateThread ScPopupThread failed %lu\n",
  700. GetLastError());
  701. }
  702. else {
  703. (void) CloseHandle(ThreadHandle);
  704. }
  705. }
  706. }
  707. //
  708. // Now we can allow database modifications from RPC callers.
  709. //
  710. ScUnlockDatabase(&Lock);
  711. #ifdef TIMING_TEST
  712. TickCount2 = GetTickCount();
  713. #endif
  714. //
  715. // Now switch to high priority class
  716. //
  717. (void) NtSetInformationProcess(
  718. NtCurrentProcess(),
  719. ProcessBasePriority,
  720. &NewBasePriority,
  721. sizeof(NewBasePriority));
  722. //
  723. // If we get this far, then from our point of view, the boot is
  724. // acceptable. We will now call the Accept Boot Program. This program
  725. // will decide whether or not the boot was good (from the administrators)
  726. // point of view.
  727. // Our default program simply says the boot was good - thus causing
  728. // LastKnownGood to be updated to the current boot.
  729. //
  730. ScRunAcceptBootPgm();
  731. //
  732. // Now that the Auto-start services have been started, notify
  733. // Terminal Server so that additional Sessions can be started.
  734. //
  735. if ( AutoStartHandle )
  736. NtSetEvent( AutoStartHandle, NULL );
  737. //
  738. // Setup complete -
  739. // This thread will become the service process watcher. Service
  740. // process handles are stored in an array of waitble objects that
  741. // the watcher thread waits on. When any ProcessHandle becomes
  742. // signaled while in this array, this indicates that the process has
  743. // terminated unexpectedly. The watcher thread then cleans up the
  744. // service controller database.
  745. //
  746. ScStillInitializing = FALSE;
  747. #ifdef TIMING_TEST
  748. TickCount3 = GetTickCount();
  749. DbgPrint("[SC_TIMING] Tick Count for autostart complete \t %d\n",TickCount2);
  750. DbgPrint("[SC-TIMING] MSec for Autostart: \t%d\n",TickCount2-TickCount1);
  751. DbgPrint("[SC-TIMING] MSec for LKG work: \t%d\n",TickCount3-TickCount2);
  752. DbgPrint("[SC-TIMING] MSec to complete init:\t%d\n",TickCount3-TickCount1);
  753. #endif
  754. ExitThread(NO_ERROR);
  755. CleanExit:
  756. //
  757. // Do minimal cleanup and let process cleanup take care of the rest.
  758. // Note that the full-blown cleanup was removed in January, 2000 as
  759. // it was for the most part unnecessary. If some of it ends up being
  760. // needed, it should be available via the checkin history.
  761. //
  762. ScStillInitializing = FALSE;
  763. ScEndServiceAccount();
  764. SvcStopRPCProxys();
  765. //
  766. // Shut down the RPC server.
  767. //
  768. SC_LOG0(TRACE,"Shutting down the RPC interface for the Service Controller\n");
  769. if (ScInitState & RPC_SERVER_STARTED)
  770. {
  771. status = RpcpStopRpcServer(svcctl_ServerIfHandle);
  772. }
  773. if (Lock != NULL)
  774. {
  775. ScUnlockDatabase(&Lock);
  776. }
  777. //
  778. // terminate SCE server
  779. //
  780. ScesrvTerminateServer( (PSVCS_STOP_RPC_SERVER) RpcpStopRpcServer );
  781. SC_LOG0(ERROR,"The Service Controller is Terminating.\n");
  782. ExitThread(0);
  783. return;
  784. }
  785. BOOL
  786. ScShutdownNotificationRoutine(
  787. DWORD dwCtrlType
  788. )
  789. /*++
  790. Routine Description:
  791. This routine is called by the system when system shutdown is occuring.
  792. Arguments:
  793. Return Value:
  794. --*/
  795. {
  796. if (dwCtrlType == CTRL_SHUTDOWN_EVENT) {
  797. SC_LOG0(TRACE," ! SHUTDOWN ! - - In ScShutdownNotificationRoutine\n");
  798. #ifndef SC_DEBUG
  799. //
  800. // First quiet all RPC interfaces
  801. //
  802. ScShutdownInProgress = TRUE;
  803. #endif
  804. //
  805. // Then shut down all services
  806. //
  807. SC_LOG0(TRACE,"[Shutdown] Begin Service Shutdown\n");
  808. ScShutdownAllServices();
  809. }
  810. return(TRUE);
  811. }
  812. VOID
  813. ScLogControlEvent(
  814. DWORD dwEvent,
  815. LPCWSTR lpServiceName,
  816. DWORD dwControl
  817. )
  818. /*++
  819. Routine Description:
  820. Wrapper for logging service control events
  821. Arguments:
  822. Return Value:
  823. --*/
  824. {
  825. WCHAR wszControlString[50];
  826. DWORD dwStringBase;
  827. //
  828. // Load the string that corresponts to this control
  829. //
  830. switch (dwEvent)
  831. {
  832. case NEVENT_SERVICE_CONTROL_SUCCESS:
  833. dwStringBase = IDS_SC_CONTROL_BASE;
  834. break;
  835. case NEVENT_SERVICE_STATUS_SUCCESS:
  836. dwStringBase = IDS_SC_STATUS_BASE;
  837. break;
  838. case NEVENT_SERVICE_CONFIG_BACKOUT_FAILED:
  839. dwStringBase = 0; // dwControl is the resource ID
  840. break;
  841. default:
  842. ASSERT(FALSE);
  843. return;
  844. }
  845. if (!LoadString(GetModuleHandle(NULL),
  846. dwStringBase + dwControl,
  847. wszControlString,
  848. LENGTH(wszControlString)))
  849. {
  850. //
  851. // The control has no string associated with it
  852. // (i.e., not a control we log).
  853. //
  854. return;
  855. }
  856. if (dwEvent == NEVENT_SERVICE_CONTROL_SUCCESS)
  857. {
  858. //
  859. // Include the user that sent the control. Use the empty
  860. // string on failure (better that than dropping the event).
  861. //
  862. PTOKEN_USER pToken = NULL;
  863. LPWSTR lpStringSid = NULL;
  864. if (ScGetClientSid(&pToken) == NO_ERROR)
  865. {
  866. if (!ConvertSidToStringSid(pToken->User.Sid, &lpStringSid))
  867. {
  868. lpStringSid = NULL;
  869. }
  870. }
  871. else
  872. {
  873. pToken = NULL;
  874. }
  875. ScLogEvent(dwEvent,
  876. lpServiceName,
  877. wszControlString,
  878. lpStringSid);
  879. LocalFree(lpStringSid);
  880. LocalFree(pToken);
  881. }
  882. else
  883. {
  884. ScLogEvent(dwEvent,
  885. lpServiceName,
  886. wszControlString);
  887. }
  888. }
  889. BOOL
  890. ScGetStartEvent(
  891. LPHANDLE pScStartEvent
  892. )
  893. /*++
  894. Routine Description:
  895. This function gets a handle to the SC_INTERNAL_START_EVENT that is
  896. used to wait on the service controller when calling OpenSCManager.
  897. Arguments:
  898. pScStartEvent - This is a pointer to the location where the handle
  899. to the event is to be placed.
  900. Return Value:
  901. TRUE - If a handle was obtained.
  902. FALSE - If a handle was not obtained.
  903. --*/
  904. {
  905. DWORD status;
  906. HANDLE ScStartEvent = NULL;
  907. SECURITY_ATTRIBUTES SecurityAttributes;
  908. PSECURITY_DESCRIPTOR SecurityDescriptor=NULL;
  909. //
  910. // Initialize the status so that if we fail to create the security
  911. // descriptor, we will still try to open the event.
  912. //
  913. status = ERROR_ALREADY_EXISTS;
  914. //
  915. // Create the event that the OpenSCManager will use to wait on the
  916. // service controller with.
  917. //
  918. status = ScCreateStartEventSD(&SecurityDescriptor);
  919. if (status == NO_ERROR) {
  920. SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
  921. SecurityAttributes.bInheritHandle = FALSE;
  922. SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor;
  923. ScStartEvent = CreateEventW(
  924. &SecurityAttributes,
  925. TRUE, // Must be manually reset
  926. FALSE, // The event is initially not signalled
  927. SC_INTERNAL_START_EVENT );
  928. if (ScStartEvent == NULL) {
  929. status = GetLastError();
  930. }
  931. LocalFree(SecurityDescriptor);
  932. }
  933. else {
  934. SC_LOG0(ERROR,"ScGetStartEvent: Couldn't allocate for SecurityDesc\n");
  935. }
  936. if (ScStartEvent == NULL){
  937. //
  938. // If the event already exists, some other process beat us to
  939. // creating it. Just open it.
  940. //
  941. if ( status == ERROR_ALREADY_EXISTS ) {
  942. ScStartEvent = OpenEvent(
  943. GENERIC_WRITE,
  944. FALSE,
  945. SC_INTERNAL_START_EVENT );
  946. }
  947. if (ScStartEvent == NULL ) {
  948. SC_LOG1(ERROR,"GetStartEvent: OpenEvent (StartEvent) Failed "
  949. FORMAT_DWORD "\n", status);
  950. return(FALSE);
  951. }
  952. }
  953. *pScStartEvent = ScStartEvent;
  954. return(TRUE);
  955. }
  956. VOID
  957. ScPopupThread(
  958. DWORD StartFailFlag
  959. )
  960. /*++
  961. Routine Description:
  962. This function reports the state of the system that has just booted.
  963. If we are running last-known-good:
  964. 1) Raise an admin alert
  965. 2) Put up a message box popup
  966. If a service has failed to start (StartFailFlag is TRUE):
  967. 1) Put up a message box popup
  968. The reason the StartFailFlag is a parameter is because its value
  969. may change while we are in this thread. We only care about
  970. its value at the time this thread is created.
  971. Arguments:
  972. StartFailFlag - Supplies a flag which indicates whether to put
  973. up a popup due to services which failed to start.
  974. Return Value:
  975. None.
  976. --*/
  977. {
  978. #define POPUP_BUFFER_CHARS 256
  979. DWORD MessageSize;
  980. HMODULE NetEventDll;
  981. WCHAR Buffer[POPUP_BUFFER_CHARS];
  982. WCHAR Title[POPUP_BUFFER_CHARS];
  983. LPWSTR pTitle=NULL;
  984. HMODULE NetApi32Dll = NULL;
  985. PF_NetAlertRaiseEx ScNetAlertRaiseEx = NULL;
  986. KPRIORITY NewBasePriority = SCREG_BASE_PRIORITY;
  987. if (ScGlobalLastKnownGood & REVERTED_TO_LKG) {
  988. //
  989. // Get address to API NetAlertRaiseEx to raise an Admin alert
  990. //
  991. NetApi32Dll = LoadLibraryW(L"netapi32.dll");
  992. if (NetApi32Dll != NULL) {
  993. ScNetAlertRaiseEx = (PF_NetAlertRaiseEx) GetProcAddress(
  994. NetApi32Dll,
  995. "NetAlertRaiseEx"
  996. );
  997. if (ScNetAlertRaiseEx != NULL) {
  998. PADMIN_OTHER_INFO Admin;
  999. //
  1000. // Raise an admin alert
  1001. //
  1002. Admin = (PADMIN_OTHER_INFO) Buffer;
  1003. Admin->alrtad_errcode = ALERT_SC_IsLastKnownGood;
  1004. Admin->alrtad_numstrings = 0;
  1005. (void) ScNetAlertRaiseEx(
  1006. ALERT_ADMIN_EVENT,
  1007. Buffer,
  1008. sizeof(ADMIN_OTHER_INFO),
  1009. SCM_NAMEW
  1010. );
  1011. }
  1012. FreeLibrary(NetApi32Dll);
  1013. }
  1014. }
  1015. NetEventDll = LoadLibraryW(L"netevent.dll");
  1016. if (NetEventDll == NULL) {
  1017. return;
  1018. }
  1019. MessageSize = FormatMessageW(
  1020. FORMAT_MESSAGE_FROM_HMODULE,
  1021. (LPVOID) NetEventDll,
  1022. TITLE_SC_MESSAGE_BOX,
  1023. 0,
  1024. Title,
  1025. POPUP_BUFFER_CHARS,
  1026. NULL
  1027. );
  1028. if (MessageSize == 0 ) {
  1029. pTitle = SCM_NAMEW;
  1030. }
  1031. else {
  1032. pTitle = Title;
  1033. }
  1034. if (ScGlobalLastKnownGood & REVERTED_TO_LKG) {
  1035. MessageSize = FormatMessageW(
  1036. FORMAT_MESSAGE_FROM_HMODULE,
  1037. (LPVOID) NetEventDll,
  1038. EVENT_RUNNING_LASTKNOWNGOOD,
  1039. 0,
  1040. Buffer,
  1041. POPUP_BUFFER_CHARS,
  1042. NULL
  1043. );
  1044. if (MessageSize != 0) {
  1045. (void) MessageBoxW(
  1046. NULL,
  1047. Buffer,
  1048. pTitle,
  1049. MB_OK | MB_SETFOREGROUND | MB_ICONEXCLAMATION |
  1050. MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION
  1051. );
  1052. //
  1053. // Now switch back to proper priority
  1054. //
  1055. NtSetInformationProcess(NtCurrentProcess(),
  1056. ProcessBasePriority,
  1057. &NewBasePriority,
  1058. sizeof(NewBasePriority));
  1059. }
  1060. else {
  1061. SC_LOG1(TRACE, "FormatMessage failed %lu\n", GetLastError());
  1062. }
  1063. }
  1064. //
  1065. // Popup a message if a service failed to start
  1066. //
  1067. if (StartFailFlag) {
  1068. MessageSize = FormatMessageW(
  1069. FORMAT_MESSAGE_FROM_HMODULE,
  1070. (LPVOID) NetEventDll,
  1071. EVENT_SERVICE_START_AT_BOOT_FAILED,
  1072. 0,
  1073. Buffer,
  1074. POPUP_BUFFER_CHARS,
  1075. NULL
  1076. );
  1077. if (MessageSize != 0) {
  1078. MessageBoxW(NULL,
  1079. Buffer,
  1080. pTitle,
  1081. MB_OK | MB_SETFOREGROUND | MB_ICONEXCLAMATION |
  1082. MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION);
  1083. //
  1084. // Now switch back to proper priority
  1085. //
  1086. NtSetInformationProcess(NtCurrentProcess(),
  1087. ProcessBasePriority,
  1088. &NewBasePriority,
  1089. sizeof(NewBasePriority));
  1090. }
  1091. else {
  1092. SC_LOG1(TRACE, "FormatMessage failed %lu\n", GetLastError());
  1093. }
  1094. }
  1095. FreeLibrary(NetEventDll);
  1096. //
  1097. // Now switch to high priority class
  1098. //
  1099. ExitThread(0);
  1100. }
  1101. DWORD
  1102. ScAddFailedDriver(
  1103. LPWSTR Driver
  1104. )
  1105. {
  1106. DWORD StrSize = (DWORD) WCSSIZE(Driver);
  1107. LPFAILED_DRIVER NewEntry;
  1108. LPFAILED_DRIVER Entry;
  1109. NewEntry = (LPFAILED_DRIVER) LocalAlloc(
  1110. LMEM_ZEROINIT,
  1111. (UINT) sizeof(FAILED_DRIVER) + StrSize
  1112. );
  1113. if (NewEntry == NULL) {
  1114. return ERROR_NOT_ENOUGH_MEMORY;
  1115. }
  1116. //
  1117. // Each string will be separated from the previous one a CR and
  1118. // LF character. We already included one for NULL terminator of each
  1119. // driver so add one more.
  1120. //
  1121. ScTotalSizeFailedDrivers += StrSize + sizeof(WCHAR);
  1122. wcscpy((LPWSTR) NewEntry->DriverName, Driver);
  1123. //
  1124. // Insert new entry into ScFailedDrivers global list
  1125. //
  1126. //
  1127. // Special case empty list
  1128. //
  1129. if (ScFailedDrivers == NULL) {
  1130. ScFailedDrivers = NewEntry;
  1131. return NO_ERROR;
  1132. }
  1133. //
  1134. // Otherwise look for end of the list and insert new entry
  1135. //
  1136. Entry = ScFailedDrivers;
  1137. while (Entry->Next != NULL) {
  1138. Entry = Entry->Next;
  1139. }
  1140. Entry->Next = NewEntry;
  1141. return NO_ERROR;
  1142. }
  1143. VOID
  1144. ScDestroyFailedDriverList(
  1145. VOID
  1146. )
  1147. {
  1148. LPFAILED_DRIVER DeleteEntry;
  1149. while (ScFailedDrivers != NULL) {
  1150. DeleteEntry = ScFailedDrivers;
  1151. ScFailedDrivers = ScFailedDrivers->Next;
  1152. LocalFree(DeleteEntry);
  1153. }
  1154. }
  1155. DWORD
  1156. ScMakeFailedDriversOneString(
  1157. LPWSTR *DriverList
  1158. )
  1159. {
  1160. LPFAILED_DRIVER Entry = ScFailedDrivers;
  1161. //
  1162. // Allocate space for concatenated string of all the drivers that
  1163. // failed plus the terminator character.
  1164. //
  1165. *DriverList = (LPWSTR) LocalAlloc(
  1166. LMEM_ZEROINIT,
  1167. (UINT) ScTotalSizeFailedDrivers + sizeof(WCHAR)
  1168. );
  1169. if (*DriverList == NULL) {
  1170. return ERROR_NOT_ENOUGH_MEMORY;
  1171. }
  1172. while (Entry != NULL) {
  1173. wcscat(*DriverList, L"\r\n");
  1174. wcscat(*DriverList, (LPWSTR) Entry->DriverName);
  1175. Entry = Entry->Next;
  1176. }
  1177. return NO_ERROR;
  1178. }
  1179. LONG
  1180. WINAPI
  1181. ScUnhandledExceptionFilter(
  1182. struct _EXCEPTION_POINTERS *ExceptionInfo
  1183. )
  1184. {
  1185. return RtlUnhandledExceptionFilter(ExceptionInfo);
  1186. }
  1187. NTSTATUS
  1188. SvcStartRPCProxys(
  1189. VOID
  1190. )
  1191. /*++
  1192. Routine Description:
  1193. This function calls the RPC Proxy startup routines for the
  1194. services that were moved out of services.exe, but have existing
  1195. clients that rely on the services.exe named pipe
  1196. Arguments:
  1197. Return Value:
  1198. STATUS_SUCCESS - If proxys were started
  1199. --*/
  1200. {
  1201. NTSTATUS dwStatus = STATUS_SUCCESS;
  1202. dwStatus = StartCryptServiceStubs(RpcpStartRpcServer,
  1203. SVCS_RPC_PIPE);
  1204. // Start the RPC stubs for the distributed link tracking client service.
  1205. dwStatus = StartTrkWksServiceStubs( RpcpStartRpcServer,
  1206. SVCS_RPC_PIPE );
  1207. return dwStatus;
  1208. }
  1209. NTSTATUS
  1210. SvcStopRPCProxys(
  1211. VOID
  1212. )
  1213. /*++
  1214. Routine Description:
  1215. This function calls the RPC Proxy startup routines for the
  1216. services that were moved out of services.exe, but have existing
  1217. clients that rely on the services.exe named pipe
  1218. Arguments:
  1219. Return Value:
  1220. STATUS_SUCCESS - If proxys were started
  1221. --*/
  1222. {
  1223. NTSTATUS dwStatus = STATUS_SUCCESS;
  1224. dwStatus = StopCryptServiceStubs(RpcpStopRpcServer);
  1225. // Stop the RPC stubs for the distributed link tracking client service.
  1226. dwStatus = StopTrkWksServiceStubs(RpcpStopRpcServer);
  1227. return dwStatus;
  1228. }
  1229. NTSTATUS
  1230. ScCreateRpcEndpointSD(
  1231. PSECURITY_DESCRIPTOR *ppSD
  1232. )
  1233. /*++
  1234. Routine Description:
  1235. This function builds a security descriptor for the SCM's
  1236. shared LPC endpoint. Everybody needs access to call it.
  1237. Arguments:
  1238. ppSD -- pointer to an SD that this routine will allocate
  1239. Return Value:
  1240. STATUS_SUCCESS - if SD was successfully created
  1241. --*/
  1242. {
  1243. #define SC_ENDPOINT_ACECOUNT 3
  1244. SC_ACE_DATA AceData[SC_ENDPOINT_ACECOUNT] = {
  1245. { ACCESS_ALLOWED_ACE_TYPE, 0, 0,
  1246. GENERIC_ALL,
  1247. &LocalSystemSid },
  1248. { ACCESS_ALLOWED_ACE_TYPE, 0, 0,
  1249. GENERIC_ALL,
  1250. &AliasAdminsSid },
  1251. { ACCESS_ALLOWED_ACE_TYPE, 0, 0,
  1252. GENERIC_READ | GENERIC_WRITE |
  1253. GENERIC_EXECUTE | SYNCHRONIZE,
  1254. &WorldSid }
  1255. };
  1256. return ScCreateAndSetSD(AceData,
  1257. SC_ENDPOINT_ACECOUNT,
  1258. NULL, // owner
  1259. NULL, // group
  1260. ppSD);
  1261. #undef SC_ENDPOINT_ACECOUNT
  1262. }