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), _shutdown(false), _root(root), 62 _bus(std::move(bus)), _manager(_bus, root) 63 #ifdef CREATE_ASSOCIATIONS 64 , 65 _associations(_bus) 66 #endif 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 _shutdown = true; 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 _bus.request_name(busname); 120 while (!_shutdown) 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 refaceit = refaces.insert( 169 refaceit, 170 std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(), 171 ifaceit->second))); 172 signals.push_back(ifaceit->first); 173 } 174 else 175 { 176 // Set the new property values. 177 auto& assign = std::get<AssignInterfaceType>(opsit->second); 178 assign(ifaceit->second, refaceit->second); 179 } 180 if (!restoreFromCache) 181 { 182 auto& serialize = 183 std::get<SerializeInterfaceType<SerialOps>>(opsit->second); 184 serialize(path, ifaceit->first, refaceit->second); 185 } 186 else 187 { 188 auto& deserialize = 189 std::get<DeserializeInterfaceType<SerialOps>>( 190 opsit->second); 191 deserialize(path, ifaceit->first, refaceit->second); 192 } 193 } 194 catch (const InterfaceError& e) 195 { 196 // Reset the binding ops iterator since we are 197 // at the end. 198 opsit = _makers.cbegin(); 199 e.log(); 200 } 201 202 ++ifaceit; 203 } 204 205 if (newObject) 206 { 207 _bus.emit_object_added(path.str.c_str()); 208 } 209 else if (!signals.empty()) 210 { 211 _bus.emit_interfaces_added(path.str.c_str(), signals); 212 } 213 } 214 215 void Manager::updateObjects( 216 const std::map<sdbusplus::message::object_path, Object>& objs, 217 bool restoreFromCache) 218 { 219 auto objit = objs.cbegin(); 220 auto refit = _refs.begin(); 221 std::string absPath; 222 bool newObj; 223 224 while (objit != objs.cend()) 225 { 226 // Find the insertion point or the object to update. 227 refit = std::lower_bound(refit, _refs.end(), objit->first, 228 compareFirst(RelPathCompare(_root))); 229 230 absPath.assign(_root); 231 absPath.append(objit->first); 232 233 newObj = false; 234 if (refit == _refs.end() || refit->first != absPath) 235 { 236 refit = _refs.insert( 237 refit, std::make_pair(absPath, decltype(_refs)::mapped_type())); 238 newObj = true; 239 } 240 241 updateInterfaces(absPath, objit->second, refit, newObj, 242 restoreFromCache); 243 #ifdef CREATE_ASSOCIATIONS 244 if (newObj) 245 { 246 _associations.createAssociations(absPath); 247 } 248 #endif 249 ++objit; 250 } 251 } 252 253 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs) 254 { 255 updateObjects(objs); 256 } 257 258 void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event, 259 const EventInfo& info) 260 { 261 auto& actions = std::get<1>(info); 262 263 for (auto& f : event) 264 { 265 if (!f(_bus, msg, *this)) 266 { 267 return; 268 } 269 } 270 for (auto& action : actions) 271 { 272 action(_bus, *this); 273 } 274 } 275 276 void Manager::destroyObjects(const std::vector<const char*>& paths) 277 { 278 std::string p; 279 280 for (const auto& path : paths) 281 { 282 p.assign(_root); 283 p.append(path); 284 _bus.emit_object_removed(p.c_str()); 285 _refs.erase(p); 286 } 287 } 288 289 void Manager::createObjects( 290 const std::map<sdbusplus::message::object_path, Object>& objs) 291 { 292 updateObjects(objs); 293 } 294 295 std::any& Manager::getInterfaceHolder(const char* path, const char* interface) 296 { 297 return const_cast<std::any&>( 298 const_cast<const Manager*>(this)->getInterfaceHolder(path, interface)); 299 } 300 301 const std::any& Manager::getInterfaceHolder(const char* path, 302 const char* interface) const 303 { 304 std::string p{path}; 305 auto oit = _refs.find(_root + p); 306 if (oit == _refs.end()) 307 throw std::runtime_error(_root + p + " was not found"); 308 309 auto& obj = oit->second; 310 auto iit = obj.find(interface); 311 if (iit == obj.end()) 312 throw std::runtime_error("interface was not found"); 313 314 return iit->second; 315 } 316 317 void Manager::restore() 318 { 319 namespace fs = std::filesystem; 320 321 if (!fs::exists(fs::path(PIM_PERSIST_PATH))) 322 { 323 return; 324 } 325 326 static const std::string remove = 327 std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT; 328 329 std::map<sdbusplus::message::object_path, Object> objects; 330 for (const auto& dirent : 331 fs::recursive_directory_iterator(PIM_PERSIST_PATH)) 332 { 333 const auto& path = dirent.path(); 334 if (fs::is_regular_file(path)) 335 { 336 auto ifaceName = path.filename().string(); 337 auto objPath = path.parent_path().string(); 338 objPath.erase(0, remove.length()); 339 auto objit = objects.find(objPath); 340 Interface propertyMap{}; 341 if (objects.end() != objit) 342 { 343 auto& object = objit->second; 344 object.emplace(std::move(ifaceName), std::move(propertyMap)); 345 } 346 else 347 { 348 Object object; 349 object.emplace(std::move(ifaceName), std::move(propertyMap)); 350 objects.emplace(std::move(objPath), std::move(object)); 351 } 352 } 353 } 354 if (!objects.empty()) 355 { 356 auto restoreFromCache = true; 357 updateObjects(objects, restoreFromCache); 358 } 359 } 360 361 } // namespace manager 362 } // namespace inventory 363 } // namespace phosphor 364 365 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 366