blob: 867d61192ad07b981a440be36c786c607765d0d3 [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 IIIbc52d932022-08-18 16:34:02 -07006#include <functional>
7#include <iterator>
William A. Kennington III0dd09372022-08-18 14:57:07 -07008#include <stdexcept>
William A. Kennington III61ef4f22022-08-18 16:29:09 -07009#include <stdplus/exception.hpp>
10#include <stdplus/fd/create.hpp>
11#include <stdplus/fd/line.hpp>
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -070012#include <string>
William A. Kennington III61ef4f22022-08-18 16:29:09 -070013#include <utility>
Ratan Guptaed123a32017-06-15 09:07:31 +053014
15namespace phosphor
16{
17namespace network
18{
19namespace config
20{
21
William A. Kennington III150753f2022-08-05 15:20:54 -070022using std::literals::string_view_literals::operator""sv;
23
24bool icaseeq(std::string_view in, std::string_view expected) noexcept
25{
26 return std::equal(in.begin(), in.end(), expected.begin(), expected.end(),
27 [](auto a, auto b) { return tolower(a) == b; });
28}
29
30std::optional<bool> parseBool(std::string_view in) noexcept
31{
32 if (in == "1"sv || icaseeq(in, "yes"sv) || icaseeq(in, "y"sv) ||
33 icaseeq(in, "true"sv) || icaseeq(in, "t"sv) || icaseeq(in, "on"sv))
34 {
35 return true;
36 }
37 if (in == "0"sv || icaseeq(in, "no"sv) || icaseeq(in, "n"sv) ||
38 icaseeq(in, "false"sv) || icaseeq(in, "f"sv) || icaseeq(in, "off"sv))
39 {
40 return false;
41 }
42 return std::nullopt;
43}
44
William A. Kennington IIIa520a392022-08-08 12:17:34 -070045fs::path pathForIntfConf(const fs::path& dir, std::string_view intf)
46{
47 return dir / fmt::format(FMT_COMPILE("00-bmc-{}.network"), intf);
48}
49
50fs::path pathForIntfDev(const fs::path& dir, std::string_view intf)
51{
52 return dir / fmt::format(FMT_COMPILE("{}.netdev"), intf);
53}
54
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -070055const std::string*
56 SectionMap::getLastValueString(std::string_view section,
57 std::string_view key) const noexcept
58{
59 auto sit = find(section);
60 if (sit == end())
61 {
62 return nullptr;
63 }
64 for (auto it = sit->second.rbegin(); it != sit->second.rend(); ++it)
65 {
66 auto kit = it->find(key);
67 if (kit == it->end() || kit->second.empty())
68 {
69 continue;
70 }
William A. Kennington III0dd09372022-08-18 14:57:07 -070071 return &kit->second.back().get();
William A. Kennington IIIe21a5cf2022-08-09 12:19:14 -070072 }
73 return nullptr;
74}
75
76std::vector<std::string> SectionMap::getValueStrings(std::string_view section,
77 std::string_view key) const
78{
79 return getValues(section, key,
80 [](const Value& v) { return std::string(v); });
81}
82
William A. Kennington III0dd09372022-08-18 14:57:07 -070083void KeyCheck::operator()(const std::string& s)
84{
85 for (auto c : s)
86 {
87 if (c == '\n' || c == '=')
88 {
89 throw std::invalid_argument(
90 fmt::format(FMT_COMPILE("Invalid Config Key: {}"), s));
91 }
92 }
93}
94
95void SectionCheck::operator()(const std::string& s)
96{
97 for (auto c : s)
98 {
99 if (c == '\n' || c == ']')
100 {
101 throw std::invalid_argument(
102 fmt::format(FMT_COMPILE("Invalid Config Section: {}"), s));
103 }
104 }
105}
106
107void ValueCheck::operator()(const std::string& s)
108{
109 for (auto c : s)
110 {
111 if (c == '\n')
112 {
113 throw std::invalid_argument(
114 fmt::format(FMT_COMPILE("Invalid Config Value: {}"), s));
115 }
116 }
117}
118
William A. Kennington III25511a12022-08-04 16:32:28 -0700119Parser::Parser(const fs::path& filename)
Ratan Guptaed123a32017-06-15 09:07:31 +0530120{
William A. Kennington III25511a12022-08-04 16:32:28 -0700121 setFile(filename);
Ratan Guptaed123a32017-06-15 09:07:31 +0530122}
123
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700124inline bool isspace(char c) noexcept
Ratan Guptaed123a32017-06-15 09:07:31 +0530125{
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700126 return c == ' ' || c == '\t';
Ratan Guptaed123a32017-06-15 09:07:31 +0530127}
128
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700129inline bool iscomment(char c) noexcept
130{
131 return c == '#' || c == ';';
132}
133
134static void removePadding(std::string_view& str) noexcept
135{
136 size_t idx = str.size();
137 for (; idx > 0 && isspace(str[idx - 1]); idx--)
138 ;
139 str.remove_suffix(str.size() - idx);
140
141 idx = 0;
142 for (; idx < str.size() && isspace(str[idx]); idx++)
143 ;
144 str.remove_prefix(idx);
145}
146
147struct Parse
148{
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700149 std::reference_wrapper<const fs::path> filename;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700150 SectionMap sections;
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700151 KeyValuesMap* section;
152 std::vector<std::string> warnings;
153 size_t lineno;
154
155 inline Parse(const fs::path& filename) :
156 filename(filename), section(nullptr), lineno(0)
157 {
158 }
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 {
165 warnings.emplace_back(fmt::format("{}:{}: Section missing ]",
166 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(
175 fmt::format("{}:{}: Characters outside section name",
176 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);
182 auto it = sections.find(s);
183 if (it == sections.end())
184 {
William A. Kennington III0dd09372022-08-18 14:57:07 -0700185 std::tie(it, std::ignore) = sections.emplace(
186 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 IIIbc52d932022-08-18 16:34:02 -0700197 new_warnings.emplace_back(fmt::format(
198 "{}:{}: 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(
205 fmt::format("{}:{}: Key `{}` missing section",
206 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
253 try
Ratan Guptaed123a32017-06-15 09:07:31 +0530254 {
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700255 auto fd = stdplus::fd::open(filename.c_str(),
256 stdplus::fd::OpenAccess::ReadOnly);
257 stdplus::fd::LineReader reader(fd);
258 while (true)
Ratan Guptaed123a32017-06-15 09:07:31 +0530259 {
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700260 parse.pump(*reader.readLine());
Ratan Guptaed123a32017-06-15 09:07:31 +0530261 }
262 }
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700263 catch (const stdplus::exception::Eof&)
264 {
265 }
266 catch (const std::exception& e)
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700267 {
268 // TODO: Pass exceptions once callers can handle them
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700269 parse.warnings.emplace_back(
270 fmt::format("{}: Read error: {}", filename.native(), e.what()));
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700271 }
272
William A. Kennington IIIa520a392022-08-08 12:17:34 -0700273 this->filename = filename;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700274 this->sections = std::move(parse.sections);
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700275 this->warnings = std::move(parse.warnings);
Ratan Guptaed123a32017-06-15 09:07:31 +0530276}
277
Gunnar Mills57d9c502018-09-14 14:42:34 -0500278} // namespace config
279} // namespace network
280} // namespace phosphor