2476 lines
75 KiB
C
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
|