/* * Copyright (c) 2020-2022 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include "ExamplePersistentStorage.h" #include #include #include #include #include #include using String = std::basic_string; using Section = std::map; using Sections = std::map; using namespace ::chip; using namespace ::chip::Controller; using namespace ::chip::IniEscaping; using namespace ::chip::Logging; constexpr char kDefaultSectionName[] = "Default"; constexpr char kPortKey[] = "ListenPort"; constexpr char kLoggingKey[] = "LoggingLevel"; constexpr char kLocalNodeIdKey[] = "LocalNodeId"; constexpr char kCommissionerCATsKey[] = "CommissionerCATs"; constexpr LogCategory kDefaultLoggingLevel = kLogCategory_Automation; const char * GetUsedDirectory(const char * directory) { const char * dir = directory; if (dir == nullptr) { dir = getenv("TMPDIR"); } if (dir == nullptr) { dir = "/tmp"; } return dir; } std::string GetFilename(const char * directory, const char * name) { const char * dir = GetUsedDirectory(directory); if (name == nullptr) { return std::string(dir) + "/chip_tool_config.ini"; } return std::string(dir) + "/chip_tool_config." + std::string(name) + ".ini"; } CHIP_ERROR PersistentStorage::Init(const char * name, const char * directory) { CHIP_ERROR err = CHIP_NO_ERROR; std::ifstream ifs; ifs.open(GetFilename(directory, name), std::ifstream::in); if (!ifs.good()) { CommitConfig(directory, name); ifs.open(GetFilename(directory, name), std::ifstream::in); } VerifyOrExit(ifs.is_open(), err = CHIP_ERROR_OPEN_FAILED); mName = name; mDirectory = directory; mConfig.clear(); mConfig.parse(ifs); ifs.close(); // To audit the contents at init, uncomment the following: #if 0 DumpKeys(); #endif exit: return err; } CHIP_ERROR PersistentStorage::SyncGetKeyValue(const char * key, void * value, uint16_t & size) { std::string iniValue; VerifyOrReturnError((value != nullptr) || (size == 0), CHIP_ERROR_INVALID_ARGUMENT); auto section = mConfig.sections[kDefaultSectionName]; VerifyOrReturnError(SyncDoesKeyExist(key), CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); std::string escapedKey = EscapeKey(key); VerifyOrReturnError(inipp::extract(section[escapedKey], iniValue), CHIP_ERROR_INVALID_ARGUMENT); iniValue = Base64ToString(iniValue); uint16_t dataSize = static_cast(iniValue.size()); VerifyOrReturnError(size != 0 || dataSize != 0, CHIP_NO_ERROR); VerifyOrReturnError(value != nullptr, CHIP_ERROR_BUFFER_TOO_SMALL); uint16_t sizeToCopy = std::min(size, dataSize); memcpy(value, iniValue.data(), sizeToCopy); size = sizeToCopy; return size < dataSize ? CHIP_ERROR_BUFFER_TOO_SMALL : CHIP_NO_ERROR; } CHIP_ERROR PersistentStorage::SyncSetKeyValue(const char * key, const void * value, uint16_t size) { VerifyOrReturnError((value != nullptr) || (size == 0), CHIP_ERROR_INVALID_ARGUMENT); auto section = mConfig.sections[kDefaultSectionName]; std::string escapedKey = EscapeKey(key); if (value == nullptr) { section[escapedKey] = ""; } else { section[escapedKey] = StringToBase64(std::string(static_cast(value), size)); } mConfig.sections[kDefaultSectionName] = section; return CommitConfig(mDirectory, mName); } CHIP_ERROR PersistentStorage::SyncDeleteKeyValue(const char * key) { auto section = mConfig.sections[kDefaultSectionName]; VerifyOrReturnError(SyncDoesKeyExist(key), CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND); std::string escapedKey = EscapeKey(key); section.erase(escapedKey); mConfig.sections[kDefaultSectionName] = section; return CommitConfig(mDirectory, mName); } bool PersistentStorage::SyncDoesKeyExist(const char * key) { std::string escapedKey = EscapeKey(key); auto section = mConfig.sections[kDefaultSectionName]; auto it = section.find(escapedKey); return (it != section.end()); } void PersistentStorage::DumpKeys() const { #if CHIP_PROGRESS_LOGGING for (const auto & section : mConfig.sections) { const std::string & sectionName = section.first; const auto & sectionContent = section.second; ChipLogProgress(chipTool, "[%s]", sectionName.c_str()); for (const auto & entry : sectionContent) { const std::string & keyName = entry.first; ChipLogProgress(chipTool, " => %s", UnescapeKey(keyName).c_str()); } } #endif // CHIP_PROGRESS_LOGGING } CHIP_ERROR PersistentStorage::SyncClearAll() { ChipLogProgress(chipTool, "Clearing %s storage", kDefaultSectionName); auto section = mConfig.sections[kDefaultSectionName]; section.clear(); mConfig.sections[kDefaultSectionName] = section; return CommitConfig(mDirectory, mName); } const char * PersistentStorage::GetDirectory() const { return GetUsedDirectory(mDirectory); } CHIP_ERROR PersistentStorage::CommitConfig(const char * directory, const char * name) { CHIP_ERROR err = CHIP_NO_ERROR; std::ofstream ofs; std::string tmpPath = GetFilename(directory, name) + ".tmp"; ofs.open(tmpPath, std::ofstream::out | std::ofstream::trunc); VerifyOrExit(ofs.good(), err = CHIP_ERROR_WRITE_FAILED); mConfig.generate(ofs); ofs.close(); VerifyOrExit(ofs.good(), err = CHIP_ERROR_WRITE_FAILED); VerifyOrExit(rename(tmpPath.c_str(), GetFilename(directory, name).c_str()) == 0, err = CHIP_ERROR_WRITE_FAILED); exit: return err; } uint16_t PersistentStorage::GetListenPort() { CHIP_ERROR err = CHIP_NO_ERROR; // By default chip-tool listens on an ephemeral port. uint16_t chipListenPort = 0; char value[6]; uint16_t size = static_cast(sizeof(value)); err = SyncGetKeyValue(kPortKey, value, size); if (CHIP_NO_ERROR == err) { uint16_t tmpValue; std::stringstream ss(value); ss >> tmpValue; if (!ss.fail() && ss.eof()) { chipListenPort = tmpValue; } } return chipListenPort; } LogCategory PersistentStorage::GetLoggingLevel() { CHIP_ERROR err = CHIP_NO_ERROR; LogCategory chipLogLevel = kDefaultLoggingLevel; char value[9]; uint16_t size = static_cast(sizeof(value)); err = SyncGetKeyValue(kLoggingKey, value, size); if (CHIP_NO_ERROR == err) { if (strcasecmp(value, "none") == 0) { chipLogLevel = kLogCategory_None; } else if (strcasecmp(value, "error") == 0) { chipLogLevel = kLogCategory_Error; } else if (strcasecmp(value, "progress") == 0) { chipLogLevel = kLogCategory_Progress; } else if (strcasecmp(value, "detail") == 0) { chipLogLevel = kLogCategory_Detail; } else if (strcasecmp(value, "automation") == 0) { chipLogLevel = kLogCategory_Automation; } } return chipLogLevel; } NodeId PersistentStorage::GetLocalNodeId() { CHIP_ERROR err = CHIP_NO_ERROR; uint64_t nodeId; uint16_t size = static_cast(sizeof(nodeId)); err = SyncGetKeyValue(kLocalNodeIdKey, &nodeId, size); if (err == CHIP_NO_ERROR) { return static_cast(Encoding::LittleEndian::HostSwap64(nodeId)); } return kTestControllerNodeId; } CHIP_ERROR PersistentStorage::SetLocalNodeId(NodeId value) { uint64_t nodeId = Encoding::LittleEndian::HostSwap64(value); return SyncSetKeyValue(kLocalNodeIdKey, &nodeId, sizeof(nodeId)); } CATValues PersistentStorage::GetCommissionerCATs() { CHIP_ERROR err = CHIP_NO_ERROR; CATValues cats; chip::CATValues::Serialized serializedCATs; uint16_t size = chip::CATValues::kSerializedLength; err = SyncGetKeyValue(kCommissionerCATsKey, serializedCATs, size); if (err == CHIP_NO_ERROR && size == chip::CATValues::kSerializedLength) { err = cats.Deserialize(serializedCATs); if (err == CHIP_NO_ERROR) { return cats; } } return chip::kUndefinedCATs; } CHIP_ERROR PersistentStorage::SetCommissionerCATs(const CATValues & cats) { chip::CATValues::Serialized serializedCATs; ReturnErrorOnFailure(cats.Serialize(serializedCATs)); return SyncSetKeyValue(kCommissionerCATsKey, serializedCATs, sizeof(serializedCATs)); }