blob: afa563ac0c197dd1d6628387ccf444072773798d [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 IIIa520a392022-08-08 12:17:34 -07003#include <fmt/compile.h>
4#include <fmt/format.h>
5
William A. Kennington III61ef4f22022-08-18 16:29:09 -07006#include <stdplus/exception.hpp>
William A. Kennington III409f1a62022-08-11 15:44:37 -07007#include <stdplus/fd/atomic.hpp>
William A. Kennington III61ef4f22022-08-18 16:29:09 -07008#include <stdplus/fd/create.hpp>
William A. Kennington III409f1a62022-08-11 15:44:37 -07009#include <stdplus/fd/fmt.hpp>
William A. Kennington III61ef4f22022-08-18 16:29:09 -070010#include <stdplus/fd/line.hpp>
Patrick Williams89d734b2023-05-10 07:50:25 -050011
12#include <functional>
13#include <iterator>
14#include <stdexcept>
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -070015#include <string>
William A. Kennington III61ef4f22022-08-18 16:29:09 -070016#include <utility>
Ratan Guptaed123a32017-06-15 09:07:31 +053017
18namespace phosphor
19{
20namespace network
21{
22namespace config
23{
24
William A. Kennington III150753f2022-08-05 15:20:54 -070025using std::literals::string_view_literals::operator""sv;
26
27bool icaseeq(std::string_view in, std::string_view expected) noexcept
28{
29 return std::equal(in.begin(), in.end(), expected.begin(), expected.end(),
30 [](auto a, auto b) { return tolower(a) == b; });
31}
32
33std::optional<bool> parseBool(std::string_view in) noexcept
34{
35 if (in == "1"sv || icaseeq(in, "yes"sv) || icaseeq(in, "y"sv) ||
36 icaseeq(in, "true"sv) || icaseeq(in, "t"sv) || icaseeq(in, "on"sv))
37 {
38 return true;
39 }
40 if (in == "0"sv || icaseeq(in, "no"sv) || icaseeq(in, "n"sv) ||
41 icaseeq(in, "false"sv) || icaseeq(in, "f"sv) || icaseeq(in, "off"sv))
42 {
43 return false;
44 }
45 return std::nullopt;
46}
47
William A. Kennington IIIa520a392022-08-08 12:17:34 -070048fs::path pathForIntfConf(const fs::path& dir, std::string_view intf)
49{
50 return dir / fmt::format(FMT_COMPILE("00-bmc-{}.network"), intf);
51}
52
53fs::path pathForIntfDev(const fs::path& dir, std::string_view intf)
54{
55 return dir / fmt::format(FMT_COMPILE("{}.netdev"), intf);
56}
57
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -070058const std::string*
59 SectionMap::getLastValueString(std::string_view section,
60 std::string_view key) const noexcept
61{
62 auto sit = find(section);
63 if (sit == end())
64 {
65 return nullptr;
66 }
67 for (auto it = sit->second.rbegin(); it != sit->second.rend(); ++it)
68 {
69 auto kit = it->find(key);
70 if (kit == it->end() || kit->second.empty())
71 {
72 continue;
73 }
William A. Kennington III0dd09372022-08-18 14:57:07 -070074 return &kit->second.back().get();
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -070075 }
76 return nullptr;
77}
78
79std::vector<std::string> SectionMap::getValueStrings(std::string_view section,
80 std::string_view key) const
81{
82 return getValues(section, key,
83 [](const Value& v) { return std::string(v); });
84}
85
William A. Kennington IIIbe3bd2f2022-10-11 14:11:27 -070086void KeyCheck::operator()(std::string_view s)
William A. Kennington III0dd09372022-08-18 14:57:07 -070087{
88 for (auto c : s)
89 {
90 if (c == '\n' || c == '=')
91 {
92 throw std::invalid_argument(
93 fmt::format(FMT_COMPILE("Invalid Config Key: {}"), s));
94 }
95 }
96}
97
William A. Kennington IIIbe3bd2f2022-10-11 14:11:27 -070098void SectionCheck::operator()(std::string_view s)
William A. Kennington III0dd09372022-08-18 14:57:07 -070099{
100 for (auto c : s)
101 {
102 if (c == '\n' || c == ']')
103 {
104 throw std::invalid_argument(
105 fmt::format(FMT_COMPILE("Invalid Config Section: {}"), s));
106 }
107 }
108}
109
William A. Kennington IIIbe3bd2f2022-10-11 14:11:27 -0700110void ValueCheck::operator()(std::string_view s)
William A. Kennington III0dd09372022-08-18 14:57:07 -0700111{
112 for (auto c : s)
113 {
114 if (c == '\n')
115 {
116 throw std::invalid_argument(
117 fmt::format(FMT_COMPILE("Invalid Config Value: {}"), s));
118 }
119 }
120}
121
William A. Kennington III25511a12022-08-04 16:32:28 -0700122Parser::Parser(const fs::path& filename)
Ratan Guptaed123a32017-06-15 09:07:31 +0530123{
William A. Kennington III25511a12022-08-04 16:32:28 -0700124 setFile(filename);
Ratan Guptaed123a32017-06-15 09:07:31 +0530125}
126
William A. Kennington IIIf55b7d82022-10-25 14:21:46 -0700127constexpr bool isspace(char c) noexcept
Ratan Guptaed123a32017-06-15 09:07:31 +0530128{
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700129 return c == ' ' || c == '\t';
Ratan Guptaed123a32017-06-15 09:07:31 +0530130}
131
William A. Kennington IIIf55b7d82022-10-25 14:21:46 -0700132constexpr bool iscomment(char c) noexcept
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700133{
134 return c == '#' || c == ';';
135}
136
137static void removePadding(std::string_view& str) noexcept
138{
139 size_t idx = str.size();
140 for (; idx > 0 && isspace(str[idx - 1]); idx--)
141 ;
142 str.remove_suffix(str.size() - idx);
143
144 idx = 0;
145 for (; idx < str.size() && isspace(str[idx]); idx++)
146 ;
147 str.remove_prefix(idx);
148}
149
150struct Parse
151{
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700152 std::reference_wrapper<const fs::path> filename;
William A. Kennington III34bb3e22022-08-18 15:17:22 -0700153 SectionMap map;
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700154 KeyValuesMap* section;
155 std::vector<std::string> warnings;
156 size_t lineno;
157
158 inline Parse(const fs::path& filename) :
159 filename(filename), section(nullptr), lineno(0)
Patrick Williams89d734b2023-05-10 07:50:25 -0500160 {}
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700161
162 void pumpSection(std::string_view line)
163 {
164 auto cpos = line.find(']');
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700165 if (cpos == line.npos)
166 {
167 warnings.emplace_back(fmt::format("{}:{}: Section missing ]",
168 filename.get().native(), lineno));
169 }
170 else
171 {
172 for (auto c : line.substr(cpos + 1))
173 {
174 if (!isspace(c))
175 {
176 warnings.emplace_back(
177 fmt::format("{}:{}: Characters outside section name",
178 filename.get().native(), lineno));
179 break;
180 }
181 }
182 }
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700183 auto s = line.substr(0, cpos);
William A. Kennington III34bb3e22022-08-18 15:17:22 -0700184 auto it = map.find(s);
185 if (it == map.end())
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700186 {
William A. Kennington III34bb3e22022-08-18 15:17:22 -0700187 std::tie(it, std::ignore) = map.emplace(
William A. Kennington III0dd09372022-08-18 14:57:07 -0700188 Section(Section::unchecked(), s), KeyValuesMapList{});
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700189 }
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -0700190 section = &it->second.emplace_back();
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700191 }
192
193 void pumpKV(std::string_view line)
194 {
195 auto epos = line.find('=');
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700196 std::vector<std::string> new_warnings;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700197 if (epos == line.npos)
198 {
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700199 new_warnings.emplace_back(fmt::format(
200 "{}:{}: KV missing `=`", filename.get().native(), lineno));
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700201 }
202 auto k = line.substr(0, epos);
203 removePadding(k);
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700204 if (section == nullptr)
205 {
206 new_warnings.emplace_back(
207 fmt::format("{}:{}: Key `{}` missing section",
208 filename.get().native(), lineno, k));
209 }
210 if (!new_warnings.empty())
211 {
212 warnings.insert(warnings.end(),
213 std::make_move_iterator(new_warnings.begin()),
214 std::make_move_iterator(new_warnings.end()));
215 return;
216 }
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700217 auto v = line.substr(epos + 1);
218 removePadding(v);
219
220 auto it = section->find(k);
221 if (it == section->end())
222 {
William A. Kennington III0dd09372022-08-18 14:57:07 -0700223 std::tie(it, std::ignore) =
224 section->emplace(Key(Key::unchecked(), k), ValueList{});
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700225 }
William A. Kennington III0dd09372022-08-18 14:57:07 -0700226 it->second.emplace_back(Value::unchecked(), v);
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700227 }
228
229 void pump(std::string_view line)
230 {
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700231 lineno++;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700232 for (size_t i = 0; i < line.size(); ++i)
233 {
234 auto c = line[i];
235 if (iscomment(c))
236 {
237 return;
238 }
239 else if (c == '[')
240 {
241 return pumpSection(line.substr(i + 1));
242 }
243 else if (!isspace(c))
244 {
245 return pumpKV(line.substr(i));
246 }
247 }
248 }
249};
250
William A. Kennington III25511a12022-08-04 16:32:28 -0700251void Parser::setFile(const fs::path& filename)
Ratan Guptaed123a32017-06-15 09:07:31 +0530252{
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700253 Parse parse(filename);
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700254
William A. Kennington III301e8ad2022-11-15 15:45:32 -0800255 bool fileExists = true;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700256 try
Ratan Guptaed123a32017-06-15 09:07:31 +0530257 {
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700258 auto fd = stdplus::fd::open(filename.c_str(),
259 stdplus::fd::OpenAccess::ReadOnly);
260 stdplus::fd::LineReader reader(fd);
261 while (true)
Ratan Guptaed123a32017-06-15 09:07:31 +0530262 {
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700263 parse.pump(*reader.readLine());
Ratan Guptaed123a32017-06-15 09:07:31 +0530264 }
265 }
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700266 catch (const stdplus::exception::Eof&)
Patrick Williams89d734b2023-05-10 07:50:25 -0500267 {}
William A. Kennington III301e8ad2022-11-15 15:45:32 -0800268 catch (const std::system_error& e)
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700269 {
William A. Kennington III301e8ad2022-11-15 15:45:32 -0800270 fileExists = false;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700271 // TODO: Pass exceptions once callers can handle them
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700272 parse.warnings.emplace_back(
William A. Kennington III301e8ad2022-11-15 15:45:32 -0800273 fmt::format("{}: Open error: {}", filename.native(), e.what()));
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700274 }
275
William A. Kennington III34bb3e22022-08-18 15:17:22 -0700276 this->map = std::move(parse.map);
William A. Kennington III301e8ad2022-11-15 15:45:32 -0800277 this->fileExists = fileExists;
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700278 this->filename = filename;
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700279 this->warnings = std::move(parse.warnings);
Ratan Guptaed123a32017-06-15 09:07:31 +0530280}
281
William A. Kennington III409f1a62022-08-11 15:44:37 -0700282static void writeFileInt(const SectionMap& map, const fs::path& filename)
283{
284 stdplus::fd::AtomicWriter writer(filename, 0644);
285 stdplus::fd::FormatBuffer out(writer);
286 for (const auto& [section, maps] : map)
287 {
288 for (const auto& map : maps)
289 {
290 out.append(FMT_COMPILE("[{}]\n"), section.get());
291 for (const auto& [key, vals] : map)
292 {
293 for (const auto& val : vals)
294 {
295 out.append(FMT_COMPILE("{}={}\n"), key.get(), val.get());
296 }
297 }
298 }
299 }
300 out.flush();
301 writer.commit();
302}
303
304void Parser::writeFile() const
305{
306 writeFileInt(map, filename);
307}
308
309void Parser::writeFile(const fs::path& filename)
310{
311 writeFileInt(map, filename);
312 this->filename = filename;
313}
314
Gunnar Mills57d9c502018-09-14 14:42:34 -0500315} // namespace config
316} // namespace network
317} // namespace phosphor