3 * Copyright (c) 2020 Project CHIP Authors
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 * Provides an implementation of the Configuration key-value store object
22 * using IniPP on Linux platform.
30 #include <platform/Linux/CHIPLinuxStorageIni.h>
31 #include <platform/internal/CHIPDeviceLayerInternal.h>
32 #include <support/Base64.h>
33 #include <support/CHIPMem.h>
34 #include <support/CodeUtils.h>
35 #include <support/logging/CHIPLogging.h>
38 namespace DeviceLayer {
41 CHIP_ERROR ChipLinuxStorageIni::Init()
46 CHIP_ERROR ChipLinuxStorageIni::GetDefaultSection(std::map<std::string, std::string> & section)
48 CHIP_ERROR retval = CHIP_NO_ERROR;
50 auto it = mConfigStore.sections.find("DEFAULT");
52 if (it != mConfigStore.sections.end())
54 section = mConfigStore.sections["DEFAULT"];
58 retval = CHIP_ERROR_KEY_NOT_FOUND;
64 CHIP_ERROR ChipLinuxStorageIni::AddConfig(const std::string & configFile)
66 CHIP_ERROR retval = CHIP_NO_ERROR;
69 ifs.open(configFile, std::ifstream::in);
73 mConfigStore.parse(ifs);
78 ChipLogError(DeviceLayer, "Failed to open config file: %s", configFile.c_str());
79 retval = CHIP_ERROR_OPEN_FAILED;
85 // Updating a file atomically and durably on Linux requires:
86 // 1. Writing to a temporary file
87 // 2. Sync'ing the temp file to commit updated data
88 // 3. Using rename() to overwrite the existing file
89 CHIP_ERROR ChipLinuxStorageIni::CommitConfig(const std::string & configFile)
91 CHIP_ERROR retval = CHIP_NO_ERROR;
93 std::string tmpPath = configFile;
95 tmpPath.append(".tmp");
97 ofs.open(tmpPath, std::ofstream::out | std::ofstream::trunc);
101 ChipLogProgress(DeviceLayer, "writing settings to file (%s)", tmpPath.c_str());
103 mConfigStore.generate(ofs);
106 if (rename(tmpPath.c_str(), configFile.c_str()) == 0)
108 ChipLogError(DeviceLayer, "renamed tmp file to file (%s)", configFile.c_str());
112 ChipLogError(DeviceLayer, "failed to rename (%s), %s (%d)", tmpPath.c_str(), strerror(errno), errno);
113 retval = CHIP_ERROR_WRITE_FAILED;
118 ChipLogError(DeviceLayer, "failed to open file (%s) for writing", tmpPath.c_str());
119 retval = CHIP_ERROR_OPEN_FAILED;
125 CHIP_ERROR ChipLinuxStorageIni::GetUIntValue(const char * key, uint32_t & val)
127 CHIP_ERROR retval = CHIP_NO_ERROR;
128 std::map<std::string, std::string> section;
130 retval = GetDefaultSection(section);
132 if (retval == CHIP_NO_ERROR)
134 auto it = section.find(key);
136 if (it != section.end())
138 if (!inipp::extract(section[key], val))
140 retval = CHIP_ERROR_INVALID_ARGUMENT;
145 retval = CHIP_ERROR_KEY_NOT_FOUND;
152 CHIP_ERROR ChipLinuxStorageIni::GetUInt64Value(const char * key, uint64_t & val)
154 CHIP_ERROR retval = CHIP_NO_ERROR;
155 std::map<std::string, std::string> section;
157 retval = GetDefaultSection(section);
159 if (retval == CHIP_NO_ERROR)
161 auto it = section.find(key);
163 if (it != section.end())
165 if (!inipp::extract(section[key], val))
167 retval = CHIP_ERROR_INVALID_ARGUMENT;
172 retval = CHIP_ERROR_KEY_NOT_FOUND;
179 CHIP_ERROR ChipLinuxStorageIni::GetStringValue(const char * key, char * buf, size_t bufSize, size_t & outLen)
181 CHIP_ERROR retval = CHIP_NO_ERROR;
182 std::map<std::string, std::string> section;
184 retval = GetDefaultSection(section);
186 if (retval == CHIP_NO_ERROR)
188 auto it = section.find(key);
190 if (it != section.end())
193 if (inipp::extract(section[key], value))
195 size_t len = value.size();
197 if (len > bufSize - 1)
200 retval = CHIP_ERROR_BUFFER_TOO_SMALL;
204 outLen = value.copy(buf, len);
210 retval = CHIP_ERROR_INVALID_ARGUMENT;
215 retval = CHIP_ERROR_KEY_NOT_FOUND;
222 CHIP_ERROR ChipLinuxStorageIni::GetBinaryBlobDataAndLengths(const char * key,
223 chip::Platform::ScopedMemoryBuffer<char> & encodedData,
224 size_t & encodedDataLen, size_t & decodedDataLen)
226 size_t encodedDataPaddingLen = 0;
227 std::map<std::string, std::string> section;
228 CHIP_ERROR err = GetDefaultSection(section);
229 if (err != CHIP_NO_ERROR)
234 auto it = section.find(key);
235 if (it == section.end())
237 return CHIP_ERROR_KEY_NOT_FOUND;
242 // Compute the expectedDecodedLen
243 if (!inipp::extract(section[key], value))
245 return CHIP_ERROR_INVALID_ARGUMENT;
248 size_t len = value.size();
249 if (!encodedData.Alloc(len + 1))
251 return CHIP_ERROR_NO_MEMORY;
253 encodedDataLen = value.copy(encodedData.Get(), len);
254 encodedData[encodedDataLen] = '\0';
256 // Check if encoded data was padded. Only "=" or "==" padding combinations are allowed.
257 if ((encodedDataLen > 0) && (encodedData[encodedDataLen - 1] == '='))
259 encodedDataPaddingLen++;
260 if ((encodedDataLen > 1) && (encodedData[encodedDataLen - 2] == '='))
261 encodedDataPaddingLen++;
264 decodedDataLen = ((encodedDataLen - encodedDataPaddingLen) * 3) / 4;
266 return CHIP_NO_ERROR;
269 CHIP_ERROR ChipLinuxStorageIni::GetBinaryBlobValue(const char * key, uint8_t * decodedData, size_t bufSize, size_t & decodedDataLen)
271 CHIP_ERROR retval = CHIP_NO_ERROR;
272 chip::Platform::ScopedMemoryBuffer<char> encodedData;
273 size_t encodedDataLen;
274 size_t expectedDecodedLen = 0;
276 retval = GetBinaryBlobDataAndLengths(key, encodedData, encodedDataLen, expectedDecodedLen);
279 if (retval != CHIP_NO_ERROR)
284 if (expectedDecodedLen > bufSize)
286 decodedDataLen = expectedDecodedLen;
287 return CHIP_ERROR_BUFFER_TOO_SMALL;
290 if (encodedDataLen > UINT16_MAX)
292 // We can't even pass this length into Base64Decode.
293 return CHIP_ERROR_DECODE_FAILED;
297 // Cast is safe because we checked encodedDataLen above.
298 decodedDataLen = Base64Decode(encodedData.Get(), static_cast<uint16_t>(encodedDataLen), decodedData);
299 if (decodedDataLen == UINT16_MAX || decodedDataLen > expectedDecodedLen)
301 return CHIP_ERROR_DECODE_FAILED;
304 return CHIP_NO_ERROR;
307 bool ChipLinuxStorageIni::HasValue(const char * key)
309 std::map<std::string, std::string> section;
311 if (GetDefaultSection(section) != CHIP_NO_ERROR)
314 auto it = section.find(key);
316 return it != section.end();
319 CHIP_ERROR ChipLinuxStorageIni::AddEntry(const char * key, const char * value)
321 CHIP_ERROR retval = CHIP_NO_ERROR;
323 if ((key != nullptr) && (value != nullptr))
325 std::map<std::string, std::string> & section = mConfigStore.sections["DEFAULT"];
326 section[key] = std::string(value);
330 ChipLogError(DeviceLayer, "Invalid input argument, failed to add entry");
331 retval = CHIP_ERROR_INVALID_ARGUMENT;
337 CHIP_ERROR ChipLinuxStorageIni::RemoveEntry(const char * key)
339 CHIP_ERROR retval = CHIP_NO_ERROR;
341 std::map<std::string, std::string> & section = mConfigStore.sections["DEFAULT"];
343 auto it = section.find(key);
345 if (it != section.end())
351 retval = CHIP_ERROR_KEY_NOT_FOUND;
357 CHIP_ERROR ChipLinuxStorageIni::RemoveAll()
359 mConfigStore.clear();
361 return CHIP_NO_ERROR;
364 } // namespace Internal
365 } // namespace DeviceLayer