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