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, Parent::action::defer_emit), 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 % if props: 217 a(${props}); 218 % endif 219 } 220 221 template<class Archive> 222 void load(Archive& a, 223 Impl& setting, 224 const std::uint32_t version) 225 { 226 <% props = [] %>\ 227 % for index, item in enumerate(settingsDict[object]): 228 % for prop, metaDict in item['Properties'].items(): 229 <% 230 t = "setting." + NamedElement(name=prop).camelCase + "()" 231 props.append(prop) 232 %>\ 233 decltype(${t}) ${prop}{}; 234 % endfor 235 % endfor 236 <% props = ', '.join(props) %> 237 % if props: 238 a(${props}); 239 % endif 240 241 <% props = [] %> 242 % for index, item in enumerate(settingsDict[object]): 243 % for prop, metaDict in item['Properties'].items(): 244 <% 245 t = "setting." + NamedElement(name=prop).camelCase + "(" + prop + ")" 246 %>\ 247 ${t}; 248 % endfor 249 % endfor 250 } 251 252 % for n in reversed(ns): 253 } // namespace ${n} 254 % endfor 255 256 % endfor 257 258 /** @class Manager 259 * 260 * @brief Compose settings objects and put them on the bus. 261 */ 262 class Manager 263 { 264 public: 265 Manager() = delete; 266 Manager(const Manager&) = delete; 267 Manager& operator=(const Manager&) = delete; 268 Manager(Manager&&) = delete; 269 Manager& operator=(Manager&&) = delete; 270 virtual ~Manager() = default; 271 272 /** @brief Constructor to put settings objects on to the bus. 273 * @param[in] bus - Bus to attach to. 274 */ 275 Manager(sdbusplus::bus::bus& bus) 276 { 277 fs::path path{}; 278 settings = 279 std::make_tuple( 280 % for index, path in enumerate(objects): 281 <% type = get_setting_type(path) + "::Impl" %>\ 282 std::make_unique<${type}>( 283 bus, 284 % if index < len(settingsDict) - 1: 285 "${path}"), 286 % else: 287 "${path}")); 288 % endif 289 % endfor 290 291 % for index, path in enumerate(objects): 292 path = fs::path(SETTINGS_PERSIST_PATH) / "${path}"; 293 path += persistent::fileSuffix; 294 auto initSetting${index} = [&]() 295 { 296 % for item in settingsDict[path]: 297 % for propName, metaDict in item['Properties'].items(): 298 <% p = NamedElement(name=propName).camelCase %>\ 299 <% defaultValue = metaDict['Default'] %>\ 300 % if isinstance(defaultValue, str) and not \ 301 defaultValue.startswith('"') and '::' in defaultValue: 302 <% ns = get_setting_sdbusplus_type(item['Interface']) 303 i = ns.rfind('::') 304 defaultValue = "{}::{}".format(ns[:i], defaultValue) 305 %>\ 306 % endif 307 std::get<${index}>(settings)-> 308 ${get_setting_sdbusplus_type(item['Interface'])}::${p}(${defaultValue}); 309 % endfor 310 % endfor 311 }; 312 313 try 314 { 315 if (fs::exists(path)) 316 { 317 std::ifstream is(path.c_str(), std::ios::in); 318 cereal::JSONInputArchive iarchive(is); 319 iarchive(*std::get<${index}>(settings)); 320 } 321 else 322 { 323 initSetting${index}(); 324 } 325 } 326 catch (const cereal::Exception& e) 327 { 328 log<level::ERR>(e.what()); 329 fs::remove(path); 330 initSetting${index}(); 331 } 332 std::get<${index}>(settings)->emit_object_added(); 333 334 % endfor 335 } 336 337 private: 338 /* @brief Composition of settings objects. */ 339 std::tuple< 340 % for index, path in enumerate(objects): 341 <% type = get_setting_type(path) + "::Impl" %>\ 342 % if index < len(settingsDict) - 1: 343 std::unique_ptr<${type}>, 344 % else: 345 std::unique_ptr<${type}>> settings; 346 % endif 347 % endfor 348 }; 349 350 } // namespace settings 351 } // namespace phosphor 352 353 // Now register the class version with Cereal 354 % for object in objects: 355 <% 356 classname = "phosphor::settings" 357 ns = object.split('/') 358 ns.pop(0) 359 %>\ 360 % for n in ns: 361 <% 362 classname += "::" + n 363 %>\ 364 % endfor 365 CEREAL_CLASS_VERSION(${classname + "::Impl"}, CLASS_VERSION); 366 % endfor 367