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 #include <phosphor-logging/log.hpp> 26 27 using namespace std::literals::chrono_literals; 28 29 namespace phosphor 30 { 31 namespace inventory 32 { 33 namespace manager 34 { 35 /** @brief Fowrarding signal callback. 36 * 37 * Extracts per-signal specific context and forwards the call to the manager 38 * instance. 39 */ 40 auto _signal(sd_bus_message* m, void* data, sd_bus_error* e) noexcept 41 { 42 try 43 { 44 auto msg = sdbusplus::message::message(m); 45 auto& args = *static_cast<Manager::SigArg*>(data); 46 sd_bus_message_ref(m); 47 auto& mgr = *std::get<0>(args); 48 mgr.handleEvent(msg, static_cast<const DbusSignal&>(*std::get<1>(args)), 49 *std::get<2>(args)); 50 } 51 catch (const std::exception& e) 52 { 53 std::cerr << e.what() << std::endl; 54 } 55 56 return 0; 57 } 58 59 Manager::Manager(sdbusplus::bus::bus&& bus, const char* busname, 60 const char* root, const char* iface) : 61 ServerObject<ManagerIface>(bus, root), 62 _shutdown(false), _root(root), _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 _bus.request_name(busname); 95 96 // Restore any persistent inventory 97 restore(); 98 } 99 100 void Manager::shutdown() noexcept 101 { 102 _shutdown = true; 103 } 104 105 void Manager::run() noexcept 106 { 107 sdbusplus::message::message unusedMsg{nullptr}; 108 109 // Run startup events. 110 for (auto& group : _events) 111 { 112 for (auto pEvent : std::get<std::vector<EventBasePtr>>(group)) 113 { 114 if (pEvent->type == Event::Type::STARTUP) 115 { 116 handleEvent(unusedMsg, *pEvent, group); 117 } 118 } 119 } 120 121 while (!_shutdown) 122 { 123 try 124 { 125 _bus.process_discard(); 126 _bus.wait((5000000us).count()); 127 } 128 catch (const std::exception& e) 129 { 130 std::cerr << e.what() << std::endl; 131 } 132 } 133 } 134 135 void Manager::updateInterfaces(const sdbusplus::message::object_path& path, 136 const Object& interfaces, 137 ObjectReferences::iterator pos, bool newObject, 138 bool restoreFromCache) 139 { 140 auto& refaces = pos->second; 141 auto ifaceit = interfaces.cbegin(); 142 auto opsit = _makers.cbegin(); 143 auto refaceit = refaces.begin(); 144 std::vector<std::string> signals; 145 146 while (ifaceit != interfaces.cend()) 147 { 148 try 149 { 150 // Find the binding ops for this interface. 151 opsit = std::lower_bound(opsit, _makers.cend(), ifaceit->first, 152 compareFirst(_makers.key_comp())); 153 154 if (opsit == _makers.cend() || opsit->first != ifaceit->first) 155 { 156 // This interface is not supported. 157 throw InterfaceError("Encountered unsupported interface.", 158 ifaceit->first); 159 } 160 161 // Find the binding insertion point or the binding to update. 162 refaceit = std::lower_bound(refaceit, refaces.end(), ifaceit->first, 163 compareFirst(refaces.key_comp())); 164 165 if (refaceit == refaces.end() || refaceit->first != ifaceit->first) 166 { 167 // Add the new interface. 168 auto& ctor = std::get<MakeInterfaceType>(opsit->second); 169 refaceit = refaces.insert( 170 refaceit, 171 std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(), 172 ifaceit->second))); 173 signals.push_back(ifaceit->first); 174 } 175 else 176 { 177 // Set the new property values. 178 auto& assign = std::get<AssignInterfaceType>(opsit->second); 179 assign(ifaceit->second, refaceit->second); 180 } 181 if (!restoreFromCache) 182 { 183 auto& serialize = 184 std::get<SerializeInterfaceType<SerialOps>>(opsit->second); 185 serialize(path, ifaceit->first, refaceit->second); 186 } 187 else 188 { 189 auto& deserialize = 190 std::get<DeserializeInterfaceType<SerialOps>>( 191 opsit->second); 192 deserialize(path, ifaceit->first, refaceit->second); 193 } 194 } 195 catch (const InterfaceError& e) 196 { 197 // Reset the binding ops iterator since we are 198 // at the end. 199 opsit = _makers.cbegin(); 200 e.log(); 201 } 202 203 ++ifaceit; 204 } 205 206 if (newObject) 207 { 208 _bus.emit_object_added(path.str.c_str()); 209 } 210 else if (!signals.empty()) 211 { 212 _bus.emit_interfaces_added(path.str.c_str(), signals); 213 } 214 } 215 216 void Manager::updateObjects( 217 const std::map<sdbusplus::message::object_path, Object>& objs, 218 bool restoreFromCache) 219 { 220 auto objit = objs.cbegin(); 221 auto refit = _refs.begin(); 222 std::string absPath; 223 bool newObj; 224 225 while (objit != objs.cend()) 226 { 227 // Find the insertion point or the object to update. 228 refit = std::lower_bound(refit, _refs.end(), objit->first, 229 compareFirst(RelPathCompare(_root))); 230 231 absPath.assign(_root); 232 absPath.append(objit->first); 233 234 newObj = false; 235 if (refit == _refs.end() || refit->first != absPath) 236 { 237 refit = _refs.insert( 238 refit, std::make_pair(absPath, decltype(_refs)::mapped_type())); 239 newObj = true; 240 } 241 242 updateInterfaces(absPath, objit->second, refit, newObj, 243 restoreFromCache); 244 #ifdef CREATE_ASSOCIATIONS 245 if (newObj) 246 { 247 _associations.createAssociations(absPath); 248 } 249 #endif 250 ++objit; 251 } 252 } 253 254 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs) 255 { 256 updateObjects(objs); 257 } 258 259 void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event, 260 const EventInfo& info) 261 { 262 auto& actions = std::get<1>(info); 263 264 for (auto& f : event) 265 { 266 if (!f(_bus, msg, *this)) 267 { 268 return; 269 } 270 } 271 for (auto& action : actions) 272 { 273 action(_bus, *this); 274 } 275 } 276 277 void Manager::destroyObjects(const std::vector<const char*>& paths) 278 { 279 std::string p; 280 281 for (const auto& path : paths) 282 { 283 p.assign(_root); 284 p.append(path); 285 _bus.emit_object_removed(p.c_str()); 286 _refs.erase(p); 287 } 288 } 289 290 void Manager::createObjects( 291 const std::map<sdbusplus::message::object_path, Object>& objs) 292 { 293 updateObjects(objs); 294 } 295 296 std::any& Manager::getInterfaceHolder(const char* path, const char* interface) 297 { 298 return const_cast<std::any&>( 299 const_cast<const Manager*>(this)->getInterfaceHolder(path, interface)); 300 } 301 302 const std::any& Manager::getInterfaceHolder(const char* path, 303 const char* interface) const 304 { 305 std::string p{path}; 306 auto oit = _refs.find(_root + p); 307 if (oit == _refs.end()) 308 throw std::runtime_error(_root + p + " was not found"); 309 310 auto& obj = oit->second; 311 auto iit = obj.find(interface); 312 if (iit == obj.end()) 313 throw std::runtime_error("interface was not found"); 314 315 return iit->second; 316 } 317 318 void Manager::restore() 319 { 320 namespace fs = std::filesystem; 321 322 if (!fs::exists(fs::path(PIM_PERSIST_PATH))) 323 { 324 return; 325 } 326 327 static const std::string remove = 328 std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT; 329 330 std::map<sdbusplus::message::object_path, Object> objects; 331 for (const auto& dirent : 332 fs::recursive_directory_iterator(PIM_PERSIST_PATH)) 333 { 334 const auto& path = dirent.path(); 335 if (fs::is_regular_file(path)) 336 { 337 auto ifaceName = path.filename().string(); 338 auto objPath = path.parent_path().string(); 339 objPath.erase(0, remove.length()); 340 auto objit = objects.find(objPath); 341 Interface propertyMap{}; 342 if (objects.end() != objit) 343 { 344 auto& object = objit->second; 345 object.emplace(std::move(ifaceName), std::move(propertyMap)); 346 } 347 else 348 { 349 Object object; 350 object.emplace(std::move(ifaceName), std::move(propertyMap)); 351 objects.emplace(std::move(objPath), std::move(object)); 352 } 353 } 354 } 355 if (!objects.empty()) 356 { 357 auto restoreFromCache = true; 358 updateObjects(objects, restoreFromCache); 359 } 360 } 361 362 } // namespace manager 363 } // namespace inventory 364 } // namespace phosphor 365 366 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 367