xref: /openbmc/phosphor-settingsd/settings_manager.mako.hpp (revision 74e3be7d863cc504c7a6ddb54e30a97acdc9f38d)
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 objects = list(settingsDict.viewkeys())
8 sdbusplus_namespaces = []
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 <experimental/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 % for object in objects:
54     % for item in settingsDict[object]:
55 <%
56     ns = get_setting_sdbusplus_type(item['Interface'])
57     i = ns.rfind('::')
58     ns = ns[:i]
59     sdbusplus_namespaces.append(ns)
60 %>\
61     % endfor
62 % endfor
63 
64 namespace phosphor
65 {
66 namespace settings
67 {
68 
69 namespace fs = std::experimental::filesystem;
70 
71 namespace persistent
72 {
73 
74 // A setting d-bus object /foo/bar/baz is persisted in the filesystem with the
75 // same path. This eases re-construction of settings objects when we restore
76 // from the filesystem. This can be a problem though when you have two objects
77 // such as - /foo/bar and /foo/bar/baz. This is because 'bar' will be treated as
78 // a file in the first case, and a subdir in the second. To solve this, suffix
79 // files with a trailing __. The __ is a safe character sequence to use, because
80 // we won't have d-bus object paths ending with this.
81 // With this the objects would be persisted as - /foo/bar__ and /foo/bar/baz__.
82 constexpr auto fileSuffix = "__";
83 
84 }
85 
86 % for n in set(sdbusplus_namespaces):
87 using namespace ${n};
88 % endfor
89 
90 % for object in objects:
91 <%
92    ns = object.split('/')
93    ns.pop(0)
94 %>\
95 % for n in ns:
96 namespace ${n}
97 {
98 % endfor
99 <%
100     interfaces = []
101     aliases = []
102     for item in settingsDict[object]:
103         interfaces.append(item['Interface'])
104         for name, meta in item['Properties'].items():
105             if 'Validation' in meta:
106                 dict = meta['Validation']
107                 if dict['Type'] == "range":
108                     validators[name] = (dict['Type'], dict['Validator'], dict['Unit'])
109                 else:
110                     validators[name] = (dict['Type'], dict['Validator'])
111 %>
112 % for index, intf in enumerate(interfaces):
113 using Iface${index} = ${get_setting_sdbusplus_type(intf)};
114 <% aliases.append("Iface" + str(index)) %>\
115 % endfor
116 <%
117     parent = "sdbusplus::server::object::object" + "<" + ", ".join(aliases) + ">"
118 %>\
119 using Parent = ${parent};
120 
121 class Impl : public Parent
122 {
123     public:
124         Impl(sdbusplus::bus::bus& bus, const char* path):
125             Parent(bus, path, true),
126             path(path)
127         {
128         }
129         virtual ~Impl() = default;
130 
131 % for index, item in enumerate(settingsDict[object]):
132     % for propName, metaDict in item['Properties'].items():
133 <% t = propName[:1].lower() + propName[1:] %>\
134 <% fname = "validate" + propName %>\
135         decltype(std::declval<Iface${index}>().${t}()) ${t}(decltype(std::declval<Iface${index}>().${t}()) value) override
136         {
137             auto result = Iface${index}::${t}();
138             if (value != result)
139             {
140             % if propName in validators:
141                 if (!${fname}(value))
142                 {
143                     namespace error =
144                         sdbusplus::xyz::openbmc_project::Common::Error;
145                     namespace metadata =
146                         phosphor::logging::xyz::openbmc_project::Common;
147                     phosphor::logging::report<error::InvalidArgument>(
148                         metadata::InvalidArgument::ARGUMENT_NAME("${t}"),
149                     % if validators[propName][0] != "regex":
150                         metadata::InvalidArgument::ARGUMENT_VALUE(std::to_string(value).c_str()));
151                     % else:
152                         metadata::InvalidArgument::ARGUMENT_VALUE(value.c_str()));
153                     % endif
154                     return result;
155                 }
156              % endif
157                 fs::path p(SETTINGS_PERSIST_PATH);
158                 p /= path;
159                 p += persistent::fileSuffix;
160                 fs::create_directories(p.parent_path());
161                 std::ofstream os(p.c_str(), std::ios::binary);
162                 cereal::JSONOutputArchive oarchive(os);
163                 result = Iface${index}::${t}(value);
164                 oarchive(*this);
165             }
166             return result;
167         }
168         using Iface${index}::${t};
169 
170     % endfor
171 % endfor
172     private:
173         fs::path path;
174 % for index, item in enumerate(settingsDict[object]):
175   % for propName, metaDict in item['Properties'].items():
176 <% t = propName[:1].lower() + propName[1:] %>\
177 <% fname = "validate" + propName %>\
178     % if propName in validators:
179 
180         bool ${fname}(decltype(std::declval<Iface${index}>().${t}()) value)
181         {
182             bool matched = false;
183         % if (validators[propName][0] == 'regex'):
184             std::regex regexToCheck("${validators[propName][1]}");
185             matched = std::regex_search(value, regexToCheck);
186             if (!matched)
187             {
188                 std::string err = "Input parameter for ${propName} is invalid "
189                     "Input: " + value + " not in the format of this regex: "
190                     "${validators[propName][1]}";
191                 using namespace phosphor::logging;
192                 log<level::ERR>(err.c_str());
193             }
194         % elif (validators[propName][0] == 'range'):
195 <% lowhigh = re.split('\.\.', validators[propName][1]) %>\
196             if ((value <= ${lowhigh[1]}) && (value >= ${lowhigh[0]}))
197             {
198                 matched = true;
199             }
200             else
201             {
202                 std::string err = "Input parameter for ${propName} is invalid "
203                     "Input: " + std::to_string(value) + "in uint: "
204                     "${validators[propName][2]} is not in range:${validators[propName][1]}";
205                 using namespace phosphor::logging;
206                 log<level::ERR>(err.c_str());
207             }
208         % else:
209             <% assert("Unknown validation type: propName") %>\
210         % endif
211             return matched;
212         }
213     % endif
214   % endfor
215 % endfor
216 };
217 
218 template<class Archive>
219 void save(Archive& a,
220           const Impl& setting,
221           const std::uint32_t version)
222 {
223 <%
224 props = []
225 for index, item in enumerate(settingsDict[object]):
226     intfProps = ["setting." + propName[:1].lower() + propName[1:] + "()" for \
227                     propName, metaDict in item['Properties'].items()]
228     props.extend(intfProps)
229 props = ', '.join(props)
230 %>\
231     a(${props});
232 }
233 
234 template<class Archive>
235 void load(Archive& a,
236           Impl& setting,
237           const std::uint32_t version)
238 {
239 <% props = [] %>\
240 % for index, item in enumerate(settingsDict[object]):
241   % for  prop, metaDict in item['Properties'].items():
242 <%
243     t = "setting." + prop[:1].lower() + prop[1:] + "()"
244     props.append(prop)
245 %>\
246     decltype(${t}) ${prop}{};
247   % endfor
248 % endfor
249 <% props = ', '.join(props) %>
250     a(${props});
251 <% props = [] %>
252 % for index, item in enumerate(settingsDict[object]):
253   % for  prop, metaDict in item['Properties'].items():
254 <%
255     t = "setting." + prop[:1].lower() + prop[1:] + "(" + prop + ")"
256 %>\
257     ${t};
258   % endfor
259 % endfor
260 }
261 
262 % for n in reversed(ns):
263 } // namespace ${n}
264 % endfor
265 
266 % endfor
267 
268 /** @class Manager
269  *
270  *  @brief Compose settings objects and put them on the bus.
271  */
272 class Manager
273 {
274     public:
275         Manager() = delete;
276         Manager(const Manager&) = delete;
277         Manager& operator=(const Manager&) = delete;
278         Manager(Manager&&) = delete;
279         Manager& operator=(Manager&&) = delete;
280         virtual ~Manager() = default;
281 
282         /** @brief Constructor to put settings objects on to the bus.
283          *  @param[in] bus - Bus to attach to.
284          */
285         Manager(sdbusplus::bus::bus& bus)
286         {
287             fs::path path{};
288             settings =
289                 std::make_tuple(
290 % for index, path in enumerate(objects):
291 <% type = get_setting_type(path) + "::Impl" %>\
292                     std::make_unique<${type}>(
293                         bus,
294   % if index < len(settingsDict) - 1:
295                         "${path}"),
296   % else:
297                         "${path}"));
298   % endif
299 % endfor
300 
301 % for index, path in enumerate(objects):
302             path = fs::path(SETTINGS_PERSIST_PATH) / "${path}";
303             path += persistent::fileSuffix;
304             auto initSetting${index} = [&]()
305             {
306   % for item in settingsDict[path]:
307     % for propName, metaDict in item['Properties'].items():
308 <% p = propName[:1].lower() + propName[1:] %>\
309 <% defaultValue = metaDict['Default'] %>\
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 (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