mirror of https://github.com/lianthony/NT4.0
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.
699 lines
17 KiB
699 lines
17 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>
|
|
|
|
|
|
//
|
|
// 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
|
|
ElfPrepareForPause()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prepares the eventlog service for pausing. Since the
|
|
caller took the global resource for exclusive access, we know that
|
|
there is no activity on any of the files.
|
|
|
|
We just have to note somewhere that the service is paused
|
|
so that any threads that start running will know that and will not
|
|
perform any further operations until the service is CONTINUEd.
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
ElfDbgPrint(("[ELF] Control: Prepare for service pause\n"));
|
|
|
|
//
|
|
// Flush all files to disk.
|
|
//
|
|
Status = ElfpFlushFiles ();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ElfPrepareForContinue()
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine restarts the eventlog service after a pause operation.
|
|
It will signal the relevant event(s) for all the threads to start
|
|
running.
|
|
|
|
The caller ensures that it has exclusive access to the global resource
|
|
so that there is no thread inside the service that is doing operations
|
|
on the log file(s) or the data structures while the control request
|
|
is being handled.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
ElfDbgPrint(("[ELF] Control: Prepare for service continue\n"));
|
|
|
|
//
|
|
// Signal the "continue" event.
|
|
//
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ElfControlResponse(
|
|
DWORD opCode
|
|
)
|
|
|
|
{
|
|
DWORD state;
|
|
|
|
ElfDbgPrint(("[ELF] Inside control handler. Control = %ld\n", opCode));
|
|
|
|
//
|
|
// Determine the type of service control message and modify the
|
|
// service status, if necessary.
|
|
//
|
|
switch(opCode) {
|
|
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
case SERVICE_CONTROL_STOP:
|
|
|
|
|
|
//
|
|
// If the service is installed, shut it down and exit.
|
|
//
|
|
|
|
ElfStatusUpdate(STOPPING);
|
|
|
|
GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
|
|
|
|
//
|
|
// Log an event that says we're stopping
|
|
//
|
|
|
|
#if DBG
|
|
ElfpCreateElfEvent(EVENT_EventlogStopped,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
0, // EventCategory
|
|
0, // NumberOfStrings
|
|
NULL, // Strings
|
|
NULL, // Data
|
|
0, // Datalength
|
|
0 // flags
|
|
);
|
|
|
|
//
|
|
// Now force it to be written before we shut down
|
|
//
|
|
|
|
WriteQueuedEvents();
|
|
#endif
|
|
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_PAUSE:
|
|
|
|
//
|
|
// If the service is not already paused, pause it.
|
|
//
|
|
state = GetElState();
|
|
if ((state != PAUSED) && (state != PAUSING)) {
|
|
|
|
GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
|
|
|
|
// NOTE: If there was any service-related pause cleanup, we'd
|
|
// set the status to PAUSE_PENDING, announce it, and
|
|
// then do the cleanup.
|
|
//
|
|
|
|
// Announce that the service is about to be paused
|
|
ElfStatusUpdate(PAUSING);
|
|
|
|
// Get into a decent state to pause the service.
|
|
|
|
ElfPrepareForPause();
|
|
|
|
|
|
// Set the status and announce that the service is paused
|
|
ElfStatusUpdate(PAUSED);
|
|
|
|
ReleaseGlobalResource();
|
|
}
|
|
|
|
break ;
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
|
|
//
|
|
// If the service is not already running, un-pause it.
|
|
//
|
|
|
|
state = GetElState();
|
|
if ((state != RUNNING) && (state != CONTINUING)) {
|
|
|
|
GetGlobalResource (ELF_GLOBAL_EXCLUSIVE);
|
|
|
|
// NOTE: If there was any service-related continue cleanup, we'd
|
|
// set the status to CONTINUE_PENDING, announce it, and
|
|
// then do the cleanup.
|
|
//
|
|
|
|
// Announce that the service is about to be continued
|
|
|
|
ElfStatusUpdate(CONTINUING);
|
|
|
|
// Start up the service.
|
|
|
|
ElfPrepareForContinue();
|
|
|
|
|
|
// Set the status and announce that the service is no longer
|
|
// paused
|
|
//
|
|
ElfStatusUpdate(RUNNING);
|
|
|
|
ReleaseGlobalResource();
|
|
}
|
|
|
|
break ;
|
|
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
|
|
// Do nothing; the status gets announced below
|
|
|
|
default:
|
|
// WARNING: This should never happen.
|
|
ElfStatusUpdate(UPDATE_ONLY);
|
|
break ;
|
|
}
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
DWORD
|
|
ElfBeginForcedShutdown(
|
|
IN BOOL PendingCode,
|
|
IN DWORD ExitCode,
|
|
IN DWORD ServiceSpecificCode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
|
|
EnterCriticalSection(&StatusCriticalSection);
|
|
|
|
ElfDbgPrint(("BeginForcedShutdown: Errors - %d 0x%lx\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) {
|
|
ElStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|
ElState = STOPPING;
|
|
}
|
|
else {
|
|
//
|
|
// The shutdown is to take immediate effect.
|
|
//
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Send the new status to the service controller.
|
|
//
|
|
if (ElfServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
|
|
ElfDbgPrint(("ElfBeginForcedShutdown, no handle to call SetServiceStatus\n"));
|
|
|
|
}
|
|
else if (! SetServiceStatus( ElfServiceStatusHandle, &ElStatus )) {
|
|
|
|
status = GetLastError();
|
|
|
|
if (status != NERR_Success) {
|
|
ElfDbgPrint(("ElfBeginForcedShutdown,SetServiceStatus Failed %X\n",
|
|
status));
|
|
}
|
|
}
|
|
|
|
status = ElState;
|
|
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 send determine which status
|
|
to send. For instance if the status was STARTING, and has changed
|
|
to RUNNING, this routine sends out an INSTALLED 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.
|
|
PAUSING -
|
|
CONTINUING -
|
|
PAUSED -
|
|
|
|
|
|
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.
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status;
|
|
BOOL inhibit = FALSE; // Used to inhibit sending the status
|
|
// to the service controller.
|
|
|
|
EnterCriticalSection(&StatusCriticalSection);
|
|
|
|
ElfDbgPrint(("ElfStatusUpdate (entry) NewState = %d, OldState = %d\n",
|
|
NewState,ElState));
|
|
|
|
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) {
|
|
inhibit = FALSE;
|
|
}
|
|
else {
|
|
//
|
|
// 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;
|
|
//
|
|
// Only stoppable/pausable if developer's build.
|
|
//
|
|
#if DBG
|
|
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE |
|
|
SERVICE_ACCEPT_SHUTDOWN;
|
|
#else
|
|
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
|
|
#endif
|
|
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;
|
|
}
|
|
else if (NewState == PAUSING) {
|
|
ElStatus.dwCurrentState = SERVICE_PAUSE_PENDING;
|
|
}
|
|
else if (NewState == PAUSED) {
|
|
ElStatus.dwCurrentState = SERVICE_PAUSED;
|
|
}
|
|
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:
|
|
//
|
|
// We're already stopped. Therefore, an uninstalled status
|
|
// has already been sent. Do nothing.
|
|
//
|
|
inhibit = TRUE;
|
|
break;
|
|
case PAUSING:
|
|
if (NewState == PAUSED) {
|
|
ElStatus.dwCurrentState = SERVICE_PAUSED;
|
|
ElStatus.dwCheckPoint = 0;
|
|
ElStatus.dwWaitHint = 0;
|
|
}
|
|
else {
|
|
ElStatus.dwCheckPoint = HintCount++;
|
|
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
|
}
|
|
ElState = NewState;
|
|
break;
|
|
case CONTINUING:
|
|
if (NewState == RUNNING) {
|
|
//
|
|
// The Eventlog Service has completed installation.
|
|
//
|
|
ElStatus.dwCurrentState = SERVICE_RUNNING;
|
|
ElStatus.dwCheckPoint = 0;
|
|
ElStatus.dwWaitHint = 0;
|
|
//
|
|
// Only stoppable/pausable if developer's build.
|
|
//
|
|
#if DBG
|
|
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE |
|
|
SERVICE_ACCEPT_SHUTDOWN;
|
|
#else
|
|
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
|
|
#endif
|
|
ElState = NewState;
|
|
}
|
|
else {
|
|
ElStatus.dwCheckPoint = HintCount++;
|
|
}
|
|
break;
|
|
case PAUSED:
|
|
if (NewState == CONTINUING) {
|
|
ElStatus.dwCheckPoint = HintCount++;
|
|
ElStatus.dwWaitHint = ELF_WAIT_HINT_TIME;
|
|
ElStatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
|
|
}
|
|
else if (NewState == RUNNING) {
|
|
//
|
|
// The Eventlog Service has completed installation.
|
|
//
|
|
ElStatus.dwCurrentState = SERVICE_RUNNING;
|
|
ElStatus.dwCheckPoint = 0;
|
|
ElStatus.dwWaitHint = 0;
|
|
//
|
|
// Only stoppable/pausable if developer's build.
|
|
//
|
|
#if DBG
|
|
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE |
|
|
SERVICE_ACCEPT_SHUTDOWN;
|
|
#else
|
|
ElStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN;
|
|
#endif
|
|
ElState = NewState;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!inhibit) {
|
|
if (ElfServiceStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
|
|
ElfDbgPrint(("ElfStatusUpdate, no handle to call SetServiceStatus\n"));
|
|
|
|
}
|
|
else if (! SetServiceStatus( ElfServiceStatusHandle, &ElStatus )) {
|
|
|
|
status = GetLastError();
|
|
|
|
if (status != NERR_Success) {
|
|
ElfDbgPrint(("ElfStatusUpdate, SetServiceStatus Failed %d\n",
|
|
status));
|
|
}
|
|
}
|
|
}
|
|
|
|
ElfDbgPrint(("ElfStatusUpdate (exit) State = %d\n",ElState));
|
|
status = 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);
|
|
}
|
|
|
|
|
|
VOID
|
|
ElInitStatus(VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes the critical section that is used to guard access to the
|
|
status database.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
InitializeCriticalSection(&StatusCriticalSection);
|
|
|
|
ElStatus.dwCurrentState = SERVICE_START_PENDING;
|
|
ElStatus.dwServiceType = SERVICE_WIN32;
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|