1 ## This file is a template.  The comment below is emitted
2 ## into the rendered file; feel free to edit this file.
3 // WARNING: Generated header. Do not edit!
4 <%
5 import re
6 from collections import defaultdict
7 from sdbusplus.namedelement import NamedElement
8 objects = settingsDict.keys()
9 sdbusplus_includes = []
10 props = defaultdict(list)
11 validators = defaultdict(tuple)
12 
13 def get_setting_sdbusplus_type(setting_intf):
14     setting = "sdbusplus::" + setting_intf.replace('.', '::')
15     i = setting.rfind('::')
16     setting = setting[:i] + '::server::' + setting[i+2:]
17     return setting
18 
19 def get_setting_type(path):
20     path = path[1:]
21     path = path.replace('/', '::')
22     return path
23 %>\
24 #pragma once
25 
26 % for object in objects:
27     % for item in settingsDict[object]:
28 <%
29     include = item['Interface']
30     include = include.replace('.', '/')
31     include = include + "/server.hpp"
32     sdbusplus_includes.append(include)
33 %>\
34     % endfor
35 % endfor
36 #include <cereal/archives/json.hpp>
37 #include <cereal/types/vector.hpp>
38 #include <fstream>
39 #include <utility>
40 #include <filesystem>
41 #include <regex>
42 #include <phosphor-logging/elog.hpp>
43 #include <phosphor-logging/elog-errors.hpp>
44 #include <phosphor-logging/log.hpp>
45 #include "config.h"
46 #include <xyz/openbmc_project/Common/error.hpp>
47 using namespace phosphor::logging;
48 
49 % for i in set(sdbusplus_includes):
50 #include "${i}"
51 % endfor
52 
53 namespace phosphor
54 {
55 namespace settings
56 {
57 
58 namespace fs = std::filesystem;
59 
60 namespace persistent
61 {
62 
63 // A setting d-bus object /foo/bar/baz is persisted in the filesystem with the
64 // same path. This eases re-construction of settings objects when we restore
65 // from the filesystem. This can be a problem though when you have two objects
66 // such as - /foo/bar and /foo/bar/baz. This is because 'bar' will be treated as
67 // a file in the first case, and a subdir in the second. To solve this, suffix
68 // files with a trailing __. The __ is a safe character sequence to use, because
69 // we won't have d-bus object paths ending with this.
70 // With this the objects would be persisted as - /foo/bar__ and /foo/bar/baz__.
71 constexpr auto fileSuffix = "__";
72 
73 }
74 
75 % for object in objects:
76 <%
77    ns = object.split('/')
78    ns.pop(0)
79 %>\
80 % for n in ns:
81 namespace ${n}
82 {
83 % endfor
84 <%
85     interfaces = []
86     aliases = []
87     for item in settingsDict[object]:
88         interfaces.append(item['Interface'])
89         for name, meta in item['Properties'].items():
90             if 'Validation' in meta:
91                 dict = meta['Validation']
92                 if dict['Type'] == "range":
93                     validators[name] = (dict['Type'], dict['Validator'], dict['Unit'])
94                 else:
95                     validators[name] = (dict['Type'], dict['Validator'])
96 %>
97 % for index, intf in enumerate(interfaces):
98 using Iface${index} = ${get_setting_sdbusplus_type(intf)};
99 <% aliases.append("Iface" + str(index)) %>\
100 % endfor
101 <%
102     parent = "sdbusplus::server::object_t" + "<" + ", ".join(aliases) + ">"
103 %>\
104 using Parent = ${parent};
105 
106 class Impl : public Parent
107 {
108     public:
109         Impl(sdbusplus::bus_t& bus, const char* path):
110             Parent(bus, path, Parent::action::defer_emit),
111             path(path)
112         {
113         }
114         virtual ~Impl() = default;
115 
116 % for index, item in enumerate(settingsDict[object]):
117     % for propName, metaDict in item['Properties'].items():
118 <% t = NamedElement(name=propName).camelCase %>\
119 <% fname = "validate" + propName %>\
120         decltype(std::declval<Iface${index}>().${t}()) ${t}(decltype(std::declval<Iface${index}>().${t}()) value) override
121         {
122             auto result = Iface${index}::${t}();
123             if (value != result)
124             {
125             % if propName in validators:
126                 if (!${fname}(value))
127                 {
128                     namespace error =
129                         sdbusplus::xyz::openbmc_project::Common::Error;
130                     namespace metadata =
131                         phosphor::logging::xyz::openbmc_project::Common;
132                     phosphor::logging::report<error::InvalidArgument>(
133                         metadata::InvalidArgument::ARGUMENT_NAME("${t}"),
134                     % if validators[propName][0] != "regex":
135                         metadata::InvalidArgument::ARGUMENT_VALUE(std::to_string(value).c_str()));
136                     % else:
137                         metadata::InvalidArgument::ARGUMENT_VALUE(value.c_str()));
138                     % endif
139                     return result;
140                 }
141              % endif
142                 fs::path p(SETTINGS_PERSIST_PATH);
143                 p /= path.relative_path();
144                 p += persistent::fileSuffix;
145                 fs::create_directories(p.parent_path());
146                 std::ofstream os(p.c_str(), std::ios::binary);
147                 cereal::JSONOutputArchive oarchive(os);
148                 result = Iface${index}::${t}(value);
149                 oarchive(*this);
150             }
151             return result;
152         }
153         using Iface${index}::${t};
154 
155     % endfor
156 % endfor
157     private:
158         fs::path path;
159 % for index, item in enumerate(settingsDict[object]):
160   % for propName, metaDict in item['Properties'].items():
161 <% t = NamedElement(name=propName).camelCase %>\
162 <% fname = "validate" + propName %>\
163     % if propName in validators:
164 
165         bool ${fname}(decltype(std::declval<Iface${index}>().${t}()) value)
166         {
167             bool matched = false;
168         % if (validators[propName][0] == 'regex'):
169             std::regex regexToCheck("${validators[propName][1]}");
170             matched = std::regex_search(value, regexToCheck);
171             if (!matched)
172             {
173                 std::string err = "Input parameter for ${propName} is invalid "
174                     "Input: " + value + " not in the format of this regex: "
175                     "${validators[propName][1]}";
176                 using namespace phosphor::logging;
177                 log<level::ERR>(err.c_str());
178             }
179         % elif (validators[propName][0] == 'range'):
180 <% lowhigh = re.split('\.\.', validators[propName][1]) %>\
181             if ((value <= ${lowhigh[1]}) && (value >= ${lowhigh[0]}))
182             {
183                 matched = true;
184             }
185             else
186             {
187                 std::string err = "Input parameter for ${propName} is invalid "
188                     "Input: " + std::to_string(value) + "in uint: "
189                     "${validators[propName][2]} is not in range:${validators[propName][1]}";
190                 using namespace phosphor::logging;
191                 log<level::ERR>(err.c_str());
192             }
193         % else:
194             <% assert("Unknown validation type: propName") %>\
195         % endif
196             return matched;
197         }
198     % endif
199   % endfor
200 % endfor
201 };
202 
203 template<class Archive>
204 void save(Archive& a,
205           const Impl& setting,
206           const std::uint32_t version)
207 {
208 <%
209 props = []
210 for index, item in enumerate(settingsDict[object]):
211     intfProps = ["setting." + NamedElement(name=propName).camelCase + "()" for \
212                     propName, metaDict in item['Properties'].items()]
213     props.extend(intfProps)
214 props = ', '.join(props)
215 %>\
216 % if props:
217     a(${props});
218 % endif
219 }
220 
221 template<class Archive>
222 void load(Archive& a,
223           Impl& setting,
224           const std::uint32_t version)
225 {
226 <% props = [] %>\
227 % for index, item in enumerate(settingsDict[object]):
228   % for  prop, metaDict in item['Properties'].items():
229 <%
230     t = "setting." + NamedElement(name=prop).camelCase + "()"
231     props.append(prop)
232 %>\
233     decltype(${t}) ${prop}{};
234   % endfor
235 % endfor
236 <% props = ', '.join(props) %>
237 % if props:
238     a(${props});
239 % endif
240 
241 <% props = [] %>
242 % for index, item in enumerate(settingsDict[object]):
243   % for  prop, metaDict in item['Properties'].items():
244 <%
245     t = "setting." + NamedElement(name=prop).camelCase + "(" + prop + ")"
246 %>\
247     ${t};
248   % endfor
249 % endfor
250 }
251 
252 % for n in reversed(ns):
253 } // namespace ${n}
254 % endfor
255 
256 % endfor
257 
258 /** @class Manager
259  *
260  *  @brief Compose settings objects and put them on the bus.
261  */
262 class Manager
263 {
264     public:
265         Manager() = delete;
266         Manager(const Manager&) = delete;
267         Manager& operator=(const Manager&) = delete;
268         Manager(Manager&&) = delete;
269         Manager& operator=(Manager&&) = delete;
270         virtual ~Manager() = default;
271 
272         /** @brief Constructor to put settings objects on to the bus.
273          *  @param[in] bus - Bus to attach to.
274          */
275         explicit Manager(sdbusplus::bus_t& bus) :
276             settings(
277                 std::make_tuple(
278 % for index, path in enumerate(objects):
279 <% type = get_setting_type(path) + "::Impl" %>\
280                     std::make_unique<${type}>(
281                         bus,
282   % if index < len(settingsDict) - 1:
283                         "${path}"),
284   % else:
285                         "${path}")
286   % endif
287 % endfor
288                 )
289             )
290         {
291 
292             fs::path path{};
293 % for index, path in enumerate(objects):
294 <% relativePath = path[1:] %>\
295             path = fs::path(SETTINGS_PERSIST_PATH) / "${relativePath}";
296             path += persistent::fileSuffix;
297             auto initSetting${index} = [&]()
298             {
299   % for item in settingsDict[path]:
300     % for propName, metaDict in item['Properties'].items():
301 <% p = NamedElement(name=propName).camelCase %>\
302 <% defaultValue = metaDict['Default'] %>\
303 % if isinstance(defaultValue, str) and  not \
304     defaultValue.startswith('"') and '::' in defaultValue:
305 <% ns = get_setting_sdbusplus_type(item['Interface'])
306 i = ns.rfind('::')
307 defaultValue = "{}::{}".format(ns[:i], defaultValue)
308 %>\
309 % endif
310                 std::get<${index}>(settings)->
311                   ${get_setting_sdbusplus_type(item['Interface'])}::${p}(${defaultValue});
312   % endfor
313 % endfor
314             };
315 
316             try
317             {
318                 if (fs::exists(path))
319                 {
320                     std::ifstream is(path.c_str(), std::ios::in);
321                     cereal::JSONInputArchive iarchive(is);
322                     iarchive(*std::get<${index}>(settings));
323                 }
324                 else
325                 {
326                     initSetting${index}();
327                 }
328             }
329             catch (const cereal::Exception& e)
330             {
331                 log<level::ERR>(e.what());
332                 fs::remove(path);
333                 initSetting${index}();
334             }
335             std::get<${index}>(settings)->emit_object_added();
336 
337 % endfor
338         }
339 
340     private:
341         /* @brief Composition of settings objects. */
342         std::tuple<
343 % for index, path in enumerate(objects):
344 <% type = get_setting_type(path) + "::Impl" %>\
345   % if index < len(settingsDict) - 1:
346             std::unique_ptr<${type}>,
347   % else:
348             std::unique_ptr<${type}>> settings;
349   % endif
350 % endfor
351 };
352 
353 } // namespace settings
354 } // namespace phosphor
355 
356 // Now register the class version with Cereal
357 % for object in objects:
358 <%
359    classname = "phosphor::settings"
360    ns = object.split('/')
361    ns.pop(0)
362 %>\
363 % for n in ns:
364 <%
365     classname += "::" + n
366 %>\
367 % endfor
368 CEREAL_CLASS_VERSION(${classname + "::Impl"}, CLASS_VERSION);
369 % endfor
370