/*++ Copyright (c) 1996-1999 Microsoft Corporation Module Name: node.c Abstract: Fix up Routines for Upgrade and Rolling Upgrades Author: Sunita Shrivastava(sunitas) 18-Mar-1998 Revision History: --*/ /**** @doc EXTERNAL INTERFACES CLUSSVC DM ****/ #define UNICODE 1 #include "nmp.h" // // Cluster registry API function pointers. // CLUSTER_REG_APIS NmpFixupRegApis = { (PFNCLRTLCREATEKEY) DmRtlCreateKey, (PFNCLRTLOPENKEY) DmRtlOpenKey, (PFNCLRTLCLOSEKEY) DmCloseKey, (PFNCLRTLSETVALUE) DmSetValue, (PFNCLRTLQUERYVALUE) DmQueryValue, (PFNCLRTLENUMVALUE) DmEnumValue, (PFNCLRTLDELETEVALUE) DmDeleteValue, (PFNCLRTLLOCALCREATEKEY) DmRtlLocalCreateKey, (PFNCLRTLLOCALSETVALUE) DmLocalSetValue, (PFNCLRTLLOCALDELETEVALUE) DmLocalDeleteValue, }; // // Data // RESUTIL_PROPERTY_ITEM NmJoinFixupSDProperties[]= { { CLUSREG_NAME_CLUS_SD, NULL, CLUSPROP_FORMAT_BINARY, 0, 0, 0, 0, 0 }, { 0 } }; // Fixup Table for WINS RESUTIL_PROPERTY_ITEM NmJoinFixupWINSProperties[]= { { CLUSREG_NAME_RESTYPE_IS_ALIVE, CLUS_RESTYPE_NAME_WINS , CLUSPROP_FORMAT_DWORD, 0,CLUSTER_RESTYPE_MINIMUM_IS_ALIVE ,CLUSTER_RESTYPE_MAXIMUM_IS_ALIVE , 0, 0 }, { CLUSREG_NAME_RESTYPE_LOOKS_ALIVE,CLUS_RESTYPE_NAME_WINS ,CLUSPROP_FORMAT_DWORD, 0,CLUSTER_RESTYPE_MINIMUM_LOOKS_ALIVE ,CLUSTER_RESTYPE_MAXIMUM_LOOKS_ALIVE , 0, sizeof(DWORD) }, { CLUSREG_NAME_RESTYPE_DLL_NAME,CLUS_RESTYPE_NAME_WINS ,CLUSPROP_FORMAT_SZ, 0,0,0, 0, 2*sizeof(DWORD) }, { CLUSREG_NAME_RESTYPE_NAME,CLUS_RESTYPE_NAME_WINS ,CLUSPROP_FORMAT_SZ, 0,0,0, 0, 2*sizeof(DWORD)+sizeof(LPWSTR*) }, { CLUSREG_NAME_RESTYPE_ADMIN_EXTENSIONS,CLUS_RESTYPE_NAME_WINS,CLUSPROP_FORMAT_MULTI_SZ, 0,0,0, 0, 2*sizeof(DWORD)+2*sizeof(LPWSTR*) }, { 0 } }; //NmJoinFixupWINSProperties //Fixup Table for DHCP RESUTIL_PROPERTY_ITEM NmJoinFixupDHCPProperties[]= { { CLUSREG_NAME_RESTYPE_IS_ALIVE, CLUS_RESTYPE_NAME_DHCP , CLUSPROP_FORMAT_DWORD, 0,CLUSTER_RESTYPE_MINIMUM_IS_ALIVE ,CLUSTER_RESTYPE_MAXIMUM_IS_ALIVE , 0, 0 }, { CLUSREG_NAME_RESTYPE_LOOKS_ALIVE,CLUS_RESTYPE_NAME_DHCP ,CLUSPROP_FORMAT_DWORD, 0,CLUSTER_RESTYPE_MINIMUM_LOOKS_ALIVE ,CLUSTER_RESTYPE_MAXIMUM_LOOKS_ALIVE , 0, sizeof(DWORD) }, { CLUSREG_NAME_RESTYPE_DLL_NAME,CLUS_RESTYPE_NAME_DHCP ,CLUSPROP_FORMAT_SZ, 0,0,0, 0, 2*sizeof(DWORD) }, { CLUSREG_NAME_RESTYPE_NAME,CLUS_RESTYPE_NAME_DHCP ,CLUSPROP_FORMAT_SZ, 0,0,0, 0, 2*sizeof(DWORD)+sizeof(LPWSTR*) }, { CLUSREG_NAME_RESTYPE_ADMIN_EXTENSIONS,CLUS_RESTYPE_NAME_DHCP,CLUSPROP_FORMAT_MULTI_SZ, 0,0,0, 0, 2*sizeof(DWORD)+2*sizeof(LPWSTR*) }, { 0 } };//NmJoinFixupDHCPProperties RESUTIL_PROPERTY_ITEM NmJoinFixupNewMSMQProperties[]= { { CLUSREG_NAME_RESTYPE_IS_ALIVE, CLUS_RESTYPE_NAME_NEW_MSMQ,CLUSPROP_FORMAT_DWORD, 0,CLUSTER_RESTYPE_MINIMUM_IS_ALIVE ,CLUSTER_RESTYPE_MAXIMUM_IS_ALIVE , 0, 0 }, { CLUSREG_NAME_RESTYPE_LOOKS_ALIVE,CLUS_RESTYPE_NAME_NEW_MSMQ,CLUSPROP_FORMAT_DWORD, 0,CLUSTER_RESTYPE_MINIMUM_LOOKS_ALIVE ,CLUSTER_RESTYPE_MAXIMUM_LOOKS_ALIVE , 0, sizeof(DWORD) }, { CLUSREG_NAME_RESTYPE_DLL_NAME,CLUS_RESTYPE_NAME_NEW_MSMQ,CLUSPROP_FORMAT_SZ, 0,0,0, 0, 2*sizeof(DWORD) }, { CLUSREG_NAME_RESTYPE_NAME,CLUS_RESTYPE_NAME_NEW_MSMQ,CLUSPROP_FORMAT_SZ, 0,0,0, 0, 2*sizeof(DWORD)+sizeof(LPWSTR*) }, { 0 } };//NmJoinFixupNewMSMQProperties //Fixup Table for changing thr dl name of MSDTC resource type RESUTIL_PROPERTY_ITEM NmJoinFixupMSDTCProperties[]= { { CLUSREG_NAME_RESTYPE_DLL_NAME,CLUS_RESTYPE_NAME_MSDTC,CLUSPROP_FORMAT_SZ, 0,0,0, 0, 0 }, { 0 } };//NmJoinFixupMSDTCProperties //Fixup table for node version info RESUTIL_PROPERTY_ITEM NmFixupVersionInfo[]= { { CLUSREG_NAME_NODE_MAJOR_VERSION,NULL,CLUSPROP_FORMAT_DWORD, 0,0,((DWORD)-1), 0, 0 }, { CLUSREG_NAME_NODE_MINOR_VERSION,NULL,CLUSPROP_FORMAT_DWORD, 0,0,((DWORD)-1), 0, sizeof(DWORD) }, { CLUSREG_NAME_NODE_BUILD_NUMBER,NULL,CLUSPROP_FORMAT_DWORD, 0,0,((DWORD)-1), 0, 2*sizeof(DWORD) }, { CLUSREG_NAME_NODE_CSDVERSION,NULL,CLUSPROP_FORMAT_SZ, 0,0,0, 0, 3*sizeof(DWORD) }, { CLUSREG_NAME_NODE_PRODUCT_SUITE,NULL,CLUSPROP_FORMAT_DWORD, 0,0,((DWORD)-1), 0, 3*sizeof(DWORD) + sizeof(LPWSTR*) }, { 0 } }; //NmFixupVersionInfo RESUTIL_PROPERTY_ITEM NmFixupClusterProperties[] = { { CLUSREG_NAME_ADMIN_EXT, NULL, CLUSPROP_FORMAT_MULTI_SZ, 0, 0, 0, 0, 0 }, { 0 } }; //Used by NmUpdatePerformFixups update RESUTIL_PROPERTY_ITEM NmpJoinFixupProperties[] = { { CLUSREG_NAME_CLUS_SD, NULL, CLUSPROP_FORMAT_BINARY, 0, 0, 0, 0, 0 }, { 0 } }; RESUTIL_PROPERTY_ITEM NmpFormFixupProperties[] = { { CLUSREG_NAME_CLUS_SD, NULL, CLUSPROP_FORMAT_BINARY, 0, 0, 0, 0, 0 }, { 0 } }; NM_FIXUP_CB_RECORD FixupTable[]= { { ApiFixupNotifyCb, NM_FORM_FIXUP|NM_JOIN_FIXUP} }; // Fixup Table used for NmUpdatePerformFixups2 update type NM_FIXUP_CB_RECORD2 FixupTable2[]= { { ApiFixupNotifyCb, NM_FORM_FIXUP|NM_JOIN_FIXUP, NmpJoinFixupProperties}, { FmBuildWINS, NM_FORM_FIXUP|NM_JOIN_FIXUP, NmJoinFixupWINSProperties,}, { FmBuildDHCP, NM_FORM_FIXUP|NM_JOIN_FIXUP, NmJoinFixupDHCPProperties}, { FmBuildNewMSMQ, NM_FORM_FIXUP|NM_JOIN_FIXUP, NmJoinFixupNewMSMQProperties}, { FmBuildMSDTC, NM_FORM_FIXUP|NM_JOIN_FIXUP, NmJoinFixupMSDTCProperties}, { NmpBuildVersionInfo, NM_JOIN_FIXUP|NM_FORM_FIXUP, NmFixupVersionInfo}, { FmBuildClusterProp, NM_FORM_FIXUP|NM_JOIN_FIXUP,NmFixupClusterProperties} }; NM_POST_FIXUP_CB PostFixupCbTable[]= { NULL, FmFixupNotifyCb, FmFixupNotifyCb, FmFixupNotifyCb, FmFixupNotifyCb, NmFixupNotifyCb, NULL }; /**** @func DWORD | NmPerformFixups| This is called when a cluster is being formed/or joining. Issues NmUpdateperforfixups GUM update for registry fixups of SECURITY_DESCRIPTOR when NT5 node joins NT4 node.Also issues NmUpdateperforfixups2 GUM update for WINS and DHCP fixups when NT5 joins NT4. This update type can be extended in future to do more fixups for postNT5 and NT5 scenario. If later it is guaranteed that there is no NT4 node in cluster, NmUpdatePerformFixups update type won't be needed and hence can be taken out. @comm The first time the cluster service forms a cluster after an upgrade, it might have registry fixups it wants to make to the existing cluster registry. Also, when an uplevel node joins a downlevel node, join fixups can be performed. Note that this is called on every form/join @rdesc Returns a result code. ERROR_SUCCESS on success. @xref ****/ DWORD NmPerformFixups( IN DWORD dwFixupType) { DWORD dwCount = sizeof(FixupTable)/sizeof(NM_FIXUP_CB_RECORD); DWORD dwStatus = ERROR_SUCCESS; PNM_FIXUP_CB_RECORD pFixupCbRec; PNM_FIXUP_CB_RECORD2 pFixupCbRec2; PVOID pPropertyList = NULL; DWORD dwPropertyListSize; DWORD i,j; DWORD dwSize; DWORD Required; LPWSTR szKeyName=NULL; LPBYTE pBuffer=NULL; PRESUTIL_PROPERTY_ITEM pPropertyItem; ClRtlLogPrint(LOG_NOISE,"[NM] NmPerformFixups Entry, dwFixupType=%1!u!\r\n", dwFixupType); // using old gum update handler - this is needed to maintain compataility // with NT4, can be discarded later. See comments above. // for (i=0; i < dwCount ; i++) { pFixupCbRec = &FixupTable[i]; //if this fixup doesnt need to be applied, skip it if (!(pFixupCbRec->dwFixupMask & dwFixupType)) continue; dwStatus = (pFixupCbRec->pfnFixupNotifyCb)(dwFixupType, &pPropertyList, &dwPropertyListSize,&szKeyName); if (dwStatus != ERROR_SUCCESS) { goto FnExit; } if (pPropertyList && dwPropertyListSize) { // // Issue a global update // dwStatus = GumSendUpdateEx( GumUpdateMembership, NmUpdatePerformFixups, 2, dwPropertyListSize, pPropertyList, sizeof(DWORD), &dwPropertyListSize ); LocalFree(pPropertyList); pPropertyList = NULL; if(szKeyName) { LocalFree(szKeyName); szKeyName=NULL; } } if (dwStatus != ERROR_SUCCESS) goto FnExit; } // Rohit (rjain): introduced new update type to fix the registry and // in-memory structures after a node with a higher version of clustering // service joins a node with a lower version. To make it extensible in future // all the information needed for a fixup is passed as arguments to the fixup // function. Any new fix can be added by adding suitable record to FixupTable2 and // a postfixup function callback to PostfixupCbTable. dwCount= sizeof(FixupTable2)/sizeof(NM_FIXUP_CB_RECORD2); for (i=0; i < dwCount ; i++) { pFixupCbRec2 = &FixupTable2[i]; //if this fixup doesnt need to be applied, skip it if (!(pFixupCbRec2->dwFixupMask & dwFixupType)) continue; dwStatus = (pFixupCbRec2->pfnFixupNotifyCb)(dwFixupType, &pPropertyList, &dwPropertyListSize,&szKeyName); if (dwStatus != ERROR_SUCCESS) { goto FnExit; } if (pPropertyList && dwPropertyListSize) { // Marshall PropertyTable into byte array Required=sizeof(DWORD); AllocMem: pBuffer=(LPBYTE)LocalAlloc(LMEM_FIXED,Required); if (pBuffer==NULL) { dwStatus=GetLastError(); goto FnExit; } dwSize=Required; dwStatus=ClRtlMarshallPropertyTable(pFixupCbRec2->pPropertyTable,dwSize,pBuffer,&Required); if(dwStatus!= ERROR_SUCCESS) { LocalFree(pBuffer); pBuffer=NULL; // ClRtlLogPrint(LOG_NOISE,"[NM] NmPerformFixups - Memory Required=%1!u!\r\n", // Required); goto AllocMem; } // // Issue a global update // dwStatus = GumSendUpdateEx( GumUpdateMembership, NmUpdatePerformFixups2, 5, dwPropertyListSize, pPropertyList, sizeof(DWORD), &dwPropertyListSize, sizeof(DWORD), &i, (lstrlenW(szKeyName)+1)*sizeof(WCHAR), szKeyName, Required, pBuffer ); LocalFree(pPropertyList); pPropertyList = NULL; LocalFree(pBuffer); pBuffer= NULL; if(szKeyName) { LocalFree(szKeyName); szKeyName= NULL; } } if (dwStatus != ERROR_SUCCESS) break; } FnExit: if(szKeyName) { LocalFree(szKeyName); szKeyName=NULL; } ClRtlLogPrint(LOG_NOISE,"[NM] NmPerformFixups Exit, dwStatus=%1!u!\r\n", dwStatus); return(dwStatus); } //NmPerformFixups /**** @func DWORD | NmpUpdatePerformFixups| The gum update handler for doing the registry fixups. @parm IN DWORD | IsSourceNode| If the gum request originated at this node. @parm IN PVOID| pPropertyList| Pointer to a property list structure. @parm IN DWORD | pdwPropertyListSize | A pointer to a DWORD containing the size of the property list structure. @comm The gum update handler commits this fixup to the cluster registry as a transaction. @rdesc Returns a result code. ERROR_SUCCESS on success. @xref ****/ DWORD NmpUpdatePerformFixups( IN BOOL IsSourceNode, IN PVOID pPropertyList, IN LPDWORD pdwPropertyListSize ) { DWORD dwStatus = ERROR_SUCCESS; HLOCALXSACTION hXaction; if (!NmpEnterApi(NmStateOnline)) { ClRtlLogPrint(LOG_NOISE, "[NM] Not in valid state to process PerformFixups update. " "update.\n" ); return(ERROR_NODE_NOT_AVAILABLE); } // // Begin a transaction // hXaction = DmBeginLocalUpdate(); if (hXaction != NULL) { dwStatus = ClRtlSetPropertyTable( hXaction, DmClusterParametersKey, &NmpFixupRegApis, NmpJoinFixupProperties, NULL, FALSE, pPropertyList, *pdwPropertyListSize, FALSE /*bForceWrite*/, NULL ); if (dwStatus == ERROR_SUCCESS) { DmCommitLocalUpdate(hXaction); } else { DmAbortLocalUpdate(hXaction); } } else { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[NM] Failed to begin a transaction " "to perform fixups, status %1!u!\n", dwStatus ); } NmpLeaveApi(); return(dwStatus); } // NmpUpdatePerformFixups /**** @func DWORD | NmpUpdatePerformFixups2| The gum update handler for doing the registry fixups and updating in-memory fixups.New fixups can be added by adding suitable record to FixupTable2. However, for these new fixups only registry fixup will be carried out. @parm IN DWORD | IsSourceNode| If the gum request originated at this node. @parm IN PVOID| pPropertyList| Pointer to a property list structure. @parm IN DWORD | pdwPropertyListSize | Pointer to a DWORD containing the size of the property list structure. @parm IN LPDWORD | lpdeFixupNum | Pointer to DWORD which specifies the the index in NmpJoinFixupProperties table. @parm IN PVOID | lpKeyName | Registry Key which needs to be updated @parm IN PVOID | pPropertyBuffer| Registry update table in marshalled form @comm The gum update handler commits this fixup to the cluster registry as a transaction. @rdesc Returns a result code. ERROR_SUCCESS on success. @xref ****/ DWORD NmpUpdatePerformFixups2( IN BOOL IsSourceNode, IN PVOID pPropertyList, IN LPDWORD pdwPropertyListSize, IN LPDWORD lpdwFixupNum, IN PVOID lpKeyName, IN PVOID pPropertyBuffer ) { DWORD dwStatus = ERROR_SUCCESS; HLOCALXSACTION hXaction = NULL; HDMKEY hdmKey; PRESUTIL_PROPERTY_ITEM pPropertyTable=NULL; PRESUTIL_PROPERTY_ITEM propertyItem; if (!NmpEnterApi(NmStateOnline)) { ClRtlLogPrint(LOG_NOISE, "[NM] Not in valid state to process PerformFixups2 update. " "update.\n" ); return(ERROR_NODE_NOT_AVAILABLE); } if(pPropertyBuffer == NULL) { ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpUpdatePerformJoinFixups2: Bad Arguments\n" ); dwStatus = ERROR_BAD_ARGUMENTS; goto FnExit; } // Begin a transaction // hXaction = DmBeginLocalUpdate(); if (hXaction == NULL) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpUpdatePerformJoinFixups2: Failed to begin a transaction, " "status %1!u!\n", dwStatus ); goto FnExit; } // special case - if fixup is for the property of key "Cluster" if(!lstrcmpW((LPCWSTR)lpKeyName,CLUSREG_KEYNAME_CLUSTER)) { hdmKey=DmClusterParametersKey; } else { hdmKey=DmOpenKey(DmClusterParametersKey, (LPCWSTR)lpKeyName, KEY_ALL_ACCESS ); if (hdmKey == NULL) { dwStatus = GetLastError(); ClRtlLogPrint(LOG_CRITICAL, "[NM] NmpUpdatePerformJoinFixups2: DmOpenKey failed to " "open key %1!ws! : error %2!u!\n", lpKeyName,dwStatus); goto FnExit; } } //unmarshall pPropertyBuffer into a RESUTIL_PROPERTY_ITEM table // dwStatus=ClRtlUnmarshallPropertyTable(&pPropertyTable,pPropertyBuffer); if(dwStatus != ERROR_SUCCESS) goto FnExit; dwStatus=ClRtlSetPropertyTable( hXaction, hdmKey, &NmpFixupRegApis, pPropertyTable, NULL, FALSE, pPropertyList, *pdwPropertyListSize, FALSE, // bForceWrite NULL ); if (dwStatus != ERROR_SUCCESS) { goto FnExit; } // callback function to update in-memory structures // for any new fixup introduced in later version, // the in-memory fixups will not be applied. if (*lpdwFixupNum < (sizeof(PostFixupCbTable)/sizeof(NM_POST_FIXUP_CB))) { if (PostFixupCbTable[*lpdwFixupNum] !=NULL){ dwStatus=(PostFixupCbTable[*lpdwFixupNum])(); ClRtlLogPrint(LOG_UNUSUAL, "[NM] NmpUpdatePerformJoinFixups2: called postfixup " "notifycb function with status %1!u!\n", dwStatus ); } } FnExit: if (hXaction != NULL) { if (dwStatus == ERROR_SUCCESS){ DmCommitLocalUpdate(hXaction); } else { DmAbortLocalUpdate(hXaction); } } if((hdmKey!= DmClusterParametersKey) && (hdmKey!= NULL)) { DmCloseKey(hdmKey); } if (pPropertyTable != NULL) { // Free pPropertyTable structure propertyItem=pPropertyTable; if(propertyItem!=NULL){ while(propertyItem->Name != NULL) { LocalFree(propertyItem->Name); if(propertyItem->KeyName!=NULL) LocalFree(propertyItem->KeyName); propertyItem++; } LocalFree(pPropertyTable); } } NmpLeaveApi(); return(dwStatus); } // NmpUpdatePerformFixups2 DWORD NmFixupNotifyCb(VOID) { ClRtlLogPrint(LOG_NOISE, "[NM] NmFixupNotifyCb: Calculating Cluster Node Limit\r\n"); //update the product suite information //when the node objects are created we assume that the suite //type is Enterprise. //Here after a node has joined and informed us about its suite //type by making fixups to the registry, we read the registry //and update the node structure NmpRefreshNodeObjects(); //recalc the cluster node limit NmpResetClusterNodeLimit(); //SS: This is ugly---we should pass in the product suits early on //also the fixup interface needs to to be richer so that the postcallback //function nodes whether it is a form fixup or a join fixup and if it //is a join fixup, which node is joining. This could certainly optimize //some of the fixup processing return(ERROR_SUCCESS); }