1668 lines
45 KiB
C++
1668 lines
45 KiB
C++
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
svcctrl.cxx
|
||
|
||
Abstract:
|
||
|
||
This is the main routine for the NT LAN Manager Service Controller.
|
||
|
||
To use this as a template for another service, simply replace the string
|
||
"svcctl" with the name of the new interface.
|
||
|
||
Author:
|
||
|
||
Dan Lafferty (danl) 20-Mar-1991
|
||
|
||
Environment:
|
||
|
||
User Mode - Win32
|
||
|
||
Revision History:
|
||
|
||
04-Aug-1999 jschwart
|
||
Added code to watch the list of network providers and write the list
|
||
of providers enabled in this HW profile to the registry for mpr.dll
|
||
22-Oct-1998 jschwart
|
||
Added an unhandled exception filter and converted SCM to use
|
||
the NT thread pool APIs
|
||
22-Jun-1998 jschwart
|
||
Added SetErrorMode call to prevent services from halting the process
|
||
with a hard error popup
|
||
10-Mar-1998 jschwart
|
||
Added RegisterScmCallback call to provide SCM support for passing
|
||
PnP messages to services
|
||
12-Dec-1997 WesW
|
||
Added support for safe boot
|
||
11-Jun-1996 AnirudhS
|
||
Don't popup messages during setup. The most common cause of popups
|
||
during upgrade is that a service runs in a domain account, and hence
|
||
has a dependency on netlogon, which is disabled during upgrade.
|
||
26-Jun-1995 AnirudhS
|
||
Added callouts to service object class code (ScNotifyServiceObject).
|
||
20-Oct-1993 Danl
|
||
Added Globals for ScConnectedToSecProc and ScGlobalNetLogonName.
|
||
28-Oct-1992 Danl
|
||
Removed ParseArgs and the NT event. Added Windows event for
|
||
synchronizing service controller with the OpenSCManager client side.
|
||
OpenScManager will now wait until the service controller event is
|
||
set.
|
||
20-Mar-1991 danl
|
||
created
|
||
|
||
--*/
|
||
//
|
||
// INCLUDES
|
||
//
|
||
#include "precomp.hxx"
|
||
#include <stdio.h> // printf
|
||
|
||
#include <winuserp.h> // RegisterServicesProcess
|
||
|
||
#include <lmcons.h> // needed by lmalert.h
|
||
#include <lmalert.h> // NetAlertRaiseEx definitions
|
||
#include <alertmsg.h> // ALERT_SC_IsLastKnownGood
|
||
|
||
#ifdef _CAIRO_
|
||
#include <wtypes.h> // HRESULT
|
||
#include <scmso.h> // ScmCallSvcObject
|
||
#endif
|
||
|
||
#include <tstr.h> // Unicode string macros
|
||
|
||
#include <ntrpcp.h> // Rpcp... function prototypes
|
||
|
||
|
||
#include <sclib.h> // SC_INTERNAL_START_EVENT
|
||
#include <svcslib.h> // CWorkItemContext
|
||
#include "scsec.h" // Security object functions
|
||
#include "scconfig.h" // ScInitSecurityProcess
|
||
#include "depend.h" // ScAutoStartServices
|
||
#include "bootcfg.h" // ScCheckLastKnownGood()
|
||
#include "account.h" // ScInitServiceAccount
|
||
#include "info.h" // ScGetBootAndSystemDriverState
|
||
#include "control.h" // ScShutdownAllServices
|
||
#include "lockapi.h" // ScLockDatabase
|
||
#include "scbsm.h" // ScInitBSM
|
||
#include "valid.h" // MAX_SERVICE_NAME_LENGTH
|
||
#include <svcsp.h> // SVCS_RPC_PIPE, SVCS_LRPC_PROTOCOl, SVCS_LRPC_PORT
|
||
#include <winsvcp.h> // SC_AUTOSTART_EVENT_NAME
|
||
#include <sddl.h> // ConvertSidToStringSid
|
||
#include "resource.h"
|
||
|
||
extern "C" {
|
||
|
||
#include <cfgmgr32.h>
|
||
#include "cfgmgrp.h"
|
||
}
|
||
|
||
#include <scesrv.h>
|
||
#include <crypstub.h> // StartCryptServiceStubs, StopCryptServiceStubs
|
||
#include <trkstub.h> // StartTrkWksServiceStubs, StopTrkWksServiceStubs
|
||
|
||
//
|
||
// Macros:
|
||
//
|
||
// IsServer -- We're running on an NT server or DC
|
||
// IsTerminalServer -- We're running Hydra
|
||
//
|
||
// Note that IsServer is not guaranteed to be accurate during GUI-mode setup since
|
||
// the product type may be changing during an upgrade.
|
||
//
|
||
#define IsServer() (USER_SHARED_DATA->NtProductType != NtProductWinNt)
|
||
#define IsTerminalServer() (BOOLEAN)(USER_SHARED_DATA->SuiteMask & (1 << TerminalServer))
|
||
|
||
#define LENGTH(array) (sizeof(array)/sizeof((array)[0]))
|
||
|
||
//
|
||
// The following turns on code that captures time info & displays it.
|
||
// To be used for performance analysis.
|
||
//
|
||
//#define TIMING_TEST 1
|
||
|
||
//
|
||
// Defines
|
||
//
|
||
|
||
#define SVCCTRL_SHUTDOWN_LEVEL 480
|
||
#define SCREG_BASE_PRIORITY 9
|
||
|
||
#define SERVICES_EXPANDPATH L"%SystemRoot%\\system32\\services.exe"
|
||
#define SECURITY_EXPANDPATH L"%SystemRoot%\\system32\\lsass.exe"
|
||
|
||
|
||
extern "C" typedef
|
||
NET_API_STATUS (NET_API_FUNCTION * PF_NetAlertRaiseEx) (
|
||
IN LPCWSTR AlertEventName,
|
||
IN LPVOID VariableInfo,
|
||
IN DWORD VariableInfoSize,
|
||
IN LPCWSTR ServiceName
|
||
);
|
||
|
||
|
||
|
||
//===========================
|
||
// Globals
|
||
//===========================
|
||
|
||
DWORD ScShutdownInProgress = FALSE;
|
||
|
||
//
|
||
// For determining if the service controller is still in its
|
||
// initialization code.
|
||
//
|
||
BOOL ScStillInitializing = TRUE;
|
||
|
||
//
|
||
// For the service controller to put up a popup to notify the first
|
||
// logged on user if any boot, system, or auto start services failed
|
||
// to start.
|
||
//
|
||
BOOL ScPopupStartFail = FALSE;
|
||
|
||
//
|
||
// Flag indicating whether or not NetLogon has been created, and we
|
||
// have successfully connected to the Security Process .
|
||
// If it hasn't then we need to look for it when it is created so that
|
||
// we can synchronize with lsass appropriately.
|
||
//
|
||
BOOL ScConnectedToSecProc = FALSE;
|
||
|
||
//
|
||
// Linked list of names of boot or system start drivers which failed
|
||
// to load. This list is logged to the eventlog.
|
||
//
|
||
LPFAILED_DRIVER ScFailedDrivers = NULL;
|
||
DWORD ScTotalSizeFailedDrivers = 0;
|
||
|
||
//
|
||
// ScGlobalThisExePath gets initialized to the full path of where this
|
||
// executable image is to be. This is later used to create an image
|
||
// record for services that run in the context of this process.
|
||
//
|
||
LPWSTR ScGlobalThisExePath = NULL;
|
||
|
||
//
|
||
// ScGlobalSecurityExePath gets initialized to the full path of the
|
||
// security process's executable image. This is later used to
|
||
// determine if we need to initialize the security proc when starting
|
||
// services (i.e., initialize if we start the first SecProc service)
|
||
//
|
||
LPWSTR ScGlobalSecurityExePath = NULL;
|
||
|
||
//
|
||
// ScGlobalProductType contains the product type for this machine.
|
||
// Possiblilties are NtProductWinNt, NtProductLanManNt, NtProductServer.
|
||
//
|
||
NT_PRODUCT_TYPE ScGlobalProductType;
|
||
|
||
//
|
||
// Global variables used for safeboot support. g_szSafebootKey contains
|
||
// the name of the safeboot key (minus the service name, which is filled
|
||
// in by ScStartService for each service in start.cxx). g_dwSafebootLen
|
||
// holds the length of the safeboot key name, minus the service name
|
||
//
|
||
|
||
WCHAR g_szSafebootKey[SAFEBOOT_KEY_LENGTH + MAX_SERVICE_NAME_LENGTH + 50 + 1] = SAFEBOOT_KEY;
|
||
DWORD g_dwSafebootLen;
|
||
DWORD g_SafeBootEnabled;
|
||
|
||
//
|
||
// Key handle for MPR provider change notification. Do this in
|
||
// the SCM so we can avoid loading 3 DLLs (needed for calling the
|
||
// client side of the PNP HW profile APIs) into every process that
|
||
// uses mpr.dll.
|
||
//
|
||
HKEY g_hProviderKey;
|
||
|
||
//
|
||
// Handle to this process' token or to LocalSystem.
|
||
//
|
||
HANDLE g_hProcessToken;
|
||
|
||
//=================================
|
||
// prototypes
|
||
//=================================
|
||
BOOL
|
||
ScGetStartEvent(
|
||
LPHANDLE pScStartEvent
|
||
);
|
||
|
||
VOID
|
||
ScPopupThread(
|
||
DWORD StartFailFlag
|
||
);
|
||
|
||
VOID
|
||
ScDestroyFailedDriverList(
|
||
VOID
|
||
);
|
||
|
||
DWORD
|
||
ScMakeFailedDriversOneString(
|
||
LPWSTR *DriverList
|
||
);
|
||
|
||
LONG
|
||
WINAPI
|
||
ScUnhandledExceptionFilter(
|
||
struct _EXCEPTION_POINTERS *ExceptionInfo
|
||
);
|
||
|
||
NTSTATUS
|
||
SvcStartRPCProxys(
|
||
VOID
|
||
);
|
||
|
||
NTSTATUS
|
||
SvcStopRPCProxys(
|
||
VOID
|
||
);
|
||
|
||
NTSTATUS
|
||
ScCreateRpcEndpointSD(
|
||
PSECURITY_DESCRIPTOR *ppSD
|
||
);
|
||
|
||
|
||
VOID
|
||
SvcctrlMain (
|
||
int argc,
|
||
PCHAR argv[]
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the main routine for the Service Controller. It sets up
|
||
the RPC interface.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
Note:
|
||
|
||
|
||
--*/
|
||
{
|
||
RPC_STATUS status;
|
||
NTSTATUS ntStatus;
|
||
DWORD dwStatus;
|
||
HANDLE ScStartEvent;
|
||
HANDLE ThreadHandle;
|
||
DWORD ThreadId;
|
||
SC_RPC_LOCK Lock=NULL;
|
||
KPRIORITY NewBasePriority = SCREG_BASE_PRIORITY;
|
||
HANDLE AutoStartHandle = NULL;
|
||
HKEY hKeySafeBoot;
|
||
HANDLE hProviderEvent = NULL;
|
||
|
||
TOKEN_PRIVILEGES TokenPrivs;
|
||
|
||
//
|
||
// Save bitwise flags to indicate the amount of initialization
|
||
// work done so that if we hit an error along the way, the
|
||
// appropriate amount of shutdown can occur.
|
||
//
|
||
DWORD ScInitState = 0;
|
||
|
||
SetUnhandledExceptionFilter(&ScUnhandledExceptionFilter);
|
||
|
||
|
||
//
|
||
// Prevent critical errors from raising hard error popups and
|
||
// halting services.exe. The flag below will have the system
|
||
// send the errors to the process instead.
|
||
//
|
||
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||
|
||
RtlSetProcessIsCritical(TRUE, NULL, TRUE);
|
||
|
||
|
||
#ifdef TIMING_TEST
|
||
DWORD TickCount1;
|
||
DWORD TickCount2;
|
||
DWORD TickCount3;
|
||
|
||
TickCount1 = GetTickCount();
|
||
#endif // TIMING_TEST
|
||
|
||
|
||
//
|
||
// Turn off privileges that we don't need. Leave the handle to the token
|
||
// open since it is now needed during Service startup processing. Make
|
||
// sure all accesses needed by the rest of the SCM are specified here.
|
||
//
|
||
|
||
ntStatus = NtOpenProcessToken(NtCurrentProcess(),
|
||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE,
|
||
&g_hProcessToken);
|
||
|
||
if (!NT_SUCCESS(ntStatus))
|
||
{
|
||
goto CleanExit;
|
||
}
|
||
|
||
TokenPrivs.PrivilegeCount = 1;
|
||
TokenPrivs.Privileges[0].Luid = RtlConvertLongToLuid(SE_CREATE_TOKEN_PRIVILEGE);
|
||
TokenPrivs.Privileges[0].Attributes = SE_PRIVILEGE_REMOVED;
|
||
|
||
ntStatus = NtAdjustPrivilegesToken(g_hProcessToken,
|
||
FALSE,
|
||
&TokenPrivs,
|
||
sizeof(TokenPrivs),
|
||
NULL,
|
||
NULL);
|
||
|
||
if (!NT_SUCCESS(ntStatus))
|
||
{
|
||
goto CleanExit;
|
||
}
|
||
|
||
|
||
//
|
||
// Create event that Service Controller will set when done starting all
|
||
// Auto-Start services (including async devices). If this call fails,
|
||
// it typically means somebody tried to start up a second instance of
|
||
// services.exe (which is running in the user's context, not LocalSystem).
|
||
//
|
||
AutoStartHandle = CreateEvent(
|
||
NULL, // Event Attributes
|
||
TRUE, // ManualReset
|
||
FALSE, // Initial State (not-signaled)
|
||
SC_AUTOSTART_EVENT_NAME); // Name
|
||
|
||
if (AutoStartHandle == NULL)
|
||
{
|
||
SC_LOG2(ERROR,
|
||
"SvcctrlMain: CreateEvent( \"%ws\" ) failed %ld\n",
|
||
SC_AUTOSTART_EVENT_NAME,
|
||
GetLastError());
|
||
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Create a string containing the pathname for this executable image
|
||
// and one containing the pathname for the security proc image
|
||
//
|
||
{
|
||
DWORD NumChars = 0;
|
||
DWORD CharsReturned = 0;
|
||
WCHAR Temp[1];
|
||
|
||
//
|
||
// Create the string for this Exe
|
||
//
|
||
NumChars = ExpandEnvironmentStringsW(SERVICES_EXPANDPATH,Temp,1);
|
||
|
||
if (NumChars > 1) {
|
||
ScGlobalThisExePath = (LPWSTR)LocalAlloc(
|
||
LPTR,
|
||
NumChars * sizeof(WCHAR));
|
||
if (ScGlobalThisExePath == NULL) {
|
||
SC_LOG0(ERROR,"Couldn't allocate for ThisExePath\n");
|
||
goto CleanExit;
|
||
}
|
||
|
||
CharsReturned = ExpandEnvironmentStringsW(
|
||
SERVICES_EXPANDPATH,
|
||
ScGlobalThisExePath,
|
||
NumChars);
|
||
|
||
if (CharsReturned > NumChars) {
|
||
SC_LOG0(ERROR,"Couldn't expand ThisExePath\n");
|
||
goto CleanExit;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Create the string for the security image
|
||
//
|
||
NumChars = ExpandEnvironmentStringsW(SECURITY_EXPANDPATH, Temp, 1);
|
||
|
||
if (NumChars > 1) {
|
||
|
||
ScGlobalSecurityExePath = (LPWSTR)LocalAlloc(LPTR,
|
||
NumChars * sizeof(WCHAR));
|
||
|
||
if (ScGlobalSecurityExePath == NULL) {
|
||
SC_LOG0(ERROR,"Couldn't allocate for SecurityExePath\n");
|
||
goto CleanExit;
|
||
}
|
||
|
||
CharsReturned = ExpandEnvironmentStringsW(
|
||
SECURITY_EXPANDPATH,
|
||
ScGlobalSecurityExePath,
|
||
NumChars);
|
||
|
||
if (CharsReturned > NumChars) {
|
||
SC_LOG0(ERROR,"Couldn't expand SecurityExePath\n");
|
||
goto CleanExit;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Create well-known SIDs
|
||
//
|
||
if (! NT_SUCCESS(ntStatus = ScCreateWellKnownSids())) {
|
||
SC_LOG1(ERROR, "ScCreateWellKnownSids failed: %08lx\n", ntStatus);
|
||
goto CleanExit;
|
||
}
|
||
ScInitState |= WELL_KNOWN_SIDS_CREATED;
|
||
|
||
//
|
||
// Set up the provider information for mpr.dll
|
||
//
|
||
dwStatus = ScRegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
||
PROVIDER_KEY_BASE L"\\" PROVIDER_KEY_ORDER,
|
||
REG_OPTION_NON_VOLATILE,
|
||
KEY_ALL_ACCESS,
|
||
&g_hProviderKey);
|
||
|
||
if (dwStatus == NO_ERROR)
|
||
{
|
||
hProviderEvent = CreateEvent(NULL,
|
||
TRUE, // Manual-reset
|
||
FALSE, // Nonsignaled
|
||
NULL);
|
||
|
||
if (hProviderEvent != NULL)
|
||
{
|
||
ScHandleProviderChange(hProviderEvent, FALSE);
|
||
}
|
||
else
|
||
{
|
||
SC_LOG1(ERROR,
|
||
"SvcctrlMain: CreateEvent for provider event FAILED %d\n",
|
||
GetLastError());
|
||
|
||
ScRegCloseKey(g_hProviderKey);
|
||
g_hProviderKey = NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SC_LOG1(ERROR,
|
||
"SvcctrlMain: Unable to open provider key %d\n",
|
||
dwStatus);
|
||
}
|
||
|
||
//
|
||
// Create the event that the OpenSCManager will use to wait on the
|
||
// service controller with.
|
||
//
|
||
if (!ScGetStartEvent(&ScStartEvent)) {
|
||
SC_LOG0(ERROR,"SvcctrlMain: ScGetStartEvent Failed\n");
|
||
goto CleanExit;
|
||
}
|
||
|
||
ScInitState |= SC_NAMED_EVENT_CREATED;
|
||
|
||
//
|
||
// Create security descriptor for SC Manager object to protect
|
||
// the SC Manager databases
|
||
//
|
||
if (ScCreateScManagerObject() != NO_ERROR) {
|
||
SC_LOG0(ERROR, "ScCreateScManagerObject failed\n");
|
||
goto CleanExit;
|
||
}
|
||
ScInitState |= SC_MANAGER_OBJECT_CREATED;
|
||
|
||
//
|
||
// Get the ProductType.
|
||
//
|
||
if (!RtlGetNtProductType(&ScGlobalProductType)) {
|
||
SC_LOG0(ERROR, "GetNtProductType failed\n");
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Check the Boot Configuration and assure that the LastKnownGood
|
||
// ControlSet is safe, and pointers are correct.
|
||
// This function initializes the ScGlobalLastKnownGood flag.
|
||
//
|
||
|
||
if (!ScCheckLastKnownGood()) {
|
||
SC_LOG0(ERROR, "ScCheckLastKnownGood failed\n");
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Initialize data structures required to remove a service account.
|
||
// They will be cleaned up by ScEndServiceAccount.
|
||
//
|
||
// NOTE: ScGetComputerNameAndMutex must be called before call to
|
||
// ScInitDatabase because ScInitDatabase may delete a service
|
||
// entry that was marked for delete from a previous boot.
|
||
//
|
||
if (! ScGetComputerNameAndMutex()) {
|
||
SC_LOG0(ERROR, "ScGetComputerName failed\n");
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Read installed services into memory
|
||
//
|
||
if (! ScInitDatabase()) {
|
||
SC_LOG0(ERROR, "ScInitDatabase failed\n");
|
||
goto CleanExit;
|
||
}
|
||
ScInitState |= SC_DATABASE_INITIALIZED;
|
||
|
||
//
|
||
// Initialize accounts functionality.
|
||
//
|
||
|
||
if (! ScInitServiceAccount()) {
|
||
SC_LOG0(ERROR, "ScInitServiceAccount failed\n");
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Create critical sections
|
||
//
|
||
ScInitStartImage();
|
||
ScInitTransactNamedPipe();
|
||
ScInitState |= CRITICAL_SECTIONS_CREATED;
|
||
|
||
if (!CWorkItemContext::Init()) {
|
||
SC_LOG0(ERROR, "CWorkItemContext::Init failed\n");
|
||
goto CleanExit;
|
||
}
|
||
|
||
|
||
//
|
||
// look to see if we booted in safeboot mode
|
||
//
|
||
|
||
dwStatus = RegOpenKey(HKEY_LOCAL_MACHINE,
|
||
L"system\\currentcontrolset\\control\\safeboot\\option",
|
||
&hKeySafeBoot);
|
||
|
||
if (dwStatus == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// we did in fact boot under safeboot control
|
||
//
|
||
ThreadId = sizeof(DWORD);
|
||
|
||
dwStatus = RegQueryValueEx(hKeySafeBoot,
|
||
L"OptionValue",
|
||
NULL,
|
||
NULL,
|
||
(LPBYTE)&g_SafeBootEnabled,
|
||
&ThreadId);
|
||
|
||
if (dwStatus != ERROR_SUCCESS) {
|
||
g_SafeBootEnabled = 0;
|
||
}
|
||
|
||
RegCloseKey(hKeySafeBoot);
|
||
|
||
if (g_SafeBootEnabled) {
|
||
|
||
g_dwSafebootLen = SAFEBOOT_KEY_LENGTH;
|
||
|
||
switch (g_SafeBootEnabled) {
|
||
case SAFEBOOT_MINIMAL:
|
||
wcscpy(g_szSafebootKey + g_dwSafebootLen, SAFEBOOT_MINIMAL_STR_W);
|
||
g_dwSafebootLen += (sizeof(SAFEBOOT_MINIMAL_STR_W) / sizeof(WCHAR)) - 1;
|
||
break;
|
||
|
||
case SAFEBOOT_NETWORK:
|
||
wcscpy(g_szSafebootKey + g_dwSafebootLen, SAFEBOOT_NETWORK_STR_W);
|
||
g_dwSafebootLen += (sizeof(SAFEBOOT_NETWORK_STR_W) / sizeof(WCHAR)) - 1;
|
||
break;
|
||
|
||
case SAFEBOOT_DSREPAIR:
|
||
wcscpy(g_szSafebootKey + g_dwSafebootLen, SAFEBOOT_DSREPAIR_STR_W);
|
||
g_dwSafebootLen += (sizeof(SAFEBOOT_DSREPAIR_STR_W) / sizeof(WCHAR)) - 1;
|
||
break;
|
||
|
||
default:
|
||
SC_ASSERT(FALSE);
|
||
break;
|
||
}
|
||
|
||
wcscpy(g_szSafebootKey + g_dwSafebootLen, L"\\");
|
||
g_dwSafebootLen += 1;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Perform initialization related to network drive arrival broadcasts.
|
||
// (This addes another work item to the object watcher work list.)
|
||
//
|
||
ScInitBSM();
|
||
|
||
//
|
||
// Get the latest state of drivers started up by boot and system
|
||
// init.
|
||
//
|
||
ScGetBootAndSystemDriverState();
|
||
|
||
//
|
||
// Create semaphores needed for handling start dependencies
|
||
//
|
||
if (! ScInitAutoStart()) {
|
||
SC_LOG0(ERROR, "ScInitAutoStart failed\n");
|
||
goto CleanExit;
|
||
}
|
||
ScInitState |= AUTO_START_INITIALIZED;
|
||
|
||
//
|
||
// Register this process with User32. This tells User32 to use the
|
||
// value from HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control
|
||
// \WaitToKillServiceTimeout (if it exists), rather than
|
||
// HKEY_CURRENT_USER\Control Panel\Desktop\WaitToKillAppTimeout,
|
||
// to decide how long to wait before killing us on shutdown.
|
||
//
|
||
if (! RegisterServicesProcess(GetCurrentProcessId())) {
|
||
SC_LOG0(ERROR, "RegisterServicesProcess failed\n");
|
||
}
|
||
|
||
//
|
||
// Lock the database until autostart is complete
|
||
//
|
||
status = ScLockDatabase(TRUE, SERVICES_ACTIVE_DATABASEW, &Lock);
|
||
if (status != NO_ERROR) {
|
||
SC_LOG1(ERROR, "ScLockDatabase failed during init %d\n",status);
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Start the RPC server
|
||
//
|
||
SC_LOG0(TRACE, "Getting ready to start RPC server\n");
|
||
|
||
//
|
||
// Listen to common LRPC port.
|
||
//
|
||
|
||
PSECURITY_DESCRIPTOR pSD;
|
||
|
||
status = ScCreateRpcEndpointSD(&pSD);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
status = RtlNtStatusToDosError(status);
|
||
|
||
SC_LOG1(ERROR,
|
||
"SvcctrlMain: ScCreateRpcEndpointSD failed %d\n",
|
||
status);
|
||
|
||
goto CleanExit;
|
||
}
|
||
|
||
status = RpcServerUseProtseqEp((unsigned short *)SVCS_LRPC_PROTOCOL,
|
||
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
||
(unsigned short *)SVCS_LRPC_PORT,
|
||
pSD);
|
||
|
||
RtlDeleteSecurityObject(&pSD);
|
||
pSD = NULL;
|
||
|
||
if (status != RPC_S_OK)
|
||
{
|
||
SC_LOG1(ERROR, "RpcServerUseProtseqEp on LRPC failed: %d\n", status);
|
||
goto CleanExit;
|
||
}
|
||
|
||
// Listen to named pipe endpoint and register interface.
|
||
|
||
status = RpcpStartRpcServer(
|
||
SVCS_RPC_PIPE,
|
||
svcctl_ServerIfHandle);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
SC_LOG1(ERROR, "RpcpStartRpcServer: %lx\n",status);
|
||
goto CleanExit;
|
||
}
|
||
|
||
ScInitState |= RPC_SERVER_STARTED;
|
||
|
||
//
|
||
// Signal the event that indicates that we are completely started.
|
||
//
|
||
if (!SetEvent(ScStartEvent))
|
||
{
|
||
SC_LOG1(ERROR, "Unable to set StartEvent: %d\n", GetLastError());
|
||
}
|
||
|
||
SC_LOG0(INFO,"Service Controller successfully initialized\n");
|
||
|
||
//
|
||
// Set up for proper shutdown.
|
||
//
|
||
|
||
if (!SetConsoleCtrlHandler(ScShutdownNotificationRoutine, TRUE))
|
||
{
|
||
SC_LOG1(ERROR, "SetConsoleCtrlHandler call failed %d\n",GetLastError());
|
||
}
|
||
|
||
if (!SetProcessShutdownParameters(SVCCTRL_SHUTDOWN_LEVEL, SHUTDOWN_NORETRY))
|
||
{
|
||
SC_LOG1(ERROR, "SetProcessShutdownParameters call failed %d\n",
|
||
GetLastError());
|
||
}
|
||
|
||
SC_LOG0(TRACE,"** ** Service Controller can now accept shutdown system request\n");
|
||
|
||
//
|
||
// init SCE server. if it fails to intialize, the process will terminate
|
||
//
|
||
|
||
dwStatus = ScesrvInitializeServer(RpcpStartRpcServer);
|
||
|
||
if (ERROR_SUCCESS != dwStatus)
|
||
{
|
||
//
|
||
// event log is not up running yet.
|
||
// just log a message to debugger
|
||
// no need to shutdown services
|
||
//
|
||
SC_LOG(ERROR,"ScesrvInitializeServer failed to initialize! %lu\n", dwStatus);
|
||
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Initialization is done, so give PnP a callback routine for them to call
|
||
// when a service needs to receive a PnP event (callback in control.cxx) and
|
||
// to validate a service calling RegisterDeviceNotification.
|
||
//
|
||
RegisterScmCallback(&ScSendPnPMessage, &ScValidatePnPService);
|
||
|
||
SvcStartRPCProxys();
|
||
|
||
//
|
||
// Init the WMI events.
|
||
//
|
||
InitNCEvents();
|
||
|
||
//
|
||
// Auto-start services
|
||
//
|
||
dwStatus = ScAutoStartServices(&Lock);
|
||
|
||
if (dwStatus != NO_ERROR)
|
||
{
|
||
SC_LOG1(ERROR,
|
||
"SvcctrlMain: ScAutoStartServices failed %d\n",
|
||
dwStatus);
|
||
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Log event if any boot/system start drivers failed.
|
||
//
|
||
if (ScFailedDrivers != NULL)
|
||
{
|
||
LPWSTR DriverList;
|
||
|
||
ScMakeFailedDriversOneString(&DriverList);
|
||
|
||
ScLogEvent(
|
||
NEVENT_BOOT_SYSTEM_DRIVERS_FAILED,
|
||
DriverList
|
||
);
|
||
|
||
LocalFree(DriverList);
|
||
|
||
ScDestroyFailedDriverList();
|
||
}
|
||
|
||
//
|
||
// Spin a thread to put up popup if a service specified to start
|
||
// automatically at boot has failed to start, or we are running the
|
||
// last-known-good configuration.
|
||
//
|
||
// Only popup a message if we're running on a server -- Workstation
|
||
// users get confused by the message and don't know where to look
|
||
// to figure out what went wrong. Note that IsServer is not valid
|
||
// during GUI-mode setup (the product type may be changing), but
|
||
// the later call to SetupInProgress handles this.
|
||
//
|
||
// Don't popup any messages if we're booting into safe mode, since
|
||
// several boot and system drivers are explicitly not started and
|
||
// will result in a "false error" when the SCM notices they "failed"
|
||
// to start.
|
||
//
|
||
// Don't popup any messages during setup/upgrade. (The most common
|
||
// cause of messages during upgrade is dependence on netlogon, which
|
||
// is disabled.)
|
||
//
|
||
if ((ScPopupStartFail || (ScGlobalLastKnownGood & REVERTED_TO_LKG))
|
||
&&
|
||
IsServer()
|
||
&&
|
||
!g_SafeBootEnabled
|
||
&&
|
||
(! SetupInProgress(NULL, NULL))) {
|
||
|
||
//
|
||
// Suppress the popups if NoPopupsOnBoot is indicated in the registry.
|
||
//
|
||
DWORD PopupStatus;
|
||
BOOLEAN bPopups = TRUE; // FALSE means suppress popups on boot
|
||
HKEY WindowsKey=NULL;
|
||
|
||
PopupStatus = ScRegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE,
|
||
CONTROL_WINDOWS_KEY_W,
|
||
REG_OPTION_NON_VOLATILE, // options
|
||
KEY_READ, // desired access
|
||
&WindowsKey
|
||
);
|
||
|
||
if (PopupStatus == ERROR_SUCCESS) {
|
||
|
||
DWORD Type;
|
||
DWORD Data;
|
||
DWORD cbData = sizeof(Data);
|
||
|
||
PopupStatus = ScRegQueryValueExW(
|
||
WindowsKey,
|
||
NOBOOTPOPUPS_VALUENAME_W,
|
||
NULL,
|
||
&Type,
|
||
(LPBYTE) &Data,
|
||
&cbData
|
||
);
|
||
|
||
//
|
||
// Popups are suppressed if the NOBOOTPOPUPS_VALUENAME_W value is
|
||
// present, is a REG_DWORD and is non-zero.
|
||
//
|
||
if (PopupStatus == ERROR_SUCCESS &&
|
||
Type == REG_DWORD &&
|
||
Data != 0) {
|
||
|
||
bPopups = FALSE;
|
||
}
|
||
|
||
ScRegCloseKey(WindowsKey);
|
||
}
|
||
|
||
|
||
if (bPopups) {
|
||
|
||
ThreadHandle = CreateThread(
|
||
NULL,
|
||
0L,
|
||
(LPTHREAD_START_ROUTINE) ScPopupThread,
|
||
(LPVOID)(DWORD_PTR) ScPopupStartFail,
|
||
0L,
|
||
&ThreadId
|
||
);
|
||
|
||
if (ThreadHandle == (HANDLE) NULL) {
|
||
SC_LOG(TRACE,"CreateThread ScPopupThread failed %lu\n",
|
||
GetLastError());
|
||
|
||
}
|
||
else {
|
||
(void) CloseHandle(ThreadHandle);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now we can allow database modifications from RPC callers.
|
||
//
|
||
ScUnlockDatabase(&Lock);
|
||
|
||
#ifdef TIMING_TEST
|
||
TickCount2 = GetTickCount();
|
||
#endif
|
||
|
||
//
|
||
// Now switch to high priority class
|
||
//
|
||
(void) NtSetInformationProcess(
|
||
NtCurrentProcess(),
|
||
ProcessBasePriority,
|
||
&NewBasePriority,
|
||
sizeof(NewBasePriority));
|
||
|
||
//
|
||
// If we get this far, then from our point of view, the boot is
|
||
// acceptable. We will now call the Accept Boot Program. This program
|
||
// will decide whether or not the boot was good (from the administrators)
|
||
// point of view.
|
||
// Our default program simply says the boot was good - thus causing
|
||
// LastKnownGood to be updated to the current boot.
|
||
//
|
||
ScRunAcceptBootPgm();
|
||
|
||
//
|
||
// Now that the Auto-start services have been started, notify
|
||
// Terminal Server so that additional Sessions can be started.
|
||
//
|
||
if ( AutoStartHandle )
|
||
NtSetEvent( AutoStartHandle, NULL );
|
||
|
||
//
|
||
// Setup complete -
|
||
// This thread will become the service process watcher. Service
|
||
// process handles are stored in an array of waitble objects that
|
||
// the watcher thread waits on. When any ProcessHandle becomes
|
||
// signaled while in this array, this indicates that the process has
|
||
// terminated unexpectedly. The watcher thread then cleans up the
|
||
// service controller database.
|
||
//
|
||
ScStillInitializing = FALSE;
|
||
|
||
#ifdef TIMING_TEST
|
||
TickCount3 = GetTickCount();
|
||
DbgPrint("[SC_TIMING] Tick Count for autostart complete \t %d\n",TickCount2);
|
||
DbgPrint("[SC-TIMING] MSec for Autostart: \t%d\n",TickCount2-TickCount1);
|
||
DbgPrint("[SC-TIMING] MSec for LKG work: \t%d\n",TickCount3-TickCount2);
|
||
DbgPrint("[SC-TIMING] MSec to complete init:\t%d\n",TickCount3-TickCount1);
|
||
#endif
|
||
|
||
ExitThread(NO_ERROR);
|
||
|
||
CleanExit:
|
||
|
||
//
|
||
// Do minimal cleanup and let process cleanup take care of the rest.
|
||
// Note that the full-blown cleanup was removed in January, 2000 as
|
||
// it was for the most part unnecessary. If some of it ends up being
|
||
// needed, it should be available via the checkin history.
|
||
//
|
||
ScStillInitializing = FALSE;
|
||
|
||
ScEndServiceAccount();
|
||
|
||
SvcStopRPCProxys();
|
||
|
||
//
|
||
// Shut down the RPC server.
|
||
//
|
||
SC_LOG0(TRACE,"Shutting down the RPC interface for the Service Controller\n");
|
||
|
||
if (ScInitState & RPC_SERVER_STARTED)
|
||
{
|
||
status = RpcpStopRpcServer(svcctl_ServerIfHandle);
|
||
}
|
||
|
||
if (Lock != NULL)
|
||
{
|
||
ScUnlockDatabase(&Lock);
|
||
}
|
||
|
||
//
|
||
// terminate SCE server
|
||
//
|
||
ScesrvTerminateServer( (PSVCS_STOP_RPC_SERVER) RpcpStopRpcServer );
|
||
|
||
SC_LOG0(ERROR,"The Service Controller is Terminating.\n");
|
||
|
||
ExitThread(0);
|
||
|
||
return;
|
||
}
|
||
|
||
BOOL
|
||
ScShutdownNotificationRoutine(
|
||
DWORD dwCtrlType
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the system when system shutdown is occuring.
|
||
|
||
Arguments:
|
||
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
|
||
--*/
|
||
{
|
||
if (dwCtrlType == CTRL_SHUTDOWN_EVENT) {
|
||
|
||
SC_LOG0(TRACE," ! SHUTDOWN ! - - In ScShutdownNotificationRoutine\n");
|
||
|
||
|
||
#ifndef SC_DEBUG
|
||
//
|
||
// First quiet all RPC interfaces
|
||
//
|
||
|
||
|
||
ScShutdownInProgress = TRUE;
|
||
#endif
|
||
|
||
//
|
||
// Then shut down all services
|
||
//
|
||
SC_LOG0(TRACE,"[Shutdown] Begin Service Shutdown\n");
|
||
ScShutdownAllServices();
|
||
|
||
}
|
||
return(TRUE);
|
||
}
|
||
|
||
|
||
VOID
|
||
ScLogControlEvent(
|
||
DWORD dwEvent,
|
||
LPCWSTR lpServiceName,
|
||
DWORD dwControl
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Wrapper for logging service control events
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
WCHAR wszControlString[50];
|
||
DWORD dwStringBase;
|
||
|
||
//
|
||
// Load the string that corresponts to this control
|
||
//
|
||
|
||
switch (dwEvent)
|
||
{
|
||
case NEVENT_SERVICE_CONTROL_SUCCESS:
|
||
dwStringBase = IDS_SC_CONTROL_BASE;
|
||
break;
|
||
|
||
case NEVENT_SERVICE_STATUS_SUCCESS:
|
||
dwStringBase = IDS_SC_STATUS_BASE;
|
||
break;
|
||
|
||
case NEVENT_SERVICE_CONFIG_BACKOUT_FAILED:
|
||
dwStringBase = 0; // dwControl is the resource ID
|
||
break;
|
||
|
||
default:
|
||
ASSERT(FALSE);
|
||
return;
|
||
}
|
||
|
||
if (!LoadString(GetModuleHandle(NULL),
|
||
dwStringBase + dwControl,
|
||
wszControlString,
|
||
LENGTH(wszControlString)))
|
||
{
|
||
//
|
||
// The control has no string associated with it
|
||
// (i.e., not a control we log).
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
if (dwEvent == NEVENT_SERVICE_CONTROL_SUCCESS)
|
||
{
|
||
//
|
||
// Include the user that sent the control. Use the empty
|
||
// string on failure (better that than dropping the event).
|
||
//
|
||
|
||
PTOKEN_USER pToken = NULL;
|
||
LPWSTR lpStringSid = NULL;
|
||
|
||
if (ScGetClientSid(&pToken) == NO_ERROR)
|
||
{
|
||
if (!ConvertSidToStringSid(pToken->User.Sid, &lpStringSid))
|
||
{
|
||
lpStringSid = NULL;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
pToken = NULL;
|
||
}
|
||
|
||
ScLogEvent(dwEvent,
|
||
lpServiceName,
|
||
wszControlString,
|
||
lpStringSid);
|
||
|
||
LocalFree(lpStringSid);
|
||
LocalFree(pToken);
|
||
}
|
||
else
|
||
{
|
||
ScLogEvent(dwEvent,
|
||
lpServiceName,
|
||
wszControlString);
|
||
}
|
||
}
|
||
|
||
|
||
BOOL
|
||
ScGetStartEvent(
|
||
LPHANDLE pScStartEvent
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function gets a handle to the SC_INTERNAL_START_EVENT that is
|
||
used to wait on the service controller when calling OpenSCManager.
|
||
|
||
Arguments:
|
||
|
||
pScStartEvent - This is a pointer to the location where the handle
|
||
to the event is to be placed.
|
||
|
||
Return Value:
|
||
|
||
TRUE - If a handle was obtained.
|
||
FALSE - If a handle was not obtained.
|
||
|
||
|
||
--*/
|
||
{
|
||
DWORD status;
|
||
HANDLE ScStartEvent = NULL;
|
||
SECURITY_ATTRIBUTES SecurityAttributes;
|
||
PSECURITY_DESCRIPTOR SecurityDescriptor=NULL;
|
||
|
||
//
|
||
// Initialize the status so that if we fail to create the security
|
||
// descriptor, we will still try to open the event.
|
||
//
|
||
status = ERROR_ALREADY_EXISTS;
|
||
|
||
//
|
||
// Create the event that the OpenSCManager will use to wait on the
|
||
// service controller with.
|
||
//
|
||
|
||
status = ScCreateStartEventSD(&SecurityDescriptor);
|
||
|
||
if (status == NO_ERROR) {
|
||
|
||
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||
SecurityAttributes.bInheritHandle = FALSE;
|
||
SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor;
|
||
|
||
ScStartEvent = CreateEventW(
|
||
&SecurityAttributes,
|
||
TRUE, // Must be manually reset
|
||
FALSE, // The event is initially not signalled
|
||
SC_INTERNAL_START_EVENT );
|
||
|
||
if (ScStartEvent == NULL) {
|
||
status = GetLastError();
|
||
}
|
||
LocalFree(SecurityDescriptor);
|
||
}
|
||
else {
|
||
SC_LOG0(ERROR,"ScGetStartEvent: Couldn't allocate for SecurityDesc\n");
|
||
}
|
||
|
||
if (ScStartEvent == NULL){
|
||
|
||
//
|
||
// If the event already exists, some other process beat us to
|
||
// creating it. Just open it.
|
||
//
|
||
if ( status == ERROR_ALREADY_EXISTS ) {
|
||
ScStartEvent = OpenEvent(
|
||
GENERIC_WRITE,
|
||
FALSE,
|
||
SC_INTERNAL_START_EVENT );
|
||
}
|
||
|
||
if (ScStartEvent == NULL ) {
|
||
SC_LOG1(ERROR,"GetStartEvent: OpenEvent (StartEvent) Failed "
|
||
FORMAT_DWORD "\n", status);
|
||
return(FALSE);
|
||
}
|
||
}
|
||
*pScStartEvent = ScStartEvent;
|
||
return(TRUE);
|
||
}
|
||
|
||
VOID
|
||
ScPopupThread(
|
||
DWORD StartFailFlag
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function reports the state of the system that has just booted.
|
||
If we are running last-known-good:
|
||
1) Raise an admin alert
|
||
2) Put up a message box popup
|
||
|
||
If a service has failed to start (StartFailFlag is TRUE):
|
||
1) Put up a message box popup
|
||
|
||
The reason the StartFailFlag is a parameter is because its value
|
||
may change while we are in this thread. We only care about
|
||
its value at the time this thread is created.
|
||
|
||
Arguments:
|
||
|
||
StartFailFlag - Supplies a flag which indicates whether to put
|
||
up a popup due to services which failed to start.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
#define POPUP_BUFFER_CHARS 256
|
||
|
||
DWORD MessageSize;
|
||
HMODULE NetEventDll;
|
||
WCHAR Buffer[POPUP_BUFFER_CHARS];
|
||
WCHAR Title[POPUP_BUFFER_CHARS];
|
||
LPWSTR pTitle=NULL;
|
||
|
||
HMODULE NetApi32Dll = NULL;
|
||
PF_NetAlertRaiseEx ScNetAlertRaiseEx = NULL;
|
||
KPRIORITY NewBasePriority = SCREG_BASE_PRIORITY;
|
||
|
||
|
||
if (ScGlobalLastKnownGood & REVERTED_TO_LKG) {
|
||
//
|
||
// Get address to API NetAlertRaiseEx to raise an Admin alert
|
||
//
|
||
NetApi32Dll = LoadLibraryW(L"netapi32.dll");
|
||
|
||
if (NetApi32Dll != NULL) {
|
||
ScNetAlertRaiseEx = (PF_NetAlertRaiseEx) GetProcAddress(
|
||
NetApi32Dll,
|
||
"NetAlertRaiseEx"
|
||
);
|
||
|
||
if (ScNetAlertRaiseEx != NULL) {
|
||
|
||
PADMIN_OTHER_INFO Admin;
|
||
|
||
|
||
//
|
||
// Raise an admin alert
|
||
//
|
||
Admin = (PADMIN_OTHER_INFO) Buffer;
|
||
Admin->alrtad_errcode = ALERT_SC_IsLastKnownGood;
|
||
Admin->alrtad_numstrings = 0;
|
||
|
||
(void) ScNetAlertRaiseEx(
|
||
ALERT_ADMIN_EVENT,
|
||
Buffer,
|
||
sizeof(ADMIN_OTHER_INFO),
|
||
SCM_NAMEW
|
||
);
|
||
|
||
}
|
||
|
||
FreeLibrary(NetApi32Dll);
|
||
}
|
||
}
|
||
|
||
NetEventDll = LoadLibraryW(L"netevent.dll");
|
||
|
||
if (NetEventDll == NULL) {
|
||
return;
|
||
}
|
||
|
||
MessageSize = FormatMessageW(
|
||
FORMAT_MESSAGE_FROM_HMODULE,
|
||
(LPVOID) NetEventDll,
|
||
TITLE_SC_MESSAGE_BOX,
|
||
0,
|
||
Title,
|
||
POPUP_BUFFER_CHARS,
|
||
NULL
|
||
);
|
||
|
||
if (MessageSize == 0 ) {
|
||
pTitle = SCM_NAMEW;
|
||
}
|
||
else {
|
||
pTitle = Title;
|
||
}
|
||
|
||
if (ScGlobalLastKnownGood & REVERTED_TO_LKG) {
|
||
|
||
MessageSize = FormatMessageW(
|
||
FORMAT_MESSAGE_FROM_HMODULE,
|
||
(LPVOID) NetEventDll,
|
||
EVENT_RUNNING_LASTKNOWNGOOD,
|
||
0,
|
||
Buffer,
|
||
POPUP_BUFFER_CHARS,
|
||
NULL
|
||
);
|
||
|
||
if (MessageSize != 0) {
|
||
|
||
(void) MessageBoxW(
|
||
NULL,
|
||
Buffer,
|
||
pTitle,
|
||
MB_OK | MB_SETFOREGROUND | MB_ICONEXCLAMATION |
|
||
MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION
|
||
);
|
||
//
|
||
// Now switch back to proper priority
|
||
//
|
||
NtSetInformationProcess(NtCurrentProcess(),
|
||
ProcessBasePriority,
|
||
&NewBasePriority,
|
||
sizeof(NewBasePriority));
|
||
}
|
||
else {
|
||
SC_LOG1(TRACE, "FormatMessage failed %lu\n", GetLastError());
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Popup a message if a service failed to start
|
||
//
|
||
if (StartFailFlag) {
|
||
|
||
MessageSize = FormatMessageW(
|
||
FORMAT_MESSAGE_FROM_HMODULE,
|
||
(LPVOID) NetEventDll,
|
||
EVENT_SERVICE_START_AT_BOOT_FAILED,
|
||
0,
|
||
Buffer,
|
||
POPUP_BUFFER_CHARS,
|
||
NULL
|
||
);
|
||
|
||
if (MessageSize != 0) {
|
||
|
||
MessageBoxW(NULL,
|
||
Buffer,
|
||
pTitle,
|
||
MB_OK | MB_SETFOREGROUND | MB_ICONEXCLAMATION |
|
||
MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION);
|
||
|
||
//
|
||
// Now switch back to proper priority
|
||
//
|
||
|
||
NtSetInformationProcess(NtCurrentProcess(),
|
||
ProcessBasePriority,
|
||
&NewBasePriority,
|
||
sizeof(NewBasePriority));
|
||
}
|
||
else {
|
||
SC_LOG1(TRACE, "FormatMessage failed %lu\n", GetLastError());
|
||
}
|
||
}
|
||
|
||
FreeLibrary(NetEventDll);
|
||
|
||
//
|
||
// Now switch to high priority class
|
||
//
|
||
|
||
ExitThread(0);
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScAddFailedDriver(
|
||
LPWSTR Driver
|
||
)
|
||
{
|
||
DWORD StrSize = (DWORD) WCSSIZE(Driver);
|
||
LPFAILED_DRIVER NewEntry;
|
||
LPFAILED_DRIVER Entry;
|
||
|
||
|
||
NewEntry = (LPFAILED_DRIVER) LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
(UINT) sizeof(FAILED_DRIVER) + StrSize
|
||
);
|
||
|
||
if (NewEntry == NULL) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
//
|
||
// Each string will be separated from the previous one a CR and
|
||
// LF character. We already included one for NULL terminator of each
|
||
// driver so add one more.
|
||
//
|
||
ScTotalSizeFailedDrivers += StrSize + sizeof(WCHAR);
|
||
|
||
wcscpy((LPWSTR) NewEntry->DriverName, Driver);
|
||
|
||
//
|
||
// Insert new entry into ScFailedDrivers global list
|
||
//
|
||
|
||
//
|
||
// Special case empty list
|
||
//
|
||
if (ScFailedDrivers == NULL) {
|
||
ScFailedDrivers = NewEntry;
|
||
return NO_ERROR;
|
||
}
|
||
|
||
//
|
||
// Otherwise look for end of the list and insert new entry
|
||
//
|
||
Entry = ScFailedDrivers;
|
||
|
||
while (Entry->Next != NULL) {
|
||
Entry = Entry->Next;
|
||
}
|
||
|
||
Entry->Next = NewEntry;
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
VOID
|
||
ScDestroyFailedDriverList(
|
||
VOID
|
||
)
|
||
{
|
||
LPFAILED_DRIVER DeleteEntry;
|
||
|
||
|
||
while (ScFailedDrivers != NULL) {
|
||
DeleteEntry = ScFailedDrivers;
|
||
ScFailedDrivers = ScFailedDrivers->Next;
|
||
LocalFree(DeleteEntry);
|
||
}
|
||
}
|
||
|
||
|
||
DWORD
|
||
ScMakeFailedDriversOneString(
|
||
LPWSTR *DriverList
|
||
)
|
||
{
|
||
LPFAILED_DRIVER Entry = ScFailedDrivers;
|
||
|
||
|
||
//
|
||
// Allocate space for concatenated string of all the drivers that
|
||
// failed plus the terminator character.
|
||
//
|
||
*DriverList = (LPWSTR) LocalAlloc(
|
||
LMEM_ZEROINIT,
|
||
(UINT) ScTotalSizeFailedDrivers + sizeof(WCHAR)
|
||
);
|
||
|
||
if (*DriverList == NULL) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
while (Entry != NULL) {
|
||
wcscat(*DriverList, L"\r\n");
|
||
wcscat(*DriverList, (LPWSTR) Entry->DriverName);
|
||
Entry = Entry->Next;
|
||
}
|
||
|
||
return NO_ERROR;
|
||
}
|
||
|
||
|
||
LONG
|
||
WINAPI
|
||
ScUnhandledExceptionFilter(
|
||
struct _EXCEPTION_POINTERS *ExceptionInfo
|
||
)
|
||
{
|
||
return RtlUnhandledExceptionFilter(ExceptionInfo);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SvcStartRPCProxys(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function calls the RPC Proxy startup routines for the
|
||
services that were moved out of services.exe, but have existing
|
||
clients that rely on the services.exe named pipe
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - If proxys were started
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS dwStatus = STATUS_SUCCESS;
|
||
|
||
dwStatus = StartCryptServiceStubs(RpcpStartRpcServer,
|
||
SVCS_RPC_PIPE);
|
||
|
||
// Start the RPC stubs for the distributed link tracking client service.
|
||
dwStatus = StartTrkWksServiceStubs( RpcpStartRpcServer,
|
||
SVCS_RPC_PIPE );
|
||
|
||
return dwStatus;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SvcStopRPCProxys(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function calls the RPC Proxy startup routines for the
|
||
services that were moved out of services.exe, but have existing
|
||
clients that rely on the services.exe named pipe
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - If proxys were started
|
||
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS dwStatus = STATUS_SUCCESS;
|
||
|
||
dwStatus = StopCryptServiceStubs(RpcpStopRpcServer);
|
||
|
||
// Stop the RPC stubs for the distributed link tracking client service.
|
||
dwStatus = StopTrkWksServiceStubs(RpcpStopRpcServer);
|
||
|
||
return dwStatus;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ScCreateRpcEndpointSD(
|
||
PSECURITY_DESCRIPTOR *ppSD
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function builds a security descriptor for the SCM's
|
||
shared LPC endpoint. Everybody needs access to call it.
|
||
|
||
Arguments:
|
||
|
||
ppSD -- pointer to an SD that this routine will allocate
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if SD was successfully created
|
||
|
||
|
||
--*/
|
||
{
|
||
|
||
#define SC_ENDPOINT_ACECOUNT 3
|
||
|
||
SC_ACE_DATA AceData[SC_ENDPOINT_ACECOUNT] = {
|
||
{ ACCESS_ALLOWED_ACE_TYPE, 0, 0,
|
||
GENERIC_ALL,
|
||
&LocalSystemSid },
|
||
|
||
{ ACCESS_ALLOWED_ACE_TYPE, 0, 0,
|
||
GENERIC_ALL,
|
||
&AliasAdminsSid },
|
||
|
||
{ ACCESS_ALLOWED_ACE_TYPE, 0, 0,
|
||
GENERIC_READ | GENERIC_WRITE |
|
||
GENERIC_EXECUTE | SYNCHRONIZE,
|
||
&WorldSid }
|
||
};
|
||
|
||
return ScCreateAndSetSD(AceData,
|
||
SC_ENDPOINT_ACECOUNT,
|
||
NULL, // owner
|
||
NULL, // group
|
||
ppSD);
|
||
|
||
#undef SC_ENDPOINT_ACECOUNT
|
||
|
||
}
|