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 (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 ObjectTree getAllAncestors(sdbusplus::bus::bus& bus, const std::string& path, 339 InterfaceList&& interfaces) 340 { 341 auto convertToString = [](InterfaceList& interfaces) -> std::string { 342 std::string intfStr; 343 for (const auto& intf : interfaces) 344 { 345 intfStr += "," + intf; 346 } 347 return intfStr; 348 }; 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::bus& 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 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr, 404 std::vector<uint8_t> writeData, 405 std::vector<uint8_t>& readBuf) 406 { 407 // Open the i2c device, for low-level combined data write/read 408 int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC); 409 if (i2cDev < 0) 410 { 411 log<level::ERR>("Failed to open i2c bus", 412 phosphor::logging::entry("BUS=%s", i2cBus.c_str())); 413 return ipmi::ccInvalidFieldRequest; 414 } 415 416 const size_t writeCount = writeData.size(); 417 const size_t readCount = readBuf.size(); 418 int msgCount = 0; 419 i2c_msg i2cmsg[2] = {0}; 420 if (writeCount) 421 { 422 // Data will be writtern to the slave address 423 i2cmsg[msgCount].addr = slaveAddr; 424 i2cmsg[msgCount].flags = 0x00; 425 i2cmsg[msgCount].len = writeCount; 426 i2cmsg[msgCount].buf = writeData.data(); 427 msgCount++; 428 } 429 if (readCount) 430 { 431 // Data will be read into the buffer from the slave address 432 i2cmsg[msgCount].addr = slaveAddr; 433 i2cmsg[msgCount].flags = I2C_M_RD; 434 i2cmsg[msgCount].len = readCount; 435 i2cmsg[msgCount].buf = readBuf.data(); 436 msgCount++; 437 } 438 439 i2c_rdwr_ioctl_data msgReadWrite = {0}; 440 msgReadWrite.msgs = i2cmsg; 441 msgReadWrite.nmsgs = msgCount; 442 443 // Perform the combined write/read 444 int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite); 445 ::close(i2cDev); 446 447 if (ret < 0) 448 { 449 log<level::ERR>("I2C WR Failed!", 450 phosphor::logging::entry("RET=%d", ret)); 451 return ipmi::ccUnspecifiedError; 452 } 453 if (readCount) 454 { 455 readBuf.resize(msgReadWrite.msgs[msgCount - 1].len); 456 } 457 458 return ipmi::ccSuccess; 459 } 460 461 } // namespace ipmi 462