/*++ 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 #ifndef MEMPHIS #include #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