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 <experimental/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 { 64 for (auto& group : _events) 65 { 66 for (auto pEvent : std::get<std::vector<EventBasePtr>>(group)) 67 { 68 if (pEvent->type != Event::Type::DBUS_SIGNAL) 69 { 70 continue; 71 } 72 73 // Create a callback context for this event group. 74 auto dbusEvent = static_cast<DbusSignal*>(pEvent.get()); 75 76 // Go ahead and store an iterator pointing at 77 // the event data to avoid lookups later since 78 // additional signal callbacks aren't added 79 // after the manager is constructed. 80 _sigargs.emplace_back( 81 std::make_unique<SigArg>(this, dbusEvent, &group)); 82 83 // Register our callback and the context for 84 // each signal event. 85 _matches.emplace_back(_bus, dbusEvent->signature, _signal, 86 _sigargs.back().get()); 87 } 88 } 89 90 _bus.request_name(busname); 91 92 // Restore any persistent inventory 93 restore(); 94 } 95 96 void Manager::shutdown() noexcept 97 { 98 _shutdown = true; 99 } 100 101 void Manager::run() noexcept 102 { 103 sdbusplus::message::message 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 while (!_shutdown) 118 { 119 try 120 { 121 _bus.process_discard(); 122 _bus.wait((5000000us).count()); 123 } 124 catch (const std::exception& e) 125 { 126 std::cerr << e.what() << std::endl; 127 } 128 } 129 } 130 131 void Manager::updateInterfaces(const sdbusplus::message::object_path& path, 132 const Object& interfaces, 133 ObjectReferences::iterator pos, bool newObject, 134 bool restoreFromCache) 135 { 136 auto& refaces = pos->second; 137 auto ifaceit = interfaces.cbegin(); 138 auto opsit = _makers.cbegin(); 139 auto refaceit = refaces.begin(); 140 std::vector<std::string> signals; 141 142 while (ifaceit != interfaces.cend()) 143 { 144 try 145 { 146 // Find the binding ops for this interface. 147 opsit = std::lower_bound(opsit, _makers.cend(), ifaceit->first, 148 compareFirst(_makers.key_comp())); 149 150 if (opsit == _makers.cend() || opsit->first != ifaceit->first) 151 { 152 // This interface is not supported. 153 throw InterfaceError("Encountered unsupported interface.", 154 ifaceit->first); 155 } 156 157 // Find the binding insertion point or the binding to update. 158 refaceit = std::lower_bound(refaceit, refaces.end(), ifaceit->first, 159 compareFirst(refaces.key_comp())); 160 161 if (refaceit == refaces.end() || refaceit->first != ifaceit->first) 162 { 163 // Add the new interface. 164 auto& ctor = std::get<MakerType>(opsit->second); 165 refaceit = refaces.insert( 166 refaceit, 167 std::make_pair(ifaceit->first, ctor(_bus, path.str.c_str(), 168 ifaceit->second))); 169 signals.push_back(ifaceit->first); 170 } 171 else 172 { 173 // Set the new property values. 174 auto& assign = std::get<AssignerType>(opsit->second); 175 assign(ifaceit->second, refaceit->second); 176 } 177 if (!restoreFromCache) 178 { 179 auto& serialize = std::get<SerializerType>(opsit->second); 180 serialize(path, ifaceit->first, refaceit->second); 181 } 182 else 183 { 184 auto& deserialize = std::get<DeserializerType>(opsit->second); 185 deserialize(path, ifaceit->first, refaceit->second); 186 } 187 } 188 catch (const InterfaceError& e) 189 { 190 // Reset the binding ops iterator since we are 191 // at the end. 192 opsit = _makers.cbegin(); 193 e.log(); 194 } 195 196 ++ifaceit; 197 } 198 199 if (newObject) 200 { 201 _bus.emit_object_added(path.str.c_str()); 202 } 203 else if (!signals.empty()) 204 { 205 _bus.emit_interfaces_added(path.str.c_str(), signals); 206 } 207 } 208 209 void Manager::updateObjects( 210 const std::map<sdbusplus::message::object_path, Object>& objs, 211 bool restoreFromCache) 212 { 213 auto objit = objs.cbegin(); 214 auto refit = _refs.begin(); 215 std::string absPath; 216 bool newObj; 217 218 while (objit != objs.cend()) 219 { 220 // Find the insertion point or the object to update. 221 refit = std::lower_bound(refit, _refs.end(), objit->first, 222 compareFirst(RelPathCompare(_root))); 223 224 absPath.assign(_root); 225 absPath.append(objit->first); 226 227 newObj = false; 228 if (refit == _refs.end() || refit->first != absPath) 229 { 230 refit = _refs.insert( 231 refit, std::make_pair(absPath, decltype(_refs)::mapped_type())); 232 newObj = true; 233 } 234 235 updateInterfaces(absPath, objit->second, refit, newObj, 236 restoreFromCache); 237 ++objit; 238 } 239 } 240 241 void Manager::notify(std::map<sdbusplus::message::object_path, Object> objs) 242 { 243 updateObjects(objs); 244 } 245 246 void Manager::handleEvent(sdbusplus::message::message& msg, const Event& event, 247 const EventInfo& info) 248 { 249 auto& actions = std::get<1>(info); 250 251 for (auto& f : event) 252 { 253 if (!f(_bus, msg, *this)) 254 { 255 return; 256 } 257 } 258 for (auto& action : actions) 259 { 260 action(_bus, *this); 261 } 262 } 263 264 void Manager::destroyObjects(const std::vector<const char*>& paths) 265 { 266 std::string p; 267 268 for (const auto& path : paths) 269 { 270 p.assign(_root); 271 p.append(path); 272 _bus.emit_object_removed(p.c_str()); 273 _refs.erase(p); 274 } 275 } 276 277 void Manager::createObjects( 278 const std::map<sdbusplus::message::object_path, Object>& objs) 279 { 280 updateObjects(objs); 281 } 282 283 any_ns::any& Manager::getInterfaceHolder(const char* path, 284 const char* interface) 285 { 286 return const_cast<any_ns::any&>( 287 const_cast<const Manager*>(this)->getInterfaceHolder(path, interface)); 288 } 289 290 const any_ns::any& Manager::getInterfaceHolder(const char* path, 291 const char* interface) const 292 { 293 std::string p{path}; 294 auto oit = _refs.find(_root + p); 295 if (oit == _refs.end()) 296 throw std::runtime_error(_root + p + " was not found"); 297 298 auto& obj = oit->second; 299 auto iit = obj.find(interface); 300 if (iit == obj.end()) 301 throw std::runtime_error("interface was not found"); 302 303 return iit->second; 304 } 305 306 void Manager::restore() 307 { 308 namespace fs = std::experimental::filesystem; 309 310 if (!fs::exists(fs::path(PIM_PERSIST_PATH))) 311 { 312 return; 313 } 314 315 static const std::string remove = 316 std::string(PIM_PERSIST_PATH) + INVENTORY_ROOT; 317 318 std::map<sdbusplus::message::object_path, Object> objects; 319 for (const auto& dirent : 320 fs::recursive_directory_iterator(PIM_PERSIST_PATH)) 321 { 322 const auto& path = dirent.path(); 323 if (fs::is_regular_file(path)) 324 { 325 auto ifaceName = path.filename().string(); 326 auto objPath = path.parent_path().string(); 327 objPath.erase(0, remove.length()); 328 auto objit = objects.find(objPath); 329 Interface propertyMap{}; 330 if (objects.end() != objit) 331 { 332 auto& object = objit->second; 333 object.emplace(std::move(ifaceName), std::move(propertyMap)); 334 } 335 else 336 { 337 Object object; 338 object.emplace(std::move(ifaceName), std::move(propertyMap)); 339 objects.emplace(std::move(objPath), std::move(object)); 340 } 341 } 342 } 343 if (!objects.empty()) 344 { 345 auto restoreFromCache = true; 346 updateObjects(objects, restoreFromCache); 347 } 348 } 349 350 } // namespace manager 351 } // namespace inventory 352 } // namespace phosphor 353 354 // vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 355