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