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