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