1 /** 2 * Copyright © 2016 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "manager.hpp" 17 18 #include "errors.hpp" 19 20 #include <algorithm> 21 #include <chrono> 22 #include <exception> 23 #include <filesystem> 24 #include <iostream> 25 26 using namespace std::literals::chrono_literals; 27 28 namespace phosphor 29 { 30 namespace inventory 31 { 32 namespace manager 33 { 34 /** @brief Fowrarding signal callback. 35 * 36 * Extracts per-signal specific context and forwards the call to the manager 37 * instance. 38 */ 39 auto _signal(sd_bus_message* m, void* data, sd_bus_error* /* e */) noexcept 40 { 41 try 42 { 43 auto msg = sdbusplus::message_t(m); 44 auto& args = *static_cast<Manager::SigArg*>(data); 45 sd_bus_message_ref(m); 46 auto& mgr = *std::get<0>(args); 47 mgr.handleEvent(msg, static_cast<const DbusSignal&>(*std::get<1>(args)), 48 *std::get<2>(args)); 49 } 50 catch (const std::exception& e) 51 { 52 std::cerr << e.what() << std::endl; 53 } 54 55 return 0; 56 } 57 58 Manager::Manager(sdbusplus::bus_t&& bus, const char* root) : 59 ServerObject<ManagerIface>(bus, root), _root(root), _bus(std::move(bus)), 60 _manager(_bus, root), 61 #ifdef CREATE_ASSOCIATIONS 62 _associations(_bus), 63 #endif 64 _status(ManagerStatus::STARTING) 65 { 66 for (auto& group : _events) 67 { 68 for (auto pEvent : std::get<std::vector<EventBasePtr>>(group)) 69 { 70 if (pEvent->type != Event::Type::DBUS_SIGNAL) 71 { 72 continue; 73 } 74 75 // Create a callback context for this event group. 76 auto dbusEvent = static_cast<DbusSignal*>(pEvent.get()); 77 78 // Go ahead and store an iterator pointing at 79 // the event data to avoid lookups later since 80 // additional signal callbacks aren't added 81 // after the manager is constructed. 82 _sigargs.emplace_back( 83 std::make_unique<SigArg>(this, dbusEvent, &group)); 84 85 // Register our callback and the context for 86 // each signal event. 87 _matches.emplace_back(_bus, dbusEvent->signature, _signal, 88 _sigargs.back().get()); 89 } 90 } 91 92 // Restore any persistent inventory 93 restore(); 94 } 95 96 void Manager::shutdown() noexcept 97 { 98 _status = ManagerStatus::STOPPING; 99 } 100 101 void Manager::run(const char* busname) 102 { 103 sdbusplus::message_t unusedMsg{nullptr}; 104 105 // Run startup events. 106 for (auto& group : _events) 107 { 108 for (auto pEvent : std::get<std::vector<EventBasePtr>>(group)) 109 { 110 if (pEvent->type == Event::Type::STARTUP) 111 { 112 handleEvent(unusedMsg, *pEvent, group); 113 } 114 } 115 } 116 117 _status = ManagerStatus::RUNNING; 118 _bus.request_name(busname); 119 120 while (_status != ManagerStatus::STOPPING) 121 { 122 try 123 { 124 _bus.process_discard(); 125 _bus.wait((5000000us).count()); 126 } 127 catch (const std::exception& e) 128 { 129 std::cerr << e.what() << std::endl; 130 } 131 } 132 } 133 134 void Manager::updateInterfaces(const sdbusplus::message::object_path& path, 135 const Object& interfaces, 136 ObjectReferences::iterator pos, bool newObject, 137 bool restoreFromCache) 138 { 139 auto& refaces = pos->second; 140 auto ifaceit = interfaces.cbegin(); 141 auto opsit = _makers.cbegin(); 142 auto refaceit = refaces.begin(); 143 std::vector<std::string> signals; 144 145 while (ifaceit != interfaces.cend()) 146 { 147 try 148 { 149 // Find the binding ops for this interface. 150 opsit = std::lower_bound(opsit, _makers.cend(), ifaceit->first, 151 compareFirst(_makers.key_comp())); 152 153 if (opsit == _makers.cend() || opsit->first != ifaceit->first) 154 { 155 // This interface is not supported. 156 throw InterfaceError("Encountered unsupported interface.", 157 ifaceit->first); 158 } 159 160 // Find the binding insertion point or the binding to update. 161 refaceit = std::lower_bound(refaceit, refaces.end(), ifaceit->first, 162 compareFirst(refaces.key_comp())); 163 164 if (refaceit == refaces.end() || refaceit->first != ifaceit->first) 165 { 166 // Add the new interface. 167 auto& ctor = std::get<MakeInterfaceType>(opsit->second); 168 // skipSignal = true here to avoid getting PropertiesChanged 169 // signals while the interface is constructed. We'll emit an 170 // ObjectManager signal for this interface below. 171 refaceit = refaces.insert( 172 refaceit, std::make_pair(ifaceit->first, 173 ctor(_bus, path.str.c_str(), 174 ifaceit->second, true))); 175 signals.push_back(ifaceit->first); 176 } 177 else 178 { 179 // Set the new property values. 180 auto& assign = std::get<AssignInterfaceType>(opsit->second); 181 assign(ifaceit->second, refaceit->second, 182 _status != ManagerStatus::RUNNING); 183 } 184 if (!restoreFromCache) 185 { 186 auto& serialize = 187 std::get<SerializeInterfaceType<SerialOps>>(opsit->second); 188 serialize(path, ifaceit->first, refaceit->second); 189 } 190 else 191 { 192 auto& deserialize = 193 std::get<DeserializeInterfaceType<SerialOps>>( 194 opsit->second); 195 deserialize(path, ifaceit->first, refaceit->second); 196 } 197 } 198 catch (const InterfaceError& e) 199 { 200 // Reset the binding ops iterator since we are 201 // at the end. 202 opsit = _makers.cbegin(); 203 e.log(); 204 } 205 206 ++ifaceit; 207 } 208 209 if (_status == ManagerStatus::RUNNING) 210 { 211 if (newObject) 212 { 213 _bus.emit_object_added(path.str.c_str()); 214 } 215 else if (!signals.empty()) 216 { 217 _bus.emit_interfaces_added(path.str.c_str(), signals); 218 } 219 } 220 } 221 222 void Manager::updateObjects( 223 const std::map<sdbusplus::message::object_path, Object>& objs, 224 bool restoreFromCache) 225 { 226 auto objit = objs.cbegin(); 227 auto refit = _refs.begin(); 228 std::string absPath; 229 bool newObj; 230 231 while (objit != objs.cend()) 232 { 233 // Find the insertion point or the object to update. 234 refit = std::lower_bound(refit, _refs.end(), objit->first, 235 compareFirst(RelPathCompare(_root))); 236 237 absPath.assign(_root); 238 absPath.append(objit->first); 239 240 newObj = false; 241 if (refit == _refs.end() || refit->first != absPath) 242 { 243 refit = _refs.insert( 244 refit, std::make_pair(absPath, decltype(_refs)::mapped_type())); 245 newObj = true; 246 } 247 248 updateInterfaces(absPath, objit->second, refit, newObj, 249 restoreFromCache); 250 #ifdef CREATE_ASSOCIATIONS 251 if (!_associations.pendingCondition() && newObj) 252 { 253 _associations.createAssociations(absPath, 254 _status != ManagerStatus::RUNNING); 255 } 256 else if (!restoreFromCache && 257 _associations.conditionMatch(objit->first, objit->second)) 258 { 259 // The objit path/interface/property matched a pending condition. 260 // Now the associations are valid so attempt to create them against 261 // all existing objects. If this was the restoreFromCache path, 262 // objit doesn't contain property values so don't bother checking. 263 std::for_each(_refs.begin(), _refs.end(), [this](const auto& ref) { 264 _associations.createAssociations( 265 ref.first, _status != ManagerStatus::RUNNING); 266 }); 267 } 268 #endif 269 ++objit; 270 } 271 } 272 273 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs) 274 { 275 updateObjects(objs); 276 } 277 278 void Manager::handleEvent(sdbusplus::message_t& msg, const Event& event, 279 const EventInfo& info) 280 { 281 auto& actions = std::get<1>(info); 282 283 for (auto& f : event) 284 { 285 if (!f(_bus, msg, *this)) 286 { 287 return; 288 } 289 } 290 for (auto& action : actions) 291 { 292 action(_bus, *this); 293 } 294 } 295 296 void Manager::destroyObjects(const std::vector<const char*>& paths) 297 { 298 std::string p; 299 300 for (const auto& path : paths) 301 { 302 p.assign(_root); 303 p.append(path); 304 _bus.emit_object_removed(p.c_str()); 305 _refs.erase(p); 306 } 307 } 308 309 void Manager::createObjects( 310 const std::map<sdbusplus::message::object_path, Object>& objs) 311 { 312 updateObjects(objs); 313 } 314 315 std::any& Manager::getInterfaceHolder(const char* path, const char* interface) 316 { 317 return const_cast<std::any&>( 318 const_cast<const Manager*>(this)->getInterfaceHolder(path, interface)); 319 } 320 321 const std::any& Manager::getInterfaceHolder(const char* path, 322 const char* interface) const 323 { 324 std::string p{path}; 325 auto oit = _refs.find(_root + p); 326 if (oit == _refs.end()) 327 throw std::runtime_error(_root + p + " was not found"); 328 329 auto& obj = oit->second; 330 auto iit = obj.find(interface); 331 if (iit == obj.end()) 332 throw std::runtime_error("interface was not found"); 333 334 return iit->second; 335 } 336 337 void Manager::restore() 338 { 339 namespace fs = std::filesystem; 340 341 if (!fs::exists(fs::path(PIM_PERSIST_PATH))) 342 { 343 return; 344 } 345 346 static const std::string remove = std::string(PIM_PERSIST_PATH) + 347 INVENTORY_ROOT; 348 349 std::map<sdbusplus::message::object_path, Object> objects; 350 for (const auto& dirent : 351 fs::recursive_directory_iterator(PIM_PERSIST_PATH)) 352 { 353 const auto& path = dirent.path(); 354 if (fs::is_regular_file(path)) 355 { 356 auto ifaceName = path.filename().string(); 357 auto objPath = path.parent_path().string(); 358 objPath.erase(0, remove.length()); 359 auto objit = objects.find(objPath); 360 Interface propertyMap{}; 361 if (objects.end() != objit) 362 { 363 auto& object = objit->second; 364 object.emplace(std::move(ifaceName), std::move(propertyMap)); 365 } 366 else 367 { 368 Object object; 369 object.emplace(std::move(ifaceName), std::move(propertyMap)); 370 objects.emplace(std::move(objPath), std::move(object)); 371 } 372 } 373 } 374 if (!objects.empty()) 375 { 376 auto restoreFromCache = true; 377 updateObjects(objects, restoreFromCache); 378 379 #ifdef CREATE_ASSOCIATIONS 380 // There may be conditional associations waiting to be loaded 381 // based on certain path/interface/property values. Now that 382 // _refs contains all objects with their property values, check 383 // which property values the conditions need and set them in the 384 // condition structure entries, using the actualValue field. Then 385 // the associations manager can check if the conditions are met. 386 if (_associations.pendingCondition()) 387 { 388 ObjectReferences::iterator refIt; 389 InterfaceComposite::iterator ifaceIt; 390 391 auto& conditions = _associations.getConditions(); 392 for (auto& condition : conditions) 393 { 394 refIt = _refs.find(_root + condition.path); 395 if (refIt != _refs.end()) 396 { 397 ifaceIt = refIt->second.find(condition.interface); 398 } 399 400 if ((refIt != _refs.end()) && (ifaceIt != refIt->second.end())) 401 { 402 const auto& maker = _makers.find(condition.interface); 403 if (maker != _makers.end()) 404 { 405 auto& getProperty = 406 std::get<GetPropertyValueType>(maker->second); 407 408 condition.actualValue = getProperty(condition.property, 409 ifaceIt->second); 410 } 411 } 412 } 413 414 // Check if a property value in a condition matches an 415 // actual property value just saved. If one did, now the 416 // associations file is valid so create its associations. 417 if (_associations.conditionMatch()) 418 { 419 std::for_each(_refs.begin(), _refs.end(), 420 [this](const auto& ref) { 421 _associations.createAssociations( 422 ref.first, _status != ManagerStatus::RUNNING); 423 }); 424 } 425 } 426 #endif 427 } 428 } 429 430 } // namespace manager 431 } // namespace inventory 432 } // namespace phosphor 433 434 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 435