//****************************************************************************** // // Microsoft Confidential. Copyright (c) Microsoft Corporation 1999. All rights reserved // // File: Collect.cpp // // Description: Support for Namespace Garbage Collection // // History: 12-01-99 leonardm Created // //****************************************************************************** #include "uenv.h" #include "collect.h" #include "..\rsoputil\smartptr.h" #include "..\rsoputil\rsoputil.h" #include "..\rsoputil\wbemtime.h" //****************************************************************************** // // Function: GetMinutesElapsed // // Description: Returns the number of minutes elapsed between a time represented // in a BSTR in WBEM format and the present time. // It expects a string in WBEM datetime format: "yyyymmddhhmmss.000000+000" // where yyyy=year, mm=month, dd=day, hh=hour, mm=minutes, ss=seconds // // // Parameters: xbstrOldTime - Reference to XBStr representing the time from which // calculate the time span. // // pMinutesElapsed - Pointer to a ULONG that receives the minutes elapsed // between xbstrOldTime and the present time. // // Return: S_OK on success. An HRESULT error code on failure. // // History: 12/01/99 leonardm Created. // //****************************************************************************** HRESULT GetMinutesElapsed(XBStr& xbstrOldTime, ULONG* pMinutesElapsed) { // // Convert the WbemTime value to a SYSTEMTIME value. // SYSTEMTIME systemTime_Old; HRESULT hr = WbemTimeToSystemTime(xbstrOldTime, systemTime_Old); if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GetMinutesElapsed: WbemTimeToSystemTime failed. hr=0x%08X."), hr)); return hr; } // // Convert the SYSTEMTIME value to a FILETIME value. // BOOL bRes; FILETIME fileTime_Old; bRes = SystemTimeToFileTime(&systemTime_Old, &fileTime_Old); if(!bRes) { DWORD dwLastError = GetLastError(); DebugMsg((DM_WARNING, TEXT("GetMinutesElapsed: SystemTimeToFileTime failed. LastError=0x%08X."), dwLastError)); return E_FAIL; } unsigned __int64 old = fileTime_Old.dwHighDateTime; old <<= 32; old |= fileTime_Old.dwLowDateTime; // // Get the current time in SYSTEMTIME format // SYSTEMTIME systemTime_Current; GetSystemTime(&systemTime_Current); // // Convert the current time from a SYSTEMTIME to a FILETIME value // FILETIME fileTime_Current; bRes = SystemTimeToFileTime(&systemTime_Current, &fileTime_Current); if(!bRes) { DWORD dwLastError = GetLastError(); DebugMsg((DM_WARNING, TEXT("GetMinutesElapsed: SystemTimeToFileTime failed. LastError=0x%08X."), dwLastError)); return E_FAIL; } // // The time passed in as a parameter must precede the current time // unsigned __int64 current = fileTime_Current.dwHighDateTime; current <<= 32; current |= fileTime_Current.dwLowDateTime; if(old > current) { return WBEM_E_INVALID_PARAMETER; } // // We have converted SYSTEMTIMEs to FILETIMEs. // "The FILETIME structure is a 64-bit value representing the number // of 100-nanosecond intervals since January 1, 1601." // Therefore we need to divide by ten million to obtain seconds // and by sixty to obtain minutes. // *pMinutesElapsed = (ULONG) (( current - old ) / (60 * 10 * 1000 * 1000)); return S_OK; } //****************************************************************************** // // Function: IsNamespaceStale // // Description: Check if namespace is stale Sub-namespaces 'User' and 'Computer' are expected to have // RSOP_Session. The data member 'creationTime' of that instance is examined when // evaluating whether the sub-namespace should be deleted. For some failures treat the // namespace as garbage-collectable because we don't clean up properly when an error is // encountered during the creation of the namespace, and setting up security etc. // // // Parameters: pChildNamespace - Pointer to IWbemServices associated with child namespace // TTLMinutes - ULONG variable that represents the maximum number of // minutes that may have elapsed before a sub-namespace is // deleted. // // Return: True if namespace is stale, false otherwise // //****************************************************************************** BOOL IsNamespaceStale( IWbemServices *pChildNamespace, ULONG TTLMinutes ) { // // compute TTL // To do so compare the "creationTime" data member of class RSOP_Session with // the current time. If the time span exceeds the threshold (as found in the // registry), the namespace is to be deleted. // XBStr xbstrInstancePath = L"RSOP_Session.id=\"Session1\""; if(!xbstrInstancePath) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory."))); return FALSE; } XInterfacexpInstance = NULL; HRESULT hr = pChildNamespace->GetObject(xbstrInstancePath, 0, NULL, &xpInstance, NULL); if(FAILED(hr)) { DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: GetObject failed. hr=0x%08X"), hr)); return TRUE; } XBStr xbstrPropertyName = L"creationTime"; if(!xbstrPropertyName) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory."))); return TRUE; } VARIANT var; VariantInit(&var); XVariant xVar(&var); hr = xpInstance->Get(xbstrPropertyName, 0, &var, NULL, NULL); if(FAILED(hr)) { DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: Get failed. hr=0x%08X."), hr)); return TRUE; } if ( var.vt == VT_NULL ) return TRUE; XBStr xbstrPropertyValue = var.bstrVal; if(!xbstrPropertyValue) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory."))); return FALSE; } ULONG minutesElapsed = 10; hr = GetMinutesElapsed(xbstrPropertyValue, &minutesElapsed); if(FAILED(hr)) { DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: GetMinutesElapsed failed. hr=0x%08X."), hr)); return TRUE; } if(minutesElapsed > TTLMinutes) return TRUE; else return FALSE; } //****************************************************************************** // // Function: GarbageCollectNamespace // // Description: Garabage-collects the namespace passed in as a parameter. // If no sub-namespaces are found or if all sub-namespaces // are deleted, it deletes the parent namespace as well. // It deletes sub-namespaces whose TTL has expired. // It computes the TTL from the 'creationTime' data member of the only // instance of class RSOP_Session as defined in rsop.mof. // // Any of the sub-namespaces that is older than TTLMinutes will be deleted. // If no sub-namespaces are left, then the parent namespace is deleted as well. // // Garbage-collectable are those namespaces which satisfy a set of // criteria which at the present time is based solely on the naming convention // as follows: namespaces under root\rsop whose name starts with "NS" // // Sub-namespaces 'User' and 'Computer' are expected to have an instance of class // RSOP_Session. The data member 'creationTime' of that instance is examined when // evaluating whether the sub-namespace should be deleted. // // // Parameters: bstrNamespace - Name of the namesapce to garbage collect. // pWbemServices - Pointer to IWbemServices associated with // the parent of bstrNamespace (root\rsop) // TTLMinutes - ULONG variable that represents the maximum number of // minutes that may have elapsed before a sub-namespace is // deleted. // // Return: S_OK on success. An HRESULT error code on failure. // // History: 12/01/99 leonardm Created. // //****************************************************************************** HRESULT GarbageCollectNamespace(BSTR bstrNamespace, IWbemServices* pWbemServices, ULONG TTLMinutes) { if(!bstrNamespace || !pWbemServices) { return E_FAIL; } // // Connect to that namespace and enumerate instances of __namespace. // It's assumed that there will be at most 2 namespaces: // "User" and "Computer". // XInterface xpParentNamespace; HRESULT hr = pWbemServices->OpenNamespace( bstrNamespace, 0, NULL, &xpParentNamespace, NULL); if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: OpenNamespace failed. hr=0x%08X"), hr)); return hr; } // // Enumerate all instances of __namespace. // XInterface xpEnum; XBStr xbstrClass = L"__namespace"; if(!xbstrClass) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory"))); return E_OUTOFMEMORY; } hr = xpParentNamespace->CreateInstanceEnum( xbstrClass, WBEM_FLAG_SHALLOW | WBEM_FLAG_FORWARD_ONLY, NULL, &xpEnum); if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: CreateInstanceEnum failed. hr=0x%08X" ), hr )); return hr; } // // We re interested in data member "Name" of class __namespace. // XBStr xbstrProperty = L"Name"; if(!xbstrProperty) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory"))); return E_FAIL; } // // This pointer will be used to iterate through every instance // in the enumeration. // XInterfacexpInstance = NULL; ULONG ulReturned = 0; long namespacesFound = 0; long namespacesDeleted = 0; while(1) { // // Retrieve the next instance in the enumeration. // hr = xpEnum->Next( WBEM_NO_WAIT, 1, &xpInstance, &ulReturned); if (hr != WBEM_S_NO_ERROR || !ulReturned) { // // Either the end of the enumeration has been reached or an error // ocurred. We will find out outside the loop. // break; } namespacesFound++; // // Get the namespace name. // VARIANT var; VariantInit(&var); hr = xpInstance->Get(xbstrProperty, 0L, &var, NULL, NULL); // // Release the pointer to the current element of the enumeration.. // xpInstance = NULL; if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Get failed. hr=0x%x" ), hr )); return E_FAIL; } // // Use the name of the namespace to connect to it. // XBStr xbstrChildNamespace = var.bstrVal; VariantClear( &var ); if(!xbstrChildNamespace) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory" ))); return E_FAIL; } XInterface xpChildNamespace = NULL; hr = xpParentNamespace->OpenNamespace( xbstrChildNamespace, 0, NULL, &xpChildNamespace, NULL); if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: OpenNamespace returned 0x%x"), hr)); return hr; } BOOL bStale = IsNamespaceStale( xpChildNamespace, TTLMinutes ); xpChildNamespace = NULL; if ( bStale ) { // // DeleteInstance // CWString sNamespaceToDelete = L"__namespace.name=\""; sNamespaceToDelete += (WCHAR*)xbstrChildNamespace; sNamespaceToDelete += L"\""; if(!sNamespaceToDelete.ValidString()) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory."))); return E_OUTOFMEMORY; } XBStr xbstrInstancePath = sNamespaceToDelete; if(!xbstrInstancePath) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory."))); return E_OUTOFMEMORY; } hr = xpParentNamespace->DeleteInstance(xbstrInstancePath, 0, NULL, NULL); if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: DeleteInstance returned 0x%x"), hr)); return hr; } DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: Deleted namespace:%ws\\%ws\n"), (WCHAR*)bstrNamespace, (WCHAR*)xbstrChildNamespace )); namespacesDeleted++; } } // // Check to find out whther the loop was exited because the end of the // enumeration was reached or because an error ocurred. // if(hr != WBEM_S_FALSE || ulReturned) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Get failed. hr=0x%x" ), hr )); return E_FAIL; } // // If no namespaces are found or if namespaces in enumeration // equal deleted namespaces delete the parent namespace as well. // if((!namespacesFound) || (namespacesDeleted == namespacesFound)) { xpParentNamespace = NULL; CWString sNamespaceToDelete = L"__namespace.name=\""; sNamespaceToDelete += (WCHAR*)bstrNamespace; sNamespaceToDelete += L"\""; if(!sNamespaceToDelete.ValidString()) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory."))); return E_OUTOFMEMORY; } XBStr xbstrInstancePath = sNamespaceToDelete; if(!xbstrInstancePath) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: Failed to allocate memory."))); return E_OUTOFMEMORY; } hr = pWbemServices->DeleteInstance(xbstrInstancePath, 0, NULL, NULL); if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespace: DeleteInstance returned 0x%x"), hr)); return hr; } DebugMsg((DM_VERBOSE, TEXT("GarbageCollectNamespace: Deleted namespace %ws\n"), (WCHAR*)bstrNamespace )); } return S_OK; } //****************************************************************************** // // Function: IsGarbageCollectable // // Description: Determines whether a namespace is garbage-collectable. // Garbage-collectable are those namespaces which satisfy a set of // criteria which at the present time is based solely on the naming convention // as follows: namespaces under root\rsop whose name starts with "NS" // // // Parameters: bstrNamespace - BSTR that represents the namespace name. // // Return: 'true' or 'false'. // // History: 12/01/99 leonardm Created. // //****************************************************************************** bool IsGarbageCollectable(BSTR bstrNamespace) { if(bstrNamespace && wcslen(bstrNamespace) > 1 && _wcsnicmp(bstrNamespace, L"NS", 2) == 0) { return true; } return false; } //****************************************************************************** // // Function: GarbageCollectNamespaces // // Description: Iterates through namespaces under root\rsop and for each of those // that are determined to be garbage-collectable, it connects to // sub-namespaces 'User' and 'Computer'. // // Any of the sub-namespaces that is older than TTLMinutes will be deleted. // If no sub-namespaces are left, then the parent namespace is deleted as well. // // Garbage-collectable are those namespaces which satisfy a set of // criteria which at the present time is based solely on the naming convention // as follows: namespaces under root\rsop whose name starts with "NS" // // Sub-namespaces 'User' and 'Computer' are expected to have an instance of class // RSOP_Session. The data member 'creationTime' of that instance is examined when // evaluating whether the sub-namespace should be deleted. // // // Parameters: TTLMinutes - The maximum number of minutes that may have // elapsed since the creation of a sub-namespace // // Return: // // History: 12/01/99 leonardm Created. // //****************************************************************************** HRESULT GarbageCollectNamespaces(ULONG TTLMinutes) { XInterface xpWbemLocator = NULL; // // Connect to namespace ROOT\RSOP // HRESULT hr = CoCreateInstance( CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*) &xpWbemLocator); if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: CoCreateInstance returned 0x%x"), hr)); return hr; } XInterfacexpWbemServices = NULL; XBStr xbstrNamespace = L"root\\rsop"; if(!xbstrNamespace) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Failed to allocate memory."))); return E_OUTOFMEMORY; } hr = xpWbemLocator->ConnectServer(xbstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &xpWbemServices); if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: ConnectServer failed. hr=0x%x" ), hr )); return hr; } // // Enumerate all instances of __namespace at the root\rsop level. // XInterface xpEnum; XBStr xbstrClass = L"__namespace"; if(!xbstrClass) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Failed to allocate memory."))); return E_OUTOFMEMORY; } hr = xpWbemServices->CreateInstanceEnum( xbstrClass, WBEM_FLAG_SHALLOW | WBEM_FLAG_FORWARD_ONLY, NULL, &xpEnum); if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: CreateInstanceEnum failed. hr=0x%x" ), hr )); return hr; } XBStr xbstrProperty = L"Name"; if(!xbstrProperty) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Failed to allocate memory"))); return E_FAIL; } XInterfacexpInstance = NULL; ULONG ulReturned = 1; while(1) { hr = xpEnum->Next( WBEM_NO_WAIT, 1, &xpInstance, &ulReturned); if (hr != WBEM_S_NO_ERROR || !ulReturned) { break; } VARIANT var; VariantInit(&var); hr = xpInstance->Get(xbstrProperty, 0L, &var, NULL, NULL); xpInstance = NULL; if(FAILED(hr)) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Get failed. hr=0x%x" ), hr )); return E_FAIL; } XBStr xbstrNamespace = var.bstrVal; VariantClear( &var ); if(!xbstrNamespace) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Failed to allocate memory."))); return E_OUTOFMEMORY; } // // For every instance of __namespace under ROOT\RSOP // find out whether it is garbage-collectable. // if(IsGarbageCollectable(xbstrNamespace)) { // // If it is garbage-collectable, delete it if it // was created more than 'TTLMinutes' minutes ago. // In case of failure, continue with next namespace // in the enumeration. // GarbageCollectNamespace(xbstrNamespace, xpWbemServices, TTLMinutes); } } if(hr != WBEM_S_FALSE || ulReturned) { DebugMsg((DM_WARNING, TEXT("GarbageCollectNamespaces: Get failed. hr=0x%x" ), hr )); return E_FAIL; } return S_OK; }