blob: e4886ec18f92a4ce87435ae58f1f8850a7e6d01f [file] [log] [blame]
Ratan Guptaed123a32017-06-15 09:07:31 +05301#include "config_parser.hpp"
Ratan Guptaed123a32017-06-15 09:07:31 +05302
William A. Kennington III61ef4f22022-08-18 16:29:09 -07003#include <stdplus/exception.hpp>
William A. Kennington III409f1a62022-08-11 15:44:37 -07004#include <stdplus/fd/atomic.hpp>
William A. Kennington III61ef4f22022-08-18 16:29:09 -07005#include <stdplus/fd/create.hpp>
William A. Kennington III409f1a62022-08-11 15:44:37 -07006#include <stdplus/fd/fmt.hpp>
William A. Kennington III61ef4f22022-08-18 16:29:09 -07007#include <stdplus/fd/line.hpp>
William A. Kennington III86642522023-07-24 17:55:55 -07008#include <stdplus/str/cat.hpp>
Patrick Williams89d734b2023-05-10 07:50:25 -05009
William A. Kennington IIIcafc1512023-07-25 02:22:32 -070010#include <format>
Patrick Williams89d734b2023-05-10 07:50:25 -050011#include <functional>
12#include <iterator>
13#include <stdexcept>
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -070014#include <string>
William A. Kennington III61ef4f22022-08-18 16:29:09 -070015#include <utility>
Ratan Guptaed123a32017-06-15 09:07:31 +053016
17namespace phosphor
18{
19namespace network
20{
21namespace config
22{
23
William A. Kennington III150753f2022-08-05 15:20:54 -070024using std::literals::string_view_literals::operator""sv;
25
26bool icaseeq(std::string_view in, std::string_view expected) noexcept
27{
28 return std::equal(in.begin(), in.end(), expected.begin(), expected.end(),
29 [](auto a, auto b) { return tolower(a) == b; });
30}
31
32std::optional<bool> parseBool(std::string_view in) noexcept
33{
34 if (in == "1"sv || icaseeq(in, "yes"sv) || icaseeq(in, "y"sv) ||
35 icaseeq(in, "true"sv) || icaseeq(in, "t"sv) || icaseeq(in, "on"sv))
36 {
37 return true;
38 }
39 if (in == "0"sv || icaseeq(in, "no"sv) || icaseeq(in, "n"sv) ||
40 icaseeq(in, "false"sv) || icaseeq(in, "f"sv) || icaseeq(in, "off"sv))
41 {
42 return false;
43 }
44 return std::nullopt;
45}
46
William A. Kennington IIIa520a392022-08-08 12:17:34 -070047fs::path pathForIntfConf(const fs::path& dir, std::string_view intf)
48{
William A. Kennington III86642522023-07-24 17:55:55 -070049 return dir / stdplus::strCat("00-bmc-"sv, intf, ".network"sv);
William A. Kennington IIIa520a392022-08-08 12:17:34 -070050}
51
52fs::path pathForIntfDev(const fs::path& dir, std::string_view intf)
53{
William A. Kennington III86642522023-07-24 17:55:55 -070054 return dir / stdplus::strCat(intf, ".netdev"sv);
William A. Kennington IIIa520a392022-08-08 12:17:34 -070055}
56
Patrick Williamsad205022024-08-16 15:20:07 -040057const std::string* SectionMap::getLastValueString(
58 std::string_view section, std::string_view key) const noexcept
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -070059{
60 auto sit = find(section);
61 if (sit == end())
62 {
63 return nullptr;
64 }
65 for (auto it = sit->second.rbegin(); it != sit->second.rend(); ++it)
66 {
67 auto kit = it->find(key);
68 if (kit == it->end() || kit->second.empty())
69 {
70 continue;
71 }
William A. Kennington III0dd09372022-08-18 14:57:07 -070072 return &kit->second.back().get();
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -070073 }
74 return nullptr;
75}
76
77std::vector<std::string> SectionMap::getValueStrings(std::string_view section,
78 std::string_view key) const
79{
80 return getValues(section, key,
81 [](const Value& v) { return std::string(v); });
82}
83
William A. Kennington IIIbe3bd2f2022-10-11 14:11:27 -070084void KeyCheck::operator()(std::string_view s)
William A. Kennington III0dd09372022-08-18 14:57:07 -070085{
86 for (auto c : s)
87 {
88 if (c == '\n' || c == '=')
89 {
90 throw std::invalid_argument(
William A. Kennington III86642522023-07-24 17:55:55 -070091 stdplus::strCat("Invalid Config Key: "sv, s));
William A. Kennington III0dd09372022-08-18 14:57:07 -070092 }
93 }
94}
95
William A. Kennington IIIbe3bd2f2022-10-11 14:11:27 -070096void SectionCheck::operator()(std::string_view s)
William A. Kennington III0dd09372022-08-18 14:57:07 -070097{
98 for (auto c : s)
99 {
100 if (c == '\n' || c == ']')
101 {
102 throw std::invalid_argument(
William A. Kennington III86642522023-07-24 17:55:55 -0700103 stdplus::strCat("Invalid Config Section: "sv, s));
William A. Kennington III0dd09372022-08-18 14:57:07 -0700104 }
105 }
106}
107
William A. Kennington IIIbe3bd2f2022-10-11 14:11:27 -0700108void ValueCheck::operator()(std::string_view s)
William A. Kennington III0dd09372022-08-18 14:57:07 -0700109{
110 for (auto c : s)
111 {
112 if (c == '\n')
113 {
114 throw std::invalid_argument(
William A. Kennington III86642522023-07-24 17:55:55 -0700115 stdplus::strCat("Invalid Config Value: "sv, s));
William A. Kennington III0dd09372022-08-18 14:57:07 -0700116 }
117 }
118}
119
William A. Kennington III25511a12022-08-04 16:32:28 -0700120Parser::Parser(const fs::path& filename)
Ratan Guptaed123a32017-06-15 09:07:31 +0530121{
William A. Kennington III25511a12022-08-04 16:32:28 -0700122 setFile(filename);
Ratan Guptaed123a32017-06-15 09:07:31 +0530123}
124
William A. Kennington IIIf55b7d82022-10-25 14:21:46 -0700125constexpr bool isspace(char c) noexcept
Ratan Guptaed123a32017-06-15 09:07:31 +0530126{
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700127 return c == ' ' || c == '\t';
Ratan Guptaed123a32017-06-15 09:07:31 +0530128}
129
William A. Kennington IIIf55b7d82022-10-25 14:21:46 -0700130constexpr bool iscomment(char c) noexcept
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700131{
132 return c == '#' || c == ';';
133}
134
135static void removePadding(std::string_view& str) noexcept
136{
137 size_t idx = str.size();
138 for (; idx > 0 && isspace(str[idx - 1]); idx--)
139 ;
140 str.remove_suffix(str.size() - idx);
141
142 idx = 0;
143 for (; idx < str.size() && isspace(str[idx]); idx++)
144 ;
145 str.remove_prefix(idx);
146}
147
148struct Parse
149{
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700150 std::reference_wrapper<const fs::path> filename;
William A. Kennington III34bb3e22022-08-18 15:17:22 -0700151 SectionMap map;
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700152 KeyValuesMap* section;
153 std::vector<std::string> warnings;
154 size_t lineno;
155
156 inline Parse(const fs::path& filename) :
157 filename(filename), section(nullptr), lineno(0)
Patrick Williams89d734b2023-05-10 07:50:25 -0500158 {}
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700159
160 void pumpSection(std::string_view line)
161 {
162 auto cpos = line.find(']');
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700163 if (cpos == line.npos)
164 {
William A. Kennington IIIcafc1512023-07-25 02:22:32 -0700165 warnings.emplace_back(std::format("{}:{}: Section missing ]",
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700166 filename.get().native(), lineno));
167 }
168 else
169 {
170 for (auto c : line.substr(cpos + 1))
171 {
172 if (!isspace(c))
173 {
174 warnings.emplace_back(
William A. Kennington IIIcafc1512023-07-25 02:22:32 -0700175 std::format("{}:{}: Characters outside section name",
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700176 filename.get().native(), lineno));
177 break;
178 }
179 }
180 }
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700181 auto s = line.substr(0, cpos);
William A. Kennington III34bb3e22022-08-18 15:17:22 -0700182 auto it = map.find(s);
183 if (it == map.end())
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700184 {
William A. Kennington III34bb3e22022-08-18 15:17:22 -0700185 std::tie(it, std::ignore) = map.emplace(
William A. Kennington III0dd09372022-08-18 14:57:07 -0700186 Section(Section::unchecked(), s), KeyValuesMapList{});
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700187 }
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -0700188 section = &it->second.emplace_back();
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700189 }
190
191 void pumpKV(std::string_view line)
192 {
193 auto epos = line.find('=');
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700194 std::vector<std::string> new_warnings;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700195 if (epos == line.npos)
196 {
William A. Kennington IIIcafc1512023-07-25 02:22:32 -0700197 new_warnings.emplace_back(std::format(
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700198 "{}:{}: KV missing `=`", filename.get().native(), lineno));
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700199 }
200 auto k = line.substr(0, epos);
201 removePadding(k);
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700202 if (section == nullptr)
203 {
204 new_warnings.emplace_back(
William A. Kennington IIIcafc1512023-07-25 02:22:32 -0700205 std::format("{}:{}: Key `{}` missing section",
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700206 filename.get().native(), lineno, k));
207 }
208 if (!new_warnings.empty())
209 {
210 warnings.insert(warnings.end(),
211 std::make_move_iterator(new_warnings.begin()),
212 std::make_move_iterator(new_warnings.end()));
213 return;
214 }
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700215 auto v = line.substr(epos + 1);
216 removePadding(v);
217
218 auto it = section->find(k);
219 if (it == section->end())
220 {
William A. Kennington III0dd09372022-08-18 14:57:07 -0700221 std::tie(it, std::ignore) =
222 section->emplace(Key(Key::unchecked(), k), ValueList{});
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700223 }
William A. Kennington III0dd09372022-08-18 14:57:07 -0700224 it->second.emplace_back(Value::unchecked(), v);
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700225 }
226
227 void pump(std::string_view line)
228 {
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700229 lineno++;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700230 for (size_t i = 0; i < line.size(); ++i)
231 {
232 auto c = line[i];
233 if (iscomment(c))
234 {
235 return;
236 }
237 else if (c == '[')
238 {
239 return pumpSection(line.substr(i + 1));
240 }
241 else if (!isspace(c))
242 {
243 return pumpKV(line.substr(i));
244 }
245 }
246 }
247};
248
William A. Kennington III25511a12022-08-04 16:32:28 -0700249void Parser::setFile(const fs::path& filename)
Ratan Guptaed123a32017-06-15 09:07:31 +0530250{
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700251 Parse parse(filename);
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700252
William A. Kennington III301e8ad2022-11-15 15:45:32 -0800253 bool fileExists = true;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700254 try
Ratan Guptaed123a32017-06-15 09:07:31 +0530255 {
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700256 auto fd = stdplus::fd::open(filename.c_str(),
257 stdplus::fd::OpenAccess::ReadOnly);
258 stdplus::fd::LineReader reader(fd);
259 while (true)
Ratan Guptaed123a32017-06-15 09:07:31 +0530260 {
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700261 parse.pump(*reader.readLine());
Ratan Guptaed123a32017-06-15 09:07:31 +0530262 }
263 }
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700264 catch (const stdplus::exception::Eof&)
Patrick Williams89d734b2023-05-10 07:50:25 -0500265 {}
William A. Kennington III301e8ad2022-11-15 15:45:32 -0800266 catch (const std::system_error& e)
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700267 {
William A. Kennington III301e8ad2022-11-15 15:45:32 -0800268 fileExists = false;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700269 // TODO: Pass exceptions once callers can handle them
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700270 parse.warnings.emplace_back(
William A. Kennington IIIcafc1512023-07-25 02:22:32 -0700271 std::format("{}: Open error: {}", filename.native(), e.what()));
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700272 }
273
William A. Kennington III34bb3e22022-08-18 15:17:22 -0700274 this->map = std::move(parse.map);
William A. Kennington III301e8ad2022-11-15 15:45:32 -0800275 this->fileExists = fileExists;
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700276 this->filename = filename;
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700277 this->warnings = std::move(parse.warnings);
Ratan Guptaed123a32017-06-15 09:07:31 +0530278}
279
William A. Kennington III409f1a62022-08-11 15:44:37 -0700280static void writeFileInt(const SectionMap& map, const fs::path& filename)
281{
282 stdplus::fd::AtomicWriter writer(filename, 0644);
283 stdplus::fd::FormatBuffer out(writer);
284 for (const auto& [section, maps] : map)
285 {
286 for (const auto& map : maps)
287 {
William A. Kennington III86642522023-07-24 17:55:55 -0700288 out.appends("["sv, section.get(), "]\n"sv);
William A. Kennington III409f1a62022-08-11 15:44:37 -0700289 for (const auto& [key, vals] : map)
290 {
291 for (const auto& val : vals)
292 {
William A. Kennington III86642522023-07-24 17:55:55 -0700293 out.appends(key.get(), "="sv, val.get(), "\n"sv);
William A. Kennington III409f1a62022-08-11 15:44:37 -0700294 }
295 }
296 }
297 }
298 out.flush();
299 writer.commit();
300}
301
302void Parser::writeFile() const
303{
304 writeFileInt(map, filename);
305}
306
307void Parser::writeFile(const fs::path& filename)
308{
309 writeFileInt(map, filename);
310 this->filename = filename;
311}
312
Gunnar Mills57d9c502018-09-14 14:42:34 -0500313} // namespace config
314} // namespace network
315} // namespace phosphor