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::xyz::openbmc_project::Common::Error; 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 auto depth = 0; 51 52 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, 53 MAPPER_INTF, "GetSubTree"); 54 55 mapperCall.append(serviceRoot, depth, interfaces); 56 57 auto mapperReply = bus.call(mapperCall); 58 if (mapperReply.is_method_error()) 59 { 60 log<level::ERR>("Error in mapper call"); 61 elog<InternalFailure>(); 62 } 63 64 ObjectTree objectTree; 65 mapperReply.read(objectTree); 66 67 if (objectTree.empty()) 68 { 69 log<level::ERR>("No Object has implemented the interface", 70 entry("INTERFACE=%s", interface.c_str())); 71 elog<InternalFailure>(); 72 } 73 74 DbusObjectInfo objectInfo; 75 76 // if match is empty then return the first object 77 if (match == "") 78 { 79 objectInfo = std::make_pair( 80 objectTree.begin()->first, 81 std::move(objectTree.begin()->second.begin()->first)); 82 return objectInfo; 83 } 84 85 // else search the match string in the object path 86 auto found = std::find_if(objectTree.begin(), objectTree.end(), 87 [&match](const auto& object) { 88 return (object.first.find(match) != std::string::npos); 89 }); 90 91 if (found == objectTree.end()) 92 { 93 log<level::ERR>("Failed to find object which matches", 94 entry("MATCH=%s", match.c_str())); 95 elog<InternalFailure>(); 96 // elog<> throws an exception. 97 } 98 99 return make_pair(found->first, std::move(found->second.begin()->first)); 100 } 101 102 Value getDbusProperty(sdbusplus::bus_t& bus, const std::string& service, 103 const std::string& objPath, const std::string& interface, 104 const std::string& property, 105 std::chrono::microseconds timeout) 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_t& 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_t& 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_t& 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 ServiceCache::ServiceCache(std::string&& intf, std::string&& path) : 206 intf(std::move(intf)), path(std::move(path)), cachedService(std::nullopt), 207 cachedBusName(std::nullopt) 208 {} 209 210 const std::string& ServiceCache::getService(sdbusplus::bus_t& bus) 211 { 212 if (!isValid(bus)) 213 { 214 cachedBusName = bus.get_unique_name(); 215 cachedService = ::ipmi::getService(bus, intf, path); 216 } 217 return cachedService.value(); 218 } 219 220 void ServiceCache::invalidate() 221 { 222 cachedBusName = std::nullopt; 223 cachedService = std::nullopt; 224 } 225 226 sdbusplus::message_t ServiceCache::newMethodCall(sdbusplus::bus_t& bus, 227 const char* intf, 228 const char* method) 229 { 230 return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf, 231 method); 232 } 233 234 bool ServiceCache::isValid(sdbusplus::bus_t& bus) const 235 { 236 return cachedService && cachedBusName == bus.get_unique_name(); 237 } 238 239 std::string getService(sdbusplus::bus_t& bus, const std::string& intf, 240 const std::string& path) 241 { 242 auto mapperCall = bus.new_method_call("xyz.openbmc_project.ObjectMapper", 243 "/xyz/openbmc_project/object_mapper", 244 "xyz.openbmc_project.ObjectMapper", 245 "GetObject"); 246 247 mapperCall.append(path); 248 mapperCall.append(std::vector<std::string>({intf})); 249 250 auto mapperResponseMsg = bus.call(mapperCall); 251 252 if (mapperResponseMsg.is_method_error()) 253 { 254 throw std::runtime_error("ERROR in mapper call"); 255 } 256 257 std::map<std::string, std::vector<std::string>> mapperResponse; 258 mapperResponseMsg.read(mapperResponse); 259 260 if (mapperResponse.begin() == mapperResponse.end()) 261 { 262 throw std::runtime_error("ERROR in reading the mapper response"); 263 } 264 265 return mapperResponse.begin()->first; 266 } 267 268 ipmi::ObjectTree getAllDbusObjects(sdbusplus::bus_t& bus, 269 const std::string& serviceRoot, 270 const std::string& interface, 271 const std::string& match) 272 { 273 std::vector<std::string> interfaces; 274 interfaces.emplace_back(interface); 275 276 auto depth = 0; 277 278 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, 279 MAPPER_INTF, "GetSubTree"); 280 281 mapperCall.append(serviceRoot, depth, interfaces); 282 283 auto mapperReply = bus.call(mapperCall); 284 if (mapperReply.is_method_error()) 285 { 286 log<level::ERR>("Error in mapper call", 287 entry("SERVICEROOT=%s", serviceRoot.c_str()), 288 entry("INTERFACE=%s", interface.c_str())); 289 290 elog<InternalFailure>(); 291 } 292 293 ObjectTree objectTree; 294 mapperReply.read(objectTree); 295 296 for (auto it = objectTree.begin(); it != objectTree.end();) 297 { 298 if (it->first.find(match) == std::string::npos) 299 { 300 it = objectTree.erase(it); 301 } 302 else 303 { 304 ++it; 305 } 306 } 307 308 return objectTree; 309 } 310 311 void deleteAllDbusObjects(sdbusplus::bus_t& bus, const std::string& serviceRoot, 312 const std::string& interface, 313 const std::string& match) 314 { 315 try 316 { 317 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match); 318 319 for (auto& object : objectTree) 320 { 321 method_no_args::callDbusMethod(bus, object.second.begin()->first, 322 object.first, DELETE_INTERFACE, 323 "Delete"); 324 } 325 } 326 catch (const sdbusplus::exception_t& e) 327 { 328 log<level::INFO>("sdbusplus exception - Unable to delete the objects", 329 entry("ERROR=%s", e.what()), 330 entry("INTERFACE=%s", interface.c_str()), 331 entry("SERVICE=%s", serviceRoot.c_str())); 332 } 333 } 334 335 static inline std::string convertToString(const InterfaceList& interfaces) 336 { 337 std::string intfStr; 338 for (const auto& intf : interfaces) 339 { 340 intfStr += "," + intf; 341 } 342 return intfStr; 343 } 344 345 ObjectTree getAllAncestors(sdbusplus::bus_t& bus, const std::string& path, 346 InterfaceList&& interfaces) 347 { 348 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, 349 MAPPER_INTF, "GetAncestors"); 350 mapperCall.append(path, interfaces); 351 352 auto mapperReply = bus.call(mapperCall); 353 if (mapperReply.is_method_error()) 354 { 355 log<level::ERR>( 356 "Error in mapper call", entry("PATH=%s", path.c_str()), 357 entry("INTERFACES=%s", convertToString(interfaces).c_str())); 358 359 elog<InternalFailure>(); 360 } 361 362 ObjectTree objectTree; 363 mapperReply.read(objectTree); 364 365 if (objectTree.empty()) 366 { 367 log<level::ERR>( 368 "No Object has implemented the interface", 369 entry("PATH=%s", path.c_str()), 370 entry("INTERFACES=%s", convertToString(interfaces).c_str())); 371 elog<InternalFailure>(); 372 } 373 374 return objectTree; 375 } 376 377 namespace method_no_args 378 { 379 380 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service, 381 const std::string& objPath, const std::string& interface, 382 const std::string& method) 383 384 { 385 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(), 386 interface.c_str(), method.c_str()); 387 388 auto reply = bus.call(busMethod); 389 390 if (reply.is_method_error()) 391 { 392 log<level::ERR>("Failed to execute method", 393 entry("METHOD=%s", method.c_str()), 394 entry("PATH=%s", objPath.c_str()), 395 entry("INTERFACE=%s", interface.c_str())); 396 elog<InternalFailure>(); 397 } 398 } 399 400 } // namespace method_no_args 401 402 /********* Begin co-routine yielding alternatives ***************/ 403 404 boost::system::error_code getService(Context::ptr ctx, const std::string& intf, 405 const std::string& path, 406 std::string& service) 407 { 408 boost::system::error_code ec; 409 std::map<std::string, std::vector<std::string>> mapperResponse = 410 ctx->bus->yield_method_call<decltype(mapperResponse)>( 411 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper", 412 "/xyz/openbmc_project/object_mapper", 413 "xyz.openbmc_project.ObjectMapper", "GetObject", path, 414 std::vector<std::string>({intf})); 415 416 if (!ec) 417 { 418 service = std::move(mapperResponse.begin()->first); 419 } 420 return ec; 421 } 422 423 boost::system::error_code getDbusObject(Context::ptr ctx, 424 const std::string& interface, 425 const std::string& subtreePath, 426 const std::string& match, 427 DbusObjectInfo& dbusObject) 428 { 429 std::vector<DbusInterface> interfaces; 430 interfaces.emplace_back(interface); 431 432 auto depth = 0; 433 boost::system::error_code ec; 434 ObjectTree objectTree = ctx->bus->yield_method_call<ObjectTree>( 435 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree", 436 subtreePath, depth, interfaces); 437 438 if (ec) 439 { 440 return ec; 441 } 442 443 if (objectTree.empty()) 444 { 445 log<level::ERR>("No Object has implemented the interface", 446 entry("INTERFACE=%s", interface.c_str()), 447 entry("NETFN=%x", ctx->netFn), 448 entry("CMD=%x,", ctx->cmd)); 449 return boost::system::errc::make_error_code( 450 boost::system::errc::no_such_process); 451 } 452 453 // if match is empty then return the first object 454 if (match == "") 455 { 456 dbusObject = std::make_pair( 457 std::move(objectTree.begin()->first), 458 std::move(objectTree.begin()->second.begin()->first)); 459 return ec; 460 } 461 462 // else search the match string in the object path 463 auto found = std::find_if(objectTree.begin(), objectTree.end(), 464 [&match](const auto& object) { 465 return (object.first.find(match) != std::string::npos); 466 }); 467 468 if (found == objectTree.end()) 469 { 470 log<level::ERR>("Failed to find object which matches", 471 entry("MATCH=%s", match.c_str()), 472 entry("NETFN=%x", ctx->netFn), 473 entry("CMD=%x,", ctx->cmd)); 474 // set ec 475 return boost::system::errc::make_error_code( 476 boost::system::errc::no_such_file_or_directory); 477 } 478 479 dbusObject = std::make_pair(std::move(found->first), 480 std::move(found->second.begin()->first)); 481 return ec; 482 } 483 484 boost::system::error_code getAllDbusProperties(Context::ptr ctx, 485 const std::string& service, 486 const std::string& objPath, 487 const std::string& interface, 488 PropertyMap& properties) 489 { 490 boost::system::error_code ec; 491 properties = ctx->bus->yield_method_call<PropertyMap>( 492 ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF, 493 METHOD_GET_ALL, interface); 494 return ec; 495 } 496 497 boost::system::error_code 498 setDbusProperty(Context::ptr ctx, const std::string& service, 499 const std::string& objPath, const std::string& interface, 500 const std::string& property, const Value& value) 501 { 502 boost::system::error_code ec; 503 ctx->bus->yield_method_call(ctx->yield, ec, service.c_str(), 504 objPath.c_str(), PROP_INTF, METHOD_SET, 505 interface, property, value); 506 return ec; 507 } 508 509 boost::system::error_code getAllDbusObjects(Context::ptr ctx, 510 const std::string& serviceRoot, 511 const std::string& interface, 512 const std::string& match, 513 ObjectTree& objectTree) 514 { 515 boost::system::error_code ec; 516 std::vector<std::string> interfaces; 517 interfaces.emplace_back(interface); 518 519 auto depth = 0; 520 521 objectTree = ctx->bus->yield_method_call<ObjectTree>( 522 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree", 523 serviceRoot, depth, interfaces); 524 525 if (ec) 526 { 527 return ec; 528 } 529 530 for (auto it = objectTree.begin(); it != objectTree.end();) 531 { 532 if (it->first.find(match) == std::string::npos) 533 { 534 it = objectTree.erase(it); 535 } 536 else 537 { 538 ++it; 539 } 540 } 541 542 return ec; 543 } 544 545 boost::system::error_code deleteAllDbusObjects(Context::ptr ctx, 546 const std::string& serviceRoot, 547 const std::string& interface, 548 const std::string& match) 549 { 550 ObjectTree objectTree; 551 boost::system::error_code ec = 552 getAllDbusObjects(ctx, serviceRoot, interface, match, objectTree); 553 if (ec) 554 { 555 return ec; 556 } 557 558 for (auto& object : objectTree) 559 { 560 ctx->bus->yield_method_call(ctx->yield, ec, 561 object.second.begin()->first, object.first, 562 DELETE_INTERFACE, "Delete"); 563 if (ec) 564 { 565 log<level::ERR>("Failed to delete all objects", 566 entry("INTERFACE=%s", interface.c_str()), 567 entry("SERVICE=%s", serviceRoot.c_str()), 568 entry("NETFN=%x", ctx->netFn), 569 entry("CMD=%x,", ctx->cmd), 570 entry("ERROR=%s", ec.message().c_str())); 571 break; 572 } 573 } 574 return ec; 575 } 576 577 boost::system::error_code getManagedObjects(Context::ptr ctx, 578 const std::string& service, 579 const std::string& objPath, 580 ObjectValueTree& objects) 581 { 582 boost::system::error_code ec; 583 objects = ctx->bus->yield_method_call<ipmi::ObjectValueTree>( 584 ctx->yield, ec, service.c_str(), objPath.c_str(), 585 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 586 return ec; 587 } 588 589 boost::system::error_code getAllAncestors(Context::ptr ctx, 590 const std::string& path, 591 const InterfaceList& interfaces, 592 ObjectTree& objectTree) 593 { 594 std::string interfaceList = convertToString(interfaces); 595 596 boost::system::error_code ec; 597 objectTree = ctx->bus->yield_method_call<ObjectTree>( 598 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 599 "GetAncestors", path, interfaceList); 600 601 if (ec) 602 { 603 return ec; 604 } 605 606 if (objectTree.empty()) 607 { 608 log<level::ERR>("No Object has implemented the interface", 609 entry("PATH=%s", path.c_str()), 610 entry("INTERFACES=%s", interfaceList.c_str())); 611 elog<InternalFailure>(); 612 } 613 614 return ec; 615 } 616 617 boost::system::error_code callDbusMethod(Context::ptr ctx, 618 const std::string& service, 619 const std::string& objPath, 620 const std::string& interface, 621 const std::string& method) 622 { 623 boost::system::error_code ec; 624 ctx->bus->yield_method_call(ctx->yield, ec, service, objPath, interface, 625 method); 626 return ec; 627 } 628 629 /********* End co-routine yielding alternatives ***************/ 630 631 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr, 632 std::vector<uint8_t> writeData, 633 std::vector<uint8_t>& readBuf) 634 { 635 // Open the i2c device, for low-level combined data write/read 636 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); 637 if (i2cDev < 0) 638 { 639 log<level::ERR>("Failed to open i2c bus", 640 phosphor::logging::entry("BUS=%s", i2cBus.c_str())); 641 return ipmi::ccInvalidFieldRequest; 642 } 643 644 const size_t writeCount = writeData.size(); 645 const size_t readCount = readBuf.size(); 646 int msgCount = 0; 647 i2c_msg i2cmsg[2] = {}; 648 if (writeCount) 649 { 650 // Data will be writtern to the slave address 651 i2cmsg[msgCount].addr = slaveAddr; 652 i2cmsg[msgCount].flags = 0x00; 653 i2cmsg[msgCount].len = writeCount; 654 i2cmsg[msgCount].buf = writeData.data(); 655 msgCount++; 656 } 657 if (readCount) 658 { 659 // Data will be read into the buffer from the slave address 660 i2cmsg[msgCount].addr = slaveAddr; 661 i2cmsg[msgCount].flags = I2C_M_RD; 662 i2cmsg[msgCount].len = readCount; 663 i2cmsg[msgCount].buf = readBuf.data(); 664 msgCount++; 665 } 666 667 i2c_rdwr_ioctl_data msgReadWrite = {}; 668 msgReadWrite.msgs = i2cmsg; 669 msgReadWrite.nmsgs = msgCount; 670 671 // Perform the combined write/read 672 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite); 673 ::close(i2cDev); 674 675 if (ret < 0) 676 { 677 log<level::ERR>("I2C WR Failed!", 678 phosphor::logging::entry("RET=%d", ret)); 679 return ipmi::ccUnspecifiedError; 680 } 681 if (readCount) 682 { 683 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len); 684 } 685 686 return ipmi::ccSuccess; 687 } 688 689 } // namespace ipmi 690