551 lines
13 KiB
C
551 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
threads.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This file contains routines that manage access to a database of
|
|||
|
worker thread handles and a database containing the current messenger
|
|||
|
status (used to report status to the Service Controller). Access to
|
|||
|
these two databases is controled via a Critical Section.
|
|||
|
|
|||
|
Functions for managing worker threads:
|
|||
|
|
|||
|
MsgThreadManagerInit
|
|||
|
MsgThreadCloseAll
|
|||
|
|
|||
|
Routines for managing _access to the status information and reporting:
|
|||
|
|
|||
|
MsgStatusInit
|
|||
|
MsgBeginForcedShutdown
|
|||
|
MsgStatusUpdate
|
|||
|
GetMsgrState
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Dan Lafferty (danl) 17-Jul-1991
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
User Mode -Win32
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
These functions must be used carefully in order to be effective in
|
|||
|
shutting the messenger threads down nicely if the shutdown happens
|
|||
|
to occur during Messenger Initialization. This note explains when
|
|||
|
each function is to be called.
|
|||
|
|
|||
|
MsgThreadManagerInit
|
|||
|
This function must be called early on in the initialization process.
|
|||
|
It should be called before NetRegisterCtrlDispatcher. This way,
|
|||
|
it is impossible for an UNINSTALL request to be received prior to
|
|||
|
initializing the Critical Section and the Messenger State.
|
|||
|
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
15-Dec-1998 jschwart
|
|||
|
Eliminated MsgThreadManagerEnd. The DLL is no longer unloaded by
|
|||
|
services.exe, so deleting the critical section can create a race
|
|||
|
condition (stop service, new service starts and calls init, first
|
|||
|
thread deletes critsec, first thread tries to enter critsec and AVs)
|
|||
|
|
|||
|
03-Nov-1992 Danl
|
|||
|
Changed status reporting so that we only accept STOP controls if
|
|||
|
the service is in the RUNNING state.
|
|||
|
|
|||
|
18-Feb-1992 RitaW
|
|||
|
Convert to Win32 service control APIs.
|
|||
|
|
|||
|
02-Oct-1991 JohnRo
|
|||
|
Work toward UNICODE.
|
|||
|
|
|||
|
17-Jul-1991 danl
|
|||
|
created
|
|||
|
|
|||
|
--*/
|
|||
|
//
|
|||
|
// Includes
|
|||
|
//
|
|||
|
#include "msrv.h"
|
|||
|
|
|||
|
#include <string.h> // strlen
|
|||
|
|
|||
|
#include <winsvc.h> // SERVICE_STATUS
|
|||
|
#include <netlib.h> // UNUSED Macro
|
|||
|
#include "msgdbg.h" // MSG_LOG
|
|||
|
#include "msgdata.h"
|
|||
|
|
|||
|
//
|
|||
|
// Global Data
|
|||
|
//
|
|||
|
|
|||
|
RTL_RESOURCE g_StateResource;
|
|||
|
SERVICE_STATUS MsgrStatus;
|
|||
|
DWORD HintCount;
|
|||
|
DWORD MsgrUninstallCode; // reason for uninstalling
|
|||
|
BOOL g_fResourceCreated;
|
|||
|
DWORD MsgrState;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
MsgThreadManagerInit(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initializes the critical section that is used to guard access to the
|
|||
|
thread and status database. Note that this critsec is created and
|
|||
|
never deleted (OK since the DLL is never unloaded by services.exe) to
|
|||
|
fix synchronization problems with stopping/restarting the service.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NO_ERROR on success, ERROR_NOT_ENOUGH_MEMORY if the init fails
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD dwError = NO_ERROR;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
if (!g_fResourceCreated)
|
|||
|
{
|
|||
|
status = MsgInitResource(&g_StateResource);
|
|||
|
|
|||
|
if (NT_SUCCESS(status))
|
|||
|
{
|
|||
|
g_fResourceCreated = TRUE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
MSG_LOG1(ERROR,
|
|||
|
"MsgThreadManagerInit: MsgInitResource failed %#x\n",
|
|||
|
status);
|
|||
|
|
|||
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return dwError;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
MsgThreadCloseAll(
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Closes all handles stored in the table of worker thread handles.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
RtlAcquireResourceExclusive(&g_StateResource, TRUE);
|
|||
|
MsgrState = STOPPING;
|
|||
|
RtlReleaseResource(&g_StateResource);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
MsgStatusInit(VOID)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initializes the status database.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
Note:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
RtlAcquireResourceExclusive(&g_StateResource, TRUE);
|
|||
|
|
|||
|
MsgrState = STARTING;
|
|||
|
|
|||
|
HintCount = 1;
|
|||
|
MsgrUninstallCode = 0;
|
|||
|
|
|||
|
MsgrStatus.dwServiceType = SERVICE_WIN32;
|
|||
|
MsgrStatus.dwCurrentState = SERVICE_START_PENDING;
|
|||
|
MsgrStatus.dwControlsAccepted = 0;
|
|||
|
MsgrStatus.dwCheckPoint = HintCount;
|
|||
|
MsgrStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
|
|||
|
SET_SERVICE_EXITCODE(
|
|||
|
NO_ERROR,
|
|||
|
MsgrStatus.dwWin32ExitCode,
|
|||
|
MsgrStatus.dwServiceSpecificExitCode
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
RtlReleaseResource(&g_StateResource);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
MsgBeginForcedShutdown(
|
|||
|
IN BOOL PendingCode,
|
|||
|
IN DWORD ExitCode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called to set the appropriate status when a shutdown
|
|||
|
is to occur due to an error in the Messenger. NOTE: if a shutdown is
|
|||
|
based on a request from the Service Controller, MsgStatusUpdate is
|
|||
|
called instead.
|
|||
|
|
|||
|
On a PENDING call, this routine will also wake up all messenger
|
|||
|
threads so that they will also shut down.
|
|||
|
|
|||
|
|
|||
|
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 messenger 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:
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NET_API_STATUS status;
|
|||
|
|
|||
|
RtlAcquireResourceExclusive(&g_StateResource, TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// See if the messenger is already stopping for some reason.
|
|||
|
// It could be that the ControlHandler thread received a control to
|
|||
|
// stop the messenger just as we decided to stop ourselves.
|
|||
|
//
|
|||
|
if ((MsgrState != STOPPING) && (MsgrState != STOPPED)) {
|
|||
|
if (PendingCode == PENDING) {
|
|||
|
MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|||
|
MsgrState = STOPPING;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// The shutdown is to take immediate effect.
|
|||
|
//
|
|||
|
MsgrStatus.dwCurrentState = SERVICE_STOPPED;
|
|||
|
MsgrStatus.dwControlsAccepted = 0;
|
|||
|
MsgrStatus.dwCheckPoint = 0;
|
|||
|
MsgrStatus.dwWaitHint = 0;
|
|||
|
MsgrState = STOPPED;
|
|||
|
}
|
|||
|
|
|||
|
MsgrUninstallCode = ExitCode;
|
|||
|
|
|||
|
SET_SERVICE_EXITCODE(
|
|||
|
ExitCode,
|
|||
|
MsgrStatus.dwWin32ExitCode,
|
|||
|
MsgrStatus.dwServiceSpecificExitCode
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Send the new status to the service controller.
|
|||
|
//
|
|||
|
if (MsgrStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
|
|||
|
MSG_LOG(ERROR,
|
|||
|
"MsgBeginForcedShutdown, no handle to call SetServiceStatus\n", 0);
|
|||
|
|
|||
|
}
|
|||
|
else if (! SetServiceStatus( MsgrStatusHandle, &MsgrStatus )) {
|
|||
|
|
|||
|
status = GetLastError();
|
|||
|
|
|||
|
if (status != NERR_Success) {
|
|||
|
MSG_LOG(ERROR,
|
|||
|
"MsgBeginForcedShutdown,SetServiceStatus Failed %X\n",
|
|||
|
status);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = MsgrState;
|
|||
|
RtlReleaseResource(&g_StateResource);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
MsgStatusUpdate(
|
|||
|
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 Messenger 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 Messenger is in the process of initializing
|
|||
|
RUNNING - The Messenger has finished with initialization
|
|||
|
STOPPING - The Messenger is in the process of shutting down
|
|||
|
STOPPED - The Messenger 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.
|
|||
|
|
|||
|
RtlAcquireResourceExclusive(&g_StateResource, TRUE);
|
|||
|
|
|||
|
|
|||
|
if (NewState == STOPPED) {
|
|||
|
if (MsgrState == STOPPED) {
|
|||
|
//
|
|||
|
// It was already stopped, don't send another SetServiceStatus.
|
|||
|
//
|
|||
|
inhibit = TRUE;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// The shut down is complete, indicate that the messenger
|
|||
|
// has stopped.
|
|||
|
//
|
|||
|
MsgrStatus.dwCurrentState = SERVICE_STOPPED;
|
|||
|
MsgrStatus.dwControlsAccepted = 0;
|
|||
|
MsgrStatus.dwCheckPoint = 0;
|
|||
|
MsgrStatus.dwWaitHint = 0;
|
|||
|
|
|||
|
SET_SERVICE_EXITCODE(
|
|||
|
MsgrUninstallCode,
|
|||
|
MsgrStatus.dwWin32ExitCode,
|
|||
|
MsgrStatus.dwServiceSpecificExitCode
|
|||
|
);
|
|||
|
}
|
|||
|
MsgrState = NewState;
|
|||
|
}
|
|||
|
else {
|
|||
|
//
|
|||
|
// We are not being asked to change to the STOPPED state.
|
|||
|
//
|
|||
|
switch(MsgrState) {
|
|||
|
|
|||
|
case STARTING:
|
|||
|
if (NewState == STOPPING) {
|
|||
|
|
|||
|
MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|||
|
MsgrStatus.dwControlsAccepted = 0;
|
|||
|
MsgrStatus.dwCheckPoint = HintCount++;
|
|||
|
MsgrStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
MsgrState = NewState;
|
|||
|
}
|
|||
|
|
|||
|
else if (NewState == RUNNING) {
|
|||
|
|
|||
|
//
|
|||
|
// The Messenger Service has completed installation.
|
|||
|
//
|
|||
|
MsgrStatus.dwCurrentState = SERVICE_RUNNING;
|
|||
|
MsgrStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|||
|
MsgrStatus.dwCheckPoint = 0;
|
|||
|
MsgrStatus.dwWaitHint = 0;
|
|||
|
|
|||
|
MsgrState = NewState;
|
|||
|
}
|
|||
|
|
|||
|
else {
|
|||
|
//
|
|||
|
// The NewState must be STARTING. So update the pending
|
|||
|
// count
|
|||
|
//
|
|||
|
|
|||
|
MsgrStatus.dwCurrentState = SERVICE_START_PENDING;
|
|||
|
MsgrStatus.dwControlsAccepted = 0;
|
|||
|
MsgrStatus.dwCheckPoint = HintCount++;
|
|||
|
MsgrStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case RUNNING:
|
|||
|
if (NewState == STOPPING) {
|
|||
|
|
|||
|
MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|||
|
MsgrStatus.dwControlsAccepted = 0;
|
|||
|
MsgrStatus.dwCheckPoint = HintCount++;
|
|||
|
MsgrStatus.dwWaitHint = 20000; // 20 seconds
|
|||
|
|
|||
|
MsgrState = NewState;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case STOPPING:
|
|||
|
//
|
|||
|
// No matter what else was passed in, force the status to
|
|||
|
// indicate that a shutdown is pending.
|
|||
|
//
|
|||
|
MsgrStatus.dwCurrentState = SERVICE_STOP_PENDING;
|
|||
|
MsgrStatus.dwControlsAccepted = 0;
|
|||
|
MsgrStatus.dwCheckPoint = HintCount++;
|
|||
|
MsgrStatus.dwWaitHint = 20000; // 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 (MsgrStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
|
|||
|
MSG_LOG(ERROR,
|
|||
|
"MsgStatusUpdate, no handle to call SetServiceStatus\n", 0);
|
|||
|
|
|||
|
}
|
|||
|
else if (! SetServiceStatus( MsgrStatusHandle, &MsgrStatus )) {
|
|||
|
|
|||
|
status = GetLastError();
|
|||
|
|
|||
|
if (status != NERR_Success) {
|
|||
|
MSG_LOG(ERROR,
|
|||
|
"MsgStatusUpdate, SetServiceStatus Failed %d\n",
|
|||
|
status);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = MsgrState;
|
|||
|
RtlReleaseResource(&g_StateResource);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
GetMsgrState (
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Obtains the state of the Messenger 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 Messenger State is returned as the return value.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
|
|||
|
RtlAcquireResourceShared(&g_StateResource, TRUE);
|
|||
|
status = MsgrState;
|
|||
|
RtlReleaseResource(&g_StateResource);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
MsgrBlockStateChange(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
RtlAcquireResourceShared(&g_StateResource, TRUE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
MsgrUnblockStateChange(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
RtlReleaseResource(&g_StateResource);
|
|||
|
}
|