/*++ Copyright (c) 1992-1996 Microsoft Corporation Module Name: iisutil.c Abstract: IIS Resource utility routine DLL Author: Pete Benoit (v-pbenoi) 12-SEP-1996 Revision History: --*/ #include "iisutil.h" #if defined(_DEBUG_SUPPORT) #include #endif #include IMSAdminBase* CMetaData::g_pMBCom; CRITICAL_SECTION CMetaData::g_cs; VOID FreeIISResource( IN LPIIS_RESOURCE ResourceEntry ) /*++ Routine Description: Free all the storage for a IIS_RESOURCE Arguments: vr - virtual root to free Return Value: NONE --*/ { if (ResourceEntry != NULL) { if ( ResourceEntry->ResourceName != NULL ) { LocalFree( ResourceEntry->ResourceName ); ResourceEntry->ResourceName = NULL; } #if 0 if ( ResourceEntry->ResourceHandle != NULL ) { CloseClusterResource( ResourceEntry->ResourceHandle ); } #endif if ( ResourceEntry->ParametersKey != NULL ) { ClusterRegCloseKey( ResourceEntry->ParametersKey ); ResourceEntry->ParametersKey = NULL; } if ( ResourceEntry->Params.ServiceName != NULL ) { LocalFree( ResourceEntry->Params.ServiceName); ResourceEntry->Params.ServiceName = NULL; } if ( ResourceEntry->Params.InstanceId != NULL ) { LocalFree( ResourceEntry->Params.InstanceId); ResourceEntry->Params.InstanceId = NULL; } if ( ResourceEntry->Params.MDPath != NULL ) { LocalFree( ResourceEntry->Params.MDPath); ResourceEntry->Params.MDPath = NULL; } } // ResourceEntry != NULL } // FreeIISResource VOID DestructIISResource( IN LPIIS_RESOURCE ResourceEntry ) /*++ Routine Description: Free all the storage for a ResourceEntry and the ResourceEntry Arguments: vr - virtual root to free Return Value: NONE --*/ { if (ResourceEntry != NULL) { FreeIISResource(ResourceEntry); LocalFree(ResourceEntry); } // ResourceEntry != NULL } // DestructIISResource DWORD GetServerBindings( LPWSTR MDPath, DWORD dwServiceType, SOCKADDR* psaServer, LPDWORD pdwPort ) /*++ Routine Description: Read the first ip address and port number form the ServerBindings property for a particlar branch of the metabase Arguments: MDPath : the particular branch of the metabase to read the information from (ex. /LM/W3SVC/1) dwServiceType : the service type id pszServer : the structure to save the ip address pdwPort : the returned port number Return Value: sucess : ERROR_SUCCESS failure : the win32 error code of the failure --*/ { DWORD dwStatus = ERROR_SUCCESS; DWORD dwR = 0; DWORD dwW = 0; TCHAR * achBindings = NULL; INT cBindingsSize = sizeof(TCHAR)*1024; TCHAR * pB = NULL; TCHAR * pP = NULL; DWORD cL = 0; BOOL bGetBindingsStatus = FALSE ; // // allocate space for the server bindings string // if( !(achBindings = (TCHAR*)LocalAlloc(LMEM_FIXED, cBindingsSize)) ) { dwStatus = GetLastError(); TR( (DEBUG_BUFFER,"[GetServerBindings] Can't allocate memory for server bindings info. (%d)\n",dwStatus) ); } if ( achBindings ) { // // Get binding info from metabase // CMetaData MD; if ( MD.Open( MDPath ) ) { dwR = cBindingsSize; if( !(bGetBindingsStatus = MD.GetMultisz( L"", MD_SERVER_BINDINGS, IIS_MD_UT_SERVER, achBindings, &dwR )) ) { dwStatus = GetLastError(); if ( ERROR_INSUFFICIENT_BUFFER == dwStatus ) { TR( (DEBUG_BUFFER,"[GetServerBindings] Buffer too small, reallocating\n") ); // // (# 296798) If the original binding's buffer size is too small so realloc memory to the required size // TCHAR* achBindingsNew = (TCHAR*)LocalReAlloc(achBindings, dwR, 0); if( !achBindingsNew ) { dwStatus = GetLastError(); LocalFree (achBindings); achBindings = NULL; } else { achBindings = achBindingsNew; bGetBindingsStatus = MD.GetMultisz( L"", MD_SERVER_BINDINGS, IIS_MD_UT_SERVER, achBindings, &dwR ); } } else { // // error getting server bindings information from the metabase // TR( (DEBUG_BUFFER,"[GetServerBindings] error getting server bindings information (%d)\n", dwStatus) ); } } MD.Close(); } else { dwStatus = GetLastError(); TR( (DEBUG_BUFFER,"[GetServerBindings] failed to open MD, error %08x\n", dwStatus) ); } } // // not getting server bindings information is a serious error // if ( !bGetBindingsStatus ) { TR( (DEBUG_BUFFER,"[GetServerBindings] error getting server bindings infomation (%d) \n", dwStatus ) ); } else { TR( (DEBUG_BUFFER,"[GetServerBindings] got server bindings string (%S) \n", achBindings ) ); (*pdwPort) = DEFAULT_PORT[dwServiceType]; ZeroMemory( psaServer, sizeof(SOCKADDR) ); ((SOCKADDR_IN*)psaServer)->sin_family = AF_INET; ((SOCKADDR_IN*)psaServer)->sin_addr.s_net = 127; ((SOCKADDR_IN*)psaServer)->sin_addr.s_impno = 1; // // Each binding is of the form "addr:port:hostname" // we look only at addr & port // // use the 1st valid binding ( i.e. contains ':' ) // dwR /= sizeof(WCHAR); for ( pB = achBindings ; *pB && dwR ; ) { if ( (cL = wcslen( pB ) + 1 ) > dwR ) { break; } if ( (pP = wcschr( pB, L':' )) ) { SOCKADDR sa; INT cL = sizeof(sa); pP[0] = '\0'; if ( achBindings[0] && WSAStringToAddress( achBindings, AF_INET, NULL, &sa, &cL ) == 0 ) { memcpy( psaServer, &sa, cL ); } else { TR( (DEBUG_BUFFER,"[GetServerBindings] WSAStringToAddress failed converting string (%S) \n", achBindings ) ); } if ( iswdigit(pP[1]) ) { (*pdwPort) = wcstoul( pP + 1, NULL, 0 ); } TR( (DEBUG_BUFFER,"[GetServerBindings] found binding addr %S %u.%u.%u.%u port %u\n", achBindings, ((SOCKADDR_IN*)psaServer)->sin_addr.s_net, ((SOCKADDR_IN*)psaServer)->sin_addr.s_host, ((SOCKADDR_IN*)psaServer)->sin_addr.s_lh, ((SOCKADDR_IN*)psaServer)->sin_addr.s_impno, (*pdwPort) ) ); goto found_bindings; } pB += cL; dwR -= cL; } } found_bindings: if( achBindings ) { LocalFree(achBindings); achBindings = NULL; } return dwStatus ; } // GetServerBindings CHAR IsAliveRequest[]="TRACK / HTTP/1.1\r\nHost: CLUSIIS\r\nConnection: close\r\nContent-length: 0\r\n\r\n"; // # 292727 DWORD VerifyIISService( IN LPWSTR MDPath, IN DWORD ServiceType, IN DWORD dwPort, IN SOCKADDR saServer, IN PLOG_EVENT_ROUTINE LogEvent ) /*++ Routine Description: Verify that the IIS service is running and that it has virtual roots contained in the resource Steps: 1. get server instance binding info 2. connect to server, send HTTP request & wait for response Arguments: Resource - supplies the resource id IsAliveFlag - says this is an IsAlive call Return Value: TRUE - if service is running and service contains resources virtual roots FALSE - service is in any other state --*/ { DWORD status = ERROR_SUCCESS; DWORD dwR; DWORD dwW; SOCKET s; CHAR IsAliveResponse[1024]; TR( (DEBUG_BUFFER,"[IISVerify] Enter\n") ); TR( (DEBUG_BUFFER,"[IISVerify] checking address %S : %u.%u.%u.%u port %u\n", MDPath, ((SOCKADDR_IN*)&saServer)->sin_addr.s_net, ((SOCKADDR_IN*)&saServer)->sin_addr.s_host, ((SOCKADDR_IN*)&saServer)->sin_addr.s_lh, ((SOCKADDR_IN*)&saServer)->sin_addr.s_impno, dwPort ) ); if ( (s = TcpSockConnectToHost( &saServer, dwPort, CHECK_IS_ALIVE_CONNECT_TIMEOUT )) != NULL ) { if ( ServiceType == IIS_SERVICE_TYPE_W3 ) { // // If this is W3 then attempt to send a request and get response from server. // if ( TcpSockSend( s, IsAliveRequest, sizeof(IsAliveRequest), &dwW, CHECK_IS_ALIVE_SEND_TIMEOUT ) && TcpSockRecv( s, IsAliveResponse, sizeof(IsAliveResponse), &dwR, CHECK_IS_ALIVE_SEND_TIMEOUT ) && dwR > 5 ) { status = ERROR_SUCCESS; TcpSockClose( s ); goto finished_check; } else { TR( (DEBUG_BUFFER,"[IISVerify] failed HTTP request\n") ); status = ERROR_SERVICE_NOT_ACTIVE; } } else if ( ServiceType == IIS_SERVICE_TYPE_SMTP ) { // // If this is SMTP then check the reply code. // (220: service ready) // if ( TcpSockRecv( s, IsAliveResponse, sizeof(IsAliveResponse), &dwR, CHECK_IS_ALIVE_SEND_TIMEOUT ) && strncmp( IsAliveResponse, "220", 3 ) == 0 ) { status = ERROR_SUCCESS; TcpSockClose( s ); goto finished_check; } else { TR( (DEBUG_BUFFER,"[IISVerify] failed SMTP request, response:%S\n", IsAliveResponse) ); status = ERROR_SERVICE_NOT_ACTIVE; } } else if ( ServiceType == IIS_SERVICE_TYPE_NNTP ) { // // If this is NNTP then check the reply code. // (200: service ready - posting allowed) or // (201: service ready - no posting allowed) // if ( TcpSockRecv( s, IsAliveResponse, sizeof(IsAliveResponse), &dwR, CHECK_IS_ALIVE_SEND_TIMEOUT ) && ( strncmp( IsAliveResponse, "200", 3 ) == 0 || strncmp( IsAliveResponse, "201", 3 ) == 0 ) ) { status = ERROR_SUCCESS; TcpSockClose( s ); goto finished_check; } else { TR( (DEBUG_BUFFER,"[IISVerify] failed NNTP request, response:%S\n", IsAliveResponse) ); status = ERROR_SERVICE_NOT_ACTIVE; } } else { status = ERROR_SUCCESS; TcpSockClose( s ); goto finished_check; } TcpSockClose( s ); } else { TR( (DEBUG_BUFFER,"[IISVerify] failed to connect port %d\n", dwPort) ); status = ERROR_SERVICE_NOT_ACTIVE; } finished_check: // // Check to see if there was an error // if ( status != ERROR_SUCCESS ) { TR( (DEBUG_BUFFER,"[IISVerify] FALSE (%d), Leave\n", status) ); #if defined(DBG_CANT_VERIFY) g_fDbgCantVerify = TRUE; #endif } else { TR( (DEBUG_BUFFER,"[IISVerify] TRUE, Leave\n") ); } return status; } // VerifyIISService LPWSTR GetClusterResourceType( HRESOURCE hResource, LPIIS_RESOURCE ResourceEntry, IN PLOG_EVENT_ROUTINE LogEvent ) /*++ Routine Description: Returns the resource type for the resource Arguments: hResource - handle to a resource Return Value: TRUE - if service is running and service contains resources virtual roots FALSE - service is in any other state --*/ { DWORD dwType = 0; DWORD dwSize = MAX_DEFAULT_WSTRING_SIZE; WCHAR lpzTypeName[MAX_DEFAULT_WSTRING_SIZE]; LPWSTR TypeName = NULL; HKEY hKey = NULL; LONG status; (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[GetClusterResourceType] entry\n"); // // Valid resource now open the reg and get it's type // hKey = GetClusterResourceKey(hResource,KEY_READ); if (hKey == NULL) { (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[GetClusterResourceType] Unable to open resource key = %1!u!\n",GetLastError()); return(NULL); } // // Got a valid key now get the type // status = ClusterRegQueryValue(hKey,L"Type",&dwType,(LPBYTE)lpzTypeName,&dwSize); if (status == ERROR_SUCCESS) { TypeName = (WCHAR*)LocalAlloc(LMEM_FIXED,(dwSize+16)*sizeof(WCHAR)); if (TypeName != NULL) { wcscpy(TypeName,lpzTypeName); } // TypeName != NULL } else { (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[GetClusterResourceType] Unable to Query Value = %1!u!\n",status); } ClusterRegCloseKey(hKey); (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[GetClusterResourceType] TypeName = %1!ws!\n",TypeName); return TypeName; }// end GetClusterResourceType HRESOURCE ClusterGetResourceDependency( IN LPCWSTR ResourceName, IN LPCWSTR ResourceType, IN LPIIS_RESOURCE ResourceEntry, IN PLOG_EVENT_ROUTINE LogEvent ) /*++ Routine Description: Returns a dependent resource Arguments: ResourceName - the resource ResourceType - the type of resource that it depends on Return Value: NULL - error (use GetLastError() to get further info) NON-NULL - Handle to a resource of type ResourceType --*/ { HCLUSTER hCluster = NULL; HRESOURCE hResource = NULL; HRESOURCE hResDepends = NULL; HRESENUM hResEnum = NULL; DWORD dwType = 0; DWORD dwIndex = 0; DWORD dwSize = MAX_DEFAULT_WSTRING_SIZE; WCHAR lpszName[MAX_DEFAULT_WSTRING_SIZE]; LPWSTR ResTypeName = NULL; DWORD status; // // Open the cluster // hCluster = OpenCluster(NULL); if (hCluster == NULL) { (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[ClusterGetResourceDependency] Unable to OpenCluster status = %1!u!\n",GetLastError()); return(NULL); } // // Open the resource // hResource = OpenClusterResource(hCluster,ResourceName); if (hResource == NULL) { (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[ClusterGetResourceDependency] Unable to OpenClusterResource status = %1!u!\n",GetLastError()); goto error_exit; } // // Open the depends on enum // hResEnum = ClusterResourceOpenEnum(hResource,CLUSTER_RESOURCE_ENUM_DEPENDS); if (hResEnum == NULL) { (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[ClusterGetResourceDependency] Unsuccessful ClusterResourceOpenEnum status = %1!u!\n",GetLastError()); goto error_exit; } // // Enumerate all the depends on keys // do { dwSize = MAX_DEFAULT_WSTRING_SIZE; status = ClusterResourceEnum(hResEnum,dwIndex++,&dwType,lpszName,&dwSize); if ((status != ERROR_SUCCESS) && (status != ERROR_MORE_DATA)) { // // This checks to see if NO dependencies were found // if ((status == ERROR_NO_MORE_ITEMS) && (dwIndex == 1)) { SetLastError( ERROR_NO_DATA ); } else { SetLastError( status ); } goto error_exit; } // // Determine the type of resource found // hResDepends = OpenClusterResource(hCluster,lpszName); if (hResDepends == NULL) { (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[ClusterGetResourceDependency] Unsuccessful OpenClusterResource status = %1!u!\n",GetLastError()); } else { // // Valid resource now open the reg and get it's type // ResTypeName = GetClusterResourceType(hResDepends,ResourceEntry,LogEvent); if (ResTypeName == NULL) { (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[ClusterGetResourceDependency] Unsuccessful GetClusterResourceType status = %1!u!\n",GetLastError()); } else { // // Compare the types and look for a match // if ( (_wcsicmp(ResTypeName,ResourceType)) == 0) { // //Found a match // goto success_exit; } // END _wcsicmp(ResTypeName,ResourceType) } // END if ResourceTypeName != NULL } // END if hResDepends != NULL // // Close all handles, key's // if (hResDepends != NULL) { CloseClusterResource(hResDepends); hResDepends = NULL; } } while ((status == ERROR_SUCCESS) || (status == ERROR_MORE_DATA)); if (hResDepends == NULL) { (LogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"[ClusterGetResourceDependency] Unsuccessful resource enum status = %1!u! %2!x! last error = %3!u!\n",status,status,GetLastError()); } success_exit: error_exit: // // At this point hResDepends is NULL if no match or non-null (success) // if (hCluster != NULL) { CloseCluster(hCluster); } if (hResource != NULL) { CloseClusterResource(hResource); } if (hResEnum != NULL) { ClusterResourceCloseEnum(hResEnum); } return hResDepends; } // ClusterGetResourceDependency DWORD SetInstanceState( IN PCLUS_WORKER pWorker, IN LPIIS_RESOURCE ResourceEntry, IN RESOURCE_STATUS* presourceStatus, IN CLUSTER_RESOURCE_STATE TargetState, IN LPWSTR TargetStateString, IN DWORD dwMdPropControl, IN DWORD dwMdPropTarget ) /*++ Routine Description: Set the server instance to te requested state ( start / stop ) Arguments: pWorker - thread context to monitor for cancel request ResourceEntry - ptr to IIS server instance resource ctx presourceStatus - ptr to struct to be updated with new status TargetState - cluster API target state dwMdPropControl - IIS metabase property value for request to server dwMdPropTarget - IIS metabase property value for target state Return Value: Win32 error code, ERROR_SUCCESS if success --*/ { DWORD status = ERROR_SERVICE_NOT_ACTIVE; int retry; DWORD dwS; DWORD dwEnabled; CMetaData MD; TR( (DEBUG_BUFFER,"[SetInstanceState] Enter\n") ); if ( MD.Open( ResourceEntry->Params.MDPath, TRUE, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) { // // Reenable clustering. If we are receiving this notification we know // enabled should be set to true. // if ( !MD.GetDword( L"", MD_CLUSTER_ENABLED, IIS_MD_UT_SERVER, &dwEnabled, 0 ) || dwEnabled == 0 ) { TR( (DEBUG_BUFFER,"[SetInstanceState] cluster not enabled\n") ); if ( !MD.SetDword( L"", MD_CLUSTER_ENABLED, IIS_MD_UT_SERVER, 1, 0 ) ) { TR( (DEBUG_BUFFER,"[SetInstanceState] failed to re-enable cluster\n") ); } } if ( MD.GetDword( L"", MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwS, 0 ) ) { TR( (DEBUG_BUFFER,"[SetInstanceState] state prob is %d\n",dwS) ); } else { TR( (DEBUG_BUFFER,"[SetInstanceState] failed to probe server state\n") ); dwS = 0xffffffff; } if ( dwS != dwMdPropTarget ) { if ( MD.SetDword( L"", MD_CLUSTER_SERVER_COMMAND, IIS_MD_UT_SERVER, dwMdPropControl, 0 ) ) { MD.Close(); TR( (DEBUG_BUFFER,"[SetInstanceState] command set to %d\n",dwMdPropControl) ); for ( retry = (TargetState == ClusterResourceOnline) ? IISCLUS_ONLINE_TIMEOUT :IISCLUS_OFFLINE_TIMEOUT ; retry-- ; ) { if ( ClusWorkerCheckTerminate(pWorker) ) { status = ERROR_OPERATION_ABORTED; break; } if ( MD.GetDword( ResourceEntry->Params.MDPath, MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwS, 0 ) ) { if ( dwS == dwMdPropTarget ) { status = ERROR_SUCCESS; break; } TR( (DEBUG_BUFFER,"[SetInstanceState] state is %d, waiting for %d\n",dwS,dwMdPropTarget) ); } else { TR( (DEBUG_BUFFER,"[SetInstanceState] failed to get server state\n") ); break; } Sleep(SERVER_START_DELAY); } } else { TR( (DEBUG_BUFFER,"[SetInstanceState] failed to set server command to %d\n",dwMdPropControl) ); MD.Close(); } } else { status = ERROR_SUCCESS; MD.Close(); } } else { if ( g_hEventLog && TargetState == ClusterResourceOnline ) { LPCTSTR aErrStr[3]; WCHAR aErrCode[32]; _ultow( GetLastError(), aErrCode, 10 ); aErrStr[0] = ResourceEntry->Params.ServiceName; aErrStr[1] = ResourceEntry->Params.InstanceId; aErrStr[2] = aErrCode; ReportEvent( g_hEventLog, EVENTLOG_ERROR_TYPE, 0, IISCL_EVENT_CANT_OPEN_METABASE, NULL, sizeof(aErrStr) / sizeof(LPCTSTR), 0, aErrStr, NULL ); } TR( (DEBUG_BUFFER,"[SetInstanceState] failed to open %S, error %08x\n",ResourceEntry->Params.MDPath, GetLastError()) ); } if ( status != ERROR_SUCCESS ) { // // Error // (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_ERROR, L"Error %1!u! cannot bring resource %2!ws! %3!ws!.\n", status, ResourceEntry->ResourceName, TargetStateString ); presourceStatus->ResourceState = ClusterResourceFailed; ResourceEntry->State = ClusterResourceFailed; } else { // // Success, update state, log message // presourceStatus->ResourceState = TargetState; ResourceEntry->State = TargetState; TR( (DEBUG_BUFFER,"[SetInstanceState] Setting Metadata Path:%S ResourceEntry->State:%d\n", ResourceEntry->Params.MDPath, TargetState) ); (g_IISLogEvent)( ResourceEntry->ResourceHandle, LOG_INFORMATION, L"Success bringing resource %1!ws! %2!ws!.\n", ResourceEntry->ResourceName, TargetStateString ); } TR( (DEBUG_BUFFER,"[SetInstanceState] status = %d, Leave\n",status) ); return status; } DWORD InstanceEnableCluster( LPWSTR pwszServiceName, LPWSTR pwszInstanceId ) /*++ Routine Description: Ensure server instance in state consistent with cluster membership. If not already part of a cluster, stop instance & flag it as cluster enabled Arguments: pwszServiceName - IIS service name ( e.g. W3SVC ) pwszInstanceId - IIS serverr instance ID Return Value: win32 error code or ERROR_SUCCESS if success --*/ { DWORD status = ERROR_SERVICE_NOT_ACTIVE; int retry; DWORD dwS; TCHAR achMDPath[80]; DWORD dwL; dwL = wsprintf( achMDPath, L"/LM/%s/%s", pwszServiceName, pwszInstanceId ); CMetaData MD; TR( (DEBUG_BUFFER,"[InstanceEnableCluster] for %S, Enter\n", achMDPath ) ); if ( MD.Open( achMDPath, TRUE, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) { // // ensure instance is marked as cluster enabled // if ( !MD.GetDword( L"", MD_CLUSTER_ENABLED, IIS_MD_UT_SERVER, &dwS, 0 ) || dwS == 0 ) { if ( MD.SetDword( L"", MD_CLUSTER_ENABLED, IIS_MD_UT_SERVER, 1, 0 ) ) { if ( MD.GetDword( L"", MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwS, 0 ) ) { TR( (DEBUG_BUFFER,"[InstanceEnableCluster] state prob is %d\n",dwS) ); } else { TR( (DEBUG_BUFFER,"[InstanceEnableCluster] failed to probe server state\n") ); dwS = 0xffffffff; } MD.Close(); if ( dwS != MD_SERVER_STATE_STOPPED ) { if ( MD.Open( achMDPath, TRUE, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) && MD.SetDword( L"", MD_CLUSTER_SERVER_COMMAND, IIS_MD_UT_SERVER, MD_SERVER_COMMAND_STOP, 0 ) ) { TR( (DEBUG_BUFFER,"[InstanceEnableCluster] command set to %d\n",MD_SERVER_COMMAND_STOP) ); for ( retry = 30 ; retry-- ; ) { if ( MD.GetDword( L"", MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwS, 0 ) ) { if ( dwS == MD_SERVER_STATE_STOPPED ) { status = ERROR_SUCCESS; break; } TR( (DEBUG_BUFFER,"[InstanceEnableCluster] state is %d, waiting for %d\n",dwS,MD_SERVER_STATE_STOPPED) ); } else { TR( (DEBUG_BUFFER,"[InstanceEnableCluster] failed to get server state\n") ); break; } Sleep(SERVER_START_DELAY); } } else { TR( (DEBUG_BUFFER,"[InstanceEnableCluster] failed to set server command to %d\n",MD_SERVER_COMMAND_STOP) ); } } else { status = ERROR_SUCCESS; } } else { TR( (DEBUG_BUFFER,"[InstanceEnableCluster] failed to set cluster enabled\n") ); } } } else { TR( (DEBUG_BUFFER,"[InstanceEnableCluster] failed to open %S, error %08x\n",achMDPath, GetLastError()) ); } TR( (DEBUG_BUFFER,"[InstanceEnableCluster] status = %d, Leave\n",status) ); MD.Close(); return status; } DWORD InstanceDisableCluster( LPWSTR pwszServiceName, LPWSTR pwszInstanceId ) /*++ Routine Description: Ensure server instance in state consistent with cluster membership. If already part of a cluster, stop instance & remove its cluster enabled flag. Arguments: pwszServiceName - IIS service name ( e.g. W3SVC ) pwszInstanceId - IIS serverr instance ID Return Value: win32 error code or ERROR_SUCCESS if success --*/ { DWORD status = ERROR_SERVICE_NOT_ACTIVE; int retry; DWORD dwS; TCHAR achMDPath[80]; DWORD dwL; dwL = wsprintf( achMDPath, L"/LM/%s/%s", pwszServiceName, pwszInstanceId ); CMetaData MD; TR( (DEBUG_BUFFER,"[InstanceDisableCluster] for %S, Enter\n", achMDPath ) ); if ( MD.Open( achMDPath, TRUE, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) { // // ensure instance is marked as cluster enabled // if ( !MD.GetDword( L"", MD_CLUSTER_ENABLED, IIS_MD_UT_SERVER, &dwS, 0 ) || ( dwS == 0) ) { MD.Close(); return status; } if ( MD.SetDword( L"", MD_CLUSTER_ENABLED, IIS_MD_UT_SERVER, 0, 0 ) ) { if ( MD.GetDword( L"", MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwS, 0 ) ) { TR( (DEBUG_BUFFER,"[InstanceDisableCluster] state prob is %d\n",dwS) ); } else { TR( (DEBUG_BUFFER,"[InstanceDisableCluster] failed to probe server state\n") ); dwS = 0xffffffff; } MD.Close(); if ( dwS != MD_SERVER_STATE_STOPPED ) { if ( MD.Open( achMDPath, TRUE, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) && MD.SetDword( L"", MD_SERVER_COMMAND, IIS_MD_UT_SERVER, MD_SERVER_COMMAND_STOP, 0 )) { TR( (DEBUG_BUFFER,"[InstanceDisableCluster] command set to %d\n",MD_SERVER_COMMAND_STOP) ); for ( retry = 30 ; retry-- ; ) { if ( MD.GetDword( L"", MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwS, 0 ) ) { if ( dwS == MD_SERVER_STATE_STOPPED ) { break; } TR( (DEBUG_BUFFER,"[InstanceDisableCluster] state is %d, waiting for %d\n",dwS,MD_SERVER_STATE_STOPPED) ); } else { TR( (DEBUG_BUFFER,"[InstanceDisableCluster] failed to get server state\n") ); break; } Sleep(SERVER_START_DELAY); } } else { TR( (DEBUG_BUFFER,"[InstanceDisableCluster] failed to set server command to %d\n",MD_SERVER_COMMAND_STOP) ); } MD.Close(); // // restart the server // if ( MD.Open( achMDPath, TRUE, METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) && MD.SetDword( L"", MD_SERVER_COMMAND, IIS_MD_UT_SERVER, MD_SERVER_COMMAND_START, 0 )) { TR( (DEBUG_BUFFER,"[InstanceDisableCluster] set server command to %d\n",MD_SERVER_COMMAND_START) ); } else { TR( (DEBUG_BUFFER,"[InstanceDisableCluster] failed to set server command to %d\n",MD_SERVER_COMMAND_START) ); } status = ERROR_SUCCESS; } } else { TR( (DEBUG_BUFFER,"[InstanceDisableCluster] failed to set cluster disbled\n") ); } } else { TR( (DEBUG_BUFFER,"[InstanceDisableCluster] failed to open %S, error %08x\n",achMDPath, GetLastError()) ); } MD.Close(); TR( (DEBUG_BUFFER,"[InstanceDisableCluster] status = %d, Leave\n",status) ); return status; } BOOL CMetaData::Open( LPWSTR pszPath, BOOL fReconnect, DWORD dwFlags ) /*++ Routine Description: Opens the metabase Arguments: pszPath - Path to open dwFlags - Open flags Return: TRUE if success, FALSE on error, (call GetLastError()) --*/ { HRESULT hRes; TR( (DEBUG_BUFFER,"[MDOpen] %S\n",pszPath) ); if ( !GetCoInit() ) { TR( (DEBUG_BUFFER,"[MDOpen] calling CoInit\n") ); hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED); if ( FAILED(hRes) ) { TR( (DEBUG_BUFFER,"[MD:GetMD::CoInitializeEx] error %08x\n",HRESULTTOWIN32( hRes )) ); SetLastError( HRESULTTOWIN32( hRes ) ); return FALSE; } SetCoInit( TRUE ); } EnterCriticalSection( &g_cs ); for ( int retry = 2 ; retry-- ; ) { if ( !GetMD() ) { LeaveCriticalSection( &g_cs ); TR( (DEBUG_BUFFER,"[MDOpen] can't get MD interface\n") ); return FALSE; } TR( (DEBUG_BUFFER,"[MDOpen] before OpenKey\n") ); hRes = g_pMBCom->OpenKey( METADATA_MASTER_ROOT_HANDLE, pszPath, dwFlags, MB_TIMEOUT, &m_hMB ); if ( SUCCEEDED( hRes ) ) { LeaveCriticalSection( &g_cs ); TR( (DEBUG_BUFFER,"[MDOpen] OK, leave\n") ); return TRUE; } if ( !fReconnect ) { break; } TR( (DEBUG_BUFFER,"[MDOpen] error %d, fReconnect=%d\n",HRESULTTOWIN32( hRes ),fReconnect) ); if ( HRESULTTOWIN32( hRes ) == RPC_S_SERVER_UNAVAILABLE || HRESULTTOWIN32( hRes ) == RPC_S_CALL_FAILED_DNE ) { ReleaseMD(); } else { break; } } SetLastError( HRESULTTOWIN32( hRes ) ); LeaveCriticalSection( &g_cs ); return FALSE; } BOOL CMetaData::Close( VOID ) /*++ Routine Description: Close opened handle to metadata Arguments: None Return Value: TRUE if success, otherwise FALSE --*/ { if ( m_hMB ) { g_pMBCom->CloseKey( m_hMB ); m_hMB = NULL; } return TRUE; } BOOL CMetaData::SetData( LPWSTR pszPath, DWORD dwPropID, DWORD dwUserType, DWORD dwDataType, VOID * pvData, DWORD cbData, DWORD dwFlags ) /*++ Routine Description: Sets a metadata property on an openned metabase Arguments: pszPath - Path to set data on dwPropID - Metabase property ID dwUserType - User type for this property dwDataType - Type of data being set (dword, string etc) pvData - Pointer to data cbData - Size of data dwFlags - Inheritance flags Return: TRUE if success, FALSE on error, (call GetLastError()) --*/ { METADATA_RECORD mdRecord; HRESULT hRes; mdRecord.dwMDIdentifier = dwPropID; mdRecord.dwMDAttributes = dwFlags; mdRecord.dwMDUserType = dwUserType; mdRecord.dwMDDataType = dwDataType; mdRecord.dwMDDataLen = cbData; mdRecord.pbMDData = (PBYTE) pvData; EnterCriticalSection( &g_cs ); if ( !GetMD() ) { LeaveCriticalSection( &g_cs ); return FALSE; } hRes = g_pMBCom->SetData( m_hMB, pszPath, &mdRecord ); if ( SUCCEEDED( hRes )) { LeaveCriticalSection( &g_cs ); return TRUE; } SetLastError( HRESULTTOWIN32( hRes ) ); LeaveCriticalSection( &g_cs ); return FALSE; } BOOL CMetaData::GetData( LPWSTR pszPath, DWORD dwPropID, DWORD dwUserType, DWORD dwDataType, VOID * pvData, DWORD * pcbData, DWORD dwFlags ) /*++ Routine Description: Retrieves a metadata property on an openned metabase Arguments: pszPath - Path to set data on dwPropID - Metabase property ID dwUserType - User type for this property dwDataType - Type of data being set (dword, string etc) pvData - Pointer to data pcbData - Size of pvData, receives size of object dwFlags - Inheritance flags Return: TRUE if success, FALSE on error, (call GetLastError()) --*/ { METADATA_RECORD mdRecord; HRESULT hRes; DWORD dwRequiredLen; BOOL fConvert; mdRecord.pbMDData = (PBYTE) pvData; mdRecord.dwMDDataLen = *pcbData; mdRecord.dwMDIdentifier = dwPropID; mdRecord.dwMDAttributes = dwFlags; mdRecord.dwMDUserType = dwUserType; mdRecord.dwMDDataType = dwDataType; EnterCriticalSection( &g_cs ); if ( !GetMD() ) { LeaveCriticalSection( &g_cs ); return FALSE; } hRes = g_pMBCom->GetData( m_hMB, pszPath, &mdRecord, &dwRequiredLen ); if ( SUCCEEDED( hRes )) { LeaveCriticalSection( &g_cs ); *pcbData = mdRecord.dwMDDataLen; return TRUE; } LeaveCriticalSection( &g_cs ); *pcbData = dwRequiredLen; SetLastError( HRESULTTOWIN32( hRes ) ); return FALSE; } BOOL CMetaData::Init( ) /*++ Routine Description: Initialize access to metadata Arguments: None Return Value: TRUE if success, otherwise FALSE --*/ { INITIALIZE_CRITICAL_SECTION( &g_cs ); g_pMBCom = NULL; return TRUE; } BOOL CMetaData::Terminate( ) /*++ Routine Description: Terminate access to metadata Arguments: None Return Value: TRUE if success, otherwise FALSE --*/ { DeleteCriticalSection( &g_cs ); if ( g_pMBCom ) { if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) { return FALSE; } g_pMBCom->Release(); g_pMBCom = NULL; } return TRUE; } BOOL CMetaData::GetMD( ) /*++ Routine Description: Initialize interface pointer to DCOM metabase object Arguments: None Return Value: TRUE if success, otherwise FALSE --*/ { if ( g_pMBCom == NULL ) { HRESULT hRes; hRes = CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_SERVER, IID_IMSAdminBase, (void**) &g_pMBCom); TR( (DEBUG_BUFFER,"[MD:GetMD] called cocreate\n") ); if ( hRes == CO_E_NOTINITIALIZED ) { TR( (DEBUG_BUFFER,"[MD:GetMD] call coinit\n") ); hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED); if ( FAILED(hRes) ) { TR( (DEBUG_BUFFER,"[MD:GetMD::CoInitializeEx] error %08x\n",HRESULTTOWIN32( hRes )) ); SetLastError( HRESULTTOWIN32( hRes ) ); return FALSE; } hRes = CoCreateInstance(CLSID_MSAdminBase, NULL, CLSCTX_SERVER, IID_IMSAdminBase, (void**) &g_pMBCom); } if ( FAILED(hRes) ) { TR( (DEBUG_BUFFER,"[MD:GetMD] error %08x\n",HRESULTTOWIN32( hRes )) ); SetLastError( HRESULTTOWIN32( hRes ) ); return FALSE; } } return TRUE; } BOOL CMetaData::ReleaseMD( ) /*++ Routine Description: Release interface pointer to DCOM metabase object Arguments: None Return Value: TRUE if success, otherwise FALSE --*/ { if ( g_pMBCom ) { g_pMBCom->Release(); g_pMBCom = NULL; } return TRUE; } SOCKET TcpSockConnectToHost( SOCKADDR* psaServer, DWORD dwPort, DWORD dwTimeOut ) /*++ Routine Description: Create a TCP connection to specified port on specified machine Arguments: psaServer - target address dwPort - target port to connect to dwTimeOut - time out for connection ( in seconds ) Return Value: socket or NULL if error --*/ { SOCKET sNew; BOOL fWrite = FALSE; INT serr = 0; SOCKADDR_IN inAddr; PSOCKADDR addr; INT addrLength; sNew = WSASocket( AF_INET, SOCK_STREAM, 0, NULL, // protocol info 0, // Group ID = 0 => no constraints WSA_FLAG_OVERLAPPED // completion port notifications ); if ( sNew == INVALID_SOCKET ) { TR( (DEBUG_BUFFER,"[TcpSockConnectToLocalHost] failed WSASocket, error %08x\n",GetLastError()) ); return NULL; } addrLength = sizeof(inAddr); ZeroMemory(&inAddr, addrLength); inAddr.sin_family = AF_INET; inAddr.sin_port = 0; ((PSOCKADDR_IN)psaServer)->sin_port = (unsigned short)htons((unsigned short)dwPort ); // // Bind an address to socket // if ( bind( sNew, (PSOCKADDR)&inAddr, addrLength ) == 0 && WSAConnect( sNew, (PSOCKADDR)psaServer, addrLength, NULL, NULL, NULL, NULL ) == 0 && WaitForSocketWorker( INVALID_SOCKET, sNew, NULL, &fWrite, dwTimeOut ) == 0 ) { return sNew; } TR( (DEBUG_BUFFER,"[TcpSockConnectToLocalHost] failed connect, error %08x\n",GetLastError()) ); closesocket( sNew ); return NULL; } VOID TcpSockClose( SOCKET hSocket ) /*++ Routine Description: Close a socket opened by TcpSockConnectToLocalHost Arguments: hSocket - socket opened by TcpSockConnectToLocalHost Return Value: Nothing --*/ { closesocket( hSocket ); } BOOL TcpSockSend( IN SOCKET sock, IN LPVOID pBuffer, IN DWORD cbBuffer, OUT PDWORD pcbTotalSent, IN DWORD nTimeout ) /*++ Description: Do async socket send Arguments: sock - socket pBuffer - buffer to send cbBuffer - size of buffer pcbTotalSent - bytes sent nTimeout - timeout in seconds to use Returns: FALSE if there is any error. TRUE otherwise --*/ { INT serr = 0; INT cbSent; DWORD dwBytesSent = 0; ULONG one; // // Loop until there's no more data to send. // while( cbBuffer > 0 ) { // // Wait for the socket to become writeable. // BOOL fWrite = FALSE; serr = WaitForSocketWorker( INVALID_SOCKET, sock, NULL, &fWrite, nTimeout ); if( serr == 0 ) { // // Write a block to the socket. // cbSent = send( sock, (CHAR *)pBuffer, (INT)cbBuffer, 0 ); if( cbSent < 0 ) { // // Socket error. // serr = WSAGetLastError(); } else { dwBytesSent += (DWORD)cbSent; } } if( serr != 0 ) { TR( (DEBUG_BUFFER,"[TcpSockSend] failed send, error %08x\n",serr) ); break; } pBuffer = (LPVOID)( (LPBYTE)pBuffer + cbSent ); cbBuffer -= (DWORD)cbSent; } if(pcbTotalSent) { *pcbTotalSent = dwBytesSent; } return (serr == 0); } // SockSend BOOL TcpSockRecv( IN SOCKET sock, IN LPVOID pBuffer, IN DWORD cbBuffer, OUT LPDWORD pbReceived, IN DWORD nTimeout ) /*++ Description: Do async socket recv Arguments: sock - The target socket. pBuffer - Will receive the data. cbBuffer - The size (in bytes) of the buffer. pbReceived - Will receive the actual number of bytes nTimeout - timeout in seconds Returns: TRUE, if successful --*/ { INT serr = 0; DWORD cbTotal = 0; INT cbReceived; DWORD dwBytesRecv = 0; ULONG one; BOOL fRead = FALSE; // // Wait for the socket to become readable. // serr = WaitForSocketWorker( sock, INVALID_SOCKET, &fRead, NULL, nTimeout ); if( serr == 0 ) { // // Read a block from the socket. // cbReceived = recv( sock, (CHAR *)pBuffer, (INT)cbBuffer, 0 ); if( cbReceived < 0 ) { // // Socket error. // serr = WSAGetLastError(); } else { cbTotal = cbReceived; } } else { TR( (DEBUG_BUFFER,"[TcpSockRecv] failed send, error %08x\n",serr) ); } if( serr == 0 ) { // // Return total byte count to caller. // *pbReceived = cbTotal; } return (serr == 0); } // SockRecv INT WaitForSocketWorker( IN SOCKET sockRead, IN SOCKET sockWrite, IN LPBOOL pfRead, IN LPBOOL pfWrite, IN DWORD nTimeout ) /*++ Description: Wait routine NOTES: Any (but not all) sockets may be INVALID_SOCKET. For each socket that is INVALID_SOCKET, the corresponding pf* parameter may be NULL. Arguments: sockRead - The socket to check for readability. sockWrite - The socket to check for writeability. pfRead - Will receive TRUE if sockRead is readable. pfWrite - Will receive TRUE if sockWrite is writeable. nTimeout - timeout in seconds Returns: SOCKERR - 0 if successful, !0 if not. Will return WSAETIMEDOUT if the timeout period expired. --*/ { INT serr = 0; TIMEVAL timeout; LPTIMEVAL ptimeout; fd_set fdsRead; fd_set fdsWrite; INT res; // // Ensure we got valid parameters. // if( ( sockRead == INVALID_SOCKET ) && ( sockWrite == INVALID_SOCKET ) ) { return WSAENOTSOCK; } timeout.tv_sec = (LONG )nTimeout; if( timeout.tv_sec == 0 ) { // // If the connection timeout == 0, then we have no timeout. // So, we block and wait for the specified conditions. // ptimeout = NULL; } else { // // The connectio timeout is > 0, so setup the timeout structure. // timeout.tv_usec = 0; ptimeout = &timeout; } for( ; ; ) { // // Setup our socket sets. // FD_ZERO( &fdsRead ); FD_ZERO( &fdsWrite ); if( sockRead != INVALID_SOCKET ) { FD_SET( sockRead, &fdsRead ); *pfRead = FALSE; } if( sockWrite != INVALID_SOCKET ) { FD_SET( sockWrite, &fdsWrite ); *pfWrite = FALSE; } // // Wait for one of the conditions to be met. // res = select( 0, &fdsRead, &fdsWrite, NULL, ptimeout ); if( res == 0 ) { // // Timeout. // serr = WSAETIMEDOUT; break; } else if( res == SOCKET_ERROR ) { // // Bad news. // serr = WSAGetLastError(); TR( (DEBUG_BUFFER,"[WaitForSocketWorker] failed send, error %08x\n",serr) ); break; } else { BOOL fSomethingWasSet = FALSE; if( pfRead != NULL ) { *pfRead = FD_ISSET( sockRead, &fdsRead ); fSomethingWasSet = TRUE; } if( pfWrite != NULL ) { *pfWrite = FD_ISSET( sockWrite, &fdsWrite ); fSomethingWasSet = TRUE; } if( fSomethingWasSet ) { // // Success. // serr = 0; break; } else { // // select() returned with neither a timeout, nor // an error, nor any bits set. This feels bad... // continue; } } } return serr; } // WaitForSocketWorker() #if defined(_DEBUG_SUPPORT) void TimeStamp( FILE* f ) { time_t t = time( NULL ); struct tm *pTm; if ( pTm = localtime( &t ) ) { fprintf( f, "%02d:%02d:%02d> ", pTm->tm_hour, pTm->tm_min, pTm->tm_sec ); } } void InitDebug( ) { char achPath[MAX_PATH]; HKEY hKey; DWORD dwValue; DWORD dwType; DWORD dwLen; BOOL fDoDebug = FALSE; BOOL fAppend = TRUE; INT cL = 0; LPSTR pTmp; // // get debug flag from registry // if ( RegOpenKey( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\W3SVC\\Parameters", &hKey ) == ERROR_SUCCESS ) { TR( (DEBUG_BUFFER,"[InitDebug] opened key\n") ); dwLen = sizeof( dwValue ); if ( RegQueryValueEx( hKey, L"ClusterDebugMode", NULL, &dwType, (LPBYTE)&dwValue, &dwLen ) == ERROR_SUCCESS && dwType == REG_DWORD ) { fDoDebug = !!dwValue; } dwLen = sizeof( dwValue ); if ( RegQueryValueEx( hKey, L"ClusterDebugAppendToFile", NULL, &dwType, (LPBYTE)&dwValue, &dwLen ) == ERROR_SUCCESS && dwType == REG_DWORD ) { fAppend = !!dwValue; } dwLen = sizeof( achPath ); if ( RegQueryValueExA( hKey, "ClusterDebugPath", NULL, &dwType, (LPBYTE)achPath, &dwLen ) == ERROR_SUCCESS && dwType == REG_SZ ) { if ( dwLen ) { cL = dwLen - 1; if ( cL && achPath[cL-1] != '\\' ) { achPath[cL++] = '\\'; } } } RegCloseKey( hKey ); } if ( fDoDebug && cL ) { memcpy( achPath + cL, "iisclus.trc", sizeof("iisclus.trc") ); debug_file = fopen(achPath, fAppend ? "a" : "w" ); } } #endif DWORD WINAPI ResUtilReadProperties( IN HKEY RegistryKey, IN const PRESUTIL_PROPERTY_ITEM PropertyTable, IN OUT LPBYTE OutParams, IN RESOURCE_HANDLE ResourceHandle, IN PLOG_EVENT_ROUTINE LogEvent ) /*++ Routine Description: Read properties based on a property table. Arguments: RegistryKey - Supplies the cluster key where the properties are stored. PropertyTable - Pointer to the property table to process. OutParams - Parameter block to read into. ResourceHandle - Handle for the resource fo which properties are being read. LogEvent - Function to call to log events to the cluster log. Return Value: ERROR_SUCCESS - Properties read successfully. ERROR_INVALID_DATA - Required property not present. --*/ { PRESUTIL_PROPERTY_ITEM propertyItem = PropertyTable; HKEY key; DWORD status = ERROR_SUCCESS; LPWSTR pszInValue; LPBYTE pbInValue; DWORD dwInValue; LPWSTR * ppszOutValue; LPBYTE * ppbOutValue; LPDWORD pdwOutValue; while ( propertyItem->Name != NULL ) { // // If the value resides at a different location, create the key. // if ( propertyItem->KeyName != NULL ) { DWORD disposition; status = ClusterRegCreateKey( RegistryKey, propertyItem->KeyName, 0, KEY_ALL_ACCESS, NULL, &key, &disposition ); if ( status != ERROR_SUCCESS ) { return(status); } } else { key = RegistryKey; } switch ( propertyItem->Format ) { case CLUSPROP_FORMAT_DWORD: pdwOutValue = (LPDWORD) &OutParams[propertyItem->Offset]; status = ResUtilGetDwordValue( RegistryKey, propertyItem->Name, pdwOutValue, propertyItem->Default ); if ( (status == ERROR_FILE_NOT_FOUND) && !(propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED) ) { *pdwOutValue = propertyItem->Default; } break; case CLUSPROP_FORMAT_SZ: case CLUSPROP_FORMAT_EXPAND_SZ: ppszOutValue = (LPWSTR*) &OutParams[propertyItem->Offset]; pszInValue = ResUtilGetSzValue( RegistryKey, propertyItem->Name ); if ( pszInValue == NULL ) { status = GetLastError(); if ( (status == ERROR_FILE_NOT_FOUND) && !(propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED) ) { if ( *ppszOutValue != NULL ) { LocalFree( *ppszOutValue ); *ppszOutValue = NULL; } // If a default is specified, copy it. if ( propertyItem->lpDefault != NULL ) { *ppszOutValue = (LPWSTR)LocalAlloc( LMEM_FIXED, (lstrlenW( (LPCWSTR) propertyItem->lpDefault ) + 1) * sizeof(WCHAR) ); if ( *ppszOutValue == NULL ) { status = GetLastError(); } else { lstrcpyW( *ppszOutValue, (LPCWSTR) propertyItem->lpDefault ); } } } } else { if ( *ppszOutValue != NULL ) { LocalFree( *ppszOutValue ); } *ppszOutValue = pszInValue; } break; case CLUSPROP_FORMAT_MULTI_SZ: case CLUSPROP_FORMAT_BINARY: ppbOutValue = (LPBYTE*) &OutParams[propertyItem->Offset]; pdwOutValue = (PDWORD) &OutParams[propertyItem->Offset+sizeof(LPBYTE*)]; status = ResUtilGetBinaryValue( RegistryKey, propertyItem->Name, &pbInValue, &dwInValue ); if ( status == ERROR_SUCCESS ) { if ( *ppbOutValue != NULL ) { LocalFree( *ppbOutValue ); } *ppbOutValue = pbInValue; *pdwOutValue = dwInValue; } else if ( (status == ERROR_FILE_NOT_FOUND) && !(propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED) ) { if ( *ppbOutValue != NULL ) { LocalFree( *ppbOutValue ); *ppbOutValue = NULL; *pdwOutValue = 0; } // If a default is specified, copy it. if ( propertyItem->lpDefault != NULL ) { *ppbOutValue = (LPBYTE)LocalAlloc( LMEM_FIXED, propertyItem->Minimum ); if ( *ppbOutValue == NULL ) { status = GetLastError(); } else { memcpy( *ppbOutValue, propertyItem->lpDefault, propertyItem->Minimum ); *pdwOutValue = propertyItem->Minimum; } } } break; } // // Close the key if we opened it. // if ( (propertyItem->KeyName != NULL) && (key != NULL) ) { ClusterRegCloseKey( key ); } // // Handle any errors that occurred. // if ( status != ERROR_SUCCESS ) { (LogEvent)( ResourceHandle, LOG_ERROR, L"Unable to read the '%1' property. Error: %2!u!.\n", propertyItem->Name, status ); if ( propertyItem->Flags & RESUTIL_PROPITEM_REQUIRED ) { if ( status == ERROR_FILE_NOT_FOUND ) { status = ERROR_INVALID_DATA; } break; } else { status = ERROR_SUCCESS; } } // // Advance to the next property. // propertyItem++; } return(status); } // ResUtilReadProperties