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