mirror of https://github.com/tongzx/nt5src
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.
574 lines
14 KiB
574 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
CONTROL.C
|
|
|
|
Abstract:
|
|
|
|
This file contains the control handler for the eventlog service.
|
|
|
|
Author:
|
|
|
|
Rajen Shah (rajens) 16-Jul-1991
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
//
|
|
// INCLUDES
|
|
//
|
|
|
|
#include <eventp.h>
|
|
|
|
//
|
|
// DEFINITIONS
|
|
//
|
|
|
|
//
|
|
// Controls accepted by the service
|
|
//
|
|
#define ELF_CONTROLS_ACCEPTED SERVICE_ACCEPT_SHUTDOWN;
|
|
|
|
|
|
//
|
|
// GLOBALS
|
|
//
|
|
|
|
CRITICAL_SECTION StatusCriticalSection = {0};
|
|
SERVICE_STATUS ElStatus = {0};
|
|
DWORD HintCount = 0;
|
|
DWORD ElUninstallCode = 0; // reason for uninstalling
|
|
DWORD ElSpecificCode = 0;
|
|
DWORD ElState = STARTING;
|
|
|
|
|
|
|
|
VOID
|
|
ElfControlResponse(
|
|
DWORD opCode
|
|
)
|
|
|
|
{
|
|
DWORD state;
|
|
|
|
ELF_LOG1(TRACE,
|
|
"ElfControlResponse: Received control %d\n",
|
|
opCode);
|
|
|
|
//
|
|
// Determine the type of service control message and modify the
|
|
// service status, if necessary.
|
|
//
|
|
switch(opCode)
|
|
{
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
{
|
|
HKEY hKey;
|
|
ULONG ValueSize;
|
|
ULONG ShutdownReason = 0xFF;
|
|
ULONG rc;
|
|
|
|
//
|
|
// If the service is installed, shut it down and exit.
|
|
//
|
|
|
|
ElfStatusUpdate(STOPPING);
|
|
|
|
GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
|
|
|
|
//
|
|
// Cause the timestamp writing thread to exit
|
|
//
|
|
|
|
if (g_hTimestampEvent != NULL)
|
|
{
|
|
SetEvent (g_hTimestampEvent);
|
|
}
|
|
|
|
//
|
|
// Indicate a normal shutdown in the registry
|
|
//
|
|
|
|
ElfWriteTimeStamp(EVENT_NormalShutdown,
|
|
FALSE);
|
|
|
|
//
|
|
// Determine the reason for this normal shutdown
|
|
//
|
|
|
|
rc = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
|
REGSTR_PATH_RELIABILITY,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hKey,
|
|
NULL);
|
|
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
ValueSize = sizeof(ULONG);
|
|
|
|
rc = RegQueryValueEx(hKey,
|
|
REGSTR_VAL_SHUTDOWNREASON,
|
|
0,
|
|
NULL,
|
|
(PUCHAR) &ShutdownReason,
|
|
&ValueSize);
|
|
|
|
if (rc == ERROR_SUCCESS)
|
|
{
|
|
RegDeleteValue (hKey, REGSTR_VAL_SHUTDOWNREASON);
|
|
}
|
|
|
|
RegCloseKey (hKey);
|
|
}
|
|
|
|
//
|
|
// Log an event that says we're stopping
|
|
//
|
|
|
|
ElfpCreateElfEvent(EVENT_EventlogStopped,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
0, // EventCategory
|
|
0, // NumberOfStrings
|
|
NULL, // Strings
|
|
&ShutdownReason, // Data
|
|
sizeof(ULONG), // Datalength
|
|
0,
|
|
FALSE); // flags
|
|
|
|
//
|
|
// Now force it to be written before we shut down
|
|
//
|
|
WriteQueuedEvents();
|
|
|
|
ReleaseGlobalResource();
|
|
|
|
//
|
|
// If the RegistryMonitor is started, wakeup that
|
|
// worker thread and have it handle the rest of the
|
|
// shutdown.
|
|
//
|
|
// Otherwise The main thread should pick up the
|
|
// fact that a shutdown during startup is occuring.
|
|
//
|
|
if (EventFlags & ELF_STARTED_REGISTRY_MONITOR)
|
|
{
|
|
StopRegistryMonitor();
|
|
}
|
|
|
|
break ;
|
|
}
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
|
|
ElfStatusUpdate(UPDATE_ONLY);
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// This should never happen.
|
|
//
|
|
ELF_LOG1(ERROR,
|
|
"ElfControlResponse: Received unexpected control %d\n",
|
|
opCode);
|
|
|
|
ASSERT(FALSE);
|
|
break ;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ElfBeginForcedShutdown(
|
|
IN BOOL PendingCode,
|
|
IN DWORD ExitCode,
|
|
IN DWORD ServiceSpecificCode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
|
|
EnterCriticalSection(&StatusCriticalSection);
|
|
|
|
ELF_LOG2(ERROR,
|
|
"ElfBeginForcedShutdown: error %d, service-specific error %d\n",
|
|
ExitCode,
|
|
ServiceSpecificCode);
|
|
|
|
//
|
|
// See if the eventlog is already stopping for some reason.
|
|
// It could be that the ControlHandler thread received a control to
|
|
// stop the eventlog just as we decided to stop ourselves.
|
|
//
|
|
if ((ElState != STOPPING) && (ElState != STOPPED))
|
|
{
|
|
if (PendingCode == PENDING)
|
|
{
|
|
ELF_LOG0(TRACE,
|
|
"ElfBeginForcedShutdown: Starting pending shutdown\n");
|
|
|
|
ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
ElState = STOPPING;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The shutdown is to take immediate effect.
|
|
//
|
|
ELF_LOG0(TRACE,
|
|
"ElfBeginForcedShutdown: Starting immediate shutdown\n");
|
|
|
|
ElStatus.dwCurrentState = SERVICE_STOPPED;
|
|
ElStatus.dwControlsAccepted = 0;
|
|
ElStatus.dwCheckPoint = 0;
|
|
ElStatus.dwWaitHint = 0;
|
|
ElState = STOPPED;
|
|
}
|
|
|
|
ElUninstallCode = ExitCode;
|
|
ElSpecificCode = ServiceSpecificCode;
|
|
|
|
ElStatus.dwWin32ExitCode = ExitCode;
|
|
ElStatus.dwServiceSpecificExitCode = ServiceSpecificCode;
|
|
}
|
|
|
|
//
|
|
// Cause the timestamp writing thread to exit
|
|
//
|
|
|
|
if (g_hTimestampEvent != NULL)
|
|
{
|
|
SetEvent (g_hTimestampEvent);
|
|
}
|
|
|
|
//
|
|
// Send the new status to the service controller.
|
|
//
|
|
|
|
ASSERT(ElfServiceStatusHandle != 0);
|
|
|
|
if (!SetServiceStatus( ElfServiceStatusHandle, &ElStatus ))
|
|
{
|
|
ELF_LOG1(ERROR,
|
|
"ElfBeginForcedShutdown: SetServicestatus failed %d\n",
|
|
GetLastError());
|
|
}
|
|
|
|
status = ElState;
|
|
|
|
ELF_LOG1(TRACE,
|
|
"ElfBeginForcedShutdown: New state is %d\n",
|
|
status);
|
|
|
|
LeaveCriticalSection(&StatusCriticalSection);
|
|
return status;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ElfStatusUpdate(
|
|
IN DWORD NewState
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sends a status to the Service Controller via SetServiceStatus.
|
|
|
|
The contents of the status message is controlled by this routine.
|
|
The caller simply passes in the desired state, and this routine does
|
|
the rest. For instance, if the Eventlog passes in a STARTING state,
|
|
This routine will update the hint count that it maintains, and send
|
|
the appropriate information in the SetServiceStatus call.
|
|
|
|
This routine uses transitions in state to determine which status
|
|
to send. For instance if the status was STARTING, and has changed
|
|
to RUNNING, this routine sends SERVICE_RUNNING to the Service
|
|
Controller.
|
|
|
|
Arguments:
|
|
|
|
NewState - Can be any of the state flags:
|
|
UPDATE_ONLY - Simply send out the current status
|
|
STARTING - The Eventlog is in the process of initializing
|
|
RUNNING - The Eventlog has finished with initialization
|
|
STOPPING - The Eventlog is in the process of shutting down
|
|
STOPPED - The Eventlog has completed the shutdown.
|
|
|
|
Return Value:
|
|
|
|
CurrentState - This may not be the same as the NewState that was
|
|
passed in. It could be that the main thread is sending in a new
|
|
install state just after the Control Handler set the state to
|
|
STOPPING. In this case, the STOPPING state will be returned so as
|
|
to inform the main thread that a shut-down is in process.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
BOOL inhibit = FALSE; // Used to inhibit sending the status
|
|
// to the service controller.
|
|
|
|
EnterCriticalSection(&StatusCriticalSection);
|
|
|
|
ELF_LOG2(TRACE,
|
|
"ElfStatusUpdate: old state = %d, new state = %d\n",
|
|
ElState,
|
|
NewState);
|
|
|
|
if (NewState == STOPPED)
|
|
{
|
|
if (ElState == STOPPED)
|
|
{
|
|
//
|
|
// It was already stopped, don't send another SetServiceStatus.
|
|
//
|
|
inhibit = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The shut down is complete, indicate that the eventlog
|
|
// has stopped.
|
|
//
|
|
ElStatus.dwCurrentState = SERVICE_STOPPED;
|
|
ElStatus.dwControlsAccepted = 0;
|
|
ElStatus.dwCheckPoint = 0;
|
|
ElStatus.dwWaitHint = 0;
|
|
|
|
ElStatus.dwWin32ExitCode = ElUninstallCode;
|
|
ElStatus.dwServiceSpecificExitCode = ElSpecificCode;
|
|
}
|
|
|
|
ElState = NewState;
|
|
}
|
|
else if (NewState != UPDATE_ONLY)
|
|
{
|
|
//
|
|
// We are not being asked to change to the STOPPED state.
|
|
//
|
|
switch(ElState)
|
|
{
|
|
case STARTING:
|
|
|
|
if (NewState == STOPPING)
|
|
{
|
|
ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
ElStatus.dwControlsAccepted = 0;
|
|
ElStatus.dwCheckPoint = HintCount++;
|
|
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
|
ElState = NewState;
|
|
|
|
EventlogShutdown = TRUE;
|
|
}
|
|
else if (NewState == RUNNING)
|
|
{
|
|
//
|
|
// The Eventlog Service has completed installation.
|
|
//
|
|
ElStatus.dwCurrentState = SERVICE_RUNNING;
|
|
ElStatus.dwCheckPoint = 0;
|
|
ElStatus.dwWaitHint = 0;
|
|
|
|
ElStatus.dwControlsAccepted = ELF_CONTROLS_ACCEPTED;
|
|
ElState = NewState;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The NewState must be STARTING. So update the pending
|
|
// count
|
|
//
|
|
|
|
ElStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
ElStatus.dwControlsAccepted = 0;
|
|
ElStatus.dwCheckPoint = HintCount++;
|
|
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
|
}
|
|
|
|
break;
|
|
|
|
case RUNNING:
|
|
|
|
if (NewState == STOPPING)
|
|
{
|
|
ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
ElStatus.dwControlsAccepted = 0;
|
|
|
|
EventlogShutdown = TRUE;
|
|
}
|
|
|
|
ElStatus.dwCheckPoint = HintCount++;
|
|
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
|
ElState = NewState;
|
|
|
|
break;
|
|
|
|
case STOPPING:
|
|
|
|
//
|
|
// No matter what else was passed in, force the status to
|
|
// indicate that a shutdown is pending.
|
|
//
|
|
ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
ElStatus.dwControlsAccepted = 0;
|
|
ElStatus.dwCheckPoint = HintCount++;
|
|
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
|
EventlogShutdown = TRUE;
|
|
|
|
break;
|
|
|
|
case STOPPED:
|
|
|
|
ASSERT(NewState == STARTING);
|
|
|
|
//
|
|
// The Eventlog Service is starting up after being stopped.
|
|
// This can occur if the service is manually started after
|
|
// failing to start.
|
|
//
|
|
ElStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
ElStatus.dwCheckPoint = 0;
|
|
ElStatus.dwWaitHint = 0;
|
|
ElStatus.dwControlsAccepted = ELF_CONTROLS_ACCEPTED;
|
|
ElState = NewState;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!inhibit)
|
|
{
|
|
ASSERT(ElfServiceStatusHandle != 0);
|
|
|
|
if (!SetServiceStatus(ElfServiceStatusHandle, &ElStatus))
|
|
{
|
|
ELF_LOG1(ERROR,
|
|
"ElfStatusUpdate: SetServiceStatus failed %d\n",
|
|
GetLastError());
|
|
}
|
|
}
|
|
|
|
status = ElState;
|
|
|
|
ELF_LOG1(TRACE,
|
|
"ElfStatusUpdate: Exiting with state = %d\n",
|
|
ElState);
|
|
|
|
LeaveCriticalSection(&StatusCriticalSection);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetElState (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Obtains the state of the Eventlog Service. This state information
|
|
is protected as a critical section such that only one thread can
|
|
modify or read it at a time.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
The Eventlog State is returned as the return value.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
|
|
EnterCriticalSection(&StatusCriticalSection);
|
|
status = ElState;
|
|
LeaveCriticalSection(&StatusCriticalSection);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ElfpInitStatus(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the critical section that is used to guard access to the
|
|
status database.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
ElStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
ElStatus.dwServiceType = SERVICE_WIN32;
|
|
|
|
return ElfpInitCriticalSection(&StatusCriticalSection);
|
|
}
|
|
|
|
|
|
VOID
|
|
ElCleanupStatus(VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes the critical section used to control access to the thread and
|
|
status database.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
DeleteCriticalSection(&StatusCriticalSection);
|
|
}
|