xref: /openbmc/phosphor-networkd/src/config_parser.cpp (revision ad205028c6e4e9deaf4f9ea886d38081b6f26f37)
11bbe3d1eSWilliam A. Kennington III #include "config_parser.hpp"
21bbe3d1eSWilliam A. Kennington III 
361ef4f2bSWilliam A. Kennington III #include <stdplus/exception.hpp>
4409f1a66SWilliam A. Kennington III #include <stdplus/fd/atomic.hpp>
561ef4f2bSWilliam A. Kennington III #include <stdplus/fd/create.hpp>
6409f1a66SWilliam A. Kennington III #include <stdplus/fd/fmt.hpp>
761ef4f2bSWilliam A. Kennington III #include <stdplus/fd/line.hpp>
88664252aSWilliam A. Kennington III #include <stdplus/str/cat.hpp>
989d734b9SPatrick Williams 
10cafc1512SWilliam A. Kennington III #include <format>
1189d734b9SPatrick Williams #include <functional>
1289d734b9SPatrick Williams #include <iterator>
1389d734b9SPatrick Williams #include <stdexcept>
14e21a5cf9SWilliam A. Kennington III #include <string>
1561ef4f2bSWilliam A. Kennington III #include <utility>
161bbe3d1eSWilliam A. Kennington III 
171bbe3d1eSWilliam A. Kennington III namespace phosphor
181bbe3d1eSWilliam A. Kennington III {
191bbe3d1eSWilliam A. Kennington III namespace network
201bbe3d1eSWilliam A. Kennington III {
211bbe3d1eSWilliam A. Kennington III namespace config
221bbe3d1eSWilliam A. Kennington III {
231bbe3d1eSWilliam A. Kennington III 
24150753f3SWilliam A. Kennington III using std::literals::string_view_literals::operator""sv;
25150753f3SWilliam A. Kennington III 
icaseeq(std::string_view in,std::string_view expected)26150753f3SWilliam A. Kennington III bool icaseeq(std::string_view in, std::string_view expected) noexcept
27150753f3SWilliam A. Kennington III {
28150753f3SWilliam A. Kennington III     return std::equal(in.begin(), in.end(), expected.begin(), expected.end(),
29150753f3SWilliam A. Kennington III                       [](auto a, auto b) { return tolower(a) == b; });
30150753f3SWilliam A. Kennington III }
31150753f3SWilliam A. Kennington III 
parseBool(std::string_view in)32150753f3SWilliam A. Kennington III std::optional<bool> parseBool(std::string_view in) noexcept
33150753f3SWilliam A. Kennington III {
34150753f3SWilliam A. Kennington III     if (in == "1"sv || icaseeq(in, "yes"sv) || icaseeq(in, "y"sv) ||
35150753f3SWilliam A. Kennington III         icaseeq(in, "true"sv) || icaseeq(in, "t"sv) || icaseeq(in, "on"sv))
36150753f3SWilliam A. Kennington III     {
37150753f3SWilliam A. Kennington III         return true;
38150753f3SWilliam A. Kennington III     }
39150753f3SWilliam A. Kennington III     if (in == "0"sv || icaseeq(in, "no"sv) || icaseeq(in, "n"sv) ||
40150753f3SWilliam A. Kennington III         icaseeq(in, "false"sv) || icaseeq(in, "f"sv) || icaseeq(in, "off"sv))
41150753f3SWilliam A. Kennington III     {
42150753f3SWilliam A. Kennington III         return false;
43150753f3SWilliam A. Kennington III     }
44150753f3SWilliam A. Kennington III     return std::nullopt;
45150753f3SWilliam A. Kennington III }
46150753f3SWilliam A. Kennington III 
pathForIntfConf(const fs::path & dir,std::string_view intf)47a520a39dSWilliam A. Kennington III fs::path pathForIntfConf(const fs::path& dir, std::string_view intf)
48a520a39dSWilliam A. Kennington III {
498664252aSWilliam A. Kennington III     return dir / stdplus::strCat("00-bmc-"sv, intf, ".network"sv);
50a520a39dSWilliam A. Kennington III }
51a520a39dSWilliam A. Kennington III 
pathForIntfDev(const fs::path & dir,std::string_view intf)52a520a39dSWilliam A. Kennington III fs::path pathForIntfDev(const fs::path& dir, std::string_view intf)
53a520a39dSWilliam A. Kennington III {
548664252aSWilliam A. Kennington III     return dir / stdplus::strCat(intf, ".netdev"sv);
55a520a39dSWilliam A. Kennington III }
56a520a39dSWilliam A. Kennington III 
getLastValueString(std::string_view section,std::string_view key) const57*ad205028SPatrick Williams const std::string* SectionMap::getLastValueString(
58*ad205028SPatrick Williams     std::string_view section, std::string_view key) const noexcept
59e21a5cf9SWilliam A. Kennington III {
60e21a5cf9SWilliam A. Kennington III     auto sit = find(section);
61e21a5cf9SWilliam A. Kennington III     if (sit == end())
62e21a5cf9SWilliam A. Kennington III     {
63e21a5cf9SWilliam A. Kennington III         return nullptr;
64e21a5cf9SWilliam A. Kennington III     }
65e21a5cf9SWilliam A. Kennington III     for (auto it = sit->second.rbegin(); it != sit->second.rend(); ++it)
66e21a5cf9SWilliam A. Kennington III     {
67e21a5cf9SWilliam A. Kennington III         auto kit = it->find(key);
68e21a5cf9SWilliam A. Kennington III         if (kit == it->end() || kit->second.empty())
69e21a5cf9SWilliam A. Kennington III         {
70e21a5cf9SWilliam A. Kennington III             continue;
71e21a5cf9SWilliam A. Kennington III         }
720dd0937dSWilliam A. Kennington III         return &kit->second.back().get();
73e21a5cf9SWilliam A. Kennington III     }
74e21a5cf9SWilliam A. Kennington III     return nullptr;
75e21a5cf9SWilliam A. Kennington III }
76e21a5cf9SWilliam A. Kennington III 
getValueStrings(std::string_view section,std::string_view key) const77e21a5cf9SWilliam A. Kennington III std::vector<std::string> SectionMap::getValueStrings(std::string_view section,
78e21a5cf9SWilliam A. Kennington III                                                      std::string_view key) const
79e21a5cf9SWilliam A. Kennington III {
80e21a5cf9SWilliam A. Kennington III     return getValues(section, key,
81e21a5cf9SWilliam A. Kennington III                      [](const Value& v) { return std::string(v); });
82e21a5cf9SWilliam A. Kennington III }
83e21a5cf9SWilliam A. Kennington III 
operator ()(std::string_view s)84be3bd2fcSWilliam A. Kennington III void KeyCheck::operator()(std::string_view s)
850dd0937dSWilliam A. Kennington III {
860dd0937dSWilliam A. Kennington III     for (auto c : s)
870dd0937dSWilliam A. Kennington III     {
880dd0937dSWilliam A. Kennington III         if (c == '\n' || c == '=')
890dd0937dSWilliam A. Kennington III         {
900dd0937dSWilliam A. Kennington III             throw std::invalid_argument(
918664252aSWilliam A. Kennington III                 stdplus::strCat("Invalid Config Key: "sv, s));
920dd0937dSWilliam A. Kennington III         }
930dd0937dSWilliam A. Kennington III     }
940dd0937dSWilliam A. Kennington III }
950dd0937dSWilliam A. Kennington III 
operator ()(std::string_view s)96be3bd2fcSWilliam A. Kennington III void SectionCheck::operator()(std::string_view s)
970dd0937dSWilliam A. Kennington III {
980dd0937dSWilliam A. Kennington III     for (auto c : s)
990dd0937dSWilliam A. Kennington III     {
1000dd0937dSWilliam A. Kennington III         if (c == '\n' || c == ']')
1010dd0937dSWilliam A. Kennington III         {
1020dd0937dSWilliam A. Kennington III             throw std::invalid_argument(
1038664252aSWilliam A. Kennington III                 stdplus::strCat("Invalid Config Section: "sv, s));
1040dd0937dSWilliam A. Kennington III         }
1050dd0937dSWilliam A. Kennington III     }
1060dd0937dSWilliam A. Kennington III }
1070dd0937dSWilliam A. Kennington III 
operator ()(std::string_view s)108be3bd2fcSWilliam A. Kennington III void ValueCheck::operator()(std::string_view s)
1090dd0937dSWilliam A. Kennington III {
1100dd0937dSWilliam A. Kennington III     for (auto c : s)
1110dd0937dSWilliam A. Kennington III     {
1120dd0937dSWilliam A. Kennington III         if (c == '\n')
1130dd0937dSWilliam A. Kennington III         {
1140dd0937dSWilliam A. Kennington III             throw std::invalid_argument(
1158664252aSWilliam A. Kennington III                 stdplus::strCat("Invalid Config Value: "sv, s));
1160dd0937dSWilliam A. Kennington III         }
1170dd0937dSWilliam A. Kennington III     }
1180dd0937dSWilliam A. Kennington III }
1190dd0937dSWilliam A. Kennington III 
Parser(const fs::path & filename)12025511a1cSWilliam A. Kennington III Parser::Parser(const fs::path& filename)
1211bbe3d1eSWilliam A. Kennington III {
12225511a1cSWilliam A. Kennington III     setFile(filename);
1231bbe3d1eSWilliam A. Kennington III }
1241bbe3d1eSWilliam A. Kennington III 
isspace(char c)125f55b7d8eSWilliam A. Kennington III constexpr bool isspace(char c) noexcept
1261bbe3d1eSWilliam A. Kennington III {
12761ef4f2bSWilliam A. Kennington III     return c == ' ' || c == '\t';
1281bbe3d1eSWilliam A. Kennington III }
1291bbe3d1eSWilliam A. Kennington III 
iscomment(char c)130f55b7d8eSWilliam A. Kennington III constexpr bool iscomment(char c) noexcept
1311bbe3d1eSWilliam A. Kennington III {
13261ef4f2bSWilliam A. Kennington III     return c == '#' || c == ';';
13361ef4f2bSWilliam A. Kennington III }
13461ef4f2bSWilliam A. Kennington III 
removePadding(std::string_view & str)13561ef4f2bSWilliam A. Kennington III static void removePadding(std::string_view& str) noexcept
13661ef4f2bSWilliam A. Kennington III {
13761ef4f2bSWilliam A. Kennington III     size_t idx = str.size();
13861ef4f2bSWilliam A. Kennington III     for (; idx > 0 && isspace(str[idx - 1]); idx--)
13961ef4f2bSWilliam A. Kennington III         ;
14061ef4f2bSWilliam A. Kennington III     str.remove_suffix(str.size() - idx);
14161ef4f2bSWilliam A. Kennington III 
14261ef4f2bSWilliam A. Kennington III     idx = 0;
14361ef4f2bSWilliam A. Kennington III     for (; idx < str.size() && isspace(str[idx]); idx++)
14461ef4f2bSWilliam A. Kennington III         ;
14561ef4f2bSWilliam A. Kennington III     str.remove_prefix(idx);
14661ef4f2bSWilliam A. Kennington III }
14761ef4f2bSWilliam A. Kennington III 
14861ef4f2bSWilliam A. Kennington III struct Parse
14961ef4f2bSWilliam A. Kennington III {
150bc52d93dSWilliam A. Kennington III     std::reference_wrapper<const fs::path> filename;
15134bb3e20SWilliam A. Kennington III     SectionMap map;
152bc52d93dSWilliam A. Kennington III     KeyValuesMap* section;
153bc52d93dSWilliam A. Kennington III     std::vector<std::string> warnings;
154bc52d93dSWilliam A. Kennington III     size_t lineno;
155bc52d93dSWilliam A. Kennington III 
Parsephosphor::network::config::Parse156bc52d93dSWilliam A. Kennington III     inline Parse(const fs::path& filename) :
157bc52d93dSWilliam A. Kennington III         filename(filename), section(nullptr), lineno(0)
15889d734b9SPatrick Williams     {}
15961ef4f2bSWilliam A. Kennington III 
pumpSectionphosphor::network::config::Parse16061ef4f2bSWilliam A. Kennington III     void pumpSection(std::string_view line)
16161ef4f2bSWilliam A. Kennington III     {
16261ef4f2bSWilliam A. Kennington III         auto cpos = line.find(']');
163bc52d93dSWilliam A. Kennington III         if (cpos == line.npos)
164bc52d93dSWilliam A. Kennington III         {
165cafc1512SWilliam A. Kennington III             warnings.emplace_back(std::format("{}:{}: Section missing ]",
166bc52d93dSWilliam A. Kennington III                                               filename.get().native(), lineno));
167bc52d93dSWilliam A. Kennington III         }
168bc52d93dSWilliam A. Kennington III         else
169bc52d93dSWilliam A. Kennington III         {
170bc52d93dSWilliam A. Kennington III             for (auto c : line.substr(cpos + 1))
171bc52d93dSWilliam A. Kennington III             {
172bc52d93dSWilliam A. Kennington III                 if (!isspace(c))
173bc52d93dSWilliam A. Kennington III                 {
174bc52d93dSWilliam A. Kennington III                     warnings.emplace_back(
175cafc1512SWilliam A. Kennington III                         std::format("{}:{}: Characters outside section name",
176bc52d93dSWilliam A. Kennington III                                     filename.get().native(), lineno));
177bc52d93dSWilliam A. Kennington III                     break;
178bc52d93dSWilliam A. Kennington III                 }
179bc52d93dSWilliam A. Kennington III             }
180bc52d93dSWilliam A. Kennington III         }
18161ef4f2bSWilliam A. Kennington III         auto s = line.substr(0, cpos);
18234bb3e20SWilliam A. Kennington III         auto it = map.find(s);
18334bb3e20SWilliam A. Kennington III         if (it == map.end())
18461ef4f2bSWilliam A. Kennington III         {
18534bb3e20SWilliam A. Kennington III             std::tie(it, std::ignore) = map.emplace(
1860dd0937dSWilliam A. Kennington III                 Section(Section::unchecked(), s), KeyValuesMapList{});
18761ef4f2bSWilliam A. Kennington III         }
188e21a5cf9SWilliam A. Kennington III         section = &it->second.emplace_back();
18961ef4f2bSWilliam A. Kennington III     }
19061ef4f2bSWilliam A. Kennington III 
pumpKVphosphor::network::config::Parse19161ef4f2bSWilliam A. Kennington III     void pumpKV(std::string_view line)
19261ef4f2bSWilliam A. Kennington III     {
19361ef4f2bSWilliam A. Kennington III         auto epos = line.find('=');
194bc52d93dSWilliam A. Kennington III         std::vector<std::string> new_warnings;
19561ef4f2bSWilliam A. Kennington III         if (epos == line.npos)
1961bbe3d1eSWilliam A. Kennington III         {
197cafc1512SWilliam A. Kennington III             new_warnings.emplace_back(std::format(
198bc52d93dSWilliam A. Kennington III                 "{}:{}: KV missing `=`", filename.get().native(), lineno));
1991bbe3d1eSWilliam A. Kennington III         }
20061ef4f2bSWilliam A. Kennington III         auto k = line.substr(0, epos);
20161ef4f2bSWilliam A. Kennington III         removePadding(k);
202bc52d93dSWilliam A. Kennington III         if (section == nullptr)
203bc52d93dSWilliam A. Kennington III         {
204bc52d93dSWilliam A. Kennington III             new_warnings.emplace_back(
205cafc1512SWilliam A. Kennington III                 std::format("{}:{}: Key `{}` missing section",
206bc52d93dSWilliam A. Kennington III                             filename.get().native(), lineno, k));
207bc52d93dSWilliam A. Kennington III         }
208bc52d93dSWilliam A. Kennington III         if (!new_warnings.empty())
209bc52d93dSWilliam A. Kennington III         {
210bc52d93dSWilliam A. Kennington III             warnings.insert(warnings.end(),
211bc52d93dSWilliam A. Kennington III                             std::make_move_iterator(new_warnings.begin()),
212bc52d93dSWilliam A. Kennington III                             std::make_move_iterator(new_warnings.end()));
213bc52d93dSWilliam A. Kennington III             return;
214bc52d93dSWilliam A. Kennington III         }
21561ef4f2bSWilliam A. Kennington III         auto v = line.substr(epos + 1);
21661ef4f2bSWilliam A. Kennington III         removePadding(v);
21761ef4f2bSWilliam A. Kennington III 
21861ef4f2bSWilliam A. Kennington III         auto it = section->find(k);
21961ef4f2bSWilliam A. Kennington III         if (it == section->end())
2201bbe3d1eSWilliam A. Kennington III         {
2210dd0937dSWilliam A. Kennington III             std::tie(it, std::ignore) =
2220dd0937dSWilliam A. Kennington III                 section->emplace(Key(Key::unchecked(), k), ValueList{});
2231bbe3d1eSWilliam A. Kennington III         }
2240dd0937dSWilliam A. Kennington III         it->second.emplace_back(Value::unchecked(), v);
2251bbe3d1eSWilliam A. Kennington III     }
22661ef4f2bSWilliam A. Kennington III 
pumpphosphor::network::config::Parse22761ef4f2bSWilliam A. Kennington III     void pump(std::string_view line)
2281bbe3d1eSWilliam A. Kennington III     {
229bc52d93dSWilliam A. Kennington III         lineno++;
23061ef4f2bSWilliam A. Kennington III         for (size_t i = 0; i < line.size(); ++i)
2311bbe3d1eSWilliam A. Kennington III         {
23261ef4f2bSWilliam A. Kennington III             auto c = line[i];
23361ef4f2bSWilliam A. Kennington III             if (iscomment(c))
23461ef4f2bSWilliam A. Kennington III             {
23561ef4f2bSWilliam A. Kennington III                 return;
23661ef4f2bSWilliam A. Kennington III             }
23761ef4f2bSWilliam A. Kennington III             else if (c == '[')
23861ef4f2bSWilliam A. Kennington III             {
23961ef4f2bSWilliam A. Kennington III                 return pumpSection(line.substr(i + 1));
24061ef4f2bSWilliam A. Kennington III             }
24161ef4f2bSWilliam A. Kennington III             else if (!isspace(c))
24261ef4f2bSWilliam A. Kennington III             {
24361ef4f2bSWilliam A. Kennington III                 return pumpKV(line.substr(i));
2441bbe3d1eSWilliam A. Kennington III             }
2451bbe3d1eSWilliam A. Kennington III         }
2461bbe3d1eSWilliam A. Kennington III     }
24761ef4f2bSWilliam A. Kennington III };
24861ef4f2bSWilliam A. Kennington III 
setFile(const fs::path & filename)24961ef4f2bSWilliam A. Kennington III void Parser::setFile(const fs::path& filename)
25061ef4f2bSWilliam A. Kennington III {
251bc52d93dSWilliam A. Kennington III     Parse parse(filename);
25261ef4f2bSWilliam A. Kennington III 
253301e8ad6SWilliam A. Kennington III     bool fileExists = true;
25461ef4f2bSWilliam A. Kennington III     try
25561ef4f2bSWilliam A. Kennington III     {
25661ef4f2bSWilliam A. Kennington III         auto fd = stdplus::fd::open(filename.c_str(),
25761ef4f2bSWilliam A. Kennington III                                     stdplus::fd::OpenAccess::ReadOnly);
25861ef4f2bSWilliam A. Kennington III         stdplus::fd::LineReader reader(fd);
25961ef4f2bSWilliam A. Kennington III         while (true)
26061ef4f2bSWilliam A. Kennington III         {
26161ef4f2bSWilliam A. Kennington III             parse.pump(*reader.readLine());
26261ef4f2bSWilliam A. Kennington III         }
26361ef4f2bSWilliam A. Kennington III     }
264bc52d93dSWilliam A. Kennington III     catch (const stdplus::exception::Eof&)
26589d734b9SPatrick Williams     {}
266301e8ad6SWilliam A. Kennington III     catch (const std::system_error& e)
26761ef4f2bSWilliam A. Kennington III     {
268301e8ad6SWilliam A. Kennington III         fileExists = false;
26961ef4f2bSWilliam A. Kennington III         // TODO: Pass exceptions once callers can handle them
270bc52d93dSWilliam A. Kennington III         parse.warnings.emplace_back(
271cafc1512SWilliam A. Kennington III             std::format("{}: Open error: {}", filename.native(), e.what()));
27261ef4f2bSWilliam A. Kennington III     }
27361ef4f2bSWilliam A. Kennington III 
27434bb3e20SWilliam A. Kennington III     this->map = std::move(parse.map);
275301e8ad6SWilliam A. Kennington III     this->fileExists = fileExists;
276a520a39dSWilliam A. Kennington III     this->filename = filename;
277bc52d93dSWilliam A. Kennington III     this->warnings = std::move(parse.warnings);
2781bbe3d1eSWilliam A. Kennington III }
2791bbe3d1eSWilliam A. Kennington III 
writeFileInt(const SectionMap & map,const fs::path & filename)280409f1a66SWilliam A. Kennington III static void writeFileInt(const SectionMap& map, const fs::path& filename)
281409f1a66SWilliam A. Kennington III {
282409f1a66SWilliam A. Kennington III     stdplus::fd::AtomicWriter writer(filename, 0644);
283409f1a66SWilliam A. Kennington III     stdplus::fd::FormatBuffer out(writer);
284409f1a66SWilliam A. Kennington III     for (const auto& [section, maps] : map)
285409f1a66SWilliam A. Kennington III     {
286409f1a66SWilliam A. Kennington III         for (const auto& map : maps)
287409f1a66SWilliam A. Kennington III         {
2888664252aSWilliam A. Kennington III             out.appends("["sv, section.get(), "]\n"sv);
289409f1a66SWilliam A. Kennington III             for (const auto& [key, vals] : map)
290409f1a66SWilliam A. Kennington III             {
291409f1a66SWilliam A. Kennington III                 for (const auto& val : vals)
292409f1a66SWilliam A. Kennington III                 {
2938664252aSWilliam A. Kennington III                     out.appends(key.get(), "="sv, val.get(), "\n"sv);
294409f1a66SWilliam A. Kennington III                 }
295409f1a66SWilliam A. Kennington III             }
296409f1a66SWilliam A. Kennington III         }
297409f1a66SWilliam A. Kennington III     }
298409f1a66SWilliam A. Kennington III     out.flush();
299409f1a66SWilliam A. Kennington III     writer.commit();
300409f1a66SWilliam A. Kennington III }
301409f1a66SWilliam A. Kennington III 
writeFile() const302409f1a66SWilliam A. Kennington III void Parser::writeFile() const
303409f1a66SWilliam A. Kennington III {
304409f1a66SWilliam A. Kennington III     writeFileInt(map, filename);
305409f1a66SWilliam A. Kennington III }
306409f1a66SWilliam A. Kennington III 
writeFile(const fs::path & filename)307409f1a66SWilliam A. Kennington III void Parser::writeFile(const fs::path& filename)
308409f1a66SWilliam A. Kennington III {
309409f1a66SWilliam A. Kennington III     writeFileInt(map, filename);
310409f1a66SWilliam A. Kennington III     this->filename = filename;
311409f1a66SWilliam A. Kennington III }
312409f1a66SWilliam A. Kennington III 
3131bbe3d1eSWilliam A. Kennington III } // namespace config
3141bbe3d1eSWilliam A. Kennington III } // namespace network
3151bbe3d1eSWilliam A. Kennington III } // namespace phosphor
316