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_t& 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_t& 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_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 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_t& 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_t ServiceCache::newMethodCall(sdbusplus::bus_t& bus, 229 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_t& bus) const 237 { 238 return cachedService && cachedBusName == bus.get_unique_name(); 239 } 240 241 std::string getService(sdbusplus::bus_t& 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_t& 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_t& bus, const std::string& serviceRoot, 314 const std::string& interface, 315 const std::string& match) 316 { 317 try 318 { 319 auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match); 320 321 for (auto& object : objectTree) 322 { 323 method_no_args::callDbusMethod(bus, object.second.begin()->first, 324 object.first, DELETE_INTERFACE, 325 "Delete"); 326 } 327 } 328 catch (const sdbusplus::exception_t& e) 329 { 330 log<level::INFO>("sdbusplus exception - Unable to delete the objects", 331 entry("ERROR=%s", e.what()), 332 entry("INTERFACE=%s", interface.c_str()), 333 entry("SERVICE=%s", serviceRoot.c_str())); 334 } 335 } 336 337 static inline std::string convertToString(const InterfaceList& interfaces) 338 { 339 std::string intfStr; 340 for (const auto& intf : interfaces) 341 { 342 intfStr += "," + intf; 343 } 344 return intfStr; 345 } 346 347 ObjectTree getAllAncestors(sdbusplus::bus_t& bus, const std::string& path, 348 InterfaceList&& interfaces) 349 { 350 auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ, 351 MAPPER_INTF, "GetAncestors"); 352 mapperCall.append(path, interfaces); 353 354 auto mapperReply = bus.call(mapperCall); 355 if (mapperReply.is_method_error()) 356 { 357 log<level::ERR>( 358 "Error in mapper call", entry("PATH=%s", path.c_str()), 359 entry("INTERFACES=%s", convertToString(interfaces).c_str())); 360 361 elog<InternalFailure>(); 362 } 363 364 ObjectTree objectTree; 365 mapperReply.read(objectTree); 366 367 if (objectTree.empty()) 368 { 369 log<level::ERR>( 370 "No Object has implemented the interface", 371 entry("PATH=%s", path.c_str()), 372 entry("INTERFACES=%s", convertToString(interfaces).c_str())); 373 elog<InternalFailure>(); 374 } 375 376 return objectTree; 377 } 378 379 namespace method_no_args 380 { 381 382 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service, 383 const std::string& objPath, const std::string& interface, 384 const std::string& method) 385 386 { 387 auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(), 388 interface.c_str(), method.c_str()); 389 390 auto reply = bus.call(busMethod); 391 392 if (reply.is_method_error()) 393 { 394 log<level::ERR>("Failed to execute method", 395 entry("METHOD=%s", method.c_str()), 396 entry("PATH=%s", objPath.c_str()), 397 entry("INTERFACE=%s", interface.c_str())); 398 elog<InternalFailure>(); 399 } 400 } 401 402 } // namespace method_no_args 403 404 /********* Begin co-routine yielding alternatives ***************/ 405 406 boost::system::error_code getService(Context::ptr ctx, const std::string& intf, 407 const std::string& path, 408 std::string& service) 409 { 410 boost::system::error_code ec; 411 std::map<std::string, std::vector<std::string>> mapperResponse = 412 ctx->bus->yield_method_call<decltype(mapperResponse)>( 413 ctx->yield, ec, "xyz.openbmc_project.ObjectMapper", 414 "/xyz/openbmc_project/object_mapper", 415 "xyz.openbmc_project.ObjectMapper", "GetObject", path, 416 std::vector<std::string>({intf})); 417 418 if (!ec) 419 { 420 service = std::move(mapperResponse.begin()->first); 421 } 422 return ec; 423 } 424 425 boost::system::error_code getDbusObject(Context::ptr ctx, 426 const std::string& interface, 427 const std::string& subtreePath, 428 const std::string& match, 429 DbusObjectInfo& dbusObject) 430 { 431 std::vector<DbusInterface> interfaces; 432 interfaces.emplace_back(interface); 433 434 auto depth = 0; 435 boost::system::error_code ec; 436 ObjectTree objectTree = ctx->bus->yield_method_call<ObjectTree>( 437 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree", 438 subtreePath, depth, interfaces); 439 440 if (ec) 441 { 442 return ec; 443 } 444 445 if (objectTree.empty()) 446 { 447 log<level::ERR>("No Object has implemented the interface", 448 entry("INTERFACE=%s", interface.c_str()), 449 entry("NETFN=%x", ctx->netFn), 450 entry("CMD=%x,", ctx->cmd)); 451 return boost::system::errc::make_error_code( 452 boost::system::errc::no_such_process); 453 } 454 455 // if match is empty then return the first object 456 if (match == "") 457 { 458 dbusObject = std::make_pair( 459 std::move(objectTree.begin()->first), 460 std::move(objectTree.begin()->second.begin()->first)); 461 return ec; 462 } 463 464 // else search the match string in the object path 465 auto found = std::find_if( 466 objectTree.begin(), objectTree.end(), [&match](const auto& object) { 467 return (object.first.find(match) != std::string::npos); 468 }); 469 470 if (found == objectTree.end()) 471 { 472 log<level::ERR>("Failed to find object which matches", 473 entry("MATCH=%s", match.c_str()), 474 entry("NETFN=%x", ctx->netFn), 475 entry("CMD=%x,", ctx->cmd)); 476 // set ec 477 return boost::system::errc::make_error_code( 478 boost::system::errc::no_such_file_or_directory); 479 } 480 481 dbusObject = std::make_pair(std::move(found->first), 482 std::move(found->second.begin()->first)); 483 return ec; 484 } 485 486 boost::system::error_code getAllDbusProperties(Context::ptr ctx, 487 const std::string& service, 488 const std::string& objPath, 489 const std::string& interface, 490 PropertyMap& properties) 491 { 492 boost::system::error_code ec; 493 properties = ctx->bus->yield_method_call<PropertyMap>( 494 ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF, 495 METHOD_GET_ALL, interface); 496 return ec; 497 } 498 499 boost::system::error_code 500 setDbusProperty(Context::ptr ctx, const std::string& service, 501 const std::string& objPath, const std::string& interface, 502 const std::string& property, const Value& value) 503 { 504 boost::system::error_code ec; 505 ctx->bus->yield_method_call(ctx->yield, ec, service.c_str(), 506 objPath.c_str(), PROP_INTF, METHOD_SET, 507 interface, property, value); 508 return ec; 509 } 510 511 boost::system::error_code getAllDbusObjects(Context::ptr ctx, 512 const std::string& serviceRoot, 513 const std::string& interface, 514 const std::string& match, 515 ObjectTree& objectTree) 516 { 517 boost::system::error_code ec; 518 std::vector<std::string> interfaces; 519 interfaces.emplace_back(interface); 520 521 auto depth = 0; 522 523 objectTree = ctx->bus->yield_method_call<ObjectTree>( 524 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree", 525 serviceRoot, depth, interfaces); 526 527 if (ec) 528 { 529 return ec; 530 } 531 532 for (auto it = objectTree.begin(); it != objectTree.end();) 533 { 534 if (it->first.find(match) == std::string::npos) 535 { 536 it = objectTree.erase(it); 537 } 538 else 539 { 540 ++it; 541 } 542 } 543 544 return ec; 545 } 546 547 boost::system::error_code deleteAllDbusObjects(Context::ptr ctx, 548 const std::string& serviceRoot, 549 const std::string& interface, 550 const std::string& match) 551 { 552 ObjectTree objectTree; 553 boost::system::error_code ec = 554 getAllDbusObjects(ctx, serviceRoot, interface, match, objectTree); 555 if (ec) 556 { 557 return ec; 558 } 559 560 for (auto& object : objectTree) 561 { 562 ctx->bus->yield_method_call(ctx->yield, ec, 563 object.second.begin()->first, object.first, 564 DELETE_INTERFACE, "Delete"); 565 if (ec) 566 { 567 log<level::ERR>("Failed to delete all objects", 568 entry("INTERFACE=%s", interface.c_str()), 569 entry("SERVICE=%s", serviceRoot.c_str()), 570 entry("NETFN=%x", ctx->netFn), 571 entry("CMD=%x,", ctx->cmd), 572 entry("ERROR=%s", ec.message().c_str())); 573 break; 574 } 575 } 576 return ec; 577 } 578 579 boost::system::error_code getManagedObjects(Context::ptr ctx, 580 const std::string& service, 581 const std::string& objPath, 582 ObjectValueTree& objects) 583 { 584 boost::system::error_code ec; 585 objects = ctx->bus->yield_method_call<ipmi::ObjectValueTree>( 586 ctx->yield, ec, service.c_str(), objPath.c_str(), 587 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 588 return ec; 589 } 590 591 boost::system::error_code getAllAncestors(Context::ptr ctx, 592 const std::string& path, 593 const InterfaceList& interfaces, 594 ObjectTree& objectTree) 595 { 596 std::string interfaceList = convertToString(interfaces); 597 598 boost::system::error_code ec; 599 objectTree = ctx->bus->yield_method_call<ObjectTree>( 600 ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, 601 "GetAncestors", path, interfaceList); 602 603 if (ec) 604 { 605 return ec; 606 } 607 608 if (objectTree.empty()) 609 { 610 log<level::ERR>("No Object has implemented the interface", 611 entry("PATH=%s", path.c_str()), 612 entry("INTERFACES=%s", interfaceList.c_str())); 613 elog<InternalFailure>(); 614 } 615 616 return ec; 617 } 618 619 boost::system::error_code callDbusMethod(Context::ptr ctx, 620 const std::string& service, 621 const std::string& objPath, 622 const std::string& interface, 623 const std::string& method) 624 { 625 boost::system::error_code ec; 626 ctx->bus->yield_method_call(ctx->yield, ec, service, objPath, interface, 627 method); 628 return ec; 629 } 630 631 /********* End co-routine yielding alternatives ***************/ 632 633 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr, 634 std::vector<uint8_t> writeData, 635 std::vector<uint8_t>& readBuf) 636 { 637 // Open the i2c device, for low-level combined data write/read 638 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); 639 if (i2cDev < 0) 640 { 641 log<level::ERR>("Failed to open i2c bus", 642 phosphor::logging::entry("BUS=%s", i2cBus.c_str())); 643 return ipmi::ccInvalidFieldRequest; 644 } 645 646 const size_t writeCount = writeData.size(); 647 const size_t readCount = readBuf.size(); 648 int msgCount = 0; 649 i2c_msg i2cmsg[2] = {}; 650 if (writeCount) 651 { 652 // Data will be writtern to the slave address 653 i2cmsg[msgCount].addr = slaveAddr; 654 i2cmsg[msgCount].flags = 0x00; 655 i2cmsg[msgCount].len = writeCount; 656 i2cmsg[msgCount].buf = writeData.data(); 657 msgCount++; 658 } 659 if (readCount) 660 { 661 // Data will be read into the buffer from the slave address 662 i2cmsg[msgCount].addr = slaveAddr; 663 i2cmsg[msgCount].flags = I2C_M_RD; 664 i2cmsg[msgCount].len = readCount; 665 i2cmsg[msgCount].buf = readBuf.data(); 666 msgCount++; 667 } 668 669 i2c_rdwr_ioctl_data msgReadWrite = {}; 670 msgReadWrite.msgs = i2cmsg; 671 msgReadWrite.nmsgs = msgCount; 672 673 // Perform the combined write/read 674 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite); 675 ::close(i2cDev); 676 677 if (ret < 0) 678 { 679 log<level::ERR>("I2C WR Failed!", 680 phosphor::logging::entry("RET=%d", ret)); 681 return ipmi::ccUnspecifiedError; 682 } 683 if (readCount) 684 { 685 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len); 686 } 687 688 return ipmi::ccSuccess; 689 } 690 691 } // namespace ipmi 692