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