blob: 61a1fbd4dda6ed671aaed2bedaf6c4ad8dc97f51 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2022 Project CHIP Authors
* Copyright (c) 2019-2020 Google LLC.
* Copyright (c) 2018 Nest Labs, Inc.
* 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
* Utilities for interacting with multiple file partitions and maps
* key-value config calls to the correct partition.
*/
#include <platform/internal/CHIPDeviceLayerInternal.h>
#include <platform/internal/testing/ConfigUnitTest.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/support/CodeUtils.h>
#include <platform/Linux/CHIPLinuxStorage.h>
#include <platform/Linux/PosixConfig.h>
namespace chip {
namespace DeviceLayer {
namespace Internal {
static ChipLinuxStorage gChipLinuxFactoryStorage;
static ChipLinuxStorage gChipLinuxConfigStorage;
static ChipLinuxStorage gChipLinuxCountersStorage;
// *** CAUTION ***: Changing the names or namespaces of these values will *break* existing devices.
// NVS namespaces used to store device configuration information.
const char PosixConfig::kConfigNamespace_ChipFactory[] = "chip-factory";
const char PosixConfig::kConfigNamespace_ChipConfig[] = "chip-config";
const char PosixConfig::kConfigNamespace_ChipCounters[] = "chip-counters";
// Keys stored in the Chip-factory namespace
const PosixConfig::Key PosixConfig::kConfigKey_SerialNum = { kConfigNamespace_ChipFactory, "serial-num" };
const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceId = { kConfigNamespace_ChipFactory, "device-id" };
const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceCert = { kConfigNamespace_ChipFactory, "device-cert" };
const PosixConfig::Key PosixConfig::kConfigKey_MfrDeviceICACerts = { kConfigNamespace_ChipFactory, "device-ca-certs" };
const PosixConfig::Key PosixConfig::kConfigKey_MfrDevicePrivateKey = { kConfigNamespace_ChipFactory, "device-key" };
const PosixConfig::Key PosixConfig::kConfigKey_HardwareVersion = { kConfigNamespace_ChipFactory, "hardware-ver" };
const PosixConfig::Key PosixConfig::kConfigKey_ManufacturingDate = { kConfigNamespace_ChipFactory, "mfg-date" };
const PosixConfig::Key PosixConfig::kConfigKey_SetupPinCode = { kConfigNamespace_ChipFactory, "pin-code" };
const PosixConfig::Key PosixConfig::kConfigKey_SetupDiscriminator = { kConfigNamespace_ChipFactory, "discriminator" };
const PosixConfig::Key PosixConfig::kConfigKey_Spake2pIterationCount = { kConfigNamespace_ChipFactory, "iteration-count" };
const PosixConfig::Key PosixConfig::kConfigKey_Spake2pSalt = { kConfigNamespace_ChipFactory, "salt" };
const PosixConfig::Key PosixConfig::kConfigKey_Spake2pVerifier = { kConfigNamespace_ChipFactory, "verifier" };
const PosixConfig::Key PosixConfig::kConfigKey_VendorId = { kConfigNamespace_ChipFactory, "vendor-id" };
const PosixConfig::Key PosixConfig::kConfigKey_ProductId = { kConfigNamespace_ChipFactory, "product-id" };
// Keys stored in the Chip-config namespace
const PosixConfig::Key PosixConfig::kConfigKey_ServiceConfig = { kConfigNamespace_ChipConfig, "service-config" };
const PosixConfig::Key PosixConfig::kConfigKey_PairedAccountId = { kConfigNamespace_ChipConfig, "account-id" };
const PosixConfig::Key PosixConfig::kConfigKey_ServiceId = { kConfigNamespace_ChipConfig, "service-id" };
const PosixConfig::Key PosixConfig::kConfigKey_LastUsedEpochKeyId = { kConfigNamespace_ChipConfig, "last-ek-id" };
const PosixConfig::Key PosixConfig::kConfigKey_FailSafeArmed = { kConfigNamespace_ChipConfig, "fail-safe-armed" };
const PosixConfig::Key PosixConfig::kConfigKey_RegulatoryLocation = { kConfigNamespace_ChipConfig, "regulatory-location" };
const PosixConfig::Key PosixConfig::kConfigKey_CountryCode = { kConfigNamespace_ChipConfig, "country-code" };
const PosixConfig::Key PosixConfig::kConfigKey_LocationCapability = { kConfigNamespace_ChipConfig, "location-capability" };
const PosixConfig::Key PosixConfig::kConfigKey_UniqueId = { kConfigNamespace_ChipFactory, "unique-id" };
// Keys stored in the Chip-counters namespace
const PosixConfig::Key PosixConfig::kCounterKey_RebootCount = { kConfigNamespace_ChipCounters, "reboot-count" };
const PosixConfig::Key PosixConfig::kCounterKey_UpTime = { kConfigNamespace_ChipCounters, "up-time" };
const PosixConfig::Key PosixConfig::kCounterKey_TotalOperationalHours = { kConfigNamespace_ChipCounters,
"total-operational-hours" };
const PosixConfig::Key PosixConfig::kCounterKey_BootReason = { kConfigNamespace_ChipCounters, "boot-reason" };
ChipLinuxStorage * PosixConfig::GetStorageForNamespace(Key key)
{
if (strcmp(key.Namespace, kConfigNamespace_ChipFactory) == 0)
return &gChipLinuxFactoryStorage;
if (strcmp(key.Namespace, kConfigNamespace_ChipConfig) == 0)
return &gChipLinuxConfigStorage;
if (strcmp(key.Namespace, kConfigNamespace_ChipCounters) == 0)
return &gChipLinuxCountersStorage;
return nullptr;
}
CHIP_ERROR PosixConfig::Init()
{
return PersistedStorage::KeyValueStoreMgrImpl().Init(CHIP_CONFIG_KVS_PATH);
}
CHIP_ERROR PosixConfig::ReadConfigValue(Key key, bool & val)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
uint32_t intVal;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->ReadValue(key.Name, intVal);
if (err == CHIP_ERROR_KEY_NOT_FOUND)
{
err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
SuccessOrExit(err);
val = (intVal != 0);
exit:
return err;
}
CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint16_t & val)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->ReadValue(key.Name, val);
if (err == CHIP_ERROR_KEY_NOT_FOUND)
{
err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint32_t & val)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->ReadValue(key.Name, val);
if (err == CHIP_ERROR_KEY_NOT_FOUND)
{
err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR PosixConfig::ReadConfigValue(Key key, uint64_t & val)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
// Special case the MfrDeviceId value, optionally allowing it to be read as a blob containing
// a 64-bit big-endian integer, instead of a u64 value.
if (key == kConfigKey_MfrDeviceId)
{
uint8_t deviceIdBytes[sizeof(uint64_t)];
size_t deviceIdLen = sizeof(deviceIdBytes);
size_t deviceIdOutLen;
err = storage->ReadValueBin(key.Name, deviceIdBytes, deviceIdLen, deviceIdOutLen);
if (err == CHIP_NO_ERROR)
{
VerifyOrExit(deviceIdOutLen == sizeof(deviceIdBytes), err = CHIP_ERROR_INCORRECT_STATE);
val = Encoding::BigEndian::Get64(deviceIdBytes);
ExitNow();
}
}
err = storage->ReadValue(key.Name, val);
if (err == CHIP_ERROR_KEY_NOT_FOUND)
{
err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR PosixConfig::ReadConfigValueStr(Key key, char * buf, size_t bufSize, size_t & outLen)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->ReadValueStr(key.Name, buf, bufSize, outLen);
if (err == CHIP_ERROR_KEY_NOT_FOUND)
{
outLen = 0;
err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
else if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
{
err = (buf == nullptr) ? CHIP_NO_ERROR : CHIP_ERROR_BUFFER_TOO_SMALL;
}
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR PosixConfig::ReadConfigValueBin(Key key, uint8_t * buf, size_t bufSize, size_t & outLen)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->ReadValueBin(key.Name, buf, bufSize, outLen);
if (err == CHIP_ERROR_KEY_NOT_FOUND)
{
outLen = 0;
err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
else if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
{
err = (buf == nullptr) ? CHIP_NO_ERROR : CHIP_ERROR_BUFFER_TOO_SMALL;
}
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR PosixConfig::WriteConfigValue(Key key, bool val)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->WriteValue(key.Name, val);
SuccessOrExit(err);
// Commit the value to the persistent store.
err = storage->Commit();
SuccessOrExit(err);
ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %s", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name),
val ? "true" : "false");
exit:
return err;
}
CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint16_t val)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->WriteValue(key.Name, val);
SuccessOrExit(err);
// Commit the value to the persistent store.
err = storage->Commit();
SuccessOrExit(err);
ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %u (0x%X)", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name), val,
val);
exit:
return err;
}
CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint32_t val)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->WriteValue(key.Name, val);
SuccessOrExit(err);
// Commit the value to the persistent store.
err = storage->Commit();
SuccessOrExit(err);
ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %" PRIu32 " (0x%" PRIX32 ")", StringOrNullMarker(key.Namespace),
StringOrNullMarker(key.Name), val, val);
exit:
return err;
}
CHIP_ERROR PosixConfig::WriteConfigValue(Key key, uint64_t val)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->WriteValue(key.Name, val);
SuccessOrExit(err);
// Commit the value to the persistent store.
err = storage->Commit();
SuccessOrExit(err);
ChipLogProgress(DeviceLayer, "NVS set: %s/%s = %" PRIu64 " (0x%" PRIX64 ")", StringOrNullMarker(key.Namespace),
StringOrNullMarker(key.Name), val, val);
exit:
return err;
}
CHIP_ERROR PosixConfig::WriteConfigValueStr(Key key, const char * str)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
if (str != nullptr)
{
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->WriteValueStr(key.Name, str);
SuccessOrExit(err);
// Commit the value to the persistent store.
err = storage->Commit();
SuccessOrExit(err);
ChipLogProgress(DeviceLayer, "NVS set: %s/%s = \"%s\"", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name),
str);
}
else
{
err = ClearConfigValue(key);
SuccessOrExit(err);
}
exit:
return err;
}
CHIP_ERROR PosixConfig::WriteConfigValueStr(Key key, const char * str, size_t strLen)
{
#if CHIP_CONFIG_MEMORY_MGMT_MALLOC
CHIP_ERROR err;
char * strCopy = nullptr;
if (str != nullptr)
{
strCopy = strndup(str, strLen);
VerifyOrExit(strCopy != nullptr, err = CHIP_ERROR_NO_MEMORY);
}
err = PosixConfig::WriteConfigValueStr(key, strCopy);
exit:
if (strCopy != nullptr)
{
free(strCopy);
}
return err;
#else
#error "Unsupported CHIP_CONFIG_MEMORY_MGMT configuration"
#endif
}
CHIP_ERROR PosixConfig::WriteConfigValueBin(Key key, const uint8_t * data, size_t dataLen)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
if (data != nullptr)
{
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->WriteValueBin(key.Name, data, dataLen);
SuccessOrExit(err);
// Commit the value to the persistent store.
err = storage->Commit();
SuccessOrExit(err);
ChipLogProgress(DeviceLayer, "NVS set: %s/%s = (blob length %u)", StringOrNullMarker(key.Namespace),
StringOrNullMarker(key.Name), static_cast<unsigned int>(dataLen));
}
else
{
err = ClearConfigValue(key);
SuccessOrExit(err);
}
exit:
return err;
}
CHIP_ERROR PosixConfig::ClearConfigValue(Key key)
{
CHIP_ERROR err;
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->ClearValue(key.Name);
if (err == CHIP_ERROR_KEY_NOT_FOUND)
{
ExitNow(err = CHIP_NO_ERROR);
}
SuccessOrExit(err);
// Commit the value to the persistent store.
err = storage->Commit();
SuccessOrExit(err);
ChipLogProgress(DeviceLayer, "NVS erase: %s/%s", StringOrNullMarker(key.Namespace), StringOrNullMarker(key.Name));
exit:
return err;
}
bool PosixConfig::ConfigValueExists(Key key)
{
ChipLinuxStorage * storage;
storage = GetStorageForNamespace(key);
if (storage == nullptr)
return false;
return storage->HasValue(key.Name);
}
CHIP_ERROR PosixConfig::EnsureNamespace(const char * ns)
{
CHIP_ERROR err = CHIP_NO_ERROR;
ChipLinuxStorage * storage = nullptr;
if (strcmp(ns, kConfigNamespace_ChipFactory) == 0)
{
storage = &gChipLinuxFactoryStorage;
err = storage->Init(CHIP_DEFAULT_FACTORY_PATH);
}
else if (strcmp(ns, kConfigNamespace_ChipConfig) == 0)
{
storage = &gChipLinuxConfigStorage;
err = storage->Init(CHIP_DEFAULT_CONFIG_PATH);
}
else if (strcmp(ns, kConfigNamespace_ChipCounters) == 0)
{
storage = &gChipLinuxCountersStorage;
err = storage->Init(CHIP_DEFAULT_DATA_PATH);
}
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR PosixConfig::ClearNamespace(const char * ns)
{
CHIP_ERROR err = CHIP_NO_ERROR;
ChipLinuxStorage * storage = nullptr;
if (strcmp(ns, kConfigNamespace_ChipConfig) == 0)
{
storage = &gChipLinuxConfigStorage;
}
else if (strcmp(ns, kConfigNamespace_ChipCounters) == 0)
{
storage = &gChipLinuxCountersStorage;
}
VerifyOrExit(storage != nullptr, err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND);
err = storage->ClearAll();
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err));
}
SuccessOrExit(err);
err = storage->Commit();
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err));
}
exit:
return err;
}
CHIP_ERROR PosixConfig::FactoryResetConfig()
{
CHIP_ERROR err = CHIP_NO_ERROR;
ChipLinuxStorage * storage;
ChipLogProgress(DeviceLayer, "Performing factory reset configuration");
storage = &gChipLinuxConfigStorage;
if (storage == nullptr)
{
ChipLogError(DeviceLayer, "Storage get failed");
err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
SuccessOrExit(err);
err = storage->ClearAll();
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err));
}
SuccessOrExit(err);
err = storage->Commit();
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err));
}
exit:
return err;
}
CHIP_ERROR PosixConfig::FactoryResetCounters()
{
CHIP_ERROR err = CHIP_NO_ERROR;
ChipLinuxStorage * storage;
ChipLogProgress(DeviceLayer, "Performing factory reset counters");
storage = &gChipLinuxCountersStorage;
if (storage == nullptr)
{
ChipLogError(DeviceLayer, "Storage get failed");
err = CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND;
}
SuccessOrExit(err);
err = storage->ClearAll();
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Storage ClearAll failed: %s", ErrorStr(err));
}
SuccessOrExit(err);
err = storage->Commit();
if (err != CHIP_NO_ERROR)
{
ChipLogError(DeviceLayer, "Storage Commit failed: %s", ErrorStr(err));
}
exit:
return err;
}
void PosixConfig::RunConfigUnitTest()
{
// Run common unit test.
::chip::DeviceLayer::Internal::RunConfigUnitTest<PosixConfig>();
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip