1 #include <arpa/inet.h> 2 #include <dirent.h> 3 #include <fcntl.h> 4 #include <linux/i2c-dev.h> 5 #include <linux/i2c.h> 6 #include <net/if.h> 7 #include <sys/ioctl.h> 8 #include <sys/types.h> 9 #include <unistd.h> 10 11 #include <ipmid/utils.hpp> 12 #include <phosphor-logging/elog-errors.hpp> 13 #include <phosphor-logging/log.hpp> 14 #include <sdbusplus/message/types.hpp> 15 #include <xyz/openbmc_project/Common/error.hpp> 16 17 #include <algorithm> 18 #include <chrono> 19 20 namespace ipmi 21 { 22 23 using namespace phosphor::logging; 24 using namespace sdbusplus::error::xyz::openbmc_project::common; 25 26 namespace network 27 { 28 29 /** @brief checks if the given ip is Link Local Ip or not. 30 * @param[in] ipaddress - IPAddress. 31 */ 32 bool isLinkLocalIP(const std::string& ipaddress); 33 34 } // namespace network 35 36 // TODO There may be cases where an interface is implemented by multiple 37 // objects,to handle such cases we are interested on that object 38 // which are on interested busname. 39 // Currently mapper doesn't give the readable busname(gives busid) so we can't 40 // use busname to find the object,will do later once the support is there. 41 42 DbusObjectInfo getDbusObject(sdbusplus::bus_t& bus, 43 const std::string& interface, 44 const std::string& serviceRoot, 45 const std::string& match) 46 { 47 std::vector<DbusInterface> interfaces; 48 interfaces.emplace_back(interface); 49 50 ObjectTree objectTree = getSubTree(bus, interfaces, serviceRoot); 51 if (objectTree.empty()) 52 { 53 log<level::ERR>("No Object has implemented the interface", 54 entry("INTERFACE=%s", interface.c_str())); 55 elog<InternalFailure>(); 56 } 57 58 DbusObjectInfo objectInfo; 59 60 // if match is empty then return the first object 61 if (match == "") 62 { 63 objectInfo = std::make_pair( 64 objectTree.begin()->first, 65 std::move(objectTree.begin()->second.begin()->first)); 66 return objectInfo; 67 } 68 69 // else search the match string in the object path 70 auto found = std::find_if(objectTree.begin(), objectTree.end(), 71 [&match](const auto& object) { 72 return (object.first.find(match) != std::string::npos); 73 }); 74 75 if (found == objectTree.end()) 76 { 77 log<level::ERR>("Failed to find object which matches", 78 entry("MATCH=%s", match.c_str())); 79 elog<InternalFailure>(); 80 // elog<> throws an exception. 81 } 82 83 return make_pair(found->first, std::move(found->second.begin()->first)); 84 } 85 86 Value getDbusProperty(sdbusplus::bus_t& bus, const std::string& service, 87 const std::string& objPath, const std::string& interface, 88 const std::string& property, 89 std::chrono::microseconds timeout) 90 { 91 Value value; 92 93 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 94 PROP_INTF, METHOD_GET); 95 96 method.append(interface, property); 97 98 auto reply = bus.call(method, timeout.count()); 99 reply.read(value); 100 101 return value; 102 } 103 104 PropertyMap getAllDbusProperties(sdbusplus::bus_t& bus, 105 const std::string& service, 106 const std::string& objPath, 107 const std::string& interface, 108 std::chrono::microseconds timeout) 109 { 110 PropertyMap properties; 111 112 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 113 PROP_INTF, METHOD_GET_ALL); 114 115 method.append(interface); 116 117 auto reply = bus.call(method, timeout.count()); 118 reply.read(properties); 119 120 return properties; 121 } 122 123 ObjectValueTree getManagedObjects(sdbusplus::bus_t& bus, 124 const std::string& service, 125 const std::string& objPath) 126 { 127 ipmi::ObjectValueTree interfaces; 128 129 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 130 "org.freedesktop.DBus.ObjectManager", 131 "GetManagedObjects"); 132 auto reply = bus.call(method); 133 reply.read(interfaces); 134 135 return interfaces; 136 } 137 138 void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service, 139 const std::string& objPath, const std::string& interface, 140 const std::string& property, const Value& value, 141 std::chrono::microseconds timeout) 142 { 143 auto method = bus.new_method_call(service.c_str(), objPath.c_str(), 144 PROP_INTF, METHOD_SET); 145 146 method.append(interface, property, value); 147 148 if (!bus.call(method, timeout.count())) 149 { 150 log<level::ERR>("Failed to set property", 151 entry("PROPERTY=%s", property.c_str()), 152 entry("PATH=%s", objPath.c_str()), 153 entry("INTERFACE=%s", interface.c_str())); 154 elog<InternalFailure>(); 155 } 156 } 157 158 ServiceCache::ServiceCache(const std::string& intf, const std::string& path) : 159 intf(intf), path(path), cachedService(std::nullopt), 160 cachedBusName(std::nullopt) 161 {} 162 163 ServiceCache::ServiceCache(std::string&& intf, std::string&& path) : 164 intf(std::move(intf)), path(std::move(path)), cachedService(std::nullopt), 165 cachedBusName(std::nullopt) 166 {} 167 168 const std::string& ServiceCache::getService(sdbusplus::bus_t& bus) 169 { 170 if (!isValid(bus)) 171 { 172 cachedBusName = bus.get_unique_name(); 173 cachedService = ::ipmi::getService(bus, intf, path); 174 } 175 return cachedService.value(); 176 } 177 178 void ServiceCache::invalidate() 179 { 180 cachedBusName = std::nullopt; 181 cachedService = std::nullopt; 182 } 183 184 sdbusplus::message_t ServiceCache::newMethodCall(sdbusplus::bus_t& bus, 185 const char* intf, 186 const char* method) 187 { 188 return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf, 189 method); 190 } 191 192 bool ServiceCache::isValid(sdbusplus::bus_t& bus) const 193 { 194 return cachedService && cachedBusName == bus.get_unique_name(); 195 } 196 197 std::string getService(sdbusplus::bus_t& bus, const std::string& intf, 198 const std::string& path) 199 { 200 auto mapperCall = bus.new_method_call("xyz.openbmc_project.ObjectMapper", 201 "/xyz/openbmc_project/object_mapper", 202 "xyz.openbmc_project.ObjectMapper", 203 "GetObject"); 204 205 mapperCall.append(path); 206 mapperCall.append(std::vector<std::string>({intf})); 207 208 auto mapperResponseMsg = bus.call(mapperCall); 209 210 std::map<std::string, std::vector<std::string>> mapperResponse; 211 mapperResponseMsg.read(mapperResponse); 212 213 if (mapperResponse.begin() == mapperResponse.end()) 214 { 215 throw std::runtime_error("ERROR in reading the mapper response"); 216 } 217 218 return mapperResponse.begin()->first; 219 } 220 221 ObjectTree getSubTree(sdbusplus::bus_t& bus, const InterfaceList& interfaces, 222 const std::string& subtreePath, int32_t depth) 223 { 224 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, 225 MAPPER_INTF, "GetSubTree"); 226 227 mapperCall.append(subtreePath, depth, interfaces); 228 229 auto mapperReply = bus.call(mapperCall); 230 ObjectTree objectTree; 231 mapperReply.read(objectTree); 232 233 return objectTree; 234 } 235 236 ipmi::ObjectTree getAllDbusObjects(sdbusplus::bus_t& bus, 237 const std::string& serviceRoot, 238 const std::string& interface, 239 const std::string& match) 240 { 241 std::vector<std::string> interfaces; 242 interfaces.emplace_back(interface); 243 244 ObjectTree objectTree = getSubTree(bus, interfaces, serviceRoot); 245 for (auto it = objectTree.begin(); it != objectTree.end();) 246 { 247 if (it->first.find(match) == std::string::npos) 248 { 249 it = objectTree.erase(it); 250 } 251 else 252 { 253 ++it; 254 } 255 } 256 257 return objectTree; 258 } 259 260 void deleteAllDbusObjects(sdbusplus::bus_t& bus, const std::string& serviceRoot, 261 const std::string& interface, 262 const std::string& match) 263 { 264 try 265 { 266 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match); 267 268 for (auto& object : objectTree) 269 { 270 method_no_args::callDbusMethod(bus, object.second.begin()->first, 271 object.first, DELETE_INTERFACE, 272 "Delete"); 273 } 274 } 275 catch (const sdbusplus::exception_t& e) 276 { 277 log<level::INFO>("sdbusplus exception - Unable to delete the objects", 278 entry("ERROR=%s", e.what()), 279 entry("INTERFACE=%s", interface.c_str()), 280 entry("SERVICE=%s", serviceRoot.c_str())); 281 } 282 } 283 284 static inline std::string convertToString(const InterfaceList& interfaces) 285 { 286 std::string intfStr; 287 for (const auto& intf : interfaces) 288 { 289 intfStr += "," + intf; 290 } 291 return intfStr; 292 } 293 294 ObjectTree getAllAncestors(sdbusplus::bus_t& bus, const std::string& path, 295 InterfaceList&& interfaces) 296 { 297 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, 298 MAPPER_INTF, "GetAncestors"); 299 mapperCall.append(path, interfaces); 300 301 auto mapperReply = bus.call(mapperCall); 302 ObjectTree objectTree; 303 mapperReply.read(objectTree); 304 305 if (objectTree.empty()) 306 { 307 log<level::ERR>( 308 "No Object has implemented the interface", 309 entry("PATH=%s", path.c_str()), 310 entry("INTERFACES=%s", convertToString(interfaces).c_str())); 311 elog<InternalFailure>(); 312 } 313 314 return objectTree; 315 } 316 317 namespace method_no_args 318 { 319 320 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service, 321 const std::string& objPath, const std::string& interface, 322 const std::string& method) 323 324 { 325 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(), 326 interface.c_str(), method.c_str()); 327 auto reply = bus.call(busMethod); 328 } 329 330 } // namespace method_no_args 331 332 /********* Begin co-routine yielding alternatives ***************/ 333 334 boost::system::error_code getService(Context::ptr ctx, const std::string& intf, 335 const std::string& path, 336 std::string& service) 337 { 338 boost::system::error_code ec; 339 std::map<std::string, std::vector<std::string>> mapperResponse = 340 ctx->bus->yield_method_call<decltype(mapperResponse)>( 341 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper", 342 "/xyz/openbmc_project/object_mapper", 343 "xyz.openbmc_project.ObjectMapper", "GetObject", path, 344 std::vector<std::string>({intf})); 345 346 if (!ec) 347 { 348 service = std::move(mapperResponse.begin()->first); 349 } 350 return ec; 351 } 352 353 boost::system::error_code getSubTree(Context::ptr ctx, 354 const InterfaceList& interfaces, 355 const std::string& subtreePath, 356 int32_t depth, ObjectTree& objectTree) 357 { 358 boost::system::error_code ec; 359 objectTree = ctx->bus->yield_method_call<ObjectTree>( 360 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree", 361 subtreePath, depth, interfaces); 362 363 return ec; 364 } 365 366 boost::system::error_code getDbusObject(Context::ptr ctx, 367 const std::string& interface, 368 const std::string& subtreePath, 369 const std::string& match, 370 DbusObjectInfo& dbusObject) 371 { 372 std::vector<DbusInterface> interfaces; 373 interfaces.emplace_back(interface); 374 375 auto depth = 0; 376 ObjectTree objectTree; 377 boost::system::error_code ec = getSubTree(ctx, interfaces, subtreePath, 378 depth, objectTree); 379 380 if (ec) 381 { 382 return ec; 383 } 384 385 if (objectTree.empty()) 386 { 387 log<level::ERR>("No Object has implemented the interface", 388 entry("INTERFACE=%s", interface.c_str()), 389 entry("NETFN=%x", ctx->netFn), 390 entry("CMD=%x,", ctx->cmd)); 391 return boost::system::errc::make_error_code( 392 boost::system::errc::no_such_process); 393 } 394 395 // if match is empty then return the first object 396 if (match == "") 397 { 398 dbusObject = std::make_pair( 399 std::move(objectTree.begin()->first), 400 std::move(objectTree.begin()->second.begin()->first)); 401 return ec; 402 } 403 404 // else search the match string in the object path 405 auto found = std::find_if(objectTree.begin(), objectTree.end(), 406 [&match](const auto& object) { 407 return (object.first.find(match) != std::string::npos); 408 }); 409 410 if (found == objectTree.end()) 411 { 412 log<level::ERR>("Failed to find object which matches", 413 entry("MATCH=%s", match.c_str()), 414 entry("NETFN=%x", ctx->netFn), 415 entry("CMD=%x,", ctx->cmd)); 416 // set ec 417 return boost::system::errc::make_error_code( 418 boost::system::errc::no_such_file_or_directory); 419 } 420 421 dbusObject = std::make_pair(std::move(found->first), 422 std::move(found->second.begin()->first)); 423 return ec; 424 } 425 426 boost::system::error_code getAllDbusProperties(Context::ptr ctx, 427 const std::string& service, 428 const std::string& objPath, 429 const std::string& interface, 430 PropertyMap& properties) 431 { 432 boost::system::error_code ec; 433 properties = ctx->bus->yield_method_call<PropertyMap>( 434 ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF, 435 METHOD_GET_ALL, interface); 436 return ec; 437 } 438 439 boost::system::error_code 440 setDbusProperty(Context::ptr ctx, const std::string& service, 441 const std::string& objPath, const std::string& interface, 442 const std::string& property, const Value& value) 443 { 444 boost::system::error_code ec; 445 ctx->bus->yield_method_call(ctx->yield, ec, service.c_str(), 446 objPath.c_str(), PROP_INTF, METHOD_SET, 447 interface, property, value); 448 return ec; 449 } 450 451 boost::system::error_code getAllDbusObjects(Context::ptr ctx, 452 const std::string& serviceRoot, 453 const std::string& interface, 454 const std::string& match, 455 ObjectTree& objectTree) 456 { 457 std::vector<std::string> interfaces; 458 interfaces.emplace_back(interface); 459 460 auto depth = 0; 461 boost::system::error_code ec = getSubTree(ctx, interfaces, serviceRoot, 462 depth, objectTree); 463 if (ec) 464 { 465 return ec; 466 } 467 468 for (auto it = objectTree.begin(); it != objectTree.end();) 469 { 470 if (it->first.find(match) == std::string::npos) 471 { 472 it = objectTree.erase(it); 473 } 474 else 475 { 476 ++it; 477 } 478 } 479 480 return ec; 481 } 482 483 boost::system::error_code deleteAllDbusObjects(Context::ptr ctx, 484 const std::string& serviceRoot, 485 const std::string& interface, 486 const std::string& match) 487 { 488 ObjectTree objectTree; 489 boost::system::error_code ec = 490 getAllDbusObjects(ctx, serviceRoot, interface, match, objectTree); 491 if (ec) 492 { 493 return ec; 494 } 495 496 for (auto& object : objectTree) 497 { 498 ctx->bus->yield_method_call(ctx->yield, ec, 499 object.second.begin()->first, object.first, 500 DELETE_INTERFACE, "Delete"); 501 if (ec) 502 { 503 log<level::ERR>("Failed to delete all objects", 504 entry("INTERFACE=%s", interface.c_str()), 505 entry("SERVICE=%s", serviceRoot.c_str()), 506 entry("NETFN=%x", ctx->netFn), 507 entry("CMD=%x,", ctx->cmd), 508 entry("ERROR=%s", ec.message().c_str())); 509 break; 510 } 511 } 512 return ec; 513 } 514 515 boost::system::error_code getManagedObjects(Context::ptr ctx, 516 const std::string& service, 517 const std::string& objPath, 518 ObjectValueTree& objects) 519 { 520 boost::system::error_code ec; 521 objects = ctx->bus->yield_method_call<ipmi::ObjectValueTree>( 522 ctx->yield, ec, service.c_str(), objPath.c_str(), 523 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 524 return ec; 525 } 526 527 boost::system::error_code getAllAncestors(Context::ptr ctx, 528 const std::string& path, 529 const InterfaceList& interfaces, 530 ObjectTree& objectTree) 531 { 532 std::string interfaceList = convertToString(interfaces); 533 534 boost::system::error_code ec; 535 objectTree = ctx->bus->yield_method_call<ObjectTree>( 536 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 537 "GetAncestors", path, interfaceList); 538 539 if (ec) 540 { 541 return ec; 542 } 543 544 if (objectTree.empty()) 545 { 546 log<level::ERR>("No Object has implemented the interface", 547 entry("PATH=%s", path.c_str()), 548 entry("INTERFACES=%s", interfaceList.c_str())); 549 elog<InternalFailure>(); 550 } 551 552 return ec; 553 } 554 555 boost::system::error_code callDbusMethod(Context::ptr ctx, 556 const std::string& service, 557 const std::string& objPath, 558 const std::string& interface, 559 const std::string& method) 560 { 561 boost::system::error_code ec; 562 ctx->bus->yield_method_call(ctx->yield, ec, service, objPath, interface, 563 method); 564 return ec; 565 } 566 567 /********* End co-routine yielding alternatives ***************/ 568 569 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t targetAddr, 570 std::vector<uint8_t> writeData, 571 std::vector<uint8_t>& readBuf) 572 { 573 // Open the i2c device, for low-level combined data write/read 574 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); 575 if (i2cDev < 0) 576 { 577 log<level::ERR>("Failed to open i2c bus", 578 phosphor::logging::entry("BUS=%s", i2cBus.c_str())); 579 return ipmi::ccInvalidFieldRequest; 580 } 581 582 const size_t writeCount = writeData.size(); 583 const size_t readCount = readBuf.size(); 584 int msgCount = 0; 585 i2c_msg i2cmsg[2] = {}; 586 if (writeCount) 587 { 588 // Data will be writtern to the target address 589 i2cmsg[msgCount].addr = targetAddr; 590 i2cmsg[msgCount].flags = 0x00; 591 i2cmsg[msgCount].len = writeCount; 592 i2cmsg[msgCount].buf = writeData.data(); 593 msgCount++; 594 } 595 if (readCount) 596 { 597 // Data will be read into the buffer from the target address 598 i2cmsg[msgCount].addr = targetAddr; 599 i2cmsg[msgCount].flags = I2C_M_RD; 600 i2cmsg[msgCount].len = readCount; 601 i2cmsg[msgCount].buf = readBuf.data(); 602 msgCount++; 603 } 604 605 i2c_rdwr_ioctl_data msgReadWrite = {}; 606 msgReadWrite.msgs = i2cmsg; 607 msgReadWrite.nmsgs = msgCount; 608 609 // Perform the combined write/read 610 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite); 611 ::close(i2cDev); 612 613 if (ret < 0) 614 { 615 log<level::ERR>("I2C WR Failed!", 616 phosphor::logging::entry("RET=%d", ret)); 617 return ipmi::ccUnspecifiedError; 618 } 619 if (readCount) 620 { 621 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len); 622 } 623 624 return ipmi::ccSuccess; 625 } 626 627 } // namespace ipmi 628