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* busname, 61 const char* root) : 62 ServerObject<ManagerIface>(bus, root), 63 _shutdown(false), _root(root), _bus(std::move(bus)), _manager(_bus, root) 64 #ifdef CREATE_ASSOCIATIONS 65 , 66 _associations(_bus) 67 #endif 68 { 69 for (auto& group : _events) 70 { 71 for (auto pEvent : std::get<std::vector<EventBasePtr>>(group)) 72 { 73 if (pEvent->type != Event::Type::DBUS_SIGNAL) 74 { 75 continue; 76 } 77 78 // Create a callback context for this event group. 79 auto dbusEvent = static_cast<DbusSignal*>(pEvent.get()); 80 81 // Go ahead and store an iterator pointing at 82 // the event data to avoid lookups later since 83 // additional signal callbacks aren't added 84 // after the manager is constructed. 85 _sigargs.emplace_back( 86 std::make_unique<SigArg>(this, dbusEvent, &group)); 87 88 // Register our callback and the context for 89 // each signal event. 90 _matches.emplace_back(_bus, dbusEvent->signature, _signal, 91 _sigargs.back().get()); 92 } 93 } 94 95 // Restore any persistent inventory 96 restore(); 97 98 _bus.request_name(busname); 99 } 100 101 void Manager::shutdown() noexcept 102 { 103 _shutdown = true; 104 } 105 106 void Manager::run() noexcept 107 { 108 sdbusplus::message::message unusedMsg{nullptr}; 109 110 // Run startup events. 111 for (auto& group : _events) 112 { 113 for (auto pEvent : std::get<std::vector<EventBasePtr>>(group)) 114 { 115 if (pEvent->type == Event::Type::STARTUP) 116 { 117 handleEvent(unusedMsg, *pEvent, group); 118 } 119 } 120 } 121 122 while (!_shutdown) 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 refaceit = refaces.insert( 171 refaceit, 172 std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(), 173 ifaceit->second))); 174 signals.push_back(ifaceit->first); 175 } 176 else 177 { 178 // Set the new property values. 179 auto& assign = std::get<AssignInterfaceType>(opsit->second); 180 assign(ifaceit->second, refaceit->second); 181 } 182 if (!restoreFromCache) 183 { 184 auto& serialize = 185 std::get<SerializeInterfaceType<SerialOps>>(opsit->second); 186 serialize(path, ifaceit->first, refaceit->second); 187 } 188 else 189 { 190 auto& deserialize = 191 std::get<DeserializeInterfaceType<SerialOps>>( 192 opsit->second); 193 deserialize(path, ifaceit->first, refaceit->second); 194 } 195 } 196 catch (const InterfaceError& e) 197 { 198 // Reset the binding ops iterator since we are 199 // at the end. 200 opsit = _makers.cbegin(); 201 e.log(); 202 } 203 204 ++ifaceit; 205 } 206 207 if (newObject) 208 { 209 _bus.emit_object_added(path.str.c_str()); 210 } 211 else if (!signals.empty()) 212 { 213 _bus.emit_interfaces_added(path.str.c_str(), signals); 214 } 215 } 216 217 void Manager::updateObjects( 218 const std::map<sdbusplus::message::object_path, Object>& objs, 219 bool restoreFromCache) 220 { 221 auto objit = objs.cbegin(); 222 auto refit = _refs.begin(); 223 std::string absPath; 224 bool newObj; 225 226 while (objit != objs.cend()) 227 { 228 // Find the insertion point or the object to update. 229 refit = std::lower_bound(refit, _refs.end(), objit->first, 230 compareFirst(RelPathCompare(_root))); 231 232 absPath.assign(_root); 233 absPath.append(objit->first); 234 235 newObj = false; 236 if (refit == _refs.end() || refit->first != absPath) 237 { 238 refit = _refs.insert( 239 refit, std::make_pair(absPath, decltype(_refs)::mapped_type())); 240 newObj = true; 241 } 242 243 updateInterfaces(absPath, objit->second, refit, newObj, 244 restoreFromCache); 245 #ifdef CREATE_ASSOCIATIONS 246 if (newObj) 247 { 248 _associations.createAssociations(absPath); 249 } 250 #endif 251 ++objit; 252 } 253 } 254 255 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs) 256 { 257 updateObjects(objs); 258 } 259 260 void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event, 261 const EventInfo& info) 262 { 263 auto& actions = std::get<1>(info); 264 265 for (auto& f : event) 266 { 267 if (!f(_bus, msg, *this)) 268 { 269 return; 270 } 271 } 272 for (auto& action : actions) 273 { 274 action(_bus, *this); 275 } 276 } 277 278 void Manager::destroyObjects(const std::vector<const char*>& paths) 279 { 280 std::string p; 281 282 for (const auto& path : paths) 283 { 284 p.assign(_root); 285 p.append(path); 286 _bus.emit_object_removed(p.c_str()); 287 _refs.erase(p); 288 } 289 } 290 291 void Manager::createObjects( 292 const std::map<sdbusplus::message::object_path, Object>& objs) 293 { 294 updateObjects(objs); 295 } 296 297 std::any& Manager::getInterfaceHolder(const char* path, const char* interface) 298 { 299 return const_cast<std::any&>( 300 const_cast<const Manager*>(this)->getInterfaceHolder(path, interface)); 301 } 302 303 const std::any& Manager::getInterfaceHolder(const char* path, 304 const char* interface) const 305 { 306 std::string p{path}; 307 auto oit = _refs.find(_root + p); 308 if (oit == _refs.end()) 309 throw std::runtime_error(_root + p + " was not found"); 310 311 auto& obj = oit->second; 312 auto iit = obj.find(interface); 313 if (iit == obj.end()) 314 throw std::runtime_error("interface was not found"); 315 316 return iit->second; 317 } 318 319 void Manager::restore() 320 { 321 namespace fs = std::filesystem; 322 323 if (!fs::exists(fs::path(PIM_PERSIST_PATH))) 324 { 325 return; 326 } 327 328 static const std::string remove = 329 std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT; 330 331 std::map<sdbusplus::message::object_path, Object> objects; 332 for (const auto& dirent : 333 fs::recursive_directory_iterator(PIM_PERSIST_PATH)) 334 { 335 const auto& path = dirent.path(); 336 if (fs::is_regular_file(path)) 337 { 338 auto ifaceName = path.filename().string(); 339 auto objPath = path.parent_path().string(); 340 objPath.erase(0, remove.length()); 341 auto objit = objects.find(objPath); 342 Interface propertyMap{}; 343 if (objects.end() != objit) 344 { 345 auto& object = objit->second; 346 object.emplace(std::move(ifaceName), std::move(propertyMap)); 347 } 348 else 349 { 350 Object object; 351 object.emplace(std::move(ifaceName), std::move(propertyMap)); 352 objects.emplace(std::move(objPath), std::move(object)); 353 } 354 } 355 } 356 if (!objects.empty()) 357 { 358 auto restoreFromCache = true; 359 updateObjects(objects, restoreFromCache); 360 } 361 } 362 363 } // namespace manager 364 } // namespace inventory 365 } // namespace phosphor 366 367 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 368