410 lines
10 KiB
C
410 lines
10 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
splstat.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Routines for managing access to the status information and reporting:
|
|||
|
|
|||
|
SpoolerStatusInit
|
|||
|
SpoolerBeginForcedShutdown
|
|||
|
SpoolerStatusUpdate
|
|||
|
GetSpoolerState
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Krishna Ganugapati (KrishnaG) 17-Oct-1993
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
User Mode -Win32
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
17-Oct-1993 KrishnaG
|
|||
|
created
|
|||
|
|
|||
|
--*/
|
|||
|
//
|
|||
|
// Includes
|
|||
|
//
|
|||
|
#define NOMINMAX
|
|||
|
#include <nt.h>
|
|||
|
#include <ntrtl.h>
|
|||
|
#include <nturtl.h>
|
|||
|
#include <windows.h>
|
|||
|
#include <winspool.h>
|
|||
|
#include <winsplp.h>
|
|||
|
#include <rpc.h>
|
|||
|
#include <winsvc.h> // Service control APIs
|
|||
|
#include <lmsname.h>
|
|||
|
|
|||
|
#include "splsvr.h"
|
|||
|
#include "splr.h"
|
|||
|
#include "server.h"
|
|||
|
|
|||
|
// Static Data
|
|||
|
//
|
|||
|
|
|||
|
static DWORD Next;
|
|||
|
static DWORD InstallState;
|
|||
|
static SERVICE_STATUS SpoolerStatus;
|
|||
|
static DWORD HintCount;
|
|||
|
static DWORD SpoolerUninstallCode; // reason for uninstalling
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
SpoolerStatusInit(VOID)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initializes the status database.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
EnterCriticalSection(&ThreadCriticalSection);
|
|||
|
|
|||
|
SpoolerState=STARTING;
|
|||
|
|
|||
|
HintCount = 1;
|
|||
|
SpoolerUninstallCode = 0;
|
|||
|
|
|||
|
SpoolerStatus.dwServiceType = SERVICE_WIN32;
|
|||
|
SpoolerStatus.dwCurrentState = SERVICE_START_PENDING;
|
|||
|
SpoolerStatus.dwControlsAccepted = 0;
|
|||
|
SpoolerStatus.dwCheckPoint = HintCount;
|
|||
|
SpoolerStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
SpoolerStatus.dwWin32ExitCode = NO_ERROR;
|
|||
|
SpoolerStatus.dwServiceSpecificExitCode = NO_ERROR;
|
|||
|
|
|||
|
LeaveCriticalSection(&ThreadCriticalSection);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
DWORD
|
|||
|
SpoolerBeginForcedShutdown(
|
|||
|
IN BOOL PendingCode,
|
|||
|
IN DWORD Win32ExitCode,
|
|||
|
IN DWORD ServiceSpecificExitCode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called to set the appropriate status when a shutdown
|
|||
|
is to occur due to an error in the Spooler. NOTE: if a shutdown is
|
|||
|
based on a request from the Service Controller, SpoolerStatusUpdate is
|
|||
|
called instead.
|
|||
|
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PendingCode - Indicates if the Shutdown is immediate or pending. If
|
|||
|
PENDING, the shutdown will take some time, so a pending status is
|
|||
|
sent to the ServiceController.
|
|||
|
|
|||
|
ExitCode - Indicates the reason for the shutdown.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
CurrentState - Contains the current state that the spooler is in
|
|||
|
upon exit from this routine. In this case it will be STOPPED
|
|||
|
if the PendingCode is PENDING, or STOPPING if the PendingCode
|
|||
|
is IMMEDIATE.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
We need to clean this code up!
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
|
|||
|
EnterCriticalSection(&ThreadCriticalSection);
|
|||
|
|
|||
|
//
|
|||
|
// See if the Spooler is already stopping for some reason.
|
|||
|
// It could be that the ControlHandler thread received a control to
|
|||
|
// stop the Spooler just as we decided to stop ourselves.
|
|||
|
//
|
|||
|
|
|||
|
if ((SpoolerState != STOPPING) && (SpoolerState != STOPPED)) {
|
|||
|
|
|||
|
if (PendingCode == PENDING) {
|
|||
|
SpoolerStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|||
|
SpoolerState = STOPPING;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// The shutdown is to take immediate effect.
|
|||
|
//
|
|||
|
SpoolerStatus.dwCurrentState = SERVICE_STOPPED;
|
|||
|
SpoolerStatus.dwControlsAccepted = 0;
|
|||
|
SpoolerStatus.dwCheckPoint = 0;
|
|||
|
SpoolerStatus.dwWaitHint = 0;
|
|||
|
SpoolerState = STOPPED;
|
|||
|
}
|
|||
|
|
|||
|
SpoolerUninstallCode = Win32ExitCode;
|
|||
|
SpoolerStatus.dwWin32ExitCode = Win32ExitCode;
|
|||
|
SpoolerStatus.dwServiceSpecificExitCode = ServiceSpecificExitCode;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Send the new status to the service controller.
|
|||
|
//
|
|||
|
if (!SpoolerStatusHandle) {
|
|||
|
DBGMSG(DBG_ERROR,
|
|||
|
("SpoolerBeginForcedShutdown, no handle to call SetServiceStatus\n"));
|
|||
|
|
|||
|
}
|
|||
|
else if (! SetServiceStatus( SpoolerStatusHandle, &SpoolerStatus )) {
|
|||
|
|
|||
|
status = GetLastError();
|
|||
|
|
|||
|
if (status != NERR_Success) {
|
|||
|
DBGMSG(ERROR,
|
|||
|
("SpoolerBeginForcedShutdown,SetServiceStatus Failed %X\n",
|
|||
|
status));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = SpoolerState;
|
|||
|
LeaveCriticalSection(&ThreadCriticalSection);
|
|||
|
return(status);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
SpoolerStatusUpdate(
|
|||
|
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 Spooler 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 Spooler is in the process of initializing
|
|||
|
RUNNING - The Spooler has finished with initialization
|
|||
|
STOPPING - The Spooler is in the process of shutting down
|
|||
|
STOPPED - The Spooler 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.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
BOOL inhibit = FALSE; // Used to inhibit sending the status
|
|||
|
// to the service controller.
|
|||
|
|
|||
|
EnterCriticalSection(&ThreadCriticalSection);
|
|||
|
|
|||
|
|
|||
|
if (NewState == STOPPED) {
|
|||
|
if (SpoolerState == STOPPED) {
|
|||
|
//
|
|||
|
// It was already stopped, don't send another SetServiceStatus.
|
|||
|
//
|
|||
|
inhibit = TRUE;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// The shut down is complete, indicate that the spooler
|
|||
|
// has stopped.
|
|||
|
//
|
|||
|
SpoolerStatus.dwCurrentState = SERVICE_STOPPED;
|
|||
|
SpoolerStatus.dwControlsAccepted = 0;
|
|||
|
SpoolerStatus.dwCheckPoint = 0;
|
|||
|
SpoolerStatus.dwWaitHint = 0;
|
|||
|
SpoolerStatus.dwWin32ExitCode = NO_ERROR;
|
|||
|
SpoolerStatus.dwServiceSpecificExitCode = NO_ERROR;
|
|||
|
}
|
|||
|
SpoolerState = NewState;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// We are not being asked to change to the STOPPED state.
|
|||
|
//
|
|||
|
switch(SpoolerState) {
|
|||
|
|
|||
|
case STARTING:
|
|||
|
if (NewState == STOPPING) {
|
|||
|
|
|||
|
SpoolerStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|||
|
SpoolerStatus.dwControlsAccepted = 0;
|
|||
|
SpoolerStatus.dwCheckPoint = HintCount++;
|
|||
|
SpoolerStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
SpoolerState = NewState;
|
|||
|
}
|
|||
|
|
|||
|
else if (NewState == RUNNING) {
|
|||
|
|
|||
|
//
|
|||
|
// The Spooler Service has completed installation.
|
|||
|
//
|
|||
|
SpoolerStatus.dwCurrentState = SERVICE_RUNNING;
|
|||
|
//
|
|||
|
// The Spooler Service cannot be stopped once started
|
|||
|
//
|
|||
|
SpoolerStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|||
|
SERVICE_ACCEPT_SHUTDOWN |
|
|||
|
SERVICE_ACCEPT_POWEREVENT;
|
|||
|
SpoolerStatus.dwCheckPoint = 0;
|
|||
|
SpoolerStatus.dwWaitHint = 0;
|
|||
|
|
|||
|
SpoolerState = NewState;
|
|||
|
}
|
|||
|
|
|||
|
else {
|
|||
|
//
|
|||
|
// The NewState must be STARTING. So update the pending
|
|||
|
// count
|
|||
|
//
|
|||
|
|
|||
|
SpoolerStatus.dwCurrentState = SERVICE_START_PENDING;
|
|||
|
SpoolerStatus.dwControlsAccepted = 0;
|
|||
|
SpoolerStatus.dwCheckPoint = HintCount++;
|
|||
|
SpoolerStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case RUNNING:
|
|||
|
if (NewState == STOPPING) {
|
|||
|
|
|||
|
SpoolerStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|||
|
SpoolerStatus.dwControlsAccepted = 0;
|
|||
|
SpoolerStatus.dwCheckPoint = HintCount++;
|
|||
|
SpoolerStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
|
|||
|
SpoolerState = NewState;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case STOPPING:
|
|||
|
//
|
|||
|
// No matter what else was passed in, force the status to
|
|||
|
// indicate that a shutdown is pending.
|
|||
|
//
|
|||
|
SpoolerStatus.dwCurrentState = SERVICE_STOPPED;
|
|||
|
SpoolerStatus.dwControlsAccepted = 0;
|
|||
|
SpoolerStatus.dwCheckPoint = 0;
|
|||
|
SpoolerStatus.dwWaitHint = 0; // 20 seconds
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case STOPPED:
|
|||
|
//
|
|||
|
// We're already stopped. Therefore, an uninstalled status
|
|||
|
// as already been sent. Do nothing.
|
|||
|
//
|
|||
|
inhibit = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!inhibit) {
|
|||
|
if (!SpoolerStatusHandle) {
|
|||
|
DBGMSG(DBG_ERROR,("SpoolerStatusUpdate, no handle to call SetServiceStatus\n"));
|
|||
|
|
|||
|
}
|
|||
|
else if (! SetServiceStatus( SpoolerStatusHandle, &SpoolerStatus )) {
|
|||
|
|
|||
|
status = GetLastError();
|
|||
|
|
|||
|
if (status != NERR_Success) {
|
|||
|
DBGMSG(DBG_ERROR,
|
|||
|
("SpoolerStatusUpdate, SetServiceStatus Failed %d\n",status));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = SpoolerState;
|
|||
|
LeaveCriticalSection(&ThreadCriticalSection);
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
DWORD
|
|||
|
GetSpoolerState (
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Obtains the state of the Spooler 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 Spooler State is returned as the return value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
|
|||
|
EnterCriticalSection(&ThreadCriticalSection);
|
|||
|
status = SpoolerState;
|
|||
|
LeaveCriticalSection(&ThreadCriticalSection);
|
|||
|
|
|||
|
return(status);
|
|||
|
}
|