|  | /* | 
|  | * | 
|  | *    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. | 
|  | */ | 
|  |  | 
|  | /** | 
|  | *    @file | 
|  | *         This file implements a class for managing client application | 
|  | *         user-editable settings on Linux platform. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <fstream> | 
|  | #include <inttypes.h> | 
|  | #include <libgen.h> | 
|  | #include <string> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <lib/support/Base64.h> | 
|  | #include <lib/support/CHIPMem.h> | 
|  | #include <lib/support/CodeUtils.h> | 
|  | #include <lib/support/ScopedBuffer.h> | 
|  | #include <lib/support/logging/CHIPLogging.h> | 
|  | #include <platform/Linux/CHIPLinuxStorage.h> | 
|  | #include <platform/internal/CHIPDeviceLayerInternal.h> | 
|  |  | 
|  | namespace chip { | 
|  | namespace DeviceLayer { | 
|  | namespace Internal { | 
|  |  | 
|  | ChipLinuxStorage::ChipLinuxStorage() | 
|  | { | 
|  | mDirty = false; | 
|  | } | 
|  |  | 
|  | ChipLinuxStorage::~ChipLinuxStorage() {} | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::Init(const char * configFile) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | if (mInitialized) | 
|  | { | 
|  | ChipLogError(DeviceLayer, "ChipLinuxStorage::Init: Attempt to re-initialize with KVS config file: %s", | 
|  | StringOrNullMarker(configFile)); | 
|  | return CHIP_NO_ERROR; | 
|  | } | 
|  |  | 
|  | ChipLogDetail(DeviceLayer, "ChipLinuxStorage::Init: Using KVS config file: %s", StringOrNullMarker(configFile)); | 
|  |  | 
|  | mConfigPath.assign(configFile); | 
|  | retval = ChipLinuxStorageIni::Init(); | 
|  |  | 
|  | if (retval == CHIP_NO_ERROR) | 
|  | { | 
|  | std::ifstream ifs; | 
|  |  | 
|  | ifs.open(configFile, std::ifstream::in); | 
|  |  | 
|  | // Create default setting file if not exist. | 
|  | if (!ifs.good()) | 
|  | { | 
|  | mDirty = true; | 
|  | retval = Commit(); | 
|  | mDirty = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (retval == CHIP_NO_ERROR) | 
|  | { | 
|  | retval = ChipLinuxStorageIni::AddConfig(mConfigPath); | 
|  | } | 
|  |  | 
|  | mInitialized = true; | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, bool & val) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  | uint32_t result; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::GetUIntValue(key, result); | 
|  | val    = (result != 0); | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, uint16_t & val) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::GetUInt16Value(key, val); | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, uint32_t & val) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::GetUIntValue(key, val); | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::ReadValue(const char * key, uint64_t & val) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::GetUInt64Value(key, val); | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::ReadValueStr(const char * key, char * buf, size_t bufSize, size_t & outLen) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::GetStringValue(key, buf, bufSize, outLen); | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::ReadValueBin(const char * key, uint8_t * buf, size_t bufSize, size_t & outLen) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::GetBinaryBlobValue(key, buf, bufSize, outLen); | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, bool val) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | if (val) | 
|  | { | 
|  | retval = WriteValue(key, static_cast<uint32_t>(1)); | 
|  | } | 
|  | else | 
|  | { | 
|  | retval = WriteValue(key, static_cast<uint32_t>(0)); | 
|  | } | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, uint16_t val) | 
|  | { | 
|  | char buf[16]; | 
|  |  | 
|  | snprintf(buf, sizeof(buf), "%u", val); | 
|  |  | 
|  | return WriteValueStr(key, buf); | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, uint32_t val) | 
|  | { | 
|  | char buf[32]; | 
|  |  | 
|  | snprintf(buf, sizeof(buf), "%d", val); | 
|  |  | 
|  | return WriteValueStr(key, buf); | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::WriteValue(const char * key, uint64_t val) | 
|  | { | 
|  | char buf[64]; | 
|  |  | 
|  | snprintf(buf, sizeof(buf), "%" PRIu64, val); | 
|  |  | 
|  | return WriteValueStr(key, buf); | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::WriteValueStr(const char * key, const char * val) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::AddEntry(key, val); | 
|  |  | 
|  | mDirty = true; | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::WriteValueBin(const char * key, const uint8_t * data, size_t dataLen) | 
|  | { | 
|  | static const size_t kMaxBlobSize = 5 * 1024; | 
|  |  | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  | chip::Platform::ScopedMemoryBuffer<char> encodedData; | 
|  | size_t encodedDataLen     = 0; | 
|  | size_t expectedEncodedLen = ((dataLen + 3) * 4) / 3; | 
|  |  | 
|  | // We only support encoding blobs up to 5kb | 
|  | if (dataLen > kMaxBlobSize) | 
|  | { | 
|  | retval = CHIP_ERROR_INVALID_ARGUMENT; | 
|  | } | 
|  |  | 
|  | // Compute our expectedEncodedLen | 
|  | // Allocate just enough space for the encoded data, and the NULL terminator | 
|  | if (retval == CHIP_NO_ERROR) | 
|  | { | 
|  | if (!encodedData.Alloc(expectedEncodedLen + 1)) | 
|  | { | 
|  | retval = CHIP_ERROR_NO_MEMORY; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Encode it | 
|  | if (retval == CHIP_NO_ERROR) | 
|  | { | 
|  | // We tested above that dataLen is no more than kMaxBlobSize. | 
|  | static_assert(kMaxBlobSize < UINT16_MAX, "dataLen won't fit"); | 
|  | encodedDataLen              = Base64Encode(data, static_cast<uint16_t>(dataLen), encodedData.Get()); | 
|  | encodedData[encodedDataLen] = 0; | 
|  | } | 
|  |  | 
|  | // Store it | 
|  | if (retval == CHIP_NO_ERROR) | 
|  | { | 
|  | WriteValueStr(key, encodedData.Get()); | 
|  | } | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::ClearValue(const char * key) | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::RemoveEntry(key); | 
|  |  | 
|  | if (retval == CHIP_NO_ERROR) | 
|  | { | 
|  | mDirty = true; | 
|  | } | 
|  | else | 
|  | { | 
|  | retval = CHIP_ERROR_KEY_NOT_FOUND; | 
|  | } | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::ClearAll() | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::RemoveAll(); | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | if (retval == CHIP_NO_ERROR) | 
|  | { | 
|  | mDirty = true; | 
|  | retval = Commit(); | 
|  | } | 
|  | else | 
|  | { | 
|  | retval = CHIP_ERROR_WRITE_FAILED; | 
|  | } | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | bool ChipLinuxStorage::HasValue(const char * key) | 
|  | { | 
|  | bool retval; | 
|  |  | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::HasValue(key); | 
|  |  | 
|  | mLock.unlock(); | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ChipLinuxStorage::Commit() | 
|  | { | 
|  | CHIP_ERROR retval = CHIP_NO_ERROR; | 
|  |  | 
|  | if (mDirty && !mConfigPath.empty()) | 
|  | { | 
|  | mLock.lock(); | 
|  |  | 
|  | retval = ChipLinuxStorageIni::CommitConfig(mConfigPath); | 
|  |  | 
|  | mLock.unlock(); | 
|  | } | 
|  | else | 
|  | { | 
|  | retval = CHIP_ERROR_WRITE_FAILED; | 
|  | } | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | } // namespace Internal | 
|  | } // namespace DeviceLayer | 
|  | } // namespace chip |