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
Get(const char * aKey,int & aIterator,char * aValue,int aValueLength)53 otError ConfigFile::Get(const char *aKey, int &aIterator, char *aValue, int aValueLength)
54 {
55 otError error = OT_ERROR_NONE;
56 char line[kLineMaxSize + 1];
57 FILE *fp = nullptr;
58 char *ret;
59 long int pos;
60
61 VerifyOrExit((aKey != nullptr) && (aValue != nullptr), error = OT_ERROR_INVALID_ARGS);
62 VerifyOrExit((fp = fopen(mFilePath, "r")) != nullptr, error = OT_ERROR_NOT_FOUND);
63 VerifyOrDie(fseek(fp, aIterator, SEEK_SET) == 0, OT_EXIT_ERROR_ERRNO);
64
65 while ((ret = fgets(line, sizeof(line), fp)) != nullptr)
66 {
67 char *str;
68 char *key;
69 char *value;
70
71 // If the string exceeds the `sizeof(line) - 1`, the string will be truncated to `sizeof(line) - 1` bytes string
72 // by the function `fgets()`.
73 if (strlen(line) + 1 == sizeof(line))
74 {
75 // The line is too long.
76 continue;
77 }
78
79 // Remove comments
80 strtok(line, kCommentDelimiter);
81
82 if ((str = strstr(line, "=")) == nullptr)
83 {
84 continue;
85 }
86
87 *str = '\0';
88 key = line;
89
90 Strip(key);
91
92 if (strcmp(aKey, key) == 0)
93 {
94 value = str + 1;
95 Strip(value);
96 aValueLength = OT_MIN(static_cast<int>(strlen(value)), (aValueLength - 1));
97 memcpy(aValue, value, static_cast<size_t>(aValueLength));
98 aValue[aValueLength] = '\0';
99 break;
100 }
101 }
102
103 VerifyOrExit(ret != nullptr, error = OT_ERROR_NOT_FOUND);
104 VerifyOrDie((pos = ftell(fp)) >= 0, OT_EXIT_ERROR_ERRNO);
105 aIterator = static_cast<int>(pos);
106
107 exit:
108 if (fp != nullptr)
109 {
110 fclose(fp);
111 }
112
113 return error;
114 }
115
Add(const char * aKey,const char * aValue)116 otError ConfigFile::Add(const char *aKey, const char *aValue)
117 {
118 otError error = OT_ERROR_NONE;
119 FILE *fp = nullptr;
120 char *path = nullptr;
121 char *dir;
122 struct stat st;
123
124 VerifyOrExit((aKey != nullptr) && (aValue != nullptr), error = OT_ERROR_INVALID_ARGS);
125 VerifyOrDie((path = strdup(mFilePath)) != nullptr, OT_EXIT_ERROR_ERRNO);
126 dir = dirname(path);
127
128 if (stat(dir, &st) == -1)
129 {
130 VerifyOrDie(mkdir(dir, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == 0, OT_EXIT_ERROR_ERRNO);
131 }
132
133 VerifyOrDie((fp = fopen(mFilePath, "at")) != NULL, OT_EXIT_ERROR_ERRNO);
134 VerifyOrDie(fprintf(fp, "%s=%s\n", aKey, aValue) > 0, OT_EXIT_ERROR_ERRNO);
135
136 exit:
137 if (fp != nullptr)
138 {
139 fclose(fp);
140 }
141
142 if (path != nullptr)
143 {
144 free(path);
145 }
146
147 return error;
148 }
149
Clear(const char * aKey)150 otError ConfigFile::Clear(const char *aKey)
151 {
152 otError error = OT_ERROR_NONE;
153 char swapFile[kFileNameMaxSize];
154 char line[kLineMaxSize];
155 FILE *fp = nullptr;
156 FILE *fpSwap = nullptr;
157
158 VerifyOrExit(aKey != nullptr, error = OT_ERROR_INVALID_ARGS);
159 VerifyOrDie((fp = fopen(mFilePath, "r")) != NULL, OT_EXIT_ERROR_ERRNO);
160 snprintf(swapFile, sizeof(swapFile), "%s%s", mFilePath, kSwapSuffix);
161 VerifyOrDie((fpSwap = fopen(swapFile, "w+")) != NULL, OT_EXIT_ERROR_ERRNO);
162
163 while (fgets(line, sizeof(line), fp) != nullptr)
164 {
165 bool containsKey;
166 char *str1;
167 char *str2;
168
169 str1 = strstr(line, kCommentDelimiter);
170 str2 = strstr(line, aKey);
171
172 // If only the comment contains the key string, ignore it.
173 containsKey = (str2 != nullptr) && (str1 == nullptr || str2 < str1);
174
175 if (!containsKey)
176 {
177 fputs(line, fpSwap);
178 }
179 }
180
181 exit:
182 if (fp != nullptr)
183 {
184 fclose(fp);
185 }
186
187 if (fpSwap != nullptr)
188 {
189 fclose(fpSwap);
190 }
191
192 if (error == OT_ERROR_NONE)
193 {
194 VerifyOrDie(rename(swapFile, mFilePath) == 0, OT_EXIT_ERROR_ERRNO);
195 }
196
197 return error;
198 }
199
Strip(char * aString)200 void ConfigFile::Strip(char *aString)
201 {
202 int count = 0;
203
204 for (int i = 0; aString[i]; i++)
205 {
206 if (aString[i] != ' ' && aString[i] != '\r' && aString[i] != '\n')
207 {
208 aString[count++] = aString[i];
209 }
210 }
211
212 aString[count] = '\0';
213 }
214 } // namespace Posix
215 } // namespace ot
216