windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/msgsvc/server/msgmain.c
2020-09-26 16:20:57 +08:00

1024 lines
28 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
msgmain.c
Abstract:
This is the main routine for the NT OS/2 LAN Manager Messenger Service.
Functions in the file include:
SvcEntry_Messenger
ParseArgs
Author:
Dan Lafferty (danl) 20-Mar-1991
Environment:
User Mode - Win32
Revision History:
27-Feb-1999 jschwart
Remove polling loop to detect lana changes -- use PnP events instead
15-Dec-1998 jschwart
Convert messenger to use NT thread pool APIs instead of Service
Controller thread pool
19-Aug-1997 wlees
Add polling loop to detect lana changes.
Provide synchronization between rpc routines and Pnp reconfiguration
14-Jun-1994 danl
Fixed problem where messenger put up an empty message as if it
received a mailslot message during init. The problem was the
order of the following events: CreateMailslot -> wait on handle ->
submit an async _read with that handle.
The new order was changed to: CreateMailslot -> submit async _read ->
wait on handle.
This causes the handle to not get signaled right away.
20-Mar-1991 danl
created
--*/
//
// INCLUDES
//
#include "msrv.h" // AdapterThread prototype,SESSION_STATUS
#include <winuser.h> // RegisterDeviceNotification
#include <dbt.h> // DEV_BROADCAST_DEVICEINTERFACE
#include <tstring.h> // Unicode string macros
#include <winsock2.h> // Windows sockets
#include <netlib.h> // UNUSED macro
#include "msgdbg.h" // MSG_LOG & STATIC definitions
#include "msgdata.h" // msrv_status
#include "msgsvc.h" // Messenger RPC interface
#include "msgsvcsend.h" // Broadcast message send interface
//
// GLOBALS
//
//
// Handles for messenger work items. These are necessary since
// the Rtl thread pool work items aren't automatically deleted
// when the callback is called.
//
HANDLE g_hGrpEvent;
HANDLE g_hNetEvent;
HANDLE g_hNetTimeoutEvent;
//
// PNP device notification handle
//
HANDLE g_hPnPNotify;
//
// Warning: this definitions of GUID_NDIS_XXX is in ndisguid.h
// but dragging that file in drags in a whole bunch of guids that
// won't get thrown out by the linker.
//
static const GUID GUID_NDIS_LAN_CLASS =
{0xad498944,0x762f,0x11d0,{0x8d,0xcb,0x00,0xc0,0x4f,0xc3,0x35,0x8c}};
//
// Global buffer pointers used to hold Alerter print text
//
LPSTR g_lpAlertSuccessMessage;
DWORD g_dwAlertSuccessLen;
LPSTR g_lpAlertFailureMessage;
DWORD g_dwAlertFailureLen;
//
// Local Function Prototypes
//
STATIC VOID
Msgdummy_complete(
short c,
int a,
char b
);
VOID
MsgGrpEventCompletion(
PVOID pvContext, // This passed in as context.
BOOLEAN fWaitStatus
);
VOID
MsgrShutdownInternal(
void
);
VOID
SvchostPushServiceGlobals(
PSVCHOST_GLOBAL_DATA pGlobals
)
{
MsgsvcGlobalData = pGlobals;
}
VOID
ServiceMain(
IN DWORD argc,
IN LPTSTR argv[]
)
/*++
Routine Description:
This is the main routine for the Messenger Service
Arguments:
Return Value:
None.
Note:
--*/
{
DWORD msgrState;
NTSTATUS ntStatus;
BOOL fGrpThreadCreated = FALSE;
BOOL fNetThreadCreated = FALSE;
DEV_BROADCAST_DEVICEINTERFACE dbdPnpFilter;
//
// Make sure svchost.exe gave us the global data
//
ASSERT(MsgsvcGlobalData != NULL);
MsgCreateWakeupEvent(); // do this once
msgrState = MsgInitializeMsgr(argc, argv);
if (msgrState != RUNNING)
{
MSG_LOG(ERROR,"[MSG],Shutdown during initialization\n",0);
MsgsvcGlobalData->NetBiosClose();
//
// To get here, the msgrState must either be STOPPING or STOPPED.
// Shutdown the Messenger Service
//
if (msgrState == STOPPING) {
MsgrShutdown();
MsgStatusUpdate(STOPPED);
}
MSG_LOG(TRACE,"MESSENGER_main: Messenger main thread is returning\n\n",0);
return;
}
else
{
//
// Read the Group Mailslot
//
MSG_LOG0(GROUP,"MESSENGER_main: Submit the Group Mailslot ReadFile\n");
MsgReadGroupMailslot();
//
// Submit the work item that will wait on the mailslot handle.
// When the handle becomes signaled, the MsgGrpEventCompletion
// function will be called.
//
MSG_LOG1(GROUP,"MESSENGER_main: Mailslot handle to wait on "
" = 0x%lx\n",GrpMailslotHandle);
ntStatus = RtlRegisterWait(&g_hGrpEvent, // Work item handle
GrpMailslotHandle, // Waitable handle
MsgGrpEventCompletion, // Callback
NULL, // pContext
INFINITE, // Timeout
WT_EXECUTEONLYONCE); // One-shot
if (!NT_SUCCESS(ntStatus)) {
//
// We want to exit in this case
//
MSG_LOG1(ERROR,"MESSENGER_main: RtlRegisterWait failed %#x\n",
ntStatus);
goto ErrorExit;
}
fGrpThreadCreated = TRUE;
ntStatus = RtlRegisterWait(&g_hNetEvent, // Work item handle
wakeupEvent, // Waitable handle
MsgNetEventCompletion, // Callback
NULL, // pContext
INFINITE, // Timeout
WT_EXECUTEONLYONCE | // One-shot and potentially lengthy
WT_EXECUTELONGFUNCTION);
if (!NT_SUCCESS(ntStatus)) {
//
// We want to exit in this case
//
MSG_LOG1(ERROR,"MsgNetEventCompletion: RtlRegisterWait failed %#x\n",
ntStatus);
goto ErrorExit;
}
fNetThreadCreated = TRUE;
//
// Register for device notifications. Specifically, we're interested
// in network adapters coming and going. If this fails, we exit.
//
MSG_LOG1(TRACE, "SvcEntry_Messenger: Calling RegisterDeviceNotification...\n", 0);
ZeroMemory (&dbdPnpFilter, sizeof(dbdPnpFilter));
dbdPnpFilter.dbcc_size = sizeof(dbdPnpFilter);
dbdPnpFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
dbdPnpFilter.dbcc_classguid = GUID_NDIS_LAN_CLASS;
g_hPnPNotify = RegisterDeviceNotification (
MsgrStatusHandle,
&dbdPnpFilter,
DEVICE_NOTIFY_SERVICE_HANDLE);
if (!g_hPnPNotify)
{
//
// We want to exit in this case
//
MSG_LOG1(ERROR, "SvcEntry_Messenger: RegisterDeviceNotificationFailed %d!\n",
GetLastError());
goto ErrorExit;
}
MSG_LOG(TRACE,"MESSENGER_main: Messenger main thread is returning\n\n",0);
return;
}
ErrorExit:
//
// We want to stop the messenger in this case.
//
MsgBeginForcedShutdown(PENDING, GetLastError());
//
// In Hydra case, the display thread never goes asleep.
//
if (!g_IsTerminalServer)
{
MsgDisplayThreadWakeup();
}
//
// MsgNetEventCompletion will shut down the group thread, call
// MsgrShutdown, and update the status to SERVICE_STOPPED
//
if (fNetThreadCreated)
{
SetEvent(wakeupEvent);
}
else
{
if (fGrpThreadCreated)
{
MsgGrpThreadShutdown();
}
MsgrShutdown();
MsgStatusUpdate(STOPPED);
}
return;
}
STATIC VOID
MsgrShutdown(
)
/*++
Routine Description:
Tidies up the network card prior to exiting. All message server async
NCBs are cancelled, and message names are deleted.
When this routine is entered, it is expected that all the worker
threads have been notified of the impending shutdown. This routine
starts out by waiting for all of them to terminate. Then it continues
with cleaning up the NCB's and deleting names.
Arguments:
none
Return Value:
none
--*/
{
NET_API_STATUS status;
DWORD neti; // Network index
DWORD ncb_i,i; // ncb array index
NCB l_ncb; // local ncb
UCHAR ncbStatus;
int nbStatus;
DWORD index;
PNCB_DATA pNcbData;
PNCB pNcb;
RPC_BINDING_VECTOR *bindingVector = NULL;
MSG_LOG(TRACE," in MsgrShutdown\n",0);
// *** SHUTDOWN HINT ***
MsgStatusUpdate (STOPPING);
// Shutdown winsock
WSACleanup();
// *** SHUTDOWN HINT ***
MsgStatusUpdate (STOPPING);
//
// Shut down the RPC interfaces
//
status = RpcServerInqBindings(&bindingVector);
if (status != ERROR_SUCCESS) {
MSG_LOG(ERROR, "RpcServerInqBindings failed with %d\n", status);
}
if (bindingVector != NULL) {
status = RpcEpUnregister(msgsvcsend_ServerIfHandle, bindingVector, NULL);
if (status != ERROR_SUCCESS && status != EPT_S_NOT_REGISTERED) {
MSG_LOG(ERROR, "RpcEpUnregister failed with %d\n", status);
}
status = RpcBindingVectorFree(&bindingVector);
}
status = RpcServerUnregisterIf(msgsvcsend_ServerIfHandle, NULL, FALSE);
if (status != ERROR_SUCCESS) {
MSG_LOG(ERROR,
"MsgrShutdown: Failed to unregister msgsend rpc interface %d\n",
status);
}
MSG_LOG(TRACE,"MsgrShutdown: Shut down RPC server\n",0);
MsgsvcGlobalData->StopRpcServer( msgsvc_ServerIfHandle );
// *** SHUTDOWN HINT ***
MsgStatusUpdate (STOPPING);
// Release lana state
if (g_hPnPNotify != NULL)
{
if (!UnregisterDeviceNotification(g_hPnPNotify))
{
//
// Note that if this call fails, PnP will get an error back from the
// SCM the next time it tries to send us a PnP message (since the
// service will no longer be running) -- it shouldn't crash things
//
MSG_LOG(ERROR, "MsgrShutdown: UnregisterDeviceNotification failed %d!\n",
GetLastError());
}
}
MsgrShutdownInternal();
// *** SHUTDOWN HINT ***
MsgStatusUpdate (STOPPING);
//
// Stop the display thread
// Note: here the RPC server is stopped so we can stop the display thread
// (NB: a RPC API call may call MsgDisplayThreadWakeup)
//
MsgDisplayEnd();
//
// All cleaning up done. Now free up all resources. The list of
// possible resources is as follows:
//
// memory to free: Handles to Close:
// --------------- -----------------
// ncbArray wakeupSems
// mpncbistate threadHandles
// net_lana_num
// MessageFileName
// dataPtr
//
MSG_LOG(TRACE,"MsgrShutdown: Free up Messenger Resources\n",0);
// Group mailslot for domain messaging
MsgGrpThreadShutdown();
CLOSE_HANDLE(GrpMailslotHandle, INVALID_HANDLE_VALUE);
MsgCloseWakeupEvent(); // do this once
MsgThreadCloseAll(); // Thread Handles
LocalFree(MessageFileName);
MessageFileName = NULL;
LocalFree(GlobalAllocatedMsgTitle);
GlobalAllocatedMsgTitle = NULL;
LocalFree(g_lpAlertSuccessMessage);
g_lpAlertSuccessMessage = NULL;
g_dwAlertSuccessLen = 0;
LocalFree(g_lpAlertFailureMessage);
g_lpAlertFailureMessage = NULL;
g_dwAlertFailureLen = 0;
MSG_LOG(TRACE,"MsgrShutdown: Done with shutdown\n",0);
return;
}
void
MsgrShutdownInternal(
void
)
/*++
Routine Description:
Release all state related to the lana's known to the system.
Arguments:
None
Return Value:
None
--*/
{
NET_API_STATUS status;
DWORD neti; // Network index
DWORD ncb_i,i; // ncb array index
NCB l_ncb; // local ncb
PMSG_SESSION_STATUS psess_stat;
UCHAR ncbStatus;
int nbStatus;
DWORD index;
PNCB_DATA pNcbData;
PNCB pNcb;
MSG_LOG(TRACE," in MsgrShutdownInternal\n",0);
if (GlobalData.NetData != NULL)
{
psess_stat = LocalAlloc(LMEM_FIXED, sizeof(MSG_SESSION_STATUS));
if (psess_stat == NULL)
{
//
// Not much else we can do here...
//
MSG_LOG(ERROR, "MsgrShutdownInternal: LocalAlloc FAILED!\n",0);
return;
}
//
// Now clean up the NCB's
//
MSG_LOG(TRACE,"MsgrShutdown: Clean up NCBs\n",0);
for ( neti = 0; neti < SD_NUMNETS(); neti++ ) // For all nets
{
clearncb(&l_ncb);
//
// First check for any incomplete Async NCBs and cancel them.
// As a precaution set the function handler for all the
// async NCBs to point to a dummy function which will not reissue
// the NCBs when the complete with cancelled status.
//
l_ncb.ncb_lana_num = GETNETLANANUM(neti); // Use the LANMAN adapter
l_ncb.ncb_command = NCBCANCEL; // Cancel (wait)
for(ncb_i = 0; ncb_i < NCBMAX(neti); ++ncb_i)
{
pNcbData = GETNCBDATA(neti,ncb_i);
pNcb = &pNcbData->Ncb;
pNcbData->IFunc = (LPNCBIFCN)Msgdummy_complete;// Set function pointer
if((pNcb->ncb_cmd_cplt == (UCHAR) 0xff) &&
(pNcb->ncb_retcode == (UCHAR) 0xff)) {
//
// If pending NCB found
//
l_ncb.ncb_buffer = (PCHAR) pNcb;
//
// There will always be an NCB reserved for cancels in the rdr
// but it may be in use so loop if the cancel status
// is NRC_NORES.
//
while( (ncbStatus = Msgsendncb(&l_ncb, neti)) == NRC_NORES) {
//
// Wait for half a sec
//
Sleep(500L);
}
MSG_LOG(TRACE,"Shutdown:Net #%d\n",neti);
MSG_LOG(TRACE,"Shutdown:Attempt to cancel rc = 0x%x\n",
ncbStatus);
//
// Now loop waiting for the cancelled ncb to complete.
// Any ncbs types which are not valid to cancel (eg Delete
// name) must complete so a wait loop here is safe.
//
// NT Change - This will only loop for 30 seconds before
// leaving - whether or not the CANCEL is complete.
//
status = NERR_InternalError;
for (i=0; i<60; i++) {
if (pNcb->ncb_cmd_cplt != (UCHAR) 0xff) {
status = NERR_Success;
break;
}
//
// Wait for half a sec
//
Sleep(500L);
}
if (status != NERR_Success) {
MSG_LOG(ERROR,
"MsgrShutdown: NCBCANCEL did not complete\n",0);
}
}
}
//
// All asyncronous ncbs cancelled completed. Now delete any
// messaging names active on the network card.
//
MSG_LOG(TRACE,"MsgrShutdown: All Async NCBs are cancelled\n",0);
MSG_LOG(TRACE,"MsgrShutdown: Delete messaging names\n",0);
for(i = 0; i < NCBMAX(neti); ++i) // Loop to find active names slot
{
//
// If any of the NFDEL or NFDEL_PENDING flags are set for
// this name slot then there is no name on the card associated
// with it.
//
clearncb(&l_ncb);
if(!(SD_NAMEFLAGS(neti, i) &
(NFDEL | NFDEL_PENDING)))
{
//
// If there is a session active on this name, hang it up
// now or the delete name will fail
//
l_ncb.ncb_command = NCBSSTAT; // session status (wait)
memcpy(l_ncb.ncb_name, (SD_NAMES(neti, i)), NCBNAMSZ);
l_ncb.ncb_buffer = (char far *)psess_stat;
l_ncb.ncb_length = sizeof(MSG_SESSION_STATUS);
l_ncb.ncb_lana_num = GETNETLANANUM(neti);
nbStatus = Msgsendncb(&l_ncb, neti);
if(nbStatus == NRC_GOODRET) // If success
{
for (index=0; index < psess_stat->SessHead.num_sess ;index++) {
l_ncb.ncb_command = NCBHANGUP; // Hangup (wait)
l_ncb.ncb_lsn = psess_stat->SessBuffer[index].lsn;
l_ncb.ncb_lana_num = GETNETLANANUM(neti);
nbStatus = Msgsendncb(&l_ncb, neti);
MSG_LOG3(TRACE,"HANGUP NetBios for Net #%d Session #%d "
"status = 0x%x\n",
neti,
psess_stat->SessBuffer[index].lsn,
nbStatus);
}
}
else {
MSG_LOG2(TRACE,"SessionSTAT NetBios Net #%d failed = 0x%x\n",
neti, nbStatus);
}
//
// With the current design of the message server there can
// be only one session per name so the name should now be
// clear of sessions and the delete name should work.
//
l_ncb.ncb_command = NCBDELNAME; // Del name (wait)
l_ncb.ncb_lana_num = GETNETLANANUM(neti);
//
// Name is still in l_ncb.ncb_name from previous SESSTAT
//
nbStatus = Msgsendncb(&l_ncb, neti);
MSG_LOG2(TRACE,"DELNAME NetBios Net #%d status = 0x%x\n",
neti, nbStatus);
}
}
} // End for all nets loop
LocalFree(psess_stat);
}
MsgsvcGlobalData->NetBiosClose();
MsgCloseWakeupSems(); // wakeupSems
MsgFreeSharedData();
if (wakeupSem != NULL) {
MsgFreeSupportSeg(); // wakeupSem
}
}
VOID
Msgdummy_complete(
short c,
int a,
char b
)
{
// just to shut up compiler
MSG_LOG(TRACE,"In dummy_complete module\n",0);
UNUSED (a);
UNUSED (b);
UNUSED (c);
}
VOID
MsgNetEventCompletion(
PVOID pvContext, // This passed in as context.
BOOLEAN fWaitStatus
)
/*++
Routine Description:
This function is called when the event handle for one of the
nets becomes signaled.
Arguments:
pvContext - This should always be zero.
fWaitStatus - TRUE if we're being called because of a timeout.
FALSE if we're being called because the waitable
event was signalled
Return Value:
None
--*/
{
DWORD neti, numNets;
DWORD msgrState;
BOOL ncbComplete = FALSE;
NET_API_STATUS success;
NTSTATUS ntStatus;
if (fWaitStatus)
{
//
// We timed out (i.e., this came from the control handler)
//
DEREGISTER_WORK_ITEM(g_hNetTimeoutEvent);
}
else
{
//
// We were signalled
//
DEREGISTER_WORK_ITEM(g_hNetEvent);
}
//
// Sychronize this routine in the following manner:
//
// 1. Protection against two threads executing simultaneously while
// the service is marked RUNNING is done by exclusively acquiring
// the MsgConfigurationLock below.
//
// 2. Protection against one thread executing below while another
// thread stops and cleans up the Messenger (and frees/NULLs out
// the data touched in the routines called below) is done by
// the MsgrBlockStateChange call below -- it blocks threads
// executing MsgrShutdown since the first thing that routine does
// is to call MsgStatusUpdate, which requires the exclusive
// resource that MsgrBlockStateChange acquires shared. This also
// blocks threads here until MsgrShutdown is done changing the
// state to STOPPING, which will prevent the same race condition.
//
MsgrBlockStateChange();
msgrState = GetMsgrState();
if (msgrState == STOPPED)
{
MsgrUnblockStateChange();
return;
}
if (msgrState == STOPPING)
{
//
// Net 0 is considered the main Net, and this thread will
// stay around until all the other messenger threads are
// done shutting down.
// Threads for all the other nets simply return.
//
MsgrUnblockStateChange();
MsgrShutdown();
MsgStatusUpdate(STOPPED);
MSG_LOG(TRACE,"MsgNetEventCompletion: Messenger main thread is returning\n\n",0);
return;
}
//
// Lock out other activity during reconfiguration
//
MsgConfigurationLock(MSG_GET_EXCLUSIVE, "MsgNetEventCompletion" );
//
// Look through the NCB's for all nets and service all
// NCB's that are complete. Continue looping until one pass
// is made through the loop without any complete NCB's being found.
//
do
{
ncbComplete = FALSE;
MSG_LOG0(TRACE,"MsgNetEventCompletion: Loop through all nets to look "
"for any complete NCBs\n");
for ( neti = 0; neti < SD_NUMNETS(); neti++ )
{
//
// For all nets
//
ncbComplete |= MsgServeNCBs((DWORD) neti);
MsgServeNameReqs((DWORD) neti);
}
}
while (ncbComplete);
numNets = MsgGetNumNets();
//
// Only rescan if the number of LANAs has changed, as this callback can be invoked
// multiple times in the course of one PnP event and when a message is received
//
if (numNets != SD_NUMNETS())
{
MSG_LOG2(ERROR,"MsgNetEventCompletion: number of lanas changed from %d to %d\n",
SD_NUMNETS(), numNets );
//
// The number of LAN Adapters has changed -- reinitialize data structures
//
// Note that by doing so, we lose all aliases other than usernames and machinename
//
MsgrShutdownInternal();
success = MsgrInitializeMsgrInternal1();
if (success == NERR_Success)
{
success = MsgrInitializeMsgrInternal2();
}
if (success != NERR_Success)
{
MSG_LOG1(ERROR,
"MsgNetEventCompletion: reinit of LANAs failed %d - shutdown\n",
success);
MsgConfigurationLock(MSG_RELEASE, "MsgNetEventCompletion" );
MsgrUnblockStateChange();
MsgBeginForcedShutdown(PENDING, success);
return;
}
//
// Loop again to see if any NCBs completed while we were reinitializing
//
do
{
ncbComplete = FALSE;
MSG_LOG0(TRACE,"MsgNetEventCompletion: Loop through all nets to look "
"for any complete NCBs\n");
for ( neti = 0; neti < SD_NUMNETS(); neti++ ) { // For all nets
ncbComplete |= MsgServeNCBs((DWORD)neti);
MsgServeNameReqs((DWORD)neti);
}
}
while (ncbComplete);
}
MsgConfigurationLock(MSG_RELEASE, "MsgNetEventCompletion" );
if (!fWaitStatus)
{
//
// Setup for the next request if we were signalled
// (submit another WorkItem to the Rtl thread pool)
//
MSG_LOG0(TRACE,"MsgNetEventCompletion: Setup for next Net Event\n");
ntStatus = RtlRegisterWait(&g_hNetEvent, // Work item handle
wakeupEvent, // Waitable handle
MsgNetEventCompletion, // Callback
NULL, // pContext
INFINITE, // Timeout
WT_EXECUTEONLYONCE | // One-shot and potentially lengthy
WT_EXECUTELONGFUNCTION);
if (!NT_SUCCESS(ntStatus))
{
//
// If we can't add the work item, then we won't ever listen
// for these kind of messages again.
//
MSG_LOG1(ERROR,"MsgNetEventCompletion: RtlRegisterWait failed %#x\n",
ntStatus);
}
}
MsgrUnblockStateChange();
//
// This thread has done all that it can do. So we can return it
// to the thread pool.
//
return;
}
VOID
MsgGrpEventCompletion(
PVOID pvContext, // This passed in as context.
BOOLEAN fWaitStatus
)
/*++
Routine Description:
This function is called when the mailslot handle for group
(domain-wide) messages becomes signalled.
Arguments:
pvContext - not used
fWaitStatus - TRUE if we're being called because of a timeout.
FALSE if we're being called because the waitable
event was signalled
Return Value:
None
--*/
{
DWORD msgrState;
NTSTATUS ntStatus;
MSG_LOG0(GROUP,"MsgGroupEventCompletion: entry point\n",);
//
// We registered an infinite wait, so we can't have timed
// out (TRUE indicates a timeout)
//
ASSERT(fWaitStatus == FALSE);
DEREGISTER_WORK_ITEM(g_hGrpEvent);
msgrState = MsgServeGroupMailslot();
if (msgrState == STOPPING || msgrState == STOPPED)
{
//
// Close the Mailslot Handle
//
CLOSE_HANDLE(GrpMailslotHandle, INVALID_HANDLE_VALUE);
MSG_LOG0(TRACE,"MsgGroupEventCompletion: No longer listening for "
"group messages\n");
}
else {
//
// Read the Group Mailslot
//
MsgReadGroupMailslot();
//
// Setup for the next request.
// (submit another WorkItem to the Rtl thread pool.)
//
MSG_LOG0(TRACE,"MsgGroupEventCompletion: Setup for next Group Event\n");
MSG_LOG1(GROUP,"MsgGroupEventCompletion: Mailslot handle to wait on "
" = 0x%lx\n",GrpMailslotHandle);
ntStatus = RtlRegisterWait(&g_hGrpEvent, // Work item handle
GrpMailslotHandle, // Waitable handle
MsgGrpEventCompletion, // Callback
NULL, // pContext
INFINITE, // Timeout
WT_EXECUTEONLYONCE); // One-shot
if (!NT_SUCCESS(ntStatus)) {
//
// If we can't add the work item, then we won't ever listen
// for these kind of messages again.
//
MSG_LOG1(ERROR,"MsgGrpEventCompletion: RtlRegisterWait failed %#x\n",
ntStatus);
}
}
//
// This thread has done all that it can do. So we can return it
// to the thread pool.
//
return;
}