1 /*
2 * Copyright (c) 2022, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config_file.hpp"
30
31 #include <libgen.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37
38 #include "utils.hpp"
39 #include <openthread/logging.h>
40 #include "common/code_utils.hpp"
41 #include "lib/platform/exit_code.h"
42
43 namespace ot {
44 namespace Posix {
45
ConfigFile(const char * aFilePath)46 ConfigFile::ConfigFile(const char *aFilePath)
47 : mFilePath(aFilePath)
48 {
49 assert(mFilePath != nullptr);
50 VerifyOrDie(strlen(mFilePath) + strlen(kSwapSuffix) < kFileNameMaxSize, OT_EXIT_FAILURE);
51 }
52
HasKey(const char * aKey) const53 bool ConfigFile::HasKey(const char *aKey) const
54 {
55 int iterator = 0;
56
57 return (Get(aKey, iterator, nullptr, 0) == OT_ERROR_NONE);
58 }
59
DoesExist(void) const60 bool ConfigFile::DoesExist(void) const { return (access(mFilePath, 0) == 0); }
61
Get(const char * aKey,int & aIterator,char * aValue,int aValueLength) const62 otError ConfigFile::Get(const char *aKey, int &aIterator, char *aValue, int aValueLength) const
63 {
64 otError error = OT_ERROR_NONE;
65 char line[kLineMaxSize + 1];
66 FILE *fp = nullptr;
67 char *ret;
68 char *psave;
69 long int pos;
70
71 VerifyOrExit(aKey != nullptr, error = OT_ERROR_INVALID_ARGS);
72 VerifyOrExit((fp = fopen(mFilePath, "r")) != nullptr, error = OT_ERROR_NOT_FOUND);
73 VerifyOrDie(fseek(fp, aIterator, SEEK_SET) == 0, OT_EXIT_ERROR_ERRNO);
74
75 while ((ret = fgets(line, sizeof(line), fp)) != nullptr)
76 {
77 char *str;
78 char *key;
79 char *value;
80
81 // If the string exceeds the `sizeof(line) - 1`, the string will be truncated to `sizeof(line) - 1` bytes string
82 // by the function `fgets()`.
83 if (strlen(line) + 1 == sizeof(line))
84 {
85 // The line is too long.
86 continue;
87 }
88
89 // Remove comments
90 strtok_r(line, kCommentDelimiter, &psave);
91
92 if ((str = strstr(line, "=")) == nullptr)
93 {
94 continue;
95 }
96
97 *str = '\0';
98 key = line;
99
100 Strip(key);
101
102 if (strcmp(aKey, key) == 0)
103 {
104 if (aValue != nullptr)
105 {
106 value = str + 1;
107 Strip(value);
108 aValueLength = OT_MIN(static_cast<int>(strlen(value)), (aValueLength - 1));
109 memcpy(aValue, value, static_cast<size_t>(aValueLength));
110 aValue[aValueLength] = '\0';
111 }
112 break;
113 }
114 }
115
116 VerifyOrExit(ret != nullptr, error = OT_ERROR_NOT_FOUND);
117 VerifyOrDie((pos = ftell(fp)) >= 0, OT_EXIT_ERROR_ERRNO);
118 aIterator = static_cast<int>(pos);
119
120 exit:
121 if (fp != nullptr)
122 {
123 fclose(fp);
124 }
125
126 return error;
127 }
128
Add(const char * aKey,const char * aValue)129 otError ConfigFile::Add(const char *aKey, const char *aValue)
130 {
131 otError error = OT_ERROR_NONE;
132 FILE *fp = nullptr;
133 char *path = nullptr;
134 char *dir;
135 struct stat st;
136
137 VerifyOrExit((aKey != nullptr) && (aValue != nullptr), error = OT_ERROR_INVALID_ARGS);
138 VerifyOrDie((path = strdup(mFilePath)) != nullptr, OT_EXIT_ERROR_ERRNO);
139 dir = dirname(path);
140
141 if (stat(dir, &st) == -1)
142 {
143 VerifyOrDie(mkdir(dir, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == 0, OT_EXIT_ERROR_ERRNO);
144 }
145
146 VerifyOrDie((fp = fopen(mFilePath, "at")) != NULL, OT_EXIT_ERROR_ERRNO);
147 VerifyOrDie(fprintf(fp, "%s=%s\n", aKey, aValue) > 0, OT_EXIT_ERROR_ERRNO);
148
149 exit:
150 if (fp != nullptr)
151 {
152 fclose(fp);
153 }
154
155 if (path != nullptr)
156 {
157 free(path);
158 }
159
160 return error;
161 }
162
Clear(const char * aKey)163 otError ConfigFile::Clear(const char *aKey)
164 {
165 otError error = OT_ERROR_NONE;
166 char swapFile[kFileNameMaxSize];
167 char line[kLineMaxSize];
168 FILE *fp = nullptr;
169 FILE *fpSwap = nullptr;
170
171 VerifyOrExit(aKey != nullptr, error = OT_ERROR_INVALID_ARGS);
172 VerifyOrDie((fp = fopen(mFilePath, "r")) != NULL, OT_EXIT_ERROR_ERRNO);
173 snprintf(swapFile, sizeof(swapFile), "%s%s", mFilePath, kSwapSuffix);
174 VerifyOrDie((fpSwap = fopen(swapFile, "w+")) != NULL, OT_EXIT_ERROR_ERRNO);
175
176 while (fgets(line, sizeof(line), fp) != nullptr)
177 {
178 bool containsKey;
179 char *str1;
180 char *str2;
181
182 str1 = strstr(line, kCommentDelimiter);
183 str2 = strstr(line, aKey);
184
185 // If only the comment contains the key string, ignore it.
186 containsKey = (str2 != nullptr) && (str1 == nullptr || str2 < str1);
187
188 if (!containsKey)
189 {
190 fputs(line, fpSwap);
191 }
192 }
193
194 exit:
195 if (fp != nullptr)
196 {
197 fclose(fp);
198 }
199
200 if (fpSwap != nullptr)
201 {
202 fclose(fpSwap);
203 }
204
205 if (error == OT_ERROR_NONE)
206 {
207 VerifyOrDie(rename(swapFile, mFilePath) == 0, OT_EXIT_ERROR_ERRNO);
208 }
209
210 return error;
211 }
212
Strip(char * aString) const213 void ConfigFile::Strip(char *aString) const
214 {
215 int count = 0;
216
217 for (int i = 0; aString[i]; i++)
218 {
219 if (aString[i] != ' ' && aString[i] != '\r' && aString[i] != '\n')
220 {
221 aString[count++] = aString[i];
222 }
223 }
224
225 aString[count] = '\0';
226 }
227 } // namespace Posix
228 } // namespace ot
229