/*------------------------------------------------------------------------- * Config.CPP * * Implementation information for classes that organize settings used by * an application, abstracting where those settings actually come from. * * Owner: MarkSn * * Copyright 1986-1998 Microsoft Corporation, All Rights Reserved *-----------------------------------------------------------------------*/ //#include "pch.h" #define __MODULE__ "Sentinal" #include "zlib.h" #include "srvdbg.h" #include "config.h" BOOL RegConfigReader::DeleteItem(const char *pszItem) { HKEY hkey; BOOL fRet = FALSE; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, m_szKey, 0, KEY_READ | KEY_SET_VALUE, &hkey) == ERROR_SUCCESS) { if (RegDeleteValue (hkey, pszItem) == ERROR_SUCCESS) { fRet = TRUE; } RegCloseKey(hkey); } return fRet; } BOOL RegConfigReader::Open() { if (m_szKey[0]) { DWORD dw; // Gets result of whether it opened or created... if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, m_szKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &m_hk, &dw) == ERROR_SUCCESS) return TRUE; SRVDBG_Info2("Missing registry key", m_szKey); } return FALSE; } BOOL RegConfigReader::Close() { return (ERROR_SUCCESS == RegCloseKey(m_hk)); } BOOL RegConfigReader::FastReadInt(const char *pszItem, int *pnVal, BOOL fRequired /* = TRUE */, int nDefault /* = 0 */) { ZAssert(pszItem); ZAssert(pnVal); DWORD dwSize = sizeof(DWORD); if (RegQueryValueEx(m_hk, pszItem, NULL, NULL, (BYTE *)pnVal, &dwSize) != ERROR_SUCCESS) { if (fRequired) { SRVDBG_Info3("Missing registry value", m_szKey, pszItem); return FALSE; } *pnVal = nDefault; } return TRUE; } BOOL RegConfigReader::FastReadSz(const char *pszItem, char *psz, int cchMax, BOOL fRequired /* = TRUE */, const char *pszDefault /* = NULL */) { ZAssert(pszItem); ZAssert(psz); ZAssert(fRequired || pszDefault); ZAssert((NULL == pszDefault) || (lstrlen(pszDefault) < cchMax-1)); DWORD dwSize = cchMax; if (RegQueryValueEx(m_hk, pszItem, NULL, NULL, (BYTE *)psz, &dwSize) != ERROR_SUCCESS) { if (fRequired) { SRVDBG_Info3("Missing registry key", m_szKey, pszItem); return FALSE; } lstrcpy(psz, pszDefault); } return TRUE; } BOOL RegConfigReader::FastWriteInt(const char *pszItem, int nVal) { return (RegSetValueEx(m_hk, pszItem, 0, REG_DWORD, (BYTE *)&nVal, sizeof(nVal)) == ERROR_SUCCESS); } BOOL RegConfigReader::FastWriteSz (const char *pszItem, const char *psz, int cch) { return (RegSetValueEx(m_hk, pszItem, 0, REG_SZ, (BYTE *)psz, cch) == ERROR_SUCCESS); } BOOL RegConfigReader::CompIdToKey() { switch (m_compId) { case kCompId_Sentinal: lstrcpy(m_szKey, HKLM_Sentinal); break; // add cases for other components here // case kCompId_: // break; default: ZAssert(FALSE); SRVDBG_Info("Bogus component specified to config reader"); m_szKey[0] = 0; return FALSE; } return TRUE; } BOOL Config::Init(const ConfigInfo *pConfigItems, int nItems, BOOL fRange, int cRange) { ZAssert(pConfigItems); KillArgs(); m_fRange = fRange; m_cRange = cRange; m_nArgs = nItems; m_pArgs = new ARGS[nItems]; ZAssert(m_pArgs); // all new's are assumed to succeed... m_pConfigInfo = new ConfigInfoBuffer[nItems]; ZAssert(m_pConfigInfo); // all new's assumed to succeed... for (int i=0; i < nItems; i++) { m_pConfigInfo[i].info.pszKeyName = new char[lstrlen(pConfigItems[i].pszKeyName) + 1]; lstrcpy(m_pConfigInfo[i].info.pszKeyName, pConfigItems[i].pszKeyName); m_pConfigInfo[i].info.type = pConfigItems[i].type; m_pConfigInfo[i].info.cb = pConfigItems[i].cb; m_pConfigInfo[i].info.nRequired = pConfigItems[i].nRequired; m_pArgs[i].type = pConfigItems[i].type; m_pArgs[i].cb = pConfigItems[i].cb; if (m_pArgs[i].type == kConfigType_SZ) { m_pArgs[i].pszVal = new char[pConfigItems[i].cb * (fRange ? cRange : 1)]; ZAssert(m_pArgs[i].pszVal); // all new's are assumed to succeed. char *pszDef = pConfigItems[i].pszDefault; if (NULL == pszDef) pszDef = ""; m_pConfigInfo[i].info.pszDefault = new char[lstrlen(pszDef) + 1]; ZAssert(m_pConfigInfo[i].info.pszDefault); // all new's assumed to succeed. lstrcpy(m_pConfigInfo[i].info.pszDefault, pszDef); m_pConfigInfo[i].pBuf = (BYTE *)m_pArgs[i].pszVal; m_pConfigInfo[i].info.cb = pConfigItems[i].cb; } else { if (fRange) { m_pArgs[i].pdwVal = new DWORD[cRange]; ZAssert(m_pArgs[i].pdwVal); // all new's assumed to succeed. m_pConfigInfo[i].pBuf = (BYTE *)m_pArgs[i].pdwVal; } else { m_pConfigInfo[i].pBuf = (BYTE *)&(m_pArgs[i].dwVal); } m_pConfigInfo[i].info.dwDefault = pConfigItems[i].dwDefault; m_pConfigInfo[i].info.cb = sizeof(DWORD); } } ConfigReader *pMgr = CreateConfigReader(m_compId); ZAssert(pMgr); BOOL fRet; if (fRange) fRet = pMgr->ReadRgRg(cRange, m_pConfigInfo, nItems); else fRet = pMgr->ReadRg(m_pConfigInfo, nItems); if (!fRet) { KillArgs(); } delete pMgr; return fRet; } //////////////////////////////////////////////////////////////////////// void Config::KillArgs() { if (m_pArgs) { ZAssert(m_pConfigInfo); for (DWORD i=0; i < m_nArgs; i++) { delete [] m_pConfigInfo[i].info.pszKeyName; // bugbug: Now that we store the whole m_pConfigInfo, we don't need to have ARGS at all... if (m_pArgs[i].type == kConfigType_SZ) { delete [] m_pArgs[i].pszVal; delete [] m_pConfigInfo[i].info.pszDefault; } else if (m_fRange) { delete [] m_pArgs[i].pdwVal; } } delete [] m_pArgs; m_pArgs = NULL; } m_nArgs = 0; delete [] m_pConfigInfo; m_pConfigInfo = NULL; } //////////////////////////////////////////////////////////////////////// BOOL Config::GetDWORDArg(DWORD nArg, DWORD *pdw, int nRange) { ZAssert(pdw); ZAssert(nArg < m_nArgs); ZAssert(!m_fRange || (nRange < m_cRange)); ZAssert(m_pArgs); if ((nArg >= m_nArgs) || (m_pArgs[nArg].type != kConfigType_DWORD) || (m_fRange && (nRange >= m_cRange))) { SRVDBG_Info("Failed to retrieve DWORD from registry"); return FALSE; } if (m_fRange) *pdw = m_pArgs[nArg].pdwVal[nRange]; else *pdw = m_pArgs[nArg].dwVal; return TRUE; } //////////////////////////////////////////////////////////////////////// BOOL Config::GetStringArg(DWORD nArg, char *pszBuf, int cchBuf, int nRange) { ZAssert(pszBuf); ZAssert(nArg < m_nArgs); ZAssert(m_pArgs); ZAssert(!m_fRange || (nRange < m_cRange)); if ((nArg >= m_nArgs) || (m_pArgs[nArg].type != kConfigType_SZ)) { SRVDBG_Info("Failed to retrieve string from registry"); return FALSE; } if (m_fRange) { if (nRange >= m_cRange) { SRVDBG_Info("Failed to retrieve string range from registry"); return FALSE; } if (cchBuf < lstrlen(&m_pArgs[nArg].pszVal[nRange * m_pArgs[nArg].cb])+1) { SRVDBG_Info("Failed to retrieve string from registry"); return FALSE; } lstrcpy(pszBuf, &m_pArgs[nArg].pszVal[nRange * m_pArgs[nArg].cb]); } else { if (cchBuf < lstrlen(m_pArgs[nArg].pszVal)+1) { SRVDBG_Info("Failed to retrieve string from registry"); return FALSE; } lstrcpy(pszBuf, m_pArgs[nArg].pszVal); } return TRUE; } ConfigInfo *Config::GetConfigInfo(int *pcConfig) { ZAssert(m_pConfigInfo); ZAssert(pcConfig); *pcConfig = m_nArgs; if (m_nArgs) { ConfigInfo *pInfo = new ConfigInfo[m_nArgs]; ZAssert(pInfo); // all new's assumed to succeed. for (DWORD i=0; i < m_nArgs; i++) { pInfo[i] = m_pConfigInfo[i].info; } return pInfo; } return NULL; } void ConfigCache::DeleteConfigList() { for (int i=0; i < kCompId_Last; i++) { while (m_rgpConfigList[i]) { CONFIG_LIST *pNext = m_rgpConfigList[i]->pNext; delete m_rgpConfigList[i]->pConfig; delete m_rgpConfigList[i]; m_rgpConfigList[i] = pNext; } } } Config * ConfigCache::FindConfig(ComponentId compId) { ZAssert(compId < kCompId_Last); // todo: with a lot of settings stored, linear search here sucks... // Should go to a tree based search. Perhaps, each node in the tree // being an array of items with close product ID's. Also might be nice // to allocate all these nodes out of one heap -- for better memory locality, // to avoid paging... CONFIG_LIST *pNode = m_rgpConfigList[compId]; while (pNode) { ZAssert(pNode->pConfig); ZAssert(pNode->pConfig->GetComponentId() == compId); Config *pCfg = pNode->pConfig; return pCfg; pNode = pNode->pNext; } return NULL; } void ConfigCache::AddConfig(Config *pConfig) { ZAssert(pConfig); ZAssert(pConfig->GetComponentId() < kCompId_Last); ComponentId compId = pConfig->GetComponentId(); CONFIG_LIST *pNode = new CONFIG_LIST; ZAssert(pNode); // we assume all new's succeed... m_pcsEditList->Enter(); pNode->dwTimeAdded = GetTickCount(); pNode->pConfig = pConfig; pNode->pNext = m_rgpConfigList[compId]; m_rgpConfigList[compId] = pNode; m_pcsEditList->Leave(); } void ConfigCache::AddConfigToTail(Config *pConfig) { ZAssert(pConfig); ZAssert(pConfig->GetComponentId() < kCompId_Last); ComponentId compId = pConfig->GetComponentId(); CONFIG_LIST *pNewNode = new CONFIG_LIST; ZAssert(pNewNode); // we assume all new's succeed... m_pcsEditList->Enter(); pNewNode->dwTimeAdded = GetTickCount(); pNewNode->pConfig = pConfig; pNewNode->pNext = NULL; CONFIG_LIST **ppNode = &m_rgpConfigList[compId]; while (NULL != *ppNode) ppNode = &((*ppNode)->pNext); *ppNode = pNewNode; m_pcsEditList->Leave(); } Config * ConfigCache::RemoveHead(ComponentId compId) { ZAssert(compId < kCompId_Last); Config *pRet = NULL; m_pcsEditList->Enter(); if (NULL != m_rgpConfigList[compId]) { CONFIG_LIST *pOld = m_rgpConfigList[compId]; pRet = pOld->pConfig; m_rgpConfigList[compId] = pOld->pNext; delete pOld; } m_pcsEditList->Leave(); return pRet; } Config * ConfigCache::RemoveHeadIfOld(ComponentId compId, DWORD dwDeltaOld) { ZAssert(compId < kCompId_Last); Config *pRet = NULL; m_pcsEditList->Enter(); DWORD dwNow = GetTickCount(); if (NULL != m_rgpConfigList[compId]) { BOOL fOld = FALSE; if (m_rgpConfigList[compId]->dwTimeAdded < dwNow) { if (dwNow - m_rgpConfigList[compId]->dwTimeAdded >= dwDeltaOld) { fOld = TRUE; } } else { if ((dwNow > dwDeltaOld) || ((dwNow + (MAXDWORD - m_rgpConfigList[compId]->dwTimeAdded)) > dwDeltaOld)) fOld = TRUE; } if (fOld) { CONFIG_LIST *pOld = m_rgpConfigList[compId]; pRet = pOld->pConfig; m_rgpConfigList[compId] = pOld->pNext; delete pOld; } } m_pcsEditList->Leave(); return pRet; } // This routine simply removes the item from the list by setting the // previous item's pNext to point at what the given node's pNext is... Config * ConfigCache::ReplaceConfig(ComponentId compId, Config *pConfig) { ZAssert(pConfig); ZAssert(pConfig->GetComponentId() < kCompId_Last); ZAssert(pConfig->GetComponentId() == compId); Config *pRet = NULL; m_pcsEditList->Enter(); // todo: with a lot of settings stored, linear search here sucks... CONFIG_LIST *pNode = m_rgpConfigList[compId]; while (pNode) { ZAssert(pNode->pConfig); ZAssert(pNode->pConfig->GetComponentId() == compId); break; pNode = pNode->pNext; } // If found, replace. Else, add... if (NULL != pNode) { pRet = pNode->pConfig; pNode->pConfig = pConfig; } else { AddConfig(pConfig); } m_pcsEditList->Leave(); return pRet; } ConfigManager::~ConfigManager() { if (m_hthrGarbageCollect) { ZAssert(m_hevtDie); SetEvent(m_hevtDie); if (WAIT_TIMEOUT == WaitForSingleObject(m_hthrGarbageCollect, 20000)) SRVDBG_Error("Failed while waiting for thread"); CloseHandle(m_hthrGarbageCollect); } if (m_hevtDie) CloseHandle(m_hevtDie); delete m_pcsAdd; } BOOL ConfigManager::Init() { ZAssert(NULL == m_hevtDie); ZAssert(NULL == m_hthrGarbageCollect); if (NULL != m_hevtDie) { SRVDBG_Warning("Called ConfigManager::Init() twice."); return FALSE; } if (NULL == m_pcsAdd) { m_pcsAdd = new CriticalSection; ZAssert(m_pcsAdd); // all new's assumed to succeed... } m_hevtDie = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == m_hevtDie) { SRVDBG_Error("Failed to create ConfigManager event"); return FALSE; } DWORD dwId; m_hthrGarbageCollect = CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE) GarbageCollectThreadThunk, this, 0, &dwId); if (NULL == m_hthrGarbageCollect) { SRVDBG_Error("Failed to create ConfigManager thread"); return FALSE; } return TRUE; } BOOL ConfigManager::LoadConfig(ComponentId compId, const ConfigInfo *pInfo, int cInfo) { m_pcsAdd->Enter(); Config *pCfg = new Config(compId); ZAssert(pCfg); // all new's are assumed to succeed. BOOL fRet = pCfg->Init(pInfo, cInfo, FALSE, 0); // if fails, will have reported error to event log if (fRet) { Config *pOld = m_cacheComponent.ReplaceConfig(compId, pCfg); // todo: could do this outside the critsec... if (pOld) m_cacheGarbage.AddConfigToTail(pOld); } else { delete pCfg; } m_pcsAdd->Leave(); return fRet; } BOOL ConfigManager::ReloadConfig(ComponentId compId) { Config *pCfg = m_cacheComponent.FindConfig(compId); if (pCfg) { int cInfo; ConfigInfo *pInfo = pCfg->GetConfigInfo(&cInfo); if (pInfo) { BOOL fRet = LoadConfig(compId, pInfo, cInfo); delete [] pInfo; return fRet; } } return FALSE; // bugbug: ... We should log a load... // bugbug: ... We should log a reload, too ... } BOOL ConfigManager::GetConfigDWORD(ComponentId compId, DWORD nArg, DWORD *pdw) { Config *pCfg = m_cacheComponent.FindConfig(compId); if (pCfg) return pCfg->GetDWORDArg(nArg, pdw, 0); return FALSE; } BOOL ConfigManager::GetConfigString(ComponentId compId, DWORD nArg, char *pszBuf, int cchBuf) { Config *pCfg = m_cacheComponent.FindConfig(compId); if (pCfg) return pCfg->GetStringArg(nArg, pszBuf, cchBuf, 0); return FALSE; } BOOL ConfigManager::IsConfigLoaded(ComponentId compId) { return (NULL != m_cacheComponent.FindConfig(compId)); } void ConfigManager::GarbageCollectThread() { ZAssert(this); while (TRUE) { // todo: No hardcoding numbers here, or below... // Garbage collector wakes up every so often to delete old, replaced nodes. // If the "die now" event is signalled, it deletes all the garbage immediately, // and exits... if (WAIT_OBJECT_0 == WaitForSingleObject(m_hevtDie, 3*60000L)) { for (int i=0; i < kCompId_Last; i++) { Config *pCfg = m_cacheGarbage.RemoveHead((ComponentId) i); while (pCfg) { delete pCfg; pCfg = m_cacheGarbage.RemoveHead((ComponentId) i); } } // we're outta here... break; } // We don't want to delete a node if it might still be being accessed, so we // wait for a while after we've added it to the garbage collector... for (int i=0; i < kCompId_Last; i++) { Config *pCfg = m_cacheGarbage.RemoveHeadIfOld((ComponentId) i, 10*60000L); while (pCfg) { delete pCfg; pCfg = m_cacheGarbage.RemoveHeadIfOld((ComponentId) i, 10*60000L); } } } } BOOL ConfigReader::ReadRg(const ConfigInfoBuffer *prgItems, int cItems) { ZAssert(prgItems); ZAssert(cItems); BOOL fRet = Open(); if (fRet) { // If we fail loading one key -- we keep going, as we'll be putting errors // in the event log -- so we might was well show all the missing entries, // rather than the 1st one... for (int i=0; i < cItems; i++) { switch (prgItems[i].info.type) { case kConfigType_SZ: fRet &= FastReadSz(prgItems[i].info.pszKeyName, (char *) prgItems[i].pBuf, prgItems[i].info.cb, prgItems[i].info.nRequired, prgItems[i].info.pszDefault); break; case kConfigType_DWORD: ZAssert(sizeof(int) == prgItems[i].info.cb); fRet &= FastReadInt(prgItems[i].info.pszKeyName, (int *) prgItems[i].pBuf, prgItems[i].info.nRequired, prgItems[i].info.dwDefault); break; default: ZAssert(FALSE); SRVDBG_Error2("Bad reg key type specified", prgItems[i].info.pszKeyName); fRet = FALSE; break; } } Close(); } return fRet; } BOOL ConfigReader::ReadRgRg(int cRepeat, const ConfigInfoBuffer *prgItems, int cItems) { ZAssert(prgItems); ZAssert(cItems); ZAssert(cRepeat); // todo? We currently don't allow a missing hive to be OK, even if all the // items had defaults... BOOL fRet = Open(); if (fRet) { for (int i=0; i < cItems; i++) { BYTE *pb = prgItems[i].pBuf; // If we fail loading one key -- we keep going, as we'll be putting errors // in the event log -- so we might was well show all the missing entries, // rather than the 1st one... for (int j=0; j < cRepeat; j++) { char szT[1024]; ::wsprintf(szT, "%s%03d", prgItems[i].info.pszKeyName, j); switch (prgItems[i].info.type) { case kConfigType_SZ: fRet &= FastReadSz(szT, (char *) pb, prgItems[i].info.cb, (j <= prgItems[i].info.nRequired), prgItems[i].info.pszDefault); break; case kConfigType_DWORD: ZAssert(sizeof(int) == prgItems[i].info.cb); fRet &= FastReadInt(szT, (int *) pb, (j <= prgItems[i].info.nRequired), prgItems[i].info.dwDefault); break; default: ZAssert(FALSE); SRVDBG_Error2("Bad reg key type specified", prgItems[i].info.pszKeyName); fRet = FALSE; break; } pb += prgItems[i].info.cb; } } Close(); } return fRet; } BOOL ConfigReader::ReadInt(const char *pszItem, int *pnVal, BOOL fRequired, int nDefault) { ZAssert(pszItem); ZAssert(pnVal); BOOL fRet = Open(); if (fRet) { fRet = FastReadInt(pszItem, pnVal, fRequired, nDefault); Close(); } else { if (!fRequired) { fRet = TRUE; *pnVal = nDefault; } } return fRet; } BOOL ConfigReader::ReadSz(const char *pszItem, char *psz, int cchMax, BOOL fRequired, const char *pszDefault) { ZAssert(pszItem); ZAssert(psz); ZAssert(fRequired || pszDefault); ZAssert((NULL == pszDefault) || (lstrlen(pszDefault) < cchMax-1)); BOOL fRet = Open(); if (fRet) { fRet = FastReadSz(pszItem, psz, cchMax, fRequired, pszDefault); Close(); } else { if (!fRequired) { fRet = TRUE; lstrcpy(psz, pszDefault); } } return fRet; } BOOL ConfigReader::WriteInt(const char *pszItem, int nVal) { ZAssert(pszItem); BOOL fRet = Open(); if (fRet) { fRet = FastWriteInt(pszItem, nVal); Close(); } return fRet; } BOOL ConfigReader::WriteSz(const char *pszItem, const char *psz, int cch) { ZAssert(pszItem); ZAssert(psz); BOOL fRet = Open(); if (fRet) { fRet = FastWriteSz(pszItem, psz, cch); Close(); } return fRet; } ConfigReader *CreateConfigReader(ComponentId compId) { // right now, we have no other reader than the registry-based one #ifdef CONFIG_OTHER_BASED return CreateOtherConfigReader(compId); #else return CreateRegConfigReader(compId); #endif } RegConfigReader *CreateRegConfigReader(ComponentId compId) { return new RegConfigReader(compId); }