WindowsXP-SP1/base/wmi/core/main.c
2020-09-30 16:53:49 +02:00

2476 lines
75 KiB
C

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
main.c
Abstract:
WMI core dll main file
Author:
16-Jan-1997 AlanWar
Revision History:
--*/
#define INITGUID
#include "wmiump.h"
#ifdef MEMPHIS
#include "dbt.h"
#endif
HANDLE WmipKMHandle;
HANDLE WmipTerminationEvent;
ULONG WmipNotificationIODelay = 500;
#ifndef MEMPHIS
HANDLE WmipEventLogHandle;
PVOID WmipProcessHeap;
HANDLE WmipQueueReadNotificationTimer;
HANDLE WmipAsyncRpcApcThread;
HANDLE WmipRestrictedToken;
typedef enum
{
InitializationRunning = 0,
InitialTimerWaiting,
TimerRunning,
IoControlWaiting,
WorkItemWaiting,
ReadCompleteRunning,
SecondaryTimerWaiting,
ServiceShutdownRunning,
BufferAllocTimerWaiting,
GetPersistentThread,
GetPersistentThreadFailed,
RequeueGetPersistentThread,
GetRequeueGetPersistentThreadWait,
RequeueInitialTimerWaiting,
QueueGetPerisitentThread
} THREADPOOLSTATUS;
THREADPOOLSTATUS WmipThreadPoolStatus;
SERVICE_STATUS WmiServiceStatus;
SERVICE_STATUS_HANDLE WmiServiceStatusHandle;
PSVCS_GLOBAL_DATA WmipSvcsGlobalData;
HANDLE WmipGlobalSvcRefHandle;
ULONG WmipQueueReadNotification(
PVOID Context,
BOOLEAN Condition
);
#endif
BOOLEAN WmipServiceShutdown;
ULONG WmipNotificationLoop(
HANDLE WmiKMHandle,
HANDLE TerminationHandle
);
ULONG WmipCoreInitialize(
HANDLE *WmiKMHandle
);
void WmipCoreDeinitialize(
HANDLE WmiKMHandle
);
BOOLEAN
WmiCoreInitialize(
IN PVOID DllBase,
IN ULONG Reason,
IN PCONTEXT Context OPTIONAL
)
/*++
Routine Description:
This function implements Win32 base dll initialization.
It's primary purpose is to provide early initialization which must
be prior to Wx86ProcessInit.
Arguments:
DllHandle -
Reason - attach\detach
Context - Not Used
Return Value:
STATUS_SUCCESS
--*/
{
if (Reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(DllBase);
}
return(TRUE);
}
ULONG WmiInitializeService(
void
)
/*++
Routine Description:
This routine will do the work of initializing the WMI service
Arguments:
Context
Return Value:
Error status value
--*/
{
ULONG Status;
//
// Reinitialize some of the global variables. We do this in case the
// service was stopped and restarted, but the dll was not unloaded and
// reloaded.
DSHeadPtr = &DSHead;
InitializeListHead(DSHeadPtr);
InitializeListHead(&DSChunkInfo.ChunkHead);
GEHeadPtr = &GEHead;
InitializeListHead(GEHeadPtr);
InitializeListHead(&GEChunkInfo.ChunkHead);
DCHeadPtr = &DCHead;
InitializeListHead(DCHeadPtr);
InitializeListHead(&DCChunkInfo.ChunkHead);
NEHeadPtr = &NEHead;
InitializeListHead(NEHeadPtr);
InitializeListHead(&NEChunkInfo.ChunkHead);
MRHeadPtr = &MRHead;
InitializeListHead(MRHeadPtr);
InitializeListHead(&MRChunkInfo.ChunkHead);
InitializeListHead(&ISChunkInfo.ChunkHead);
#ifdef WMI_USER_MODE
MCHeadPtr = &MCHead;
InitializeListHead(MCHeadPtr);
InitializeListHead(&MCChunkInfo.ChunkHead);
#endif
#ifndef MEMPHIS
GMHeadPtr = &GMHead;
InitializeListHead(GMHeadPtr);
Status = RtlInitializeCriticalSection(&SMCritSect);
if (! NT_SUCCESS(Status))
{
return(RtlNtStatusToDosError(Status));
}
#endif
WmipServiceShutdown = FALSE;
WmipTerminationEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WmipTerminationEvent == NULL)
{
return(GetLastError());
}
#ifdef MEMPHIS
SMMutex = CreateMutex(NULL, FALSE, NULL);
if (SMMutex == NULL)
{
CloseHandle(WmipTerminationEvent);
return(GetLastError());
}
#else
WmipEventLogHandle = RegisterEventSource(NULL,
L"WMI");
if (WmipEventLogHandle == NULL)
{
WmipDebugPrint(("WMI: Couldn't register event source %d\n",
GetLastError()));
//
// This is not serious enough for us to quit the service
}
WmipProcessHeap = RtlCreateHeap(HEAP_GROWABLE,
NULL,
DLLRESERVEDHEAPSIZE,
DLLCOMMITHEAPSIZE,
NULL,
NULL);
if (WmipProcessHeap == NULL)
{
WmipDebugPrint(("WMI: Cannot create core WmipProcessHeap\n"));
RtlDeleteCriticalSection(&SMCritSect);
CloseHandle(WmipTerminationEvent);
return(ERROR_NOT_ENOUGH_MEMORY);
}
#endif
//
// Initialize core internal components of the service
Status = WmipCoreInitialize(&WmipKMHandle);
if (Status != ERROR_SUCCESS)
{
CloseHandle(WmipTerminationEvent);
#ifdef MEMPHIS
CloseHandle(SMMutex);
#else
if (WmipEventLogHandle != NULL)
{
DeregisterEventSource(WmipEventLogHandle);
}
RtlDestroyHeap(WmipProcessHeap);
RtlDeleteCriticalSection(&SMCritSect);
#endif
return(Status);
}
#ifdef MEMPHIS
//
// Establish the RPC server
Status = WmipRpcServerInitialize();
if (Status != ERROR_SUCCESS)
{
//
// If we can't establish an RPC server then bail out, no point to run
WmipCoreDeinitialize(WmipKMHandle);
CloseHandle(WmipTerminationEvent);
CloseHandle(SMMutex);
return(Status);
}
#endif
return(Status);
}
void WmiDeinitializeService(
void
)
/*++
Routine Description:
This routine will do the work of deinitializing the WMI service
Arguments:
Context
Return Value:
Error status value
--*/
{
WmipCoreDeinitialize(WmipKMHandle);
#ifdef MEMPHIS
WmipRpcServerDeinitialize();
CloseHandle(SMMutex);
#else
if (WmipEventLogHandle != NULL)
{
DeregisterEventSource(WmipEventLogHandle);
WmipEventLogHandle = NULL;
}
if (WmipProcessHeap != NULL)
{
RtlDestroyHeap(WmipProcessHeap);
WmipProcessHeap = NULL;
}
RtlDeleteCriticalSection(&SMCritSect);
#endif
CloseHandle(WmipTerminationEvent);
}
#ifdef MEMPHIS
void WmiTerminateService(
void
)
{
SetEvent(WmipTerminationEvent);
}
ULONG WmiRunService(
ULONG Context,
HINSTANCE InstanceHandle
)
/*++
Routine Description:
This is the main entrypoint for the WMI service. It will initialize WMI
start up the WMI rpc server and then enter the main loop getting and
dispatching notifications. This is the memphis only version.
Arguments:
Context
Return Value:
Error status value
--*/
{
ULONG Status;
MSG Msg;
if (WmipKMHandle == (PVOID)-1)
{
//
// On memphis we could have the situation where the WMI kernel mode
// device is not created when the WMI service starts up. WMI driver
// is only loaded on memphis when a driver is loaded that needs its
// services. So it is possible that the system will boot with the
// WMI kernel mode code and then a user mode provider will startup
// the service (wmiexe.exe), and the service will not be able to
// open the WMI device. When a subsequent driver that pulls in WMI
// loads we need to get a notification here so that we can open
// the WMI device and proceed normally with WmipNotificationLoop.
// Note that in memphis kernel mode dlls (like wmi.sys) will never
// be unloaded even if all the drivers that need them are.
// Our plan then is to create a hidden window and wait for
// WM_DEVICECHANGE messages with wParam set to DBT_DEVICEARRIVAL.
// When this message arrives it means that a new PnP device has been
// added to the system. Since (on memphis) only PnP devices can use
// WMI we can check again to see if the WMI device can be opened.
// If it can then we fall out of the loop and call
// WmipNotificationLoop.
HWND WindowHandle;
Status = WmipCreateDeviceNotificationWindow(InstanceHandle,
&WindowHandle);
if (Status == ERROR_SUCCESS)
{
while (GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
WmipDestroyDeviceNotificationWindow(InstanceHandle,
WindowHandle);
Status = WmipNotificationLoop(WmipKMHandle, WmipTerminationEvent);
} else {
//
// If we couldn't create the window then forget about KM stuff
WaitForSingleObject(WmipTerminationEvent, INFINITE);
}
} else {
Status = WmipNotificationLoop(WmipKMHandle, WmipTerminationEvent);
}
return(Status);
}
#endif
ULONG WmipCoreInitialize(
HANDLE *WmiKMHandle
)
/*++
Routine Description:
Initialization for WMI core fucntionality
Arguments:
Return Value:
ERROR_SUCCESS or an error code
--*/
{
ULONG Status;
PBGUIDENTRY GuidEntry;
//
// Build a guid entry for registration change notifications
GuidEntry = WmipAllocGuidEntry();
if (GuidEntry == NULL)
{
return(ERROR_NOT_ENOUGH_MEMORY);
}
memcpy(&GuidEntry->Guid, &RegChangeNotificationGuid, sizeof(GUID));
GuidEntry->Flags = GE_FLAG_INTERNAL;
InsertTailList(GEHeadPtr, &GuidEntry->MainGEList);
Status = WmipReadBuiltinMof();
if (Status != ERROR_SUCCESS)
{
WmipDebugPrint(("WMI: Error reading builtin mof %d\n", Status));
}
Status = WmipRegisterInternalDataSource();
if (Status != ERROR_SUCCESS)
{
return(Status);
}
//
// Get registration information for all kernel mode data
// providers.
Status = WmipInitializeKM(WmiKMHandle);
#ifdef MEMPHIS
//
// On memphis it is ok to be running without wmi kernel mode code loaded
if (Status != ERROR_SUCCESS)
{
Status = ERROR_SUCCESS;
}
#endif
return(Status);
}
void WmipCoreDeinitialize(
HANDLE WmiKMHandle
)
/*++
Routine Description:
Deinitialization for WMI core fucntionality
Arguments:
Return Value:
--*/
{
if (WmiKMHandle != (HANDLE)-1)
{
CloseHandle(WmiKMHandle);
}
}
#ifdef MEMPHIS
ULONG WmipNotificationLoop(
HANDLE WmiKMHandle,
HANDLE TerminationEvent
)
/*++
Routine Description:
This is the main wmi service loop that waits for events to occur in
kernel mode and then dispatches them to the appropriate user mode
event sinks.
Arguments:
Return Value:
Never returns or an error code
--*/
{
BYTE *NotificationBuffer;
ULONG NotificationBufferSize = STARTNOTIFICATIONBUFFERSIZE;
BYTE *NewNotificationBuffer;
ULONG NewNotificationBufferSize;
ULONG RetSize;
PWNODE_HEADER Wnode;
BOOL IoctlSuccess;
OVERLAPPED Overlapped;
HANDLE WaitObjects[2];
ULONG Status;
ULONG RetSizeLeft;
ULONG Linkage;
Overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (Overlapped.hEvent == NULL)
{
return(GetLastError());
}
WaitObjects[0] = TerminationEvent;
WaitObjects[1] = Overlapped.hEvent;
NotificationBuffer = WmipAlloc(NotificationBufferSize);
if (NotificationBuffer == NULL)
{
return(ERROR_NO_SYSTEM_RESOURCES);
}
while (1)
{
DeviceIoControl(WmiKMHandle,
IOCTL_WMI_READ_NOTIFICATIONS,
NULL,
0,
NotificationBuffer,
NotificationBufferSize,
&RetSize,
&Overlapped);
IoctlSuccess = GetOverlappedResult(WmiKMHandle,
&Overlapped,
&RetSize,
TRUE);
if (IoctlSuccess)
{
Wnode = (PWNODE_HEADER)NotificationBuffer;
WmipDebugPrint(("WMI: Received Notification %x length 0x%x\n",
NotificationBuffer,
RetSize));
if ((RetSize == sizeof(WNODE_TOO_SMALL)) &&
(Wnode->Flags & WNODE_FLAG_TOO_SMALL))
{
//
// Buffer is not large enough to return an event so
// increase buffer size.
NewNotificationBufferSize = ((PWNODE_TOO_SMALL)Wnode)->SizeNeeded;
NewNotificationBuffer = WmipAlloc(NewNotificationBufferSize);
if (NewNotificationBuffer != NULL)
{
NotificationBufferSize = NewNotificationBufferSize;
WmipFree(NotificationBuffer);
NotificationBuffer = NewNotificationBuffer;
}
continue;
}
//
// We have one or more events to process
RetSizeLeft = RetSize;
do
{
if ((RetSizeLeft >= sizeof(WNODE_HEADER)) &&
(Wnode->BufferSize <= RetSizeLeft))
{
Linkage = Wnode->Linkage;
Wnode->Linkage = 0;
if (Wnode->Flags & WNODE_FLAG_INTERNAL)
{
WmipKMNonEventNotification(WmiKMHandle, Wnode);
} else {
WmipEventNotification(Wnode, TRUE, RetSize);
}
if (Linkage != 0)
{
if (Linkage < RetSizeLeft)
{
Wnode = (PWNODE_HEADER)((PUCHAR)Wnode + Linkage);
RetSizeLeft -= Linkage;
} else {
WmipDebugPrint(("WMI: Invalid Linkage field %x in a WNODE_EVENT_ITEM\n",
Linkage));
Linkage = 0;
}
}
} else {
WmipDebugPrint(("WMI: Received bad notification Wnode %x BufferSize %x RetSize %x RetSizeLeft %x\n",
Wnode,
Wnode->BufferSize, RetSize, RetSizeLeft));
Linkage = 0;
}
} while (Linkage != 0);
} else {
WmipDebugPrint(("WMI: IOCTL_WMI_READ_NOTIFICATIONS failed %x\n",
GetLastError()));
Sleep(60 * 1000);
}
}
}
#else
PBYTE WmipNotificationBuffer;
ULONG WmipNotificationBufferSize = STARTNOTIFICATIONBUFFERSIZE;
OVERLAPPED WmipNotificationOverlapped;
HANDLE WmipNotificationWait;
ULONG WmipReadNotificationComplete(
PVOID Context
)
/*++
Routine Description:
Win32 work item that is executed because the DeviceIoControl in
WmipQueueReadNotification completed. This work item assumes that it has
been queued for execution in a non IO thread.
Arguments:
Context
Condition
Return Value:
--*/
{
ULONG RetSize;
PWNODE_HEADER Wnode;
BOOL IoctlSuccess;
ULONG RetSizeLeft;
ULONG Linkage;
BOOLEAN MultiEvent;
ULONG NotificationIODelay;
WmipThreadPoolStatus = ReadCompleteRunning;
if (WmipServiceShutdown)
{
//
// If service is shutdown then just return
WmipThreadPoolStatus = ServiceShutdownRunning;
return(0);
}
//
// The IOCTL from the WMI device completed so that
// means we have a notification to handle. Get the result
// of the IOCTL and process it
IoctlSuccess = GetOverlappedResult(WmipKMHandle,
&WmipNotificationOverlapped,
&RetSize,
TRUE);
if (IoctlSuccess)
{
NotificationIODelay = WmipNotificationIODelay;
Wnode = (PWNODE_HEADER)WmipNotificationBuffer;
WmipDebugPrint(("WMI: Received Notification %x length 0x%x\n",
WmipNotificationBuffer,
RetSize));
if ((RetSize == sizeof(WNODE_TOO_SMALL)) &&
(Wnode->Flags & WNODE_FLAG_TOO_SMALL))
{
//
// Buffer is not large enough to return an event so
// note the new size and free the old notification buffer.
// The WmipQueueReadNotification work item will reallocate
// the buffer with the new size
WmipNotificationBufferSize = ((PWNODE_TOO_SMALL)Wnode)->SizeNeeded;
WmipFree(WmipNotificationBuffer);
WmipNotificationBuffer = NULL;
NotificationIODelay = 0;
} else {
//
// We have one or more events to process
RetSizeLeft = RetSize;
MultiEvent = FALSE;
do
{
if ((RetSizeLeft >= sizeof(WNODE_HEADER)) &&
(Wnode->BufferSize <= RetSizeLeft))
{
Linkage = Wnode->Linkage;
MultiEvent |= (Linkage != 0);
Wnode->Linkage = 0;
if (Wnode->Flags & WNODE_FLAG_INTERNAL)
{
WmipKMNonEventNotification(WmipKMHandle, Wnode);
} else {
WmipEventNotification(Wnode,
(BOOLEAN)( ! MultiEvent),
RetSize);
}
if (Linkage != 0)
{
if (Linkage < RetSizeLeft)
{
Wnode = (PWNODE_HEADER)((PUCHAR)Wnode + Linkage);
RetSizeLeft -= Linkage;
} else {
WmipDebugPrint(("WMI: Invalid Linkage field %x in a WNODE_EVENT_ITEM\n",
Linkage));
Linkage = 0;
}
}
} else {
WmipDebugPrint(("WMI: Received bad notification Wnode %x BufferSize %x RetSize %x RetSizeLeft %x\n",
Wnode,
Wnode->BufferSize, RetSize, RetSizeLeft));
Linkage = 0;
}
} while (Linkage != 0);
//
// If there are more events waiting then don't delay
if (Wnode->Flags & WNODE_FLAG_INTERNAL2)
{
NotificationIODelay = 0;
WmipDebugPrint(("WMI: Don't delay notficationread\n"));
} else {
WmipDebugPrint(("WMI: delaying notficationread\n"));
}
//
// Now send out any events that were queued up
if (MultiEvent)
{
WmipSendQueuedEvents();
}
}
} else {
//
// If the IOCTL failed there is something really bad happening in
// the WMI device so we don't want to spin just calling and getting
// back an error.
NotificationIODelay = 5000;
WmipDebugPrint(("WMI: IOCTL_WMI_READ_NOTIFICATIONS failed %x\n",
GetLastError()));
}
//
// Requeue the work item that initiates another notification read. We
// add it to the timer queue and not the work queue so that we can
// delay between notification reads. We do this so that in the case of
// a notification storm the notifications can be batched. Doing this will
// delay event delivery by up to WmipNotificationDely, but will decrease
// the cpu utilized in the event of a storm.
WmipThreadPoolStatus = SecondaryTimerWaiting;
while ( ! CreateTimerQueueTimer(&WmipQueueReadNotificationTimer,
NULL,
WmipQueueReadNotification,
WmipKMHandle,
NotificationIODelay,
0,
WT_EXECUTEINIOTHREAD))
{
//
// If this failed then hope it is a resource error and that it
// will go away after waiting a second.
WmipDebugPrint(("WMI: SetTimerQueueTimer requeue failed %d\n",
GetLastError()));
//
// It is a bad idea to sleep in a system pool thread, but there is
// nothing else to do if we can't queue another read
Sleep(1000);
}
return(0);
}
VOID WmipReadNotificationCompleteHack(
PVOID Context,
BOOLEAN Condition
)
{
if (WmipServiceShutdown)
{
//
// If service is shutdown then just return
WmipThreadPoolStatus = ServiceShutdownRunning;
return;
}
WmipThreadPoolStatus = WorkItemWaiting;
while (! QueueUserWorkItem(WmipReadNotificationComplete,
Context,
WT_EXECUTEDEFAULT))
{
Sleep(1000);
}
}
ULONG WmipQueueReadNotification(
PVOID Context,
BOOLEAN Condition
)
/*++
Routine Description:
Win32 work item that queues a read for notifications. This must have
been queued for execution in an IO thread
Arguments:
Context
Return Value:
--*/
{
HANDLE WmiKMHandle = (HANDLE)Context;
BOOL b, IoError;
ULONG Status;
HANDLE TimerQueue;
TimerQueue = WmipQueueReadNotificationTimer;
WmipQueueReadNotificationTimer = NULL;
WmipThreadPoolStatus = TimerRunning;
b = DeleteTimerQueueTimer(NULL,
TimerQueue,
NULL);
if (! b)
{
WmipDebugPrint(("WMI: DeleteTimerQueueTimer Failed %d\n",
GetLastError()));
}
if (WmipServiceShutdown)
{
//
// If service is shutdown then just return
WmipThreadPoolStatus = ServiceShutdownRunning;
return(0);
}
//
// make sure buffer is allocated properly. If the buffer was previously
// too small then the wait callback (ie callback when DeviceIoControl
// completes) will update the size of WmipNotificationBufferSize and
// free WmipNotificationBuffer.
while (WmipNotificationBuffer == NULL)
{
WmipNotificationBuffer = WmipAlloc(WmipNotificationBufferSize);
if (WmipNotificationBuffer == NULL)
{
//
// If we could not allocate a buffer then lets hope that this is
// a temporary resource problem and so wait a second and try again
WmipDebugPrint(("WMI: Could not reallocate buffer for NotificationRead\n"));
WmipThreadPoolStatus = BufferAllocTimerWaiting;
while ( ! CreateTimerQueueTimer(&WmipQueueReadNotificationTimer,
NULL,
WmipQueueReadNotification,
WmipKMHandle,
1000,
0,
WT_EXECUTEINIOTHREAD))
{
//
// If this failed then hope it is a resource error and that it
// will go away after waiting a second.
WmipDebugPrint(("WMI: SetTimerQueueTimer 2 requeue failed %d\n",
GetLastError()));
//
// It is a bad idea to sleep in a system pool thread, but
// there is nothing else to do if we can't queue another read
Sleep(1000);
}
return(0);
}
}
//
// Queue a read for notifications
do
{
WmipThreadPoolStatus = IoControlWaiting;
if (! DeviceIoControl(WmiKMHandle,
IOCTL_WMI_READ_NOTIFICATIONS,
NULL,
0,
WmipNotificationBuffer,
WmipNotificationBufferSize,
NULL,
&WmipNotificationOverlapped))
{
Status = GetLastError();
if ((Status != ERROR_SUCCESS) &&
(Status != ERROR_IO_PENDING))
{
IoError = TRUE;
WmipDebugPrint(("WMI: DeviceIoControl for notification failed %d\n",
Status));
Sleep(1000);
} else {
IoError = FALSE;
}
} else {
Status = ERROR_SUCCESS;
IoError = FALSE;
}
} while (IoError);
return(0);
}
ULONG WmipGetPersistThread(
PVOID Context
)
{
HANDLE Process, Thread;
if (WmipServiceShutdown)
{
//
// If service is shutdown then just return
WmipThreadPoolStatus = ServiceShutdownRunning;
return(0);
}
Process = GetCurrentProcess();
Thread = GetCurrentThread();
//
// We should be running in a persistent thread. Get the thread handle.
WmipThreadPoolStatus = GetPersistentThread;
if (! DuplicateHandle(Process,
Thread,
Process,
&WmipAsyncRpcApcThread,
THREAD_ALL_ACCESS,
FALSE,
0))
{
//
// Duplication of handle failed, Wait and try again
WmipThreadPoolStatus = GetPersistentThreadFailed;
Sleep(1000);
WmipThreadPoolStatus = RequeueGetPersistentThread;
while (! QueueUserWorkItem(WmipGetPersistThread,
NULL,
WT_EXECUTEINPERSISTENTTHREAD))
{
WmipThreadPoolStatus = GetRequeueGetPersistentThreadWait;
Sleep(1000);
}
} else {
WmipThreadPoolStatus = InitialTimerWaiting;
while ( ! CreateTimerQueueTimer(&WmipQueueReadNotificationTimer,
NULL,
WmipQueueReadNotification,
WmipKMHandle,
0,
0,
WT_EXECUTEINIOTHREAD))
{
WmipThreadPoolStatus = RequeueInitialTimerWaiting;
Sleep(1000);
}
}
return(0);
}
#endif
#ifndef MEMPHIS
void WmipNotificationDeliveryApcRoutine
(
IN PRPC_ASYNC_STATE pAsync,
IN void *Context,
IN RPC_ASYNC_EVENT Flags
)
{
ULONG RetVal;
ULONG Status;
PDCENTRY DataConsumer = (PDCENTRY)pAsync->UserInfo;
InterlockedDecrement(&DataConsumer->RpcCallsOutstanding);
WmipDebugPrint(("WMI: WmipNotificationDeliveryApcRoutine %x, 0x%x outstanding\n", pAsync, DataConsumer->RpcCallsOutstanding));
Status = RpcAsyncCompleteCall(pAsync, &RetVal);
#if DBG
if (Status != ERROR_SUCCESS)
{
WmipDebugPrint(("WMI: Server RpcAsyncCompleteCall returned %d\n",
Status));
}
#endif
WmipFree(pAsync);
WmipUnreferenceDC(DataConsumer);
}
#endif
#ifdef MEMPHIS
void WmipDeliverNotification(
PDCENTRY DataConsumer,
PWNODE_HEADER Wnode,
ULONG WnodeSize
)
/*++
Routine Description:
This routine does the actual delivery of notifications via rpc
Arguments:
DataConsumer is the data consumer to whom the event should be delivered
Wnode has the WNODE_HEADER that describes the notification
Return Value:
--*/
{
//
// Don't deliver notification if data consumer has been cleaned up
if (DataConsumer->Flags & DC_FLAG_RUNDOWN)
{
WmipDebugPrint(("WMI: Event not sent DC %x is rundown\n", DataConsumer));
return;
}
try
{
WmipClient_NotificationDelivery(
(handle_t)DataConsumer->RpcBindingHandle,
WnodeSize,
(PBYTE)Wnode,
0,
NOTIFICATION_FLAG_BATCHED);
} except(EXCEPTION_EXECUTE_HANDLER) {
WmipDebugPrint(("WMI: NotificationDeliver threw exception %d\n",
GetExceptionCode()));
}
}
#else
void WmipCalcLostEvents(
PDCENTRY DataConsumer,
PWNODE_HEADER Wnode
)
{
ULONG Linkage = 1;
PNOTIFICATIONENTRY NotificationEntry;
PDCREF DcRef;
ULONG EventsLost;
while (Linkage != 0)
{
if (Wnode->Flags & WNODE_FLAG_INTERNAL)
{
//
// Internal notifications are not counted
//
Linkage = 0;
} else {
if (Wnode->Flags & WNODE_FLAG_TRACED_GUID)
{
//
// External notifications are handled here
NotificationEntry = WmipFindNEByGuid(&Wnode->Guid, FALSE);
if (NotificationEntry != NULL)
{
DcRef = WmipFindDCRefInNE(NotificationEntry,
DataConsumer);
if (DcRef != NULL)
{
WmipEnterSMCritSection();
DcRef->LostEventCount += Wnode->CountLost + 1;
WmipLeaveSMCritSection();
WmipDebugPrint(("WMI: Lost event %d for NE %p, DC %p\n",
DcRef->LostEventCount,
NotificationEntry,
DataConsumer));
}
WmipUnreferenceNE(NotificationEntry);
}
}
Linkage = Wnode->Linkage;
}
Wnode = (PWNODE_HEADER)OffsetToPtr(Wnode, Linkage);
}
}
void WmipDeliverNotification(
PDCENTRY DataConsumer,
PWNODE_HEADER Wnode,
ULONG WnodeSize
)
/*++
Routine Description:
This routine does the actual delivery of notifications via rpc
Arguments:
DataConsumer is the data consumer to whom the event should be delivered
Wnode has the WNODE_HEADER that describes the notification
Return Value:
--*/
{
PRPC_ASYNC_STATE RpcAsyncState;
ULONG Status;
//
// Don't deliver notification if data consumer has been cleaned up
if (DataConsumer->Flags & DC_FLAG_RUNDOWN)
{
WmipDebugPrint(("WMI: Event not sent DC %x is rundown\n", DataConsumer));
return;
}
if (DataConsumer->RpcCallsOutstanding >= RPCOUTSTANDINGCALLLIMIT)
{
WmipDebugPrint(("WMI: Event not sent DC %x has 0x%x calls outstandeing rundown\n", DataConsumer, DataConsumer->RpcCallsOutstanding));
WmipCalcLostEvents(DataConsumer, Wnode);
return;
}
Status = WmipRestrictToken(WmipRestrictedToken);
if (Status != ERROR_SUCCESS)
{
return;
}
RpcAsyncState = (PRPC_ASYNC_STATE)WmipAlloc(sizeof(RPC_ASYNC_STATE));
if (RpcAsyncState == NULL)
{
WmipDebugPrint(("WMI: Couldn't allocate async state for call\n"));
return;
}
Status = RpcAsyncInitializeHandle(RpcAsyncState,
RPC_ASYNC_VERSION_1_0);
if (Status != ERROR_SUCCESS)
{
WmipFree(RpcAsyncState);
return;
}
WmipAssert(WmipAsyncRpcApcThread != NULL);
RpcAsyncState->NotificationType = RpcNotificationTypeApc;
RpcAsyncState->u.NotificationRoutine = WmipNotificationDeliveryApcRoutine;
RpcAsyncState->u.APC.hThread = WmipAsyncRpcApcThread;
RpcAsyncState->UserInfo = (PVOID)DataConsumer;
InterlockedIncrement(&DataConsumer->RpcCallsOutstanding);
WmipDebugPrint(("WMI: Calling Async NotificationDeliver %x, 0x%x outstanding\n",
RpcAsyncState, DataConsumer->RpcCallsOutstanding));
//
// Take an extra refcount so DC will stick around until the call completes
WmipReferenceDC(DataConsumer);
try
{
WmipClient_NotificationDelivery(
RpcAsyncState,
(handle_t)DataConsumer->RpcBindingHandle,
WnodeSize,
(PBYTE)Wnode,
0,
NOTIFICATION_FLAG_BATCHED);
} except(EXCEPTION_EXECUTE_HANDLER) {
InterlockedDecrement(&DataConsumer->RpcCallsOutstanding);
WmipUnreferenceDC(DataConsumer);
WmipDebugPrint(("WMI: NotificationDeliver threw exception %d\n",
GetExceptionCode()));
}
WmipDebugPrint(("WMI: Return Async NotificationDeliver %x\n",
RpcAsyncState));
WmipUnrestrictToken();
}
#endif
PWNODE_HEADER WmipIncludeStaticNames(
PWNODE_HEADER Wnode
)
{
PWMIINSTANCEINFO WmiInstanceInfo, TargetInstanceInfo;
ULONG InstanceCount;
PWNODE_HEADER WnodeFull;
PWNODE_ALL_DATA WnodeAllData;
PWNODE_SINGLE_INSTANCE WnodeSI;
ULONG i;
PWCHAR InstanceName;
ULONG InstanceNameLen;
ULONG InstanceIndex;
ULONG Status;
LPGUID EventGuid = &Wnode->Guid;
ULONG WnodeFullSize;
PWCHAR TargetInstanceName;
WCHAR Index[7];
//
// If no static instance names then nothing to do
if (! (Wnode->Flags & WNODE_FLAG_STATIC_INSTANCE_NAMES))
{
return(Wnode);
}
Status = GetGuidInfo(NULL,
EventGuid,
&InstanceCount,
&WmiInstanceInfo,
FALSE);
if (Status != ERROR_SUCCESS)
{
//
// If guid is not registered then nothing we can do
WmipReportEventLog(EVENT_WMI_CANT_RESOLVE_INSTANCE,
EVENTLOG_WARNING_TYPE,
0,
Wnode->BufferSize,
Wnode,
0);
WmipDebugPrint(("WMI: Static instance name in event, but guid not registered\n"));
if (Wnode->Flags & WNODE_FLAG_ALL_DATA)
{
WnodeAllData = (PWNODE_ALL_DATA)Wnode;
WnodeAllData->OffsetInstanceNameOffsets = 0;
} else if ((Wnode->Flags & WNODE_FLAG_SINGLE_INSTANCE) ||
(Wnode->Flags & WNODE_FLAG_SINGLE_ITEM))
{
WnodeSI = (PWNODE_SINGLE_INSTANCE)Wnode;
WnodeSI->OffsetInstanceName = 0;
}
return(Wnode);
}
//
// Select the InstanceInfo for the specified provider id
TargetInstanceInfo = NULL;
for (i = 0; i < InstanceCount; i++)
{
if (Wnode->ProviderId == WmiInstanceInfo[i].ProviderId)
{
TargetInstanceInfo = &WmiInstanceInfo[i];
break;
}
}
if (TargetInstanceInfo == NULL)
{
//
// If no matching provider id
WmipReportEventLog(EVENT_WMI_CANT_RESOLVE_INSTANCE,
EVENTLOG_WARNING_TYPE,
0,
Wnode->BufferSize,
Wnode,
0);
WmipDebugPrint(("WMI: Static instance name in event, but provider id not registered\n"));
midl_user_free(WmiInstanceInfo);
return(Wnode);
}
if ((TargetInstanceInfo->Flags &
(IS_INSTANCE_BASENAME | IS_INSTANCE_STATICNAMES)) == 0)
{
WmipReportEventLog(EVENT_WMI_CANT_RESOLVE_INSTANCE,
EVENTLOG_WARNING_TYPE,
0,
Wnode->BufferSize,
Wnode,
0);
WmipDebugPrint(("WMI: Static instance name event but guid registered as dynamic\n"));
return(Wnode);
}
if (Wnode->Flags & WNODE_FLAG_ALL_DATA)
{
//
// Fill instance names in WNODE_ALL_DATA
WnodeFullSize = Wnode->BufferSize +
(TargetInstanceInfo->InstanceCount * sizeof(ULONG)) +
WmipStaticInstanceNameSize(TargetInstanceInfo);
WnodeFull = WmipAlloc(WnodeFullSize);
if (WnodeFull == NULL)
{
WnodeFull = Wnode;
} else {
memcpy(WnodeFull, Wnode, Wnode->BufferSize);
WnodeAllData = (PWNODE_ALL_DATA)WnodeFull;
WmipInsertStaticNames(WnodeAllData,
WnodeFullSize,
TargetInstanceInfo);
}
} else if ((Wnode->Flags & WNODE_FLAG_SINGLE_INSTANCE) ||
(Wnode->Flags & WNODE_FLAG_SINGLE_ITEM))
{
//
// Fill instance names in WNODE_SINGLE_INSTANCE or _ITEM
WnodeFull = Wnode;
WnodeSI = (PWNODE_SINGLE_INSTANCE)Wnode;
InstanceIndex = WnodeSI->InstanceIndex;
if (InstanceIndex < TargetInstanceInfo->InstanceCount)
{
if (TargetInstanceInfo->Flags & IS_INSTANCE_STATICNAMES)
{
InstanceName = TargetInstanceInfo->StaticNamePtr[InstanceIndex];
InstanceNameLen = (wcslen(InstanceName) + 2) * sizeof(WCHAR);
} else if (TargetInstanceInfo->Flags & IS_INSTANCE_BASENAME) {
InstanceName = TargetInstanceInfo->BaseName;
InstanceNameLen = (wcslen(InstanceName) + 2 + MAXBASENAMESUFFIXSIZE) * sizeof(WCHAR);
}
//
// Allocate a new Wnode and fill in the instance name
WnodeFullSize = ((Wnode->BufferSize+1) & ~1) + InstanceNameLen;
WnodeFull = WmipAlloc(WnodeFullSize);
if (WnodeFull != NULL)
{
memcpy(WnodeFull, Wnode, Wnode->BufferSize);
WnodeFull->BufferSize = WnodeFullSize;
WnodeSI = (PWNODE_SINGLE_INSTANCE)WnodeFull;
WnodeSI->OffsetInstanceName = (Wnode->BufferSize+1)& ~1;
TargetInstanceName = (PWCHAR)((PBYTE)WnodeSI + WnodeSI->OffsetInstanceName);
if (TargetInstanceInfo->Flags & IS_INSTANCE_STATICNAMES)
{
InstanceNameLen -= sizeof(WCHAR);
*TargetInstanceName++ = (USHORT)InstanceNameLen;
wcscpy(TargetInstanceName, InstanceName);
} else {
if (TargetInstanceInfo->Flags & IS_PDO_INSTANCENAME)
{
WnodeFull->Flags |= WNODE_FLAG_PDO_INSTANCE_NAMES;
}
#ifdef MEMPHIS
swprintf(Index, L"%d",
TargetInstanceInfo->BaseIndex + InstanceIndex);
wcscpy(TargetInstanceName+1, InstanceName);
wcscat(TargetInstanceName+1, Index);
InstanceNameLen = wcslen(TargetInstanceName+1);
#else
InstanceNameLen = wsprintfW(TargetInstanceName+1,
L"%ws%d", InstanceName,
TargetInstanceInfo->BaseIndex + InstanceIndex);
#endif
*TargetInstanceName = ((USHORT)InstanceNameLen+1) * sizeof(WCHAR);
}
} else {
WmipDebugPrint(("WMI: Couldn't alloc memory for Static instance name in event\n"));
WnodeFull = Wnode;
}
} else {
WmipReportEventLog(EVENT_WMI_CANT_RESOLVE_INSTANCE,
EVENTLOG_WARNING_TYPE,
0,
Wnode->BufferSize,
Wnode,
0);
WmipDebugPrint(("WMI: Static instance name index in event too large\n"));
}
}
midl_user_free(WmiInstanceInfo);
return(WnodeFull);
}
#define DCPERLOOP 16
void WmipSendQueuedEvents(
void
)
{
PLIST_ENTRY DataConsumerList;
PDCENTRY DataConsumer;
PDCENTRY DCList[DCPERLOOP];
ULONG DCCount, i;
do
{
WmipEnterSMCritSection();
DataConsumerList = DCHead.Flink;
DCCount = 0;
while ((DataConsumerList != &DCHead) &&
(DCCount < DCPERLOOP))
{
DataConsumer = CONTAINING_RECORD(DataConsumerList,
DCENTRY,
MainDCList);
if (DataConsumer->EventData != NULL)
{
DCList[DCCount++] = DataConsumer;
}
DataConsumerList = DataConsumerList->Flink;
}
WmipLeaveSMCritSection();
for (i = 0; i < DCCount; i++)
{
//
// Send off event, free queue buffer and unreference DC
WmipDeliverNotification(DCList[i],
(PWNODE_HEADER)DCList[i]->EventData,
DCList[i]->NextEventOffset);
WmipFree(DCList[i]->EventData);
DCList[i]->EventData = NULL;
WmipUnreferenceDC(DCList[i]);
}
} while (DataConsumerList != &DCHead);
}
void WmipDeliverEventToAllConsumers(
PNOTIFICATIONENTRY NotificationEntry,
PWNODE_HEADER Wnode
)
{
ULONG i;
NOTIFICATIONENTRY NotificationEntryCopy;
CheckContinuation:
//
// While we own the critical section we make a copy of the
// NotificationEntry so that it will remain consistent while we callout
// to the event consumers. We also reference all of the data consumers
// that will be receiving events so that they don't go away while we
// are calling out. We don't need to worry about the NotificationEntry
// going away since it was reference by the caller.
WmipEnterSMCritSection();
NotificationEntryCopy = *NotificationEntry;
for (i = 0; i < DCREFPERNOTIFICATION; i++)
{
if (NotificationEntryCopy.DcRef[i].Flags & DCREF_FLAG_NOTIFICATION_ENABLED)
{
WmipReferenceDC(NotificationEntryCopy.DcRef[i].DcEntry);
}
}
WmipLeaveSMCritSection();
//
// While we aren't holding the critical section we call out to all of the
// data consumers who are interested in this event
for (i = 0; i < DCREFPERNOTIFICATION; i++)
{
PDCREF DcRef = &NotificationEntryCopy.DcRef[i];
PDCREF DcRef2 = &NotificationEntry->DcRef[i];
if (DcRef->Flags & DCREF_FLAG_NOTIFICATION_ENABLED)
{
if (Wnode->Flags & WNODE_FLAG_TRACED_GUID)
{
Wnode->CountLost = InterlockedExchange(
&DcRef2->LostEventCount,
0);
}
WmipDeliverNotification(DcRef->DcEntry,
Wnode,
Wnode->BufferSize);
//
// Now that the data consumer in the NotificationEntry
// have been called we will unreference them so that they
// can go away if they are freed
WmipUnreferenceDC(DcRef->DcEntry);
}
}
//
// Finally we need to see if the notification entry has a continuation
// block with more data consumers to notify. If so we go back and
// do this all again. We don't worry about the continuation block going
// away since it is only freed when the entire notification entry is
// freed and that can't happen since the caller took an extra ref count
// on it.
if (NotificationEntryCopy.Continuation != NULL)
{
NotificationEntry = NotificationEntryCopy.Continuation;
goto CheckContinuation;
}
}
void WmipQueueEventToAllConsumers(
PNOTIFICATIONENTRY NotificationEntry,
PWNODE_HEADER Wnode,
ULONG EventSizeGuess
)
{
ULONG i;
NOTIFICATIONENTRY NotificationEntryCopy;
PDCENTRY DataConsumer;
ULONG DCCount;
ULONG NextEventOffset, LastEventOffset;
ULONG WnodeSize;
PWNODE_HEADER EventBuffer;
BOOLEAN FirstEventBuffer = FALSE;
WmipEnterSMCritSection();
CheckContinuation:
for (i = 0, DCCount = 0; i < DCREFPERNOTIFICATION; i++)
{
PDCREF DcRef = &NotificationEntry->DcRef[i];
if (DcRef->Flags & DCREF_FLAG_NOTIFICATION_ENABLED)
{
DataConsumer = DcRef->DcEntry;
//
// if an event data buffer has not been allocated for the
// data consumer yet then allocate one.
if (DataConsumer->EventData == NULL)
{
DataConsumer->EventData = WmipAlloc(EventSizeGuess);
if (DataConsumer->EventData == NULL)
{
WmipDebugPrint(("WMI: Events lost since DataConsumer->EventData could not be allocated, size %d\n", EventSizeGuess));
continue;
}
DataConsumer->NextEventOffset = 0;
FirstEventBuffer = TRUE;
DataConsumer->EventDataSizeLeft = EventSizeGuess;
WmipReferenceDC(DataConsumer);
}
//
// Ensure there is enough room to copy the new event
NextEventOffset = DataConsumer->NextEventOffset;
LastEventOffset = DataConsumer->LastEventOffset;
WnodeSize = Wnode->BufferSize;
WnodeSize = (WnodeSize + 7) & ~7;
if (DataConsumer->EventDataSizeLeft < WnodeSize)
{
EventSizeGuess = NextEventOffset +
DataConsumer->EventDataSizeLeft;
EventSizeGuess += (EventSizeGuess / 2) > WnodeSize ?
(EventSizeGuess / 2) :
(EventSizeGuess / 2) + WnodeSize;
if (! WmipRealloc(&DataConsumer->EventData,
NextEventOffset,
EventSizeGuess,
TRUE))
{
WmipDebugPrint(("WMI: Event lost, couldn't realloc DataConsumer->EventData for size %d\n", EventSizeGuess));
continue;
}
DataConsumer->EventDataSizeLeft = EventSizeGuess -
NextEventOffset;
WmipAssert(DataConsumer->EventDataSizeLeft >= WnodeSize);
}
//
// Copy the new event to the end of the buffer
EventBuffer = (PWNODE_HEADER)OffsetToPtr(DataConsumer->EventData,
NextEventOffset);
memcpy(EventBuffer, Wnode, Wnode->BufferSize);
EventBuffer->Linkage = 0;
if (EventBuffer->Flags & WNODE_FLAG_TRACED_GUID)
{
EventBuffer->CountLost = InterlockedExchange(
&DcRef->LostEventCount,
0);
}
//
// Link the previously last event to this one
if (! FirstEventBuffer)
{
EventBuffer = (PWNODE_HEADER)OffsetToPtr(DataConsumer->EventData,
LastEventOffset);
EventBuffer->Linkage = NextEventOffset - LastEventOffset;
}
DataConsumer->LastEventOffset = NextEventOffset;
DataConsumer->NextEventOffset = NextEventOffset + WnodeSize;
DataConsumer->EventDataSizeLeft -= WnodeSize;
}
}
//
// Finally we need to see if the notification entry has a continuation
// block with more data consumers to notify. If so we go back and
// do this all again. We don't worry about the continuation block going
// away since it is only freed when the entire notification entry is
// freed and that can't happen since the caller took an extra ref count
// on it.
if (NotificationEntry->Continuation != NULL)
{
NotificationEntry = NotificationEntry->Continuation;
goto CheckContinuation;
}
WmipLeaveSMCritSection();
}
PWNODE_HEADER WmipDereferenceEvent(
PWNODE_HEADER Wnode
)
{
ULONG WnodeTargetSize;
ULONG IsStaticInstanceNames;
ULONG InstanceNameLen, InstanceNameLen2;
PWNODE_SINGLE_INSTANCE WnodeTarget;
PWCHAR Ptr;
PWNODE_EVENT_REFERENCE WnodeRef = (PWNODE_EVENT_REFERENCE)Wnode;
PBDATASOURCE DataSource;
ULONG Status;
ULONG Retries;
//
// Determine if the data source is valid or not
DataSource = WmipFindDSByProviderId(WnodeRef->WnodeHeader.ProviderId);
if (DataSource == NULL)
{
WmipDebugPrint(("WMI: Invalid Data Source in referenced guid \n"));
return(NULL);
}
//
// Compute the size of any dynamic name that must go into the TargetWnode
IsStaticInstanceNames = WnodeRef->WnodeHeader.Flags &
WNODE_FLAG_STATIC_INSTANCE_NAMES;
if (IsStaticInstanceNames == 0)
{
InstanceNameLen = *WnodeRef->TargetInstanceName + sizeof(USHORT);
} else {
InstanceNameLen = 0;
}
WnodeTargetSize = WnodeRef->TargetDataBlockSize +
FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData) +
InstanceNameLen +
8;
Retries = 0;
do
{
WnodeTarget = WmipAlloc(WnodeTargetSize);
if (WnodeTarget != NULL)
{
//
// Build WNODE_SINGLE_INSTANCE that we use to query for event data
memset(WnodeTarget, 0, WnodeTargetSize);
WnodeTarget->WnodeHeader.BufferSize = WnodeTargetSize;
memcpy(&WnodeTarget->WnodeHeader.Guid,
&WnodeRef->TargetGuid,
sizeof(GUID));
WnodeTarget->WnodeHeader.Version = WnodeRef->WnodeHeader.Version;
WnodeTarget->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE |
IsStaticInstanceNames;
if (IsStaticInstanceNames != 0)
{
WnodeTarget->InstanceIndex = WnodeRef->TargetInstanceIndex;
WnodeTarget->DataBlockOffset = FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData);
} else {
WnodeTarget->OffsetInstanceName = FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData);
Ptr = (PWCHAR)OffsetToPtr(WnodeTarget, WnodeTarget->OffsetInstanceName);
InstanceNameLen2 = InstanceNameLen - sizeof(USHORT);
*Ptr++ = (USHORT)InstanceNameLen2;
memcpy(Ptr,
&WnodeRef->TargetInstanceName[1],
InstanceNameLen2);
//
// Round data block offset to 8 byte alignment
WnodeTarget->DataBlockOffset = ((FIELD_OFFSET(WNODE_SINGLE_INSTANCE,
VariableData) +
InstanceNameLen2 +
sizeof(USHORT)+7) & 0xfffffff8);
}
Status = WmipDeliverWnodeToDS(WmiGetSingleInstance,
DataSource,
(PWNODE_HEADER)WnodeTarget);
if ((Status == ERROR_SUCCESS) &&
(WnodeTarget->WnodeHeader.Flags & WNODE_FLAG_TOO_SMALL))
{
WnodeTargetSize = ((PWNODE_TOO_SMALL)WnodeTarget)->SizeNeeded;
WmipFree(WnodeTarget);
Retries++;
Status = ERROR_MORE_DATA;
}
} else {
Status = ERROR_NOT_ENOUGH_MEMORY;
}
} while ((Status == ERROR_MORE_DATA) && (Retries < 2));
WmipUnreferenceDS(DataSource);
if (Status != ERROR_SUCCESS)
{
WmipReportEventLog(EVENT_WMI_CANT_GET_EVENT_DATA,
EVENTLOG_WARNING_TYPE,
0,
Wnode->BufferSize,
Wnode,
0);
WmipDebugPrint(("WMI: Query to dereference WNODE failed %d\n",
Status));
if (WnodeTarget != NULL)
{
WmipFree(WnodeTarget);
WnodeTarget = NULL;
}
} else {
WnodeTarget->WnodeHeader.Flags |= (WnodeRef->WnodeHeader.Flags &
WNODE_FLAG_SEVERITY_MASK) |
WNODE_FLAG_EVENT_ITEM;
}
return((PWNODE_HEADER)WnodeTarget);
}
void WmipEventNotification(
PWNODE_HEADER Wnode,
BOOLEAN SingleEvent,
ULONG EventSizeGuess
)
/*++
Routine Description:
This routine is called to deliver a notification to all of the consumers
who have registered to receive it. If the data provider
fired a WNODE_EVENT_REFERENCE then we query for the guid that is
referenced in the event and put the results of the query in the event.
Arguments:
Wnode has the WNODE_HEADER that describes the notification
Return Value:
--*/
{
PNOTIFICATIONENTRY NotificationEntry;
LPGUID EventGuid = &Wnode->Guid;
PWNODE_HEADER WnodeFull, WnodeTarget;
//
// If the event references a guid that needs to be queried then
// go do the dereferencing here.
if (Wnode->Flags & WNODE_FLAG_EVENT_REFERENCE)
{
WnodeTarget = WmipDereferenceEvent(Wnode);
if (WnodeTarget == NULL)
{
return;
}
Wnode = WnodeTarget;
} else {
WnodeTarget = NULL;
}
//
// Be sure to use the guid of the referenced event, not the event that
// was originally fired.
EventGuid = &Wnode->Guid;
// If it is Trace error notification, disable providers
//
#ifndef MEMPHIS
if (IsEqualGUID(EventGuid, & TraceErrorGuid)) {
WmipServiceDisableTraceProviders(Wnode);
}
#endif
//
// See if this event has a static name and if so fill it in
WnodeFull = WmipIncludeStaticNames(Wnode);
//
// Deliver notification to those data consumers who registered for it
NotificationEntry = WmipFindNEByGuid(EventGuid, TRUE);
if (NotificationEntry != NULL)
{
if (SingleEvent)
{
WmipDeliverEventToAllConsumers(NotificationEntry, WnodeFull);
} else {
WmipQueueEventToAllConsumers(NotificationEntry,
WnodeFull,
EventSizeGuess);
}
WmipUnreferenceNE(NotificationEntry);
}
if (WnodeFull != Wnode)
{
WmipFree(WnodeFull);
}
if (WnodeTarget != NULL)
{
WmipFree(WnodeTarget);
}
}
ULONG WmipCleanupDataConsumer(
PDCENTRY DataConsumer
#if DBG
,BOOLEAN *NotificationsEnabled,
BOOLEAN *CollectionsEnabled
#endif
)
/*++
Routine Description:
This routine cleans up after a data consumer who has gone way. We walk
the list of notifications and collections and if there is any enabled
collections or notifications then these are disabled. Also any memory
used by the data consumer is freed.
Arguments:
DataConsumer is the data consumer to cleanup
*NotificationsEnabled returns TRUE if the data consumer had left any
notifications enabled on debug builds. On Free builds it always
returns FALSE.
*CollectionsEnabled returns TRUE if the data consumer had left any
collections enabled on debug builds. On Free builds it always
returns FALSE.
Return Value:
ERROR_SUCCESS or an error code
--*/
{
PLIST_ENTRY NEList;
PNOTIFICATIONENTRY NotificationEntry, Continuation;
ULONG i;
TCHAR s[256];
ULONG Cookie;
//
// Some yahoo is unregistering more than once
if (DataConsumer->Flags & FLAG_ENTRY_ON_FREE_LIST)
{
WmipAssert(FALSE);
return(ERROR_INVALID_PARAMETER);
}
WmipAssert(DataConsumer->RpcBindingHandle != 0);
//
// Loop over notifications to see if this data consumer left anything
// enabled
CheckAgain:
WmipEnterSMCritSection();
NEList = NEHeadPtr->Flink;
while (NEList != NEHeadPtr)
{
NotificationEntry = CONTAINING_RECORD(NEList,
NOTIFICATIONENTRY,
MainNotificationList);
Continuation = NotificationEntry;
while (Continuation != NULL)
{
for (i = 0; i < DCREFPERNOTIFICATION; i++)
{
if (NotificationEntry->DcRef[i].DcEntry == DataConsumer)
{
WmipAssert(NotificationEntry->DcRef[i].Flags != 0);
if (NotificationEntry->DcRef[i].Flags & DCREF_FLAG_NOTIFICATION_ENABLED)
{
#if DBG
*NotificationsEnabled = TRUE;
#endif
WmipLeaveSMCritSection();
CollectOrEventWorker(
DataConsumer,
&NotificationEntry->Guid,
FALSE,
TRUE,
&Cookie,
0,
0);
//
// We have to jump out and restart the loop
// since the notification entry could have
// been freed while in the NotificationControl
// routine
goto CheckAgain;
}
if (NotificationEntry->DcRef[i].Flags & DCREF_FLAG_COLLECTION_ENABLED)
{
#if DBG
*CollectionsEnabled = TRUE;
#endif
//
// We force the collection ref count for the DC entry
// to 1 in case there were multiple threads in the
// data consumer who had collection enabled
NotificationEntry->DcRef[i].CollectRefCount = 1;
WmipLeaveSMCritSection();
CollectionControl( (DCCTXHANDLE)DataConsumer,
&NotificationEntry->Guid,
FALSE);
//
// We have to jump out and restart the loop since
// the notification entry could have been freed
// while in the CollectionControl routine
goto CheckAgain;
}
}
}
Continuation = Continuation->Continuation;
NotificationEntry = Continuation;
}
NEList = NEList->Flink;
}
//
// Mark DC as being rundown so no more events are sent to it.
// Binding handle will be freed when the DataConsumer is freed.
DataConsumer->Flags |= DC_FLAG_RUNDOWN;
WmipDebugPrint(("WMI: DC %x has just been rundown\n", DataConsumer));
WmipUnreferenceDC(DataConsumer);
WmipLeaveSMCritSection();
if ((WmipServiceShutdown) && (IsListEmpty(DCHeadPtr)))
{
//
// Set event if there are no more data consumers attached
// and we are waiting to exit service. Note that once we set this
// event, all resources such as memory allocations, critical sections,
// file handles, etc are gone and must not be used.
if (WmipTerminationEvent != NULL)
{
SetEvent(WmipTerminationEvent);
}
}
return(ERROR_SUCCESS);
}
void __cdecl WmipReportEventLog(
ULONG MessageCode,
WORD MessageType,
WORD MessageCategory,
DWORD RawDataSize,
PVOID RawData,
WORD StringCount,
...
)
{
#ifndef MEMPHIS
LPCTSTR StringList[MAX_MESSAGE_STRINGS];
va_list pArg;
ULONG i;
if (WmipEventLogHandle != NULL)
{
if (StringCount > MAX_MESSAGE_STRINGS)
{
WmipAssert(FALSE);
StringCount = MAX_MESSAGE_STRINGS;
}
va_start(pArg, StringCount);
for (i = 0; i < StringCount; i++)
{
StringList[i] = va_arg(pArg, LPCTSTR);
}
ReportEvent(WmipEventLogHandle,
MessageType,
MessageCategory,
MessageCode,
NULL,
StringCount,
RawDataSize,
StringList,
RawData);
}
#endif
}
#ifdef MEMPHIS
long WINAPI
DeviceNotificationWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
ULONG Status;
WmipDebugPrint(("WMI: 0x%x Got Message %d, wParam 0x%x, lParam 0x%x\n",
hWnd, Message, wParam, lParam));
//
// If we get a WM_DEVICECHANGE message with wParam set to
// DBT_DEVICEARRIVAL then it means that a new device has been loaded
// on the system. We use this opportunity to recheck if the WMI device
// has been created.
if (Message == WM_DEVICECHANGE)
{
if (wParam == DBT_DEVICEARRIVAL)
{
//
// Attempt kernel mode initialization. If it succeeds then
// WmipKMHandle will be set and we will fall out of the GetMessage
// loop, destroy the window, etc. If it fails then nothing has
// changed.
if (WmipKMHandle == (HANDLE)-1)
{
Status = WmipInitializeKM(&WmipKMHandle);
WmipDebugPrint(("WMI: WmipInitializeKM returned %d\n", Status));
if (Status == ERROR_SUCCESS)
{
PostQuitMessage(0);
}
}
}
}
return (DefWindowProc(hWnd, Message, wParam, lParam));
}
#define WMIHIDDENCLASSNAME "WMIHIDDENCLASSNAME"
ULONG WmipCreateDeviceNotificationWindow(
HINSTANCE InstanceHandle,
HWND *DeviceNotificationWindow
)
{
WNDCLASS WndClass;
ULONG Status;
WndClass.style = CS_BYTEALIGNWINDOW;
WndClass.lpfnWndProc = DeviceNotificationWndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = InstanceHandle;
WndClass.hIcon = NULL;
WndClass.hCursor = NULL;
WndClass.hbrBackground = 0;
WndClass.lpszMenuName = 0;
WndClass.lpszClassName = WMIHIDDENCLASSNAME;
if (! RegisterClass(&WndClass))
{
return(GetLastError());
}
*DeviceNotificationWindow = CreateWindowEx(0,
WMIHIDDENCLASSNAME,
NULL,
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
InstanceHandle,
NULL);
if (*DeviceNotificationWindow == NULL)
{
Status = GetLastError();
UnregisterClass(WMIHIDDENCLASSNAME,
InstanceHandle);
} else {
Status = ERROR_SUCCESS;
}
return(Status);
}
void WmipDestroyDeviceNotificationWindow(
HINSTANCE InstanceHandle,
HWND WindowHandle
)
{
DestroyWindow(WindowHandle);
UnregisterClass(WMIHIDDENCLASSNAME,
InstanceHandle);
}
#endif
#ifndef MEMPHIS
#define WmipNoActiveDC() (IsListEmpty((&DCHead)))
void WmiServiceCtrlHandler(
DWORD Opcode
)
/*++
Routine Description:
This routine is the service control handler for the WMI service. The
service control manager will call this routine to pause, stop,
continue or obtain the status of the WMI service.
Arguments:
OpCode is the function that the service control manager asks WMI to
perform
Return Value:
--*/
{
ULONG Status;
switch(Opcode)
{
case SERVICE_CONTROL_PAUSE:
{
WmipDebugPrint(("WMI: service does not support Pause\n"));
break;
}
case SERVICE_CONTROL_CONTINUE:
{
WmipDebugPrint(("WMI: service does not support Continue\n"));
break;
}
case SERVICE_CONTROL_STOP:
{
WmipEnterSMCritSection();
if (WmipNoActiveDC())
{
//
// No data consumers attached. We can shut down. First stop
// the rpc interface so no new data consumers can attach
// Remember we'll need to cleanup quickly since another
// consumer may want to attach now.
WmiServiceStatus.dwWin32ExitCode = 0;
WmiServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
WmiServiceStatus.dwCheckPoint = 0;
WmiServiceStatus.dwWaitHint = 0;
if (!SetServiceStatus (WmiServiceStatusHandle,
&WmiServiceStatus))
{
WmipLeaveSMCritSection();
Status = GetLastError();
WmipDebugPrint(("WMI: SetServiceStatus error %ld\n", Status));
break;
}
WmipSvcsGlobalData->StopRpcServer(wmicore_ServerIfHandle);
WmipServiceShutdown = TRUE;
WmipLeaveSMCritSection();
WmiDeinitializeService();
WmipNotificationBuffer = NULL;
if (WmipNotificationWait != NULL)
{
UnregisterWait(WmipNotificationWait);
WmipNotificationWait = NULL;
}
if (WmipNotificationOverlapped.hEvent != NULL)
{
CloseHandle(WmipNotificationOverlapped.hEvent);
WmipNotificationOverlapped.hEvent = NULL;
}
if (WmipAsyncRpcApcThread != NULL)
{
CloseHandle(WmipAsyncRpcApcThread);
WmipAsyncRpcApcThread = NULL;
}
if (WmipRestrictedToken != NULL)
{
CloseHandle(WmipRestrictedToken);
WmipRestrictedToken = NULL;
}
WmiServiceStatus.dwCurrentState = SERVICE_STOPPED;
WmiServiceStatus.dwCheckPoint = 0;
WmiServiceStatus.dwWaitHint = 0;
WmiServiceStatus.dwWin32ExitCode = ERROR_SUCCESS;
WmiServiceStatus.dwServiceSpecificExitCode = ERROR_SUCCESS;
WmipDebugPrint(("WMI: Leaving Service\n"));
} else {
WmipDebugPrint(("WMI: Service can't be stopped since data consumers are active\n"));
WmipLeaveSMCritSection();
}
break;
}
case SERVICE_CONTROL_INTERROGATE:
{
// Fall through to send current status.
break;
}
default:
{
WmipDebugPrint(("WMI: Unrecognized opcode %ld\n", Opcode));
break;
}
}
//
// Send current status.
if (!SetServiceStatus (WmiServiceStatusHandle, &WmiServiceStatus))
{
Status = GetLastError();
WmipDebugPrint(("WMI: SetServiceStatus error %ld\n",Status));
}
}
VOID
SVCS_ENTRY_POINT(
DWORD argc,
LPWSTR argv[],
PSVCS_GLOBAL_DATA SvcsGlobalData,
HANDLE SvcRefHandle
)
/*++
Routine Description:
This is the main thread of the WMI service. First the routine will
initialize the service, call the service control manager to start
listening for RPC calls on the WMI service's interface, and then enter
the notification loop. If the notification loop returns then the
service was requested to stop so the routine will stop the service's
rpc interface, wait for all rpc clients to disconnect and then
return. Note that once this routine returns the wmi service's dll
is unloaded.
Arguments:
argc is count of command-line arguments.
argv is command-line arguments.
SvcsGlobalData is a table of information the wmi service uses to
interface with the service control manager.
SvcRefHandle
Return Value:
NONE
Note:
--*/
{
DWORD Status;
BOOLEAN NoDCReged;
BOOLEAN ServiceInitialized;
WmipSvcsGlobalData = SvcsGlobalData;
WmipGlobalSvcRefHandle = SvcRefHandle;
WmiServiceStatus.dwServiceType = SERVICE_WIN32;
WmiServiceStatus.dwCurrentState = SERVICE_START_PENDING;
WmiServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
WmiServiceStatus.dwWin32ExitCode = 0;
WmiServiceStatus.dwServiceSpecificExitCode = 0;
WmiServiceStatus.dwCheckPoint = 0;
WmiServiceStatus.dwWaitHint = 0;
WmiServiceStatusHandle = RegisterServiceCtrlHandler(L"Wmi",
WmiServiceCtrlHandler);
if (WmiServiceStatusHandle == 0)
{
WmipDebugPrint(("WMI: RegisterServiceCtrlHandler failed %d\n", GetLastError()));
return;
}
//
// Initialize the WMI service
Status = WmiInitializeService();
//
// Obtain a restricted token to use when calling out
if (Status == ERROR_SUCCESS)
{
Status = WmipCreateRestrictedToken(&WmipRestrictedToken);
}
//
// Establish a work item to start the service running
WmipNotificationOverlapped.hEvent = NULL;
WmipNotificationWait = NULL;
if (Status == ERROR_SUCCESS)
{
ServiceInitialized = TRUE;
WmipNotificationOverlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WmipNotificationOverlapped.hEvent != NULL)
{
if (RegisterWaitForSingleObject(&WmipNotificationWait,
WmipNotificationOverlapped.hEvent,
WmipReadNotificationCompleteHack,
NULL,
INFINITE,
WT_EXECUTEINIOTHREAD))
{
WmipThreadPoolStatus = QueueGetPerisitentThread;
if (! QueueUserWorkItem(WmipGetPersistThread,
NULL,
WT_EXECUTEINPERSISTENTTHREAD))
{
// TODO: Fire eventlog event
Status = GetLastError();
WmipDebugPrint(("WMI: SetTimerQueueTimer 3 failed %d\n", Status));
}
} else {
Status = GetLastError();
WmipDebugPrint(("WMI: RegisterWaitForSingleObjectEx failed %d\n", Status));
}
}
} else {
WmipDebugPrint(("WMI: WmiInitialize service failed %d\n", Status));
ServiceInitialized = FALSE;
}
if (Status == ERROR_SUCCESS)
{
Status = WmipSvcsGlobalData->StartRpcServer(
WmipSvcsGlobalData->SvcsRpcPipeName,
wmicore_ServerIfHandle);
if (Status == RPC_NT_TYPE_ALREADY_REGISTERED)
{
//
// RPC_NT_TYPE_ALREADY_REGISTERED indicates that the rpc interface
// has already been registered so it is safe to continue.
Status = ERROR_SUCCESS;
}
#if DBG
if (Status != ERROR_SUCCESS)
{
WmipDebugPrint(("WMI: WmipSvcsGlobalData->StartRpcServer failed %d\n", Status));
}
#endif
}
if (Status != ERROR_SUCCESS)
{
WmipServiceShutdown = TRUE;
if (ServiceInitialized)
{
WmiDeinitializeService();
}
if (WmipNotificationWait != NULL)
{
UnregisterWait(WmipNotificationWait);
WmipNotificationWait = NULL;
}
if (WmipNotificationOverlapped.hEvent != NULL)
{
CloseHandle(WmipNotificationOverlapped.hEvent);
WmipNotificationOverlapped.hEvent = NULL;
}
if (WmipAsyncRpcApcThread != NULL)
{
CloseHandle(WmipAsyncRpcApcThread);
WmipAsyncRpcApcThread = NULL;
}
if (WmipRestrictedToken != NULL)
{
CloseHandle(WmipRestrictedToken);
WmipRestrictedToken = NULL;
}
//
// If an error occurs we just stop ourselves
WmiServiceStatus.dwCurrentState = SERVICE_STOPPED;
WmiServiceStatus.dwCheckPoint = 0;
WmiServiceStatus.dwWaitHint = 0;
WmiServiceStatus.dwWin32ExitCode = Status;
WmiServiceStatus.dwServiceSpecificExitCode = Status;
SetServiceStatus (WmiServiceStatusHandle, &WmiServiceStatus);
return;
}
// Initialization complete - report running status.
WmiServiceStatus.dwCurrentState = SERVICE_RUNNING;
WmiServiceStatus.dwCheckPoint = 0;
WmiServiceStatus.dwWaitHint = 0;
if (!SetServiceStatus (WmiServiceStatusHandle, &WmiServiceStatus))
{
Status = GetLastError();
WmipDebugPrint(("WMI: SetServiceStatus error %ld\n",Status));
}
}
#endif