| /* |
| * |
| * 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; |
| |
| ChipLogDetail(DeviceLayer, "ChipLinuxStorage::Init: Using KVS config file: %s", StringOrNullMarker(configFile)); |
| if (mInitialized) |
| { |
| ChipLogError(DeviceLayer, "ChipLinuxStorage::Init: Attempt to re-initialize with KVS config file: %s", |
| StringOrNullMarker(configFile)); |
| return CHIP_NO_ERROR; |
| } |
| |
| 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 |