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