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

3245 lines
101 KiB
C

/*++
Copyright (c) 1997-1999 Microsoft Corporation
Module Name:
server.c
Abstract:
WMI server functionality
Author:
16-Jan-1997 AlanWar
Revision History:
--*/
#include "wmiump.h"
#include "evntrace.h"
#include <rpc.h>
#ifndef MEMPHIS
#include <aclapi.h>
#endif
#define MAXSTR 1024
// {4EE0B301-94BC-11d0-A4EC-00A0C9062910}
GUID RegChangeNotificationGuid =
{ 0x4ee0b301, 0x94bc, 0x11d0, { 0xa4, 0xec, 0x0, 0xa0, 0xc9, 0x6, 0x29, 0x10 } };
extern HANDLE WmipKMHandle;
#ifdef MEMPHIS
ULONG WmipRpcServerInitialize(
void
)
/*++
Routine Description:
Initialize access to the RPC server that provides support from the
WMI service
Arguments:
Return Value:
ERROR_SUCCESS or an error code
--*/
{
ULONG Status;
Status = RpcServerUseProtseqEp(WmiServiceRpcProtocolSequence,
MaxRpcCalls,
WmiServiceRpcEndpoint,
NULL
);
if (Status == ERROR_SUCCESS)
{
Status = RpcServerRegisterIfEx(wmicore_ServerIfHandle,
NULL,
NULL,
0,
MaxRpcCalls,
NULL);
if (Status == ERROR_SUCCESS)
{
Status = RpcServerListen(MinRpcCalls,
MaxRpcCalls,
TRUE);
if (Status != ERROR_SUCCESS)
{
RpcServerUnregisterIf(wmicore_ServerIfHandle, NULL, TRUE);
}
}
}
return(Status);
}
void WmipRpcServerDeinitialize(
void
)
/*++
Routine Description:
Initialize access to the RPC server that provides support from the
WMI service
Arguments:
Return Value:
ERROR_SUCCESS or an error code
--*/
{
ULONG Status;
Status = RpcServerUnregisterIf(wmicore_ServerIfHandle,
NULL,
TRUE);
}
#endif
#ifndef MEMPHIS
BOOLEAN
WmipIsRpcClientLocal(
PVOID Context
)
{
ULONG IsLocal = 0;
ULONG Status;
Status = I_RpcBindingIsClientLocal(Context,
&IsLocal);
if ((Status != RPC_S_OK) ||
(IsLocal == 0))
{
WmipDebugPrint(("WMI: Incoming remote service call received\n"));
return(FALSE);
}
return(TRUE);
}
#endif
ULONG WmipGetInstanceInfo(
IN PBINSTANCESET InstanceSet,
OUT PWMIINSTANCEINFO Info
)
/*++
Routine Description:
Build a WMIINSTANCEINFO structure from information in the INSTANCESET.
This routine assumes that any synchronization of InstanceSet is done
outsize of the routine.
Arguments:
InstanceSet is the set of instances for which we are building the
WMIINSTANCEINFO
Info returns with a completed WMIINSTAMNCEINFO structure
Return Value:
ERROR_SUCCESS or an error code
--*/
{
PBDATASOURCE DataSource = InstanceSet->DataSource;
ULONG j;
PBINSTANCESET InstanceSetRef;
PBISSTATICNAMES IsStaticNames = InstanceSet->IsStaticNames;
ULONG Flags;
ULONG Status = ERROR_SUCCESS;
Info->BaseName = NULL;
Info->StaticNamePtr = NULL;
Flags = InstanceSet->Flags;
//
// Can't have both an instance base name and a static instance name list
WmipAssert( (Flags & (IS_INSTANCE_BASENAME | IS_INSTANCE_STATICNAMES)) !=
(IS_INSTANCE_BASENAME | IS_INSTANCE_STATICNAMES));
//
// Can't be both a kernel mode and user mode provider
WmipAssert( (Flags & (IS_KM_PROVIDER | IS_UM_PROVIDER)) !=
(IS_KM_PROVIDER | IS_UM_PROVIDER))
WmipAssert( (Flags & (IS_KM_PROVIDER | IS_SM_PROVIDER)) !=
(IS_KM_PROVIDER | IS_SM_PROVIDER))
//
// Copy BaseName info into Info->IsBaseName
if (Flags & IS_INSTANCE_BASENAME)
{
Info->BaseIndex = InstanceSet->IsBaseName->BaseIndex;
Info->BaseName = InstanceSet->IsBaseName->BaseName;
//
// Copy static instance names into Info
} else if (Flags & IS_INSTANCE_STATICNAMES) {
Info->StaticNamePtr = IsStaticNames->StaticNamePtr;
}
if (Status == ERROR_SUCCESS)
{
//
// This is OK to cast this down from ULONG_PTR to ULONG. Only
// provider ids for Kernel mode drivers are important to the
// data consumer side and these are only 32 bits
Info->ProviderId = (ULONG)DataSource->ProviderId;
Info->Flags = Flags;
Info->InstanceCount = InstanceSet->Count;
Info->InstanceNameSize = 0;
if (DataSource->BindingString != NULL)
{
Info->Flags |= IS_UM_PROVIDER;
} else if (Info->ProviderId == INTERNAL_PROVIDER_ID) {
Info->Flags |= IS_INTERNAL_PROVIDER;
} else {
Info->Flags |= IS_KM_PROVIDER;
}
}
return(Status);
}
GUID GuidNull = NULL_GUID;
ULONG GetGuidInfo(
/* [in] */ DCCTXHANDLE DcCtxHandle,
/* [in] */ LPGUID Guid,
/* [out] */ ULONG __RPC_FAR *InstanceCount,
/* [size_is][size_is][out] */ PWMIINSTANCEINFO __RPC_FAR *InstanceInfo,
/* [in] */ BOOLEAN EnableCollection
)
/*++
Routine Description:
Obtain information about the instances available for a particular guid.
If any of the data providers require a collection enable request to
start data collection this routine will send it.
Arguments:
Guid is the guid of interest
*InstanceCount returns the count of data sources that implement the
guid.
*InstanceInfo returns an array of WMIINSTANCEINFO structures
EnableCollection is TRUE then Collection requests will be forwarded to
the data providers.
Return Value:
ERROR_SUCCESS or an error code
--*/
{
PBGUIDENTRY GuidEntry;
ULONG Count, i;
PLIST_ENTRY InstanceSetList;
PWMIINSTANCEINFO Info;
PBINSTANCESET InstanceSet;
ULONG Status;
BOOLEAN Collecting;
#if DBG
TCHAR s[MAX_PATH];
#endif
WmipAssert(WmipValidateGuid(Guid));
if (! VERIFY_DCCTXHANDLE(DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
if (IsEqualGUID(Guid, &GuidNull))
{
//
// Request is for all unique WMIREGINFOs that are registered
return(ERROR_WMI_GUID_NOT_FOUND);
} else {
//
// See if the specific guid is registered at all
GuidEntry = WmipFindGEByGuid(Guid, FALSE);
if (GuidEntry == NULL)
{
return(ERROR_WMI_GUID_NOT_FOUND);
}
if (GuidEntry->Flags & GE_FLAG_INTERNAL)
{
WmipUnreferenceGE(GuidEntry);
return(ERROR_WMI_GUID_NOT_FOUND);
}
//
// We lock the registration information while picking out the
// information so that any changes that occur while this is in
// progress will notaffect us
WmipEnterSMCritSection();
Count = GuidEntry->ISCount;
WmipAssert(Count != 0);
//
// We use MIDL to allocate the memory for us so that rpc can
// free it once the buffer has been transmitted back to the caller
Info = midl_user_allocate(Count * sizeof(WMIINSTANCEINFO));
if (Info == NULL)
{
WmipLeaveSMCritSection();
WmipUnreferenceGE(GuidEntry);
WmipDebugPrint(("WMI: Couldn't alloc memory for WMIINSTANCEINFO\n"));
return(ERROR_NOT_ENOUGH_MEMORY);
}
//
// Loop over all instance sets and extract the instance name information
// making sure that we don't overwrite if we have too many
i = 0;
InstanceSetList = GuidEntry->ISHead.Flink;
Collecting = FALSE;
while ((InstanceSetList != &GuidEntry->ISHead) && (i < Count))
{
WmipAssert(i < Count);
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
if ( ! ((InstanceSet->Flags & IS_TRACED) ||
((InstanceSet->Flags & IS_EVENT_ONLY) && EnableCollection)))
{
//
// Only those guids not Traced guids, event only guids
// and unresolved references are not available for queries
WmipGetInstanceInfo(InstanceSet, &Info[i]);
Collecting = (Collecting || (InstanceSet->Flags & IS_EXPENSIVE));
i++;
}
InstanceSetList = InstanceSetList->Flink;
}
WmipLeaveSMCritSection();
if (i == 0)
{
//
// If there are no guids available for querying then we can free
// the buffers and return an error
midl_user_free(Info);
Info = NULL;
Status = ERROR_WMI_GUID_NOT_FOUND;
} else {
if (i != Count)
{
//
// if the actual number of InstanceSets does not match the number
// stored in the guid entry then only return the actual number.
// There probably was a traced guid
Count = i;
}
if (EnableCollection && Collecting)
{
Status = CollectionControl(DcCtxHandle,
Guid,
TRUE);
} else {
Status = ERROR_SUCCESS;
}
}
WmipUnreferenceGE(GuidEntry);
}
*InstanceInfo = Info;
*InstanceCount = Count;
return(Status);
}
ULONG ReleaseGuidInfo(
/* [in] */ DCCTXHANDLE DcCtxHandle,
/* [in] */ LPGUID Guid
)
/*++
Routine Description:
Called when a data consumer is no longer querying a data block. This
routine will send any collection disable requests if needed.
Arguments:
Guid is the guid of interest
Return Value:
ERROR_SUCCESS or an error code
--*/
{
PBGUIDENTRY GuidEntry;
ULONG i;
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
ULONG Status;
BOOLEAN Collecting;
if (! VERIFY_DCCTXHANDLE(DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
if (! WmipValidateGuid(Guid))
{
return(ERROR_INVALID_PARAMETER);
}
//
// See if the guid is registered at all
GuidEntry = WmipFindGEByGuid(Guid, FALSE);
if (GuidEntry == NULL)
{
return(ERROR_WMI_GUID_NOT_FOUND);
}
//
// We lock the registration information while picking out the information
// so that any changes that occur while this is in progress will not
// affect us
WmipEnterSMCritSection();
InstanceSetList = GuidEntry->ISHead.Flink;
Collecting = FALSE;
while (InstanceSetList != &GuidEntry->ISHead)
{
//
// Loop over all instance sets to determine if any are expensive
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
if (InstanceSet->Flags & IS_EXPENSIVE)
{
WmipLeaveSMCritSection();
//
// One of the instances is expensive so disable collection
Status = CollectionControl(DcCtxHandle,
Guid,
FALSE);
WmipEnterSMCritSection();
break;
}
InstanceSetList = InstanceSetList->Flink;
}
WmipLeaveSMCritSection();
WmipUnreferenceGE(GuidEntry);
return(ERROR_SUCCESS);
}
#ifndef MEMPHIS
#if 0
void WmipShowPrivs(
HANDLE TokenHandle
)
{
PTOKEN_PRIVILEGES TokenPrivInfo;
UCHAR Buffer[4096];
BOOLEAN b;
ULONG SizeNeeded;
ULONG i;
TokenPrivInfo = (PTOKEN_PRIVILEGES)Buffer;
b = GetTokenInformation(TokenHandle,
TokenPrivileges,
TokenPrivInfo,
sizeof(Buffer),
&SizeNeeded);
if (b)
{
WmipDebugPrint(("Priv count is %d\n", TokenPrivInfo->PrivilegeCount));
for (i = 0; i < TokenPrivInfo->PrivilegeCount; i++)
{
WmipDebugPrint(("Priv %x%x has attr %x\n",
TokenPrivInfo->Privileges[i].Luid.HighPart,
TokenPrivInfo->Privileges[i].Luid.LowPart,
TokenPrivInfo->Privileges[i].Attributes));
}
WmipDebugPrint(("\n"));
}
}
#endif
ULONG WmipCreateRestrictedToken(
HANDLE *RestrictedTokenHandle
)
/*++
Routine Description:
Arguments:
Return Value:
ERROR_SUCCESS or an error code
--*/
{
HANDLE TokenHandle;
ULONG Status;
if (OpenProcessToken(GetCurrentProcess(),
TOKEN_ALL_ACCESS,
&TokenHandle))
{
if (DuplicateTokenEx(TokenHandle,
TOKEN_ALL_ACCESS,
NULL,
SecurityImpersonation,
TokenImpersonation,
RestrictedTokenHandle))
{
if (AdjustTokenPrivileges(*RestrictedTokenHandle,
TRUE,
NULL,
0,
NULL,
0))
{
Status = ERROR_SUCCESS;
} else {
Status = GetLastError();
CloseHandle(*RestrictedTokenHandle);
*RestrictedTokenHandle = NULL;
}
}
CloseHandle(TokenHandle);
} else {
Status = GetLastError();
}
WmipAssert(Status == ERROR_SUCCESS);
return(Status);
}
ULONG WmipRestrictToken(
HANDLE RestrictedToken
)
/*++
Routine Description:
Arguments:
Return Value:
ERROR_SUCCESS or an error code
--*/
{
ULONG Status;
if (SetThreadToken(NULL, RestrictedToken))
{
Status = ERROR_SUCCESS;
} else {
Status = GetLastError();
}
WmipAssert(Status == ERROR_SUCCESS);
return(Status);
}
ULONG WmipUnrestrictToken(
void
)
/*++
Routine Description:
Arguments:
Return Value:
ERROR_SUCCESS or an error code
--*/
{
ULONG Status;
if (RevertToSelf())
{
Status = ERROR_SUCCESS;
} else {
Status = GetLastError();
}
#if 0
{
HANDLE TokenHandle;
if (ImpersonateSelf(SecurityImpersonation))
{
if (OpenThreadToken(GetCurrentThread(),
TOKEN_ALL_ACCESS,
TRUE,
&TokenHandle))
{
WmipDebugPrint(("WMI: After reversion:\n"));
WmipShowPrivs(TokenHandle);
}
}
}
#endif
WmipAssert(Status == ERROR_SUCCESS);
return(Status);
}
#endif
#ifdef MEMPHIS
ULONG WmipBindToWmiClient(
TCHAR *BindingString,
RPC_BINDING_HANDLE *BindingHandle
)
{
ULONG Status;
if (*BindingString != 0)
{
Status = RpcBindingFromStringBinding(BindingString,
BindingHandle);
if (Status == ERROR_SUCCESS)
{
WmipAssert(*BindingHandle != 0);
}
} else {
WmipDebugPrint(("WMI: Invalid binding string %ws\n", BindingString));
Status = ERROR_INVALID_PARAMETER;
}
return(Status);
}
#else
ULONG WmipBindToWmiClient(
TCHAR *BindingString,
RPC_BINDING_HANDLE *BindingHandle
)
/*++
Routine Description:
This routine will cause a binding to the WMI server in a data consumer
or data provider. We need to setup identification impersonation, Dynamic
tracking and effective only security. This means that the server will
only get to see our access token (not use it), the rpc code will send
over the token at the time of the call not when it first recorded it,
and the token sent over will not include any privs that were disabled
when the call is made.
Arguments:
BindingString is the string binding for the server
*BindingHandle returns with the RPC binding handle
Return Value:
ERROR_SUCCESS or an error code
--*/
{
ULONG Status;
TCHAR *ObjectUuid;
TCHAR *ProtSeq;
TCHAR *NetworkAddr;
TCHAR *EndPoint;
TCHAR *NetworkOptions;
TCHAR *SecureBindingString;
#define SecureNetworkOption TEXT("Security=Identification Static True")
if (*BindingString != 0)
{
Status = RpcStringBindingParse(BindingString,
&ObjectUuid,
&ProtSeq,
&NetworkAddr,
&EndPoint,
&NetworkOptions);
if (Status == ERROR_SUCCESS)
{
Status = RpcStringBindingCompose(ObjectUuid,
ProtSeq,
NetworkAddr,
EndPoint,
SecureNetworkOption,
&SecureBindingString);
if (Status == ERROR_SUCCESS)
{
WmipDebugPrint(("WMI: SecureBindingString %ws\n",
SecureBindingString));
Status = RpcBindingFromStringBinding(SecureBindingString,
BindingHandle);
RpcStringFree(&SecureBindingString);
if (Status == ERROR_SUCCESS)
{
WmipAssert(*BindingHandle != 0);
}
}
RpcStringFree(&ObjectUuid);
RpcStringFree(&ProtSeq);
RpcStringFree(&NetworkAddr);
RpcStringFree(&EndPoint);
RpcStringFree(&NetworkOptions);
} else {
WmipDebugPrint(("WMI: Invalid binding string (%d) %ws\n",
Status, *BindingString));
}
} else {
WmipDebugPrint(("WMI: Invalid binding string %ws\n", BindingString));
Status = ERROR_INVALID_PARAMETER;
}
return(Status);
}
#endif
ULONG RegisterDataConsumer(
/* [out] */ DCCTXHANDLE *DcCtxHandle,
/* [in, string] */ TCHAR *RpcBindingString
)
/*++
Routine Description:
This routine manages the registration of a data consumer's notification
sinks. All notification sinks receive all internal (registration change)
and those external (data provider fired) events for which the data
consumer had registered for. Typically a data consumer will
register its notification sink as soon as a guid is accesssed or an
event enabled. The context handle will track the events and collections
enabled by this data consumer so if the data consumer disappears cleanup
can be handled by the rundown routine.
Arguments:
*DcCtxHandle returns the data consumer context handle
RpcBindingString is the binding string if notifications are sinked via RPC.
Return Value:
ERROR_SUCCESS or an error code
--*/
{
PDCENTRY DataConsumer;
ULONG Status;
#ifndef MEMPHIS
if (! WmipIsRpcClientLocal(NULL))
{
*DcCtxHandle = NULL;
return(ERROR_ACCESS_DENIED);
}
#endif
DataConsumer = WmipAllocDataConsumer();
if (DataConsumer != NULL)
{
Status = WmipBindToWmiClient(RpcBindingString,
&DataConsumer->RpcBindingHandle);
if (Status == ERROR_SUCCESS)
{
#if DBG
TCHAR *SavedBindingString;
SavedBindingString = WmipAlloc((_tcslen(RpcBindingString)+1) *
sizeof(TCHAR));
if (SavedBindingString != NULL)
{
_tcscpy(SavedBindingString, RpcBindingString);
}
DataConsumer->BindingString = SavedBindingString;
#endif
WmipEnterSMCritSection();
InsertTailList(DCHeadPtr, &DataConsumer->MainDCList);
WmipLeaveSMCritSection();
*DcCtxHandle = (DCCTXHANDLE)DataConsumer;
} else {
DataConsumer->Flags |= DC_FLAG_RUNDOWN;
WmipUnreferenceDC(DataConsumer);
*DcCtxHandle = NULL;
}
} else {
Status = ERROR_NOT_ENOUGH_MEMORY;
*DcCtxHandle = NULL;
}
return(Status);
}
ULONG UnregisterDataConsumer(
/* [in, out] */ DCCTXHANDLE *DcCtxHandle,
/* [out] */ BOOLEAN *NotificationsEnabled,
/* [out] */ BOOLEAN *CollectionsEnabled
)
/*++
Routine Description:
This routine unregisters a data consumer. The data consumer will not
longer receive any notifications. Any notifications or collections that
have been enabled by this data consumer will be disabled.
Arguments:
*DcCtxHandle is the data consumer context handle to unregister
*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
--*/
{
ULONG Status;
if (! VERIFY_DCCTXHANDLE(*DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", *DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
Status = WmipCleanupDataConsumer( (PDCENTRY)*DcCtxHandle
#if DBG
,NotificationsEnabled,
CollectionsEnabled
#endif
);
if (Status == ERROR_SUCCESS)
{
*DcCtxHandle = NULL;
}
return(Status);
}
ULONG IoctlActionCode[WmiExecuteMethodCall+1] =
{
IOCTL_WMI_QUERY_ALL_DATA,
IOCTL_WMI_QUERY_SINGLE_INSTANCE,
IOCTL_WMI_SET_SINGLE_INSTANCE,
IOCTL_WMI_SET_SINGLE_ITEM,
IOCTL_WMI_ENABLE_EVENT,
IOCTL_WMI_DISABLE_EVENT,
IOCTL_WMI_ENABLE_COLLECTION,
IOCTL_WMI_DISABLE_COLLECTION,
IOCTL_WMI_GET_REGINFO,
IOCTL_WMI_EXECUTE_METHOD
};
ULONG WmipDeliverWnodeToDS(
ULONG ActionCode,
PBDATASOURCE DataSource,
PWNODE_HEADER Wnode
)
{
ULONG Ioctl;
ULONG Status;
ULONG RetSize;
OVERLAPPED Overlapped;
BOOL IoctlSuccess;
ULONG Size = Wnode->BufferSize;
//
// Only the lower 32 bits of the provider id is significant for
// kernel mode providers
Wnode->ProviderId = (ULONG)DataSource->ProviderId;
if (DataSource->Flags & DS_KERNEL_MODE)
{
WmipAssert(WmipKMHandle != (PVOID)-1);
Overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (Overlapped.hEvent != NULL)
{
Ioctl = IoctlActionCode[ActionCode];
IoctlSuccess = DeviceIoControl(WmipKMHandle,
Ioctl,
(PBYTE)Wnode,
Size,
(PBYTE)Wnode,
Size,
&RetSize,
&Overlapped);
if (GetLastError() == ERROR_IO_PENDING)
{
IoctlSuccess = GetOverlappedResult(WmipKMHandle,
&Overlapped,
&RetSize,
TRUE);
}
if (IoctlSuccess)
{
Status = ERROR_SUCCESS;
} else {
Status = GetLastError();
}
CloseHandle(Overlapped.hEvent);
}
} else {
//
// Provider is a user mode app
Status = WmipRestrictToken(WmipRestrictedToken);
if (Status == ERROR_SUCCESS)
{
try
{
//
// In general WMI service only sends Enable/Disable requests
// which dont have ansi/unicode requirements
Status = WmipClient_ServiceRequest(
DataSource->RpcBindingHandle,
DataSource->RequestAddress,
DataSource->RequestContext,
ActionCode,
Size,
&Size,
(PBYTE)Wnode,
0
);
} except(EXCEPTION_EXECUTE_HANDLER) {
#if DBG
Status = GetExceptionCode();
WmipDebugPrint(("WMI: ServiceRquest thew exception %d\n",
Status));
#endif
Status = ERROR_WMI_DP_FAILED;
}
WmipUnrestrictToken();
}
}
return(Status);
}
ULONG WmipSendEnableDisableRequest(
ULONG ActionCode,
PBGUIDENTRY GuidEntry,
BOOLEAN IsEvent,
BOOLEAN IsTraceLog,
ULONG64 LoggerContext
)
/*++
Routine Description:
This routine will deliver an event or collection WNODE to all data
providers of a guid. This routine assumes that it is called with the
SM critical section held. The routine does not hold the critical
section for the duration of the call.
Arguments:
ActionCode is WMI_ENABLE_EVENTS, WMI_DISABLE_EVENTS,
WMI_ENABLE_COLLECTION or WMI_DISABLE_COLLECTION
GuidEntry is the guid entry for the guid that is being enabled/disable
or collected/stop collected
IsEvent is TRUE then ActionCode is to enable or disable events.
If FALSE then ActionCode is to enable or disbale collecton
IsTraceLog is TRUE then enable is only sent to those guids registered as
being a tracelog guid
LoggerContext is a logger context handle that should be placed in the
HistoricalContext field of the WNODE_HEADER if IsTraceLog is TRUE.
Return Value:
ERROR_SUCCESS or an error code
--*/
{
#if DBG
#define AVGISPERGUID 1
#else
#define AVGISPERGUID 64
#endif
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
PBDATASOURCE DataSourceArray[AVGISPERGUID];
PBDATASOURCE *DataSourceList;
ULONG Size;
ULONG Status;
WNODE_HEADER Wnode;
ULONG i;
PBDATASOURCE DataSource;
ULONG DSCount;
BOOLEAN IsEnable;
ULONG IsFlags;
if ((GuidEntry == NULL) || (GuidEntry->Flags & GE_FLAG_INTERNAL))
{
//
// Guids that have been unregistered and Internally defined guids
// have no data source to send requests to, so just leave happily
return(ERROR_SUCCESS);
}
IsEnable = ((ActionCode == WMI_ENABLE_EVENTS) ||
(ActionCode == WMI_ENABLE_COLLECTION));
IsFlags = IsEvent ? IS_ENABLE_EVENT : IS_ENABLE_COLLECTION;
//
// First we make a list of all of the DataSources that need to be called
// while we have the critical section and take a reference on them so
// they don't go away after we release them. Note that the DataSource
// structure will stay, but the actual data provider may in fact go away.
// In this case sending the request will fail.
DSCount = 0;
if (GuidEntry->ISCount > AVGISPERGUID)
{
DataSourceList = WmipAlloc(GuidEntry->ISCount * sizeof(PBDATASOURCE));
if (DataSourceList == NULL)
{
WmipDebugPrint(("WMI: alloc failed for DataSource array in WmipSendEnableDisableRequest\n"));
return(ERROR_NOT_ENOUGH_MEMORY);
}
} else {
DataSourceList = &DataSourceArray[0];
}
#if DBG
memset(DataSourceList, 0, GuidEntry->ISCount * sizeof(PBDATASOURCE));
#endif
InstanceSetList = GuidEntry->ISHead.Flink;
while ((InstanceSetList != &GuidEntry->ISHead) &&
(DSCount < GuidEntry->ISCount))
{
WmipAssert(DSCount < GuidEntry->ISCount);
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
//
// We send requests to those data providers that are not inprocs when
// it is an event being enabled or it is collection being enabled
// and they are defined to be expensive (collection needs to be
// enabled)
if (
( (IsTraceLog && (InstanceSet->Flags & IS_TRACED)) ||
( ! IsTraceLog && (! (InstanceSet->Flags & IS_TRACED)) &&
(IsEvent || (InstanceSet->Flags & IS_EXPENSIVE))
)
)
)
{
if ( (! IsEnable && (InstanceSet->Flags & IsFlags)) ||
(IsEnable && ! (InstanceSet->Flags & IsFlags)) )
{
DataSourceList[DSCount] = InstanceSet->DataSource;
WmipReferenceDS(DataSourceList[DSCount]);
DSCount++;
}
if (IsEnable)
{
InstanceSet->Flags |= IsFlags;
} else {
InstanceSet->Flags &= ~IsFlags;
}
}
InstanceSetList = InstanceSetList->Flink;
}
WmipLeaveSMCritSection();
//
// Now without the critical section we send the request to all of the
// data providers. Any new data providers who register after we made our
// list will be enabled by the registration code.
if (DSCount > 0)
{
memset(&Wnode, 0, sizeof(WNODE_HEADER));
memcpy(&Wnode.Guid, &GuidEntry->Guid, sizeof(GUID));
Wnode.BufferSize = sizeof(WNODE_HEADER);
if (IsTraceLog)
{
Wnode.HistoricalContext = LoggerContext;
Wnode.Flags |= WNODE_FLAG_TRACED_GUID;
}
for (i = 0; i < DSCount; i++)
{
DataSource = DataSourceList[i];
WmipAssert(DataSource != NULL);
WmipDeliverWnodeToDS(ActionCode, DataSource, &Wnode);
WmipUnreferenceDS(DataSource);
}
}
Status = ERROR_SUCCESS;
if (DataSourceList != DataSourceArray)
{
WmipFree(DataSourceList);
}
WmipEnterSMCritSection();
return(Status);
}
void WmipReleaseCollectionEnabled(
PNOTIFICATIONENTRY NotificationEntry
)
{
WmipAssert(NotificationEntry->CollectInProgress != NULL);
WmipDebugPrint(("WMI: %x enable releasning %p %x event %p\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
NotificationEntry->CollectInProgress));
SetEvent(NotificationEntry->CollectInProgress);
CloseHandle(NotificationEntry->CollectInProgress);
NotificationEntry->CollectInProgress = NULL;
}
void WmipWaitForCollectionEnabled(
PNOTIFICATIONENTRY NotificationEntry
)
{
WmipAssert((NotificationEntry->Flags & NE_FLAG_COLLECTION_IN_PROGRESS) ==
NE_FLAG_COLLECTION_IN_PROGRESS);
//
// Collection Enable/Disable is in progress so
// we cannot return just yet. Right now there could be a
// disable request being processed and if we didn't wait, we
// might get back to this caller before that disable request
// got around to realizing that it needs to send and enable
// request (needed by this thread's caller). So we'd have a
// situation where a thread though that collection was enabled
// but in reality it wasn't yet enabled.
if (NotificationEntry->CollectInProgress == NULL)
{
NotificationEntry->CollectInProgress = CreateEvent(NULL,
TRUE,
FALSE,
NULL);
WmipDebugPrint(("WMI: %x for %p %x created event %p\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
NotificationEntry->CollectInProgress));
}
WmipLeaveSMCritSection();
WmipDebugPrint(("WMI: %x waiting for %p %x on event %p\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
NotificationEntry->CollectInProgress));
WaitForSingleObject(NotificationEntry->CollectInProgress,
INFINITE);
WmipDebugPrint(("WMI: %x done %p %x waiting on event %p\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
NotificationEntry->CollectInProgress));
WmipEnterSMCritSection();
}
ULONG WmipSendEnableRequest(
PNOTIFICATIONENTRY NotificationEntry,
PBGUIDENTRY GuidEntry,
BOOLEAN IsEvent,
BOOLEAN IsTraceLog,
ULONG64 LoggerContext
)
/*++
Routine Description:
This routine will send an enable collection or notification request to
all of the data providers that have registered the guid being enabled.
This routine will manage any race conditions that might occur when
multiple threads are enabling and disabling the notification
simultaneously.
This routine is called while the SM critical section is being held and
will increment the appropriate reference count. if the ref count
transitions from 0 to 1 then the enable request will need to be forwarded
to the data providers otherwise the routine is all done and returns.
Before sending the enable request the routine checks to see if any
enable or disable requests are currently in progress and if not then sets
the in progress flag, releases the critical section and sends the enable
request. If there was a request in progress then the routine does not
send a request, but just returns. When the other thread that was sending
the request returns from processing the request it will recheck the
refcount and notice that it is greater than 0 and then send the enable
request.
Arguments:
NotificationEntry is the Notification entry that describes the guid
being enabled.
GuidEntry is the guid entry that describes the guid being enabled. For
a notification it may be NULL.
NotificationContext is the notification context to use if enabling events
IsEvent is TRUE if notifications are being enables else FALSE if
collection is being enabled
IsTraceLog is TRUE if enable is for a trace log guid
LoggerContext is a context value to forward in the enable request
Return Value:
ERROR_SUCCESS or an error code
--*/
{
ULONG InProgressFlag;
ULONG RefCount;
ULONG Status;
if (IsEvent)
{
InProgressFlag = NE_FLAG_NOTIFICATION_IN_PROGRESS;
RefCount = NotificationEntry->EventRefCount++;
} else {
InProgressFlag = NE_FLAG_COLLECTION_IN_PROGRESS;
RefCount = NotificationEntry->CollectRefCount++;
WmipDebugPrint(("WMI: %p enable collect for %p %x\n",
GetCurrentThreadId(),
NotificationEntry, NotificationEntry->Flags ));
}
//
// If the guid is transitioning from a refcount of 0 to 1 and there
// is not currently a request in progress, then we need to set the
// request in progress flag, release the critical section and
// send an enable request. If there is a request in progress we can't
// do another request. Whenever the current request finishes it
// will notice the ref count change and send the enable request on
// our behalf.
if ((RefCount == 0) &&
! (NotificationEntry->Flags & InProgressFlag))
{
//
// Take an extra ref count so that even if this gets disabled
// while the enable request is in progress the NotificationEntry
// will stay valid.
WmipReferenceNE(NotificationEntry);
NotificationEntry->Flags |= InProgressFlag;
WmipDebugPrint(("WMI: %p NE %p flags -> %x at %d\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
__LINE__));
EnableNotification:
Status = WmipSendEnableDisableRequest(IsEvent ?
WMI_ENABLE_EVENTS :
WMI_ENABLE_COLLECTION,
GuidEntry,
IsEvent,
IsTraceLog,
LoggerContext);
RefCount = IsEvent ? NotificationEntry->EventRefCount :
NotificationEntry->CollectRefCount;
if (RefCount == 0)
{
// This is the bogus situation we were worried about. While
// the enable request was being processed the notification
// was disabled. So leave the in progress flag set and
// send the disable.
Status = WmipSendEnableDisableRequest(IsEvent ?
WMI_DISABLE_EVENTS :
WMI_DISABLE_COLLECTION,
GuidEntry,
IsEvent,
IsTraceLog,
LoggerContext);
RefCount = IsEvent ? NotificationEntry->EventRefCount :
NotificationEntry->CollectRefCount;
if (RefCount > 0)
{
//
// We have hit a pathological case. One thread called to
// enable and while the enable request was being processed
// another thread called to disable, but was postponed
// since the enable was in progress. So once the enable
// completed we realized that the ref count reached 0 and
// so we need to disable and sent the disable request.
// But while the disable request was being processed
// an enable request came in so now we need to enable
// the notification. Sheesh.
goto EnableNotification;
}
}
NotificationEntry->Flags &= ~InProgressFlag;
WmipDebugPrint(("WMI: %p NE %p flags -> %x at %d\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
__LINE__));
//
// If there are any other threads that were waiting until all of
// the enable/disable work completed, we close the event handle
// to release them from their wait.
//
if ((! IsEvent) && (NotificationEntry->CollectInProgress != NULL))
{
WmipReleaseCollectionEnabled(NotificationEntry);
}
//
// Get rid of extra ref count we took above. Note that the
// NotificationEntry could be going away here if there was a
// disable while the enable was in progress.
WmipUnreferenceNE(NotificationEntry);
} else {
if ((! IsEvent) && (NotificationEntry->Flags & InProgressFlag))
{
WmipDebugPrint(("WMI: %x going to wait for %p %x at %d\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
__LINE__));
WmipWaitForCollectionEnabled(NotificationEntry);
WmipDebugPrint(("WMI: %x done to wait for %p %x at %d\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
__LINE__));
}
Status = ERROR_SUCCESS;
}
if (! IsEvent)
{
WmipDebugPrint(("WMI: %p enable collect done for %p %x\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags));
}
return(Status);
}
ULONG WmipDoDisableRequest(
PNOTIFICATIONENTRY NotificationEntry,
PBGUIDENTRY GuidEntry,
BOOLEAN IsEvent,
BOOLEAN IsTraceLog,
ULONG64 LoggerContext,
ULONG InProgressFlag
)
{
ULONG RefCount;
ULONG Status;
DisableNotification:
Status = WmipSendEnableDisableRequest(IsEvent ?
WMI_DISABLE_EVENTS :
WMI_DISABLE_COLLECTION,
GuidEntry,
IsEvent,
IsTraceLog,
LoggerContext);
RefCount = IsEvent ? NotificationEntry->EventRefCount :
NotificationEntry->CollectRefCount;
if (RefCount > 0)
{
//
// While we were processing the disable request an
// enable request arrived. Since the in progress
// flag was set the enable request was not sent
// so now we need to do that.
Status = WmipSendEnableDisableRequest(IsEvent ?
WMI_ENABLE_EVENTS :
WMI_ENABLE_COLLECTION,
GuidEntry,
IsEvent,
IsTraceLog,
LoggerContext);
RefCount = IsEvent ? NotificationEntry->EventRefCount:
NotificationEntry->CollectRefCount;
if (RefCount == 0)
{
//
// While processing the enable request above the
// notification was disabled and since a request
// was in progress the disable request was not
// forwarded. Now it is time to forward the
// request.
goto DisableNotification;
}
}
NotificationEntry->Flags &= ~InProgressFlag;
WmipDebugPrint(("WMI: %p NE %p flags -> %x at %d\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
__LINE__));
//
// If there are any other threads that were waiting until all of
// the enable/disable work completed, we close the event handle
// to release them from their wait.
//
if ((! IsEvent) && (NotificationEntry->CollectInProgress != NULL))
{
WmipReleaseCollectionEnabled(NotificationEntry);
}
return(Status);
}
ULONG WmipSendDisableRequest(
PNOTIFICATIONENTRY NotificationEntry,
PBGUIDENTRY GuidEntry,
BOOLEAN IsEvent,
BOOLEAN IsTraceLog,
ULONG64 LoggerContext
)
/*++
Routine Description:
This routine will send an disable collection or notification request to
all of the data providers that have registered the guid being disabled.
This routine will manage any race conditions that might occur when
multiple threads are enabling and disabling the notification
simultaneously.
This routine is called while the SM critical section is being held and
will increment the appropriate reference count. if the ref count
transitions from 1 to 0 then the disable request will need to be forwarded
to the data providers otherwise the routine is all done and returns.
Before sending the disable request the routine checks to see if any
enable or disable requests are currently in progress and if not then sets
the in progress flag, releases the critical section and sends the disable
request. If there was a request in progress then the routine does not
send a request, but just returns. When the other thread that was sending
the request returns from processing the request it will recheck the
refcount and notice that it is 0 and then send the disable
request.
Arguments:
NotificationEntry is the Notification entry that describes the guid
being enabled.
GuidEntry is the guid entry that describes the guid being enabled. For
a notification it may be NULL.
NotificationContext is the notification context to use if enabling events
IsEvent is TRUE if notifications are being enables else FALSE if
collection is being enabled
IsTraceLog is TRUE if enable is for a trace log guid
LoggerContext is a context value to forward in the enable request
Return Value:
ERROR_SUCCESS or an error code
--*/
{
ULONG InProgressFlag;
ULONG RefCount;
ULONG Status;
if (IsEvent)
{
InProgressFlag = NE_FLAG_NOTIFICATION_IN_PROGRESS;
RefCount = NotificationEntry->EventRefCount;
if (RefCount == 0)
{
//
// A bad data consumer is disabling his event more
// than once. Just ignore it
return(ERROR_SUCCESS);
}
RefCount = --NotificationEntry->EventRefCount;
} else {
WmipDebugPrint(("WMI: %x Disabling for %p %x\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags));
InProgressFlag = NE_FLAG_COLLECTION_IN_PROGRESS;
RefCount = --NotificationEntry->CollectRefCount;
WmipAssert(RefCount != 0xffffffff);
}
//
// If we have transitioned to a refcount of zero and there is
// not a request in progress then forward the disable request.
if ((RefCount == 0) &&
! (NotificationEntry->Flags & InProgressFlag))
{
//
// Take an extra ref count so that even if this gets
// disabled while the disable request is in progress the
// NotificationEntry will stay valid.
WmipReferenceNE(NotificationEntry);
NotificationEntry->Flags |= InProgressFlag;
WmipDebugPrint(("WMI: %p NE %p flags -> %x at %d\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
__LINE__));
Status = WmipDoDisableRequest(NotificationEntry,
GuidEntry,
IsEvent,
IsTraceLog,
LoggerContext,
InProgressFlag);
//
// Get rid of extra ref count we took above.
WmipUnreferenceNE(NotificationEntry);
} else {
Status = ERROR_SUCCESS;
}
if (! IsEvent)
{
WmipDebugPrint(("WMI: %x Disable complete for %p %x\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags));
}
return(Status);
}
ULONG CollectOrEventWorker(
PDCENTRY DataConsumer,
LPGUID Guid,
BOOLEAN Enable,
BOOLEAN IsEvent,
ULONG *NotificationCookie,
ULONG64 LoggerContext,
ULONG NotificationFlags
)
/*++
Routine Description:
This routine manages enabling and disabling events or collections. It
will update the notification entries and forward requests as appropriate.
Note that this routine will hold a mutex associated with the notification
entry around the call to the data provider to enable or disable in order
to avoid races.
Arguments:
DataConsumer is the DC that is enabling the event or collection
Guid specifies the guid to enable or disable
Enable is TRUE to Enable collection or FALSE to disable collection
IsEvent is TRUE if events are being enabled/disabled or FALSE if
collection is being enabled/disabled
*NotificationCookie on entry has the notification if Enable is TRUE. If
Enable is FALSE then *NotificationCookie returns with the cookie
passed when it was enabled.
LoggerContext is the logger context to use if enabling trace events
NotificationFlags can have one of these values
NOTIFICATION_TRACE_FLAG - enable or disable trace logging
NOTIFICATION_FLAG_CALLBACK_DIRECT - enable or disable events
for direct callback
NOTIFICATION_FLAG_CALLBACK_QUEUED - enable or disable events
for queued callback
NOTIFICATION_WINDOW_HANDLE - enable/disable events for HWND delivery
DCREF_FLAG_ANSI - Caller is enabling ansi events
Return Value:
ERROR_SUCCESS or an error code
--*/
{
PDCREF DcRef, FreeDcRef;
PNOTIFICATIONENTRY NotificationEntry;
ULONG Status;
PBGUIDENTRY GuidEntry;
ULONG DcRefFlags;
BOOLEAN IsTraceLog;
ULONG EventRefCount;
#if DBG
TCHAR s[MAX_PATH];
#endif
WmipAssert(DataConsumer->RpcBindingHandle != 0);
IsTraceLog = ((NotificationFlags & NOTIFICATION_TRACE_FLAG) != 0);
if (IsEvent)
{
DcRefFlags = DCREF_FLAG_NOTIFICATION_ENABLED;
} else {
DcRefFlags = DCREF_FLAG_COLLECTION_ENABLED;
}
GuidEntry = WmipFindGEByGuid(Guid, FALSE);
if (IsTraceLog && !WmipIsControlGuid(GuidEntry))
{
Status = ERROR_INVALID_OPERATION;
goto done;
}
WmipEnterSMCritSection();
NotificationEntry = WmipFindNEByGuid(Guid, FALSE);
if (Enable)
{
if ((GuidEntry == NULL) && (! IsEvent))
{
//
// A guid entry is required in the case of enabling collection
WmipAssert(FALSE);
if (NotificationEntry != NULL)
{
WmipUnreferenceNE(NotificationEntry);
}
WmipLeaveSMCritSection();
return(ERROR_WMI_GUID_NOT_FOUND);
}
if (NotificationEntry == NULL)
{
//
// There is no notification entry setup for this yet. Allocate
// one and add it to the main NE list
NotificationEntry = WmipAllocNotificationEntry();
if (NotificationEntry == NULL)
{
//
// Failed to allocate Notification Entry so clean up
WmipLeaveSMCritSection();
if (GuidEntry != NULL)
{
WmipUnreferenceGE(GuidEntry);
}
return(ERROR_NOT_ENOUGH_MEMORY);
}
//
// Initialize Notification Entry and put it on main list
if (IsTraceLog)
{
NotificationEntry->LoggerContext = LoggerContext;
NotificationEntry->Flags |= NOTIFICATION_TRACE_FLAG;
}
memcpy(&NotificationEntry->Guid, Guid, sizeof(GUID));
InsertTailList(NEHeadPtr,
&NotificationEntry->MainNotificationList);
if (IsTraceLog)
{
//
// No DcRef for trace notification, since we tie it to a logger
//
Status = WmipSendEnableRequest(NotificationEntry,
GuidEntry,
IsEvent,
IsTraceLog,
LoggerContext);
if (GuidEntry != NULL)
{
WmipUnreferenceGE(GuidEntry);
}
WmipLeaveSMCritSection();
return Status;
}
}
else // Notification != NULL
{
Status = ERROR_SUCCESS;
if (IsTraceLog)
{
Status = ERROR_WMI_ALREADY_ENABLED;
}
else if ((NotificationEntry->Flags & NOTIFICATION_TRACE_FLAG) != 0)
{ // prevent enabling existing traced Guid as regular event
Status = ERROR_INVALID_OPERATION;
}
if (Status != ERROR_SUCCESS)
{
//
// if trace is already enabled for this GUID, not do it again
//
WmipUnreferenceNE(NotificationEntry);
if (GuidEntry != NULL)
{
WmipUnreferenceGE(GuidEntry);
}
WmipLeaveSMCritSection();
return Status;
}
}
//
// Now that we have a notification entry fill in the data consumer
// reference
DcRef = WmipFindExistingAndFreeDCRefInNE(NotificationEntry,
DataConsumer,
&FreeDcRef);
if (DcRef != NULL)
{
if (DcRef->Flags & DcRefFlags)
{
if (DcRefFlags & DCREF_FLAG_NOTIFICATION_ENABLED)
{
InterlockedIncrement(&DcRef->EventRefCount);
} else {
//
// If a data consumer is enabling collection while
// collection is already enabled then increment the
// DCEntry refcount, but not the NotificationEntry
// refcount. The latter is only changed when the
// DCEntry is added or removed. This situation could occur
// when many threads in a data consumer are opening
// the same guid at the same time.
WmipAssert(DcRefFlags & DCREF_FLAG_COLLECTION_ENABLED);
DcRef->CollectRefCount++;
if (NotificationEntry->Flags & NE_FLAG_COLLECTION_IN_PROGRESS)
{
WmipDebugPrint(("WMI: %x going to wait for %p %x at %d\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
__LINE__));
WmipWaitForCollectionEnabled(NotificationEntry);
WmipDebugPrint(("WMI: %x done to wait for %p %x at %d\n",
GetCurrentThreadId(),
NotificationEntry,
NotificationEntry->Flags,
__LINE__));
}
}
Status = ERROR_SUCCESS;
if (GuidEntry != NULL)
{
WmipUnreferenceGE(GuidEntry);
}
WmipUnreferenceNE(NotificationEntry);
WmipLeaveSMCritSection();
return(Status);
}
} else if (FreeDcRef != NULL) {
//
// Data consumer is not referenced in NotificationEntry so
// initialize a new data consumer reference block
DcRef = FreeDcRef;
DcRef->DcEntry = DataConsumer;
DcRef->LostEventCount = 0;
} else {
//
// We could not allocate a free DCRef from the notification entry
if (GuidEntry != NULL)
{
WmipUnreferenceGE(GuidEntry);
}
WmipUnreferenceNE(NotificationEntry);
WmipLeaveSMCritSection();
return(ERROR_NOT_ENOUGH_MEMORY);
}
//
// We have a DCRef for this so update it by setting the flags
// bumping the ref count and setting up the notification info
DcRef->Flags |= DcRefFlags;
if (IsEvent)
{
InterlockedIncrement(&DcRef->EventRefCount);
} else {
DcRef->CollectRefCount++;
}
//
// Note WmipSendEnableRequest may not hold critical section through
// the entire call.
Status = WmipSendEnableRequest(NotificationEntry,
GuidEntry,
IsEvent,
IsTraceLog,
LoggerContext);
//
// We leave a reference on the notification entry to account for
// the event being enabled or the first time a collection was enabled.
// The reference will be removed when the event or last collection is
// disabled.
WmipLeaveSMCritSection();
} else {
//
// Here is where we process a disable collection request.
if (NotificationEntry != NULL)
{
if (IsTraceLog)
{
Status = WmipSendDisableRequest(NotificationEntry,
GuidEntry,
IsEvent,
IsTraceLog,
LoggerContext);
WmipUnreferenceNE(NotificationEntry);
WmipAssert(NotificationEntry->RefCount == 1);
WmipUnreferenceNE(NotificationEntry);
if (GuidEntry)
{
WmipUnreferenceGE(GuidEntry);
}
WmipLeaveSMCritSection();
return Status;
}
DcRef = WmipFindDCRefInNE(NotificationEntry,
DataConsumer);
if (DcRef != NULL)
{
//
// Make sure we are disabling something that has been
// previously enabled
if ((IsEvent &&
! (DcRef->Flags & DCREF_FLAG_NOTIFICATION_ENABLED)) ||
( (! IsEvent) &&
! (DcRef->Flags & DCREF_FLAG_COLLECTION_ENABLED)))
{
WmipLeaveSMCritSection();
WmipUnreferenceNE(NotificationEntry);
Status = ERROR_WMI_ALREADY_DISABLED;
goto done;
}
//
// We found the DCRef which is begin disabled. Clear
// flags and possibly entire DCRef
if (! IsEvent)
{
//
// Disable collection
DcRef->CollectRefCount--;
WmipAssert(DcRef->CollectRefCount != (ULONG)-1);
if (DcRef->CollectRefCount > 0)
{
WmipLeaveSMCritSection();
WmipUnreferenceNE(NotificationEntry);
Status = ERROR_SUCCESS;
goto done;
}
//
// We fall through to allow the DCEntry to be cleaned up
// since the CollectionRefCount for the DCEntry has
// reached 0.
} else {
//
// Disable an event
EventRefCount = InterlockedDecrement(&DcRef->EventRefCount);
WmipAssert(EventRefCount != (ULONG)-1);
if (EventRefCount > 0)
{
WmipLeaveSMCritSection();
WmipUnreferenceNE(NotificationEntry);
Status = ERROR_SUCCESS;
goto done;
}
}
WmipUnreferenceNE(NotificationEntry);
//
// All cookies for notification completely disabled so send
// disable events and possibly clean up data structures
DcRef->Flags &= ~DcRefFlags;
if ((DcRef->Flags & (DCREF_FLAG_NOTIFICATION_ENABLED |
DCREF_FLAG_COLLECTION_ENABLED)) == 0)
{
DcRef->DcEntry = NULL;
}
WmipSendDisableRequest(NotificationEntry,
GuidEntry,
IsEvent,
IsTraceLog,
LoggerContext);
//
// We unreference twice: once to account for the reference
// gained above in WmipFindNEByGuid and once to account for
// the reference taken when the event or first collection was
// enabled
WmipUnreferenceNE(NotificationEntry);
Status = ERROR_SUCCESS;
} else {
//
// Attempt to disable an event which has no DcRef
Status = ERROR_WMI_ALREADY_DISABLED;
WmipUnreferenceNE(NotificationEntry);
}
} else {
//
// Attempt to disable an event which has no NotificationEntry
Status = ERROR_WMI_ALREADY_DISABLED;
}
WmipLeaveSMCritSection();
}
done:
if (GuidEntry != NULL)
{
WmipUnreferenceGE(GuidEntry);
}
return(Status);
}
ULONG CollectionControl(
/* [in]*/ DCCTXHANDLE DcCtxHandle,
/* [in] */ LPGUID Guid,
/* [in] */ BOOLEAN Enable
)
/*++
Routine Description:
This routine manages enabling and disabling collection of data for those
guids that are marked as expensive to collect.
Arguments:
DcCtxHandle is the data consumer context handle
Guid specifies the guid to enable collection
Enable is TRUE to Enable collection or FALSE to disable collection
Return Value:
ERROR_SUCCESS or an error code
--*/
{
if (! VERIFY_DCCTXHANDLE(DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
return(CollectOrEventWorker(
(PDCENTRY)DcCtxHandle,
Guid,
Enable,
FALSE,
NULL,
0,
0));
}
ULONG NotificationControl(
/* [in]*/ DCCTXHANDLE DcCtxHandle,
/* [in] */ LPGUID Guid,
/* [in] */ BOOLEAN Enable,
/* [in, out] */ ULONG *NotificationCookie,
/* [in] */ ULONG64 LoggerContext,
/* [in] */ ULONG NotificationFlags
)
/*++
Routine Description:
This routine manages enabling and disabling of event generation for a
guid.
Arguments:
Guid specifies the guid to enable event generation
Enable is TRUE to Enable collection or FALSE to disable event generation
NotificationCookie is the cookie that represents a notification callback
context on the client side
LoggerContext is the logger context handle for calls enabling a trace
logger
NotificationFlags specify the type of notification (event or callback)
Return Value:
ERROR_SUCCESS or an error code
--*/
{
ULONG Status;
#ifndef MEMPHIS
ULONG IsTraceLog;
#endif
if (! VERIFY_DCCTXHANDLE(DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
#ifndef MEMPHIS
//
// RegChangeNotificationGuid does not need to have its access checked
// since it is used for registration notifications back to the data
// consumers and we do not want to restrict it in any way.
IsTraceLog = ((NotificationFlags & NOTIFICATION_TRACE_FLAG) != 0);
if ((!IsEqualGUID(Guid, &RegChangeNotificationGuid)) ||
(IsTraceLog))
{
Status = RpcImpersonateClient(0);
if (Status == ERROR_SUCCESS)
{
if (IsTraceLog)
{
Status = WmipCheckGuidAccess(Guid, TRACELOG_GUID_ENABLE);
}
else
{
Status = WmipCheckGuidAccess(Guid, WMIGUID_NOTIFICATION);
}
RpcRevertToSelf();
}
if (Status != ERROR_SUCCESS)
{
return(Status);
}
}
#endif
Status = CollectOrEventWorker(
(PDCENTRY)DcCtxHandle,
Guid,
Enable,
TRUE,
NotificationCookie,
LoggerContext,
NotificationFlags);
return(Status);
}
void __RPC_USER DCCTXHANDLE_rundown(
DCCTXHANDLE DcCtxHandle
)
/*++
Routine Description:
This is the rundown routine for a DC context handle. Whenever a data
consumer goes away unexpectedly we need to clean up any events or
collections that he has left enabled
Arguments:
DcCtxHandle is the Data consumer context handle
Return Value:
ERROR_SUCCESS or an error code
--*/
{
#if DBG
BOOLEAN NotificationsEnabled, CollectionsEnabled;
#endif
WmipCleanupDataConsumer((PDCENTRY)DcCtxHandle
#if DBG
,&NotificationsEnabled,
&CollectionsEnabled
#endif
);
}
ULONG RegisterGuids(
/* [out] */ DPCTXHANDLE __RPC_FAR *DpCtxHandle,
/* [string][in] */ TCHAR __RPC_FAR *RpcBinding,
/* [in] */ ULONG RequestCookie,
/* [out][in] */ ULONG __RPC_FAR *GuidCount,
/* [size_is][size_is][out][in] */ PTRACEGUIDMAP __RPC_FAR *GuidMap,
/* [in] */ LPCWSTR ImagePath,
/* [in] */ ULONG Size,
/* [size_is][in] */ BYTE __RPC_FAR *WmiRegInfo,
/* [out] */ ULONG64 *LoggerContext)
{
ULONG Status;
ULONG_PTR ProviderId = 0;
PWMIREGINFOW RegInfo = (PWMIREGINFOW)WmiRegInfo;
WMIREGGUIDW UNALIGNED64 *RegGuid = RegInfo->WmiRegGuid;
PTRACEGUIDMAP pGuidMap = *GuidMap;
PBGUIDENTRY GuidEntry;
PNOTIFICATIONENTRY NotificationEntry;
ULONG i;
#ifndef MEMPHIS
if (! WmipIsRpcClientLocal(NULL))
{
*DpCtxHandle = NULL;
return(ERROR_ACCESS_DENIED);
}
#endif
*LoggerContext = 0;
Status = WmipAddDataSource(
RpcBinding,
RequestCookie,
0,
(LPTSTR)ImagePath,
RegInfo,
Size,
&ProviderId,
FALSE);
if (Status == ERROR_SUCCESS)
{
PBDATASOURCE DataSource;
PBINSTANCESET InstanceSet;
DataSource = WmipFindDSByProviderId(ProviderId);
if (DataSource == NULL)
{
WmipAssert(FALSE);
*DpCtxHandle = NULL;
*GuidCount = 0;
return (ERROR_WMI_GUID_NOT_FOUND);
}
//
// Establish binding to Data provider
Status = WmipBindToWmiClient(DataSource->BindingString,
&DataSource->RpcBindingHandle);
if (Status == ERROR_SUCCESS)
{
*DpCtxHandle = (DPCTXHANDLE)DataSource;
//
// If the Registration was successful then find the
// the GuidEntry for these Trace Guids and return them as well.
//
*GuidCount = RegInfo->GuidCount;
for (i = 0; i < *GuidCount; i++, RegGuid++, pGuidMap++)
{
InstanceSet = WmipFindISByGuid( DataSource, &RegGuid->Guid );
if (InstanceSet == NULL)
{
WmipUnreferenceDS(DataSource);
return( ERROR_WMI_GUID_NOT_FOUND );
}
pGuidMap->Guid = RegGuid->Guid;
pGuidMap->GuidMapHandle = (ULONG_PTR)InstanceSet;
WmipUnreferenceIS(InstanceSet);
}
WmipUnreferenceDS(DataSource);
//
// Find out if this Guid is currently Enabled. If so find its
// LoggerContext
//
RegGuid = RegInfo->WmiRegGuid;
NotificationEntry = WmipFindNEByGuid(&RegGuid->Guid, FALSE);
if (NotificationEntry != NULL)
{
if ((NotificationEntry->Flags & NOTIFICATION_TRACE_FLAG) != 0)
{
*LoggerContext = NotificationEntry->LoggerContext;
}
WmipUnreferenceNE(NotificationEntry);
}
} else {
//
// Unreference twice so that it is removed from the DS lists
WmipUnreferenceDS(DataSource);
WmipUnreferenceDS(DataSource);
*DpCtxHandle = NULL;
*GuidCount = 0;
}
} else {
*DpCtxHandle = NULL;
*GuidCount = 0;
}
return(Status);
}
ULONG UnregisterGuids(
/* [in, out] */ DPCTXHANDLE *DpCtxHandle,
/* [in] */ LPGUID Guid,
/* [out] */ ULONG64 *LoggerContext
)
{
PBDATASOURCE DataSource = *DpCtxHandle;
ULONG Status;
if (VERIFY_DPCTXHANDLE(*DpCtxHandle))
{
PNOTIFICATIONENTRY NotificationEntry;
*LoggerContext = 0;
WmipRemoveDataSourceByDS(DataSource);
*DpCtxHandle = NULL;
//
// Check to see if this GUID got disabled in the middle
// of Unregister Call. If so, send the LoggerContext back
//
NotificationEntry = WmipFindNEByGuid(Guid, FALSE);
if (NotificationEntry != NULL)
{
if ((NotificationEntry->Flags & NOTIFICATION_TRACE_FLAG) != 0)
{
*LoggerContext = NotificationEntry->LoggerContext;
}
WmipUnreferenceNE(NotificationEntry);
}
Status = ERROR_SUCCESS;
} else {
WmipDebugPrint(("WMI: Invalid DPCTXHANDLE %x\n", *DpCtxHandle));
Status = ERROR_INVALID_PARAMETER;
}
return(Status);
}
void __RPC_USER DPCTXHANDLE_rundown(
DPCTXHANDLE DpCtxHandle
)
{
WmipDebugPrint(("WMI: DP Context rundown for %x\n", DpCtxHandle));
WmipRemoveDataSource((ULONG_PTR)DpCtxHandle);
}
ULONG WmipUniqueEndpointIndex;
ULONG GetUniqueEndpointIndex(
void
)
/*++
Routine Description:
This routine will provide a mostly unique index that data consumers
and data providers use to create a unique RPC endpoint.
Arguments:
Return Value:
A mostly unique endpoint index or 0 in case of failure.
--*/
{
ULONG Index;
#ifndef MEMPHIS
if (! WmipIsRpcClientLocal(NULL))
{
return(0);
}
#endif
WmipEnterSMCritSection();
if (++WmipUniqueEndpointIndex == 0)
{
//
// If we have wrapped around we want to avoid returning 0
// as that indicates failure.
WmipUniqueEndpointIndex = 1;
}
Index = WmipUniqueEndpointIndex;
WmipLeaveSMCritSection();
return(Index);
}
ULONG
GetGuidPropertiesFromGuidEntry(
PWMIGUIDPROPERTIES GuidInfo,
PGUIDENTRY GuidEntry)
/*++
Routine Description:
This routine fills GuidInfo with the properties for the Guid
represented by the GuidEntry. Note that this call is made holding
the SMCritSection.
Arguments:
Return Value:
ERROR_SUCCESS or an error code
--*/
{
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
PNOTIFICATIONENTRY NotificationEntry;
GuidInfo->GuidType = WMI_GUIDTYPE_DATA;
GuidInfo->IsEnabled = FALSE;
GuidInfo->LoggerId = 0;
GuidInfo->EnableLevel = 0;
GuidInfo->EnableFlags = 0;
InstanceSetList = GuidEntry->ISHead.Flink;
while (InstanceSetList != &GuidEntry->ISHead)
{
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
if (InstanceSet->Flags & IS_EVENT_ONLY)
{
GuidInfo->GuidType = WMI_GUIDTYPE_EVENT;
}
if (((InstanceSet->Flags & IS_ENABLE_EVENT) ||
(InstanceSet->Flags & IS_ENABLE_COLLECTION)) ||
(InstanceSet->Flags & IS_COLLECTING))
{
GuidInfo->IsEnabled = TRUE;
}
if ( (InstanceSet->Flags & IS_TRACED) &&
(InstanceSet->Flags & IS_CONTROL_GUID) )
{
GuidInfo->GuidType = WMI_GUIDTYPE_TRACECONTROL;
break;
}
InstanceSetList = InstanceSetList->Flink;
}
NotificationEntry = WmipFindNEByGuid(&GuidEntry->Guid, FALSE);
if (NotificationEntry != NULL)
{
if (GuidInfo->GuidType == WMI_GUIDTYPE_TRACECONTROL) {
//
// If a NotificationEntry is found for a TraceControlGuid
// it means that it is enabled.
//
ULONG64 LoggerContext = NotificationEntry->LoggerContext;
GuidInfo->IsEnabled = TRUE;
GuidInfo->LoggerId = WmipGetLoggerId(LoggerContext);
GuidInfo->EnableLevel = WmipGetLoggerEnableLevel(LoggerContext);
GuidInfo->EnableFlags = WmipGetLoggerEnableFlags(LoggerContext);
}
WmipUnreferenceNE(NotificationEntry);
}
return ERROR_SUCCESS;
}
ULONG EnumerateGuids(
/* [in] */ DCCTXHANDLE DcCtxHandle,
/* [in] */ ULONG MaxGuidCount,
/* [in] */ ULONG MaxGuidInfoCount,
/* [out] */ ULONG __RPC_FAR *TotalGuidCount,
/* [out] */ ULONG __RPC_FAR *GuidCount,
/* [out] */ ULONG __RPC_FAR *GuidInfoCount,
/* [length_is][size_is][out] */ LPGUID GuidList,
/* [length_is][size_is][out] */ PWMIGUIDPROPERTIES GuidInfo)
{
ULONG i, i1;
PGUIDENTRY GuidEntry;
PLIST_ENTRY GuidEntryList;
if (! VERIFY_DCCTXHANDLE(DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
WmipEnterSMCritSection();
GuidEntryList = GEHeadPtr->Flink;
i = 0;
i1 = 0;
while(GuidEntryList != GEHeadPtr)
{
GuidEntry = CONTAINING_RECORD(GuidEntryList,
GUIDENTRY,
MainGEList);
if (! (GuidEntry->Flags & GE_FLAG_INTERNAL))
{
if ((i++ < MaxGuidCount))
{
*GuidList = GuidEntry->Guid;
GuidList++;
if (i1++ < MaxGuidInfoCount)
{
GetGuidPropertiesFromGuidEntry(GuidInfo, GuidEntry);
GuidInfo++;
}
}
}
GuidEntryList = GuidEntryList->Flink;
}
WmipLeaveSMCritSection();
*TotalGuidCount = i;
*GuidCount = i1;
*GuidInfoCount = (i1 > MaxGuidInfoCount) ? MaxGuidInfoCount: i1;
return( (i <= MaxGuidCount) ? ERROR_SUCCESS : ERROR_MORE_DATA);
}
ULONG GetMofResource(
/* [in] */ DCCTXHANDLE DcCtxHandle,
/* [out] */ ULONG *MofResourceCount,
/* [out, sizeis(, *MofResourceCount)] */ PMOFRESOURCEINFOW *MofResourceInfo
)
{
ULONG Status;
PMOFRESOURCEINFOW MRInfo;
ULONG MRCount;
ULONG i;
PLIST_ENTRY MofResourceList;
PMOFRESOURCE MofResource;
if (! VERIFY_DCCTXHANDLE(DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
//
// TODO: Restrict this call to only admins or the LocalSystem Account
// It is really only for WBEM
*MofResourceInfo = NULL;
*MofResourceCount = 0;
//
// Looking for the entire list of Mof resources
MRCount = 0;
WmipEnterSMCritSection();
MofResourceList = MRHeadPtr->Flink;
while (MofResourceList != MRHeadPtr)
{
MofResource = CONTAINING_RECORD(MofResourceList,
MOFRESOURCE,
MainMRList);
MRCount++;
MofResourceList = MofResourceList->Flink;
}
MRInfo = midl_user_allocate(MRCount * sizeof(MOFRESOURCEINFOW));
if (MRInfo != NULL)
{
MofResourceList = MRHeadPtr->Flink;
i = 0;
while (MofResourceList != MRHeadPtr)
{
MofResource = CONTAINING_RECORD(MofResourceList,
MOFRESOURCE,
MainMRList);
WmipAssert(i < MRCount);
MRInfo[i].ImagePath = MofResource->MofImagePath;
MRInfo[i].ResourceName = MofResource->MofResourceName;
MRInfo[i].ResourceSize = 0;
MRInfo[i].ResourceBuffer = NULL;
i++;
MofResourceList = MofResourceList->Flink;
}
*MofResourceInfo = MRInfo;
*MofResourceCount = MRCount;
Status = ERROR_SUCCESS;
} else {
Status = ERROR_NOT_ENOUGH_MEMORY;
}
WmipLeaveSMCritSection();
return(Status);
}
void __RPC_FAR * __RPC_USER WmipMidlUserAllocate(size_t len)
{
return(WmipAlloc(len));
}
void __RPC_USER WmipMidlUserFree(void __RPC_FAR * ptr)
{
WmipFree(ptr);
}
#ifndef MEMPHIS
ULONG EnumerateTraceGuidMap(
/* [in] */ DCCTXHANDLE DcCtxHandle,
/* [in] */ ULONG MaxGuidCount,
/* [out] */ ULONG __RPC_FAR *TotalGuidCount,
/* [out] */ ULONG __RPC_FAR *GuidCount,
/* [length_is][size_is][out] */ PTRACEGUIDMAP GuidList)
{
ULONG i, i1;
PGUIDENTRY GuidEntry;
PLIST_ENTRY GuidEntryList;
PLIST_ENTRY GuidMapEntryList;
PGUIDMAPENTRY GuidMapEntry;
if (! VERIFY_DCCTXHANDLE(DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
WmipEnterSMCritSection();
GuidEntryList = GEHeadPtr->Flink;
i = 0;
i1 = 0;
while(GuidEntryList != GEHeadPtr)
{
GuidEntry = CONTAINING_RECORD(GuidEntryList,
GUIDENTRY,
MainGEList);
if ( !(GuidEntry->Flags & GE_FLAG_INTERNAL) )
// && (WmipIsItaTraceGuid(GuidEntry) ) )
{
PLIST_ENTRY InstanceSetList;
PBINSTANCESET InstanceSet;
if (GuidEntry != NULL)
{
InstanceSetList = GuidEntry->ISHead.Flink;
while (InstanceSetList != &GuidEntry->ISHead)
{
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
if (InstanceSet->Flags & IS_TRACED)
{
if ((i++ < MaxGuidCount) && (GuidList != NULL))
{
GuidList->Guid = GuidEntry->Guid;
GuidList->GuidMapHandle = (ULONG_PTR)InstanceSet;
GuidList++;
i1++;
}
}
InstanceSetList = InstanceSetList->Flink;
}
}
}
GuidEntryList = GuidEntryList->Flink;
}
//
// TODO: Need to walk through the GMHead list as well to list the
// Guids that have been UnRegistered
GuidMapEntryList = GMHeadPtr->Flink;
while(GuidMapEntryList != GMHeadPtr)
{
GuidMapEntry = CONTAINING_RECORD(GuidMapEntryList,
GUIDMAPENTRY,
Entry);
if ((i++ < MaxGuidCount) && (GuidList != NULL))
{
*GuidList = GuidMapEntry->GuidMap;
GuidList++; i1++;
}
GuidMapEntryList = GuidMapEntryList->Flink;
}
WmipLeaveSMCritSection();
*TotalGuidCount = i;
*GuidCount = i1;
return( (i <= MaxGuidCount) ? ERROR_SUCCESS : ERROR_MORE_DATA);
}
ULONG
WmipDisableTraceWorkItem(
PVOID pContext
)
{
PLIST_ENTRY NEList;
PNOTIFICATIONENTRY NotificationEntry = (PNOTIFICATIONENTRY) pContext;
PBGUIDENTRY GuidEntry;
ULONG status;
PLIST_ENTRY GuidMapEntryList;
PGUIDMAPENTRY GuidMapEntry;
ULONG LoggerId = WmipGetLoggerId(NotificationEntry->LoggerContext);
WmipEnterSMCritSection();
GuidEntry = WmipFindGEByGuid(&NotificationEntry->Guid, FALSE);
status = WmipSendDisableRequest(NotificationEntry,
GuidEntry,
TRUE,
TRUE,
NotificationEntry->LoggerContext);
if (GuidEntry)
WmipUnreferenceGE(GuidEntry);
GuidMapEntryList = GMHeadPtr->Flink;
while(GuidMapEntryList != GMHeadPtr)
{
GuidMapEntry = CONTAINING_RECORD(GuidMapEntryList,
GUIDMAPENTRY,
Entry);
GuidMapEntryList = GuidMapEntryList->Flink;
if (WmipGetLoggerId(GuidMapEntry->LoggerContext) == LoggerId ) {
RemoveEntryList(&GuidMapEntry->Entry);
WmipFree(GuidMapEntry);
}
}
WmipUnreferenceNE(NotificationEntry); // One taken for the work item
WmipUnreferenceNE(NotificationEntry); // One from trace enable
WmipLeaveSMCritSection();
return ERROR_SUCCESS;
}
ULONG
WmipServiceDisableTraceProviders(
PWNODE_HEADER pWnode
)
{
PLIST_ENTRY NEList;
PNOTIFICATIONENTRY NotificationEntry;
PBGUIDENTRY GuidEntry;
ULONG status;
PLIST_ENTRY GuidMapEntryList;
PGUIDMAPENTRY GuidMapEntry;
ULONG LoggerId;
PTRACE_ENABLE_CONTEXT pContext;
if (pWnode->BufferSize >= (sizeof(WNODE_HEADER) + sizeof(ULONG)))
{
ULONG64 LoggerContext = pWnode->HistoricalContext;
ULONG Status = * ((ULONG *)
(((PUCHAR) pWnode) + sizeof(WNODE_HEADER)));
if (Status != STATUS_LOG_FILE_FULL)
return ERROR_SUCCESS;
LoggerId = WmipGetLoggerId(LoggerContext);
pContext = (PTRACE_ENABLE_CONTEXT) &LoggerContext;
if ((LoggerId == KERNEL_LOGGER_ID) ||
(LoggerId == 0)) {
return ERROR_INVALID_HANDLE;
}
WmipEnterSMCritSection();
NEList = NEHeadPtr->Flink;
while (NEList != NEHeadPtr)
{
NotificationEntry = CONTAINING_RECORD(NEList,
NOTIFICATIONENTRY,
MainNotificationList);
NEList = NEList->Flink;
if (pContext->InternalFlag & EVENT_TRACE_INTERNAL_FLAG_PRIVATE)
{
continue;
}
if ((NotificationEntry->Flags & NOTIFICATION_TRACE_FLAG)
&& ((NotificationEntry->Flags & NE_FLAG_TRACEDISABLE_IN_PROGRESS)
!= NE_FLAG_TRACEDISABLE_IN_PROGRESS)
&& (WmipGetLoggerId(NotificationEntry->LoggerContext)
== LoggerId)) {
NotificationEntry->Flags |= NE_FLAG_TRACEDISABLE_IN_PROGRESS;
// Take an extra ref count on the Notification Entry
WmipReferenceNE(NotificationEntry);
// Call the Work item
QueueUserWorkItem (WmipDisableTraceWorkItem,
(PVOID)NotificationEntry,
WT_EXECUTELONGFUNCTION);
}
}
WmipLeaveSMCritSection();
}
return ERROR_SUCCESS;
}
ULONG
DisableTraceProviders(
/* [in] */ DCCTXHANDLE DcCtxHandle,
/* [in] */ LPGUID Guid,
/* [in] */ ULONG64 LoggerContext
)
/*++
Routine Description:
This routine is called immediately after a logger has been stopped
successfully to clean up all the existing Notification Entries
enabled to that logger.
Arguments:
LoggerContext
Return Value:
ERROR_SUCCESS
--*/
{
PLIST_ENTRY NEList;
PNOTIFICATIONENTRY NotificationEntry;
PBGUIDENTRY GuidEntry;
ULONG status;
PLIST_ENTRY GuidMapEntryList;
PGUIDMAPENTRY GuidMapEntry;
ULONG LoggerId = WmipGetLoggerId(LoggerContext);
PTRACE_ENABLE_CONTEXT pContext = (PTRACE_ENABLE_CONTEXT) &LoggerContext;
if (! VERIFY_DCCTXHANDLE(DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
if ((LoggerId == KERNEL_LOGGER_ID) ||
(LoggerId == 0)) {
return ERROR_INVALID_HANDLE;
}
WmipEnterSMCritSection();
NEList = NEHeadPtr->Flink;
while (NEList != NEHeadPtr)
{
NotificationEntry = CONTAINING_RECORD(NEList,
NOTIFICATIONENTRY,
MainNotificationList);
NEList = NEList->Flink;
if (pContext->InternalFlag & EVENT_TRACE_INTERNAL_FLAG_PRIVATE)
{
//
// If this is a PrivateLoggerHandle, we must compare the Guid
// to determine the right Trace Provider.
//
if ( !IsEqualGUID(Guid, & NotificationEntry->Guid ) )
{
continue;
}
}
if ((NotificationEntry->Flags & NOTIFICATION_TRACE_FLAG)
&& WmipGetLoggerId(NotificationEntry->LoggerContext)
== LoggerId) {
GuidEntry = WmipFindGEByGuid(&NotificationEntry->Guid, FALSE);
status = WmipSendDisableRequest(NotificationEntry,
GuidEntry,
TRUE,
TRUE,
LoggerContext);
if (GuidEntry)
WmipUnreferenceGE(GuidEntry);
GuidMapEntryList = GMHeadPtr->Flink;
while(GuidMapEntryList != GMHeadPtr)
{
GuidMapEntry = CONTAINING_RECORD(GuidMapEntryList,
GUIDMAPENTRY,
Entry);
GuidMapEntryList = GuidMapEntryList->Flink;
if (WmipGetLoggerId(GuidMapEntry->LoggerContext) == LoggerId) {
RemoveEntryList(&GuidMapEntry->Entry);
WmipFree(GuidMapEntry);
}
}
WmipUnreferenceNE(NotificationEntry);
}
}
WmipLeaveSMCritSection();
return ERROR_SUCCESS;
}
ULONG UmLogRequest(
/* [in] */ DCCTXHANDLE DcCtxHandle,
/* [in] */ ULONG RequestCode,
/* [in] */ ULONG WnodeSize,
/* [out][in] */ ULONG __RPC_FAR *SizeUsed,
/* [out] */ ULONG __RPC_FAR *SizeNeeded,
/* [length_is][size_is][out][in] */ BYTE __RPC_FAR *Wnode)
/*++
Routine Description:
This routine routes a user mode private logger creation request to the
appropriate datasource (Trace Provider of the specified Control Guid).
If multiple datasources exist for this Guid, this will result in a
logger created in each one. The caller must have LOGGER_CREATION rights
on the Guid.
Arguments:
DcCtxHandle - data consumer context handle
RequestCode - for Start, Stop or Query Logger
WnodeSize - size of data block to be passed on to the Provider
*SizeUsed - returns the size used for return data
*SizeNeeded - returns the size needed for return data
Wnode - Data Block containing the WMI_LOGGER_INFORMATION.
Return Value:
ERROR_SUCCESS or an error code
--*/
{
#if DBG
#define AVGISPERGUID 1
#else
#define AVGISPERGUID 64
#endif
LPGUID Guid;
PLIST_ENTRY InstanceSetList;
PBGUIDENTRY GuidEntry;
PBINSTANCESET InstanceSet;
PBDATASOURCE DataSourceArray[AVGISPERGUID];
PBDATASOURCE *DataSourceList;
PBDATASOURCE DataSource;
PBINSTANCESET ISArray[AVGISPERGUID];
PBINSTANCESET *ISList;
PBYTE LoggerInfo = Wnode;
ULONG Status;
ULONG i;
ULONG DSCount = 0;
ULONG RequiredSize = 0;
ULONG RemainingSize = 0;
ULONG SizePassedIn = 0;
ULONG SizePassMax = sizeof(WMI_LOGGER_INFORMATION)
+ 2 * MAXSTR * sizeof(WCHAR);
ULONG SizeFilled = 0;
ULONG lRequestCode = RequestCode;
BOOL fEnabledOnly = (lRequestCode == TRACELOG_QUERYENABLED);
PBYTE WnodeTemp = NULL;
if (fEnabledOnly) {
lRequestCode = TRACELOG_QUERYALL;
}
if (! VERIFY_DCCTXHANDLE(DcCtxHandle))
{
WmipDebugPrint(("WMI: Invalid DCCTXHANDLE %x\n", DcCtxHandle));
return(ERROR_INVALID_PARAMETER);
}
if ((WnodeSize < sizeof(WMI_LOGGER_INFORMATION)) ||
(WnodeSize < ((PWNODE_HEADER)Wnode)->BufferSize)) {
return ERROR_INVALID_PARAMETER;
}
Guid = &((PWNODE_HEADER)Wnode)->Guid;
//
// Check to see if the client has access to start logger on this Guid
//
Status = RpcImpersonateClient(0);
if (Status == ERROR_SUCCESS)
{
if (lRequestCode != TRACELOG_QUERYALL) {
Status = WmipCheckGuidAccess(Guid, TRACELOG_CREATE_INPROC);
}
RpcRevertToSelf();
}
if (Status != ERROR_SUCCESS)
{
return(Status);
}
if (RequestCode == TRACELOG_UPDATE) {
WnodeTemp = WmipAlloc(WnodeSize);
if (WnodeTemp == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
RtlCopyMemory(WnodeTemp, Wnode, WnodeSize);
}
WmipEnterSMCritSection();
if (lRequestCode == TRACELOG_QUERYALL) {
PLIST_ENTRY GENext = GEHeadPtr->Flink;
ULONG DSSize = AVGISPERGUID;
BOOLEAN fDSListFull = FALSE;
DataSourceList = WmipAlloc(DSSize * sizeof(PBDATASOURCE));
if (DataSourceList == NULL) {
WmipLeaveSMCritSection();
goto cleanup;
}
while (GENext != GEHeadPtr) {
if (fDSListFull)
break;
GuidEntry = CONTAINING_RECORD(GENext, GUIDENTRY, MainGEList);
WmipReferenceGE(GuidEntry);
if (fEnabledOnly) {
PNOTIFICATIONENTRY NotificationEntry =
WmipFindNEByGuid(& GuidEntry->Guid, FALSE);
PTRACE_ENABLE_CONTEXT pContext;
if (NotificationEntry == NULL) {
goto GetNextGuidEntry;
}
pContext = (PTRACE_ENABLE_CONTEXT)
& NotificationEntry->LoggerContext;
if (! pContext->InternalFlag & EVENT_TRACE_INTERNAL_FLAG_PRIVATE){
WmipUnreferenceNE(NotificationEntry);
goto GetNextGuidEntry;
}
WmipUnreferenceNE(NotificationEntry);
}
try {
InstanceSetList = GuidEntry->ISHead.Flink;
while (InstanceSetList != & GuidEntry->ISHead) {
if (fDSListFull) {
goto GetNextGuidEntry;
}
InstanceSet = CONTAINING_RECORD(
InstanceSetList, INSTANCESET, GuidISList);
if ( ( (InstanceSet->Flags & IS_TRACED)
&& (InstanceSet->Flags & IS_CONTROL_GUID))
&& ( (InstanceSet->Flags & IS_ENABLE_EVENT)
|| (InstanceSet->Flags & IS_ENABLE_COLLECTION)
|| (InstanceSet->Flags & IS_COLLECTING))
&& !(InstanceSet->DataSource->Flags & DS_KERNEL_MODE))
{
DataSourceList[DSCount] = InstanceSet->DataSource;
WmipReferenceDS(DataSourceList[DSCount]);
DSCount ++;
if (DSCount >= DSSize) {
if (!WmipRealloc((PVOID *) & DataSourceList,
DSSize * sizeof(PBDATASOURCE),
2 * DSSize * sizeof(PBDATASOURCE),
TRUE))
{
fDSListFull = TRUE;
}
DSSize = 2 * DSSize;
}
}
InstanceSetList = InstanceSetList->Flink;
}
}
except (EXCEPTION_EXECUTE_HANDLER) {
WmipDebugPrint(("WMI: UmLogRequest() threw exception %d, Bogus GuidEntry.\n",
GetExceptionCode()));
}
GetNextGuidEntry:
WmipUnreferenceGE(GuidEntry);
GENext = GENext->Flink;
}
GuidEntry = NULL;
lRequestCode = TRACELOG_QUERY;
}
else {
// From the ControlGuid we get the data source ...
//
GuidEntry = WmipFindGEByGuid(Guid, FALSE);
if (!WmipIsControlGuid(GuidEntry))
{
Status = ERROR_INVALID_OPERATION;
WmipLeaveSMCritSection();
goto cleanup;
}
// NOTE: If there are multiple DataSources for the Guid, we will end up
// creating a logger in each Provider with this.
//
// First we make a list of all of the DataSources that need to be called
// while we have the critical section and take a reference on them so
// they don't go away after we release them. Note that the DataSource
// structure will stay, but the actual data provider may in fact go away.
// In this case sending the request will fail.
DSCount = 0;
if (GuidEntry->ISCount > AVGISPERGUID)
{
DataSourceList = WmipAlloc(GuidEntry->ISCount * sizeof(PBDATASOURCE));
if (DataSourceList == NULL)
{
WmipLeaveSMCritSection();
goto cleanup;
}
} else {
DataSourceList = &DataSourceArray[0];
}
#if DBG
memset(DataSourceList, 0, GuidEntry->ISCount * sizeof(PBDATASOURCE));
#endif
InstanceSetList = GuidEntry->ISHead.Flink;
while ((InstanceSetList != &GuidEntry->ISHead) &&
(DSCount < GuidEntry->ISCount))
{
WmipAssert(DSCount < GuidEntry->ISCount);
InstanceSet = CONTAINING_RECORD(InstanceSetList,
INSTANCESET,
GuidISList);
DataSource = InstanceSet->DataSource;
if ( ((InstanceSet->Flags & IS_TRACED) &&
(InstanceSet->Flags & IS_CONTROL_GUID)) &&
!(DataSource->Flags & DS_KERNEL_MODE) )
{
DataSourceList[DSCount] = DataSource;
WmipReferenceDS(DataSourceList[DSCount]);
DSCount++;
}
InstanceSetList = InstanceSetList->Flink;
}
}
WmipLeaveSMCritSection();
// Now without the critical section we send the request to all of the
// data providers. Any new data providers who register after we made our
// list will be enabled by the registration code.
((PWMI_LOGGER_INFORMATION)Wnode)->InstanceCount = DSCount;
*SizeNeeded = 0;
*SizeUsed = 0;
RemainingSize = WnodeSize;
if (DSCount > 0)
{
for (i = 0; i < DSCount; i++)
{
if (RequestCode == TRACELOG_UPDATE) {
RtlCopyMemory(Wnode, WnodeTemp, WnodeSize);
((PWMI_LOGGER_INFORMATION)Wnode)->InstanceCount = DSCount;
}
DataSource = DataSourceList[i];
SizePassedIn = (RemainingSize > SizePassMax)
? SizePassMax : RemainingSize;
SizeFilled = SizePassedIn;
((PWNODE_HEADER) LoggerInfo)->BufferSize = SizePassedIn;
WmipAssert(DataSource != NULL);
((PWMI_LOGGER_INFORMATION)Wnode)->InstanceId = i;
((PWNODE_HEADER)Wnode)->ClientContext = DataSource->RequestAddress;
if (!(DataSource->Flags & DS_KERNEL_MODE))
{
Status = WmipRestrictToken(WmipRestrictedToken);
if (Status == ERROR_SUCCESS)
{
try
{
Status = WmipClient_LoggerCreation(
DataSource->RpcBindingHandle,
lRequestCode,
SizePassedIn,
&SizeFilled,
&RequiredSize,
LoggerInfo
);
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
WmipDebugPrint(("WMI: UmLogCreate threw exception %d\n",
Status));
Status = ERROR_WMI_DP_FAILED;
}
WmipUnrestrictToken();
if (Status == ERROR_SUCCESS)
{
WmipEnterSMCritSection();
InstanceSet = WmipFindISInDSByGuid(DataSource, Guid);
WmipLeaveSMCritSection();
if (InstanceSet != NULL)
{
if (lRequestCode == TRACELOG_START)
{
InstanceSet->Flags |= IS_COLLECTING;
}
else if (lRequestCode == TRACELOG_STOP)
{
InstanceSet->Flags &= ~IS_COLLECTING;
}
WmipUnreferenceIS(InstanceSet);
}
//
// If querying multiple providers we return
// properties from each one.
//
if (lRequestCode == TRACELOG_QUERY) {
*SizeUsed += RequiredSize;
if (*SizeUsed > WnodeSize) {
*SizeUsed = WnodeSize;
}
if (RemainingSize > RequiredSize) {
LoggerInfo += RequiredSize;
RemainingSize -= RequiredSize;
}
}
else {
*SizeUsed = RequiredSize;
}
}
*SizeNeeded += RequiredSize;
}
}
WmipUnreferenceDS(DataSource);
}
}
if (* SizeUsed > 0) {
Status = ERROR_SUCCESS;
}
if (DataSourceList != DataSourceArray)
{
WmipFree(DataSourceList);
}
cleanup:
if (WnodeTemp != NULL) {
WmipFree(WnodeTemp);
}
if (GuidEntry != NULL)
{
WmipUnreferenceGE(GuidEntry);
}
return Status;
}
#endif