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