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