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