summaryrefslogtreecommitdiffstats
path: root/libipmid/utils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libipmid/utils.cpp')
-rw-r--r--libipmid/utils.cpp544
1 files changed, 544 insertions, 0 deletions
diff --git a/libipmid/utils.cpp b/libipmid/utils.cpp
new file mode 100644
index 0000000..4fd219e
--- /dev/null
+++ b/libipmid/utils.cpp
@@ -0,0 +1,544 @@
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <net/if.h>
+
+#include <algorithm>
+#include <chrono>
+#include <ipmid/utils.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/message/types.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+namespace ipmi
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+namespace variant_ns = sdbusplus::message::variant_ns;
+
+namespace network
+{
+
+/** @brief checks if the given ip is Link Local Ip or not.
+ * @param[in] ipaddress - IPAddress.
+ */
+bool isLinkLocalIP(const std::string& ipaddress);
+
+} // namespace network
+
+// TODO There may be cases where an interface is implemented by multiple
+// objects,to handle such cases we are interested on that object
+// which are on interested busname.
+// Currently mapper doesn't give the readable busname(gives busid) so we can't
+// use busname to find the object,will do later once the support is there.
+
+DbusObjectInfo getDbusObject(sdbusplus::bus::bus& bus,
+ const std::string& interface,
+ const std::string& serviceRoot,
+ const std::string& match)
+{
+ std::vector<DbusInterface> interfaces;
+ interfaces.emplace_back(interface);
+
+ auto depth = 0;
+
+ auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
+ MAPPER_INTF, "GetSubTree");
+
+ mapperCall.append(serviceRoot, depth, interfaces);
+
+ auto mapperReply = bus.call(mapperCall);
+ if (mapperReply.is_method_error())
+ {
+ log<level::ERR>("Error in mapper call");
+ elog<InternalFailure>();
+ }
+
+ ObjectTree objectTree;
+ mapperReply.read(objectTree);
+
+ if (objectTree.empty())
+ {
+ log<level::ERR>("No Object has implemented the interface",
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+
+ DbusObjectInfo objectInfo;
+
+ // if match is empty then return the first object
+ if (match == "")
+ {
+ objectInfo = std::make_pair(
+ objectTree.begin()->first,
+ std::move(objectTree.begin()->second.begin()->first));
+ return objectInfo;
+ }
+
+ // else search the match string in the object path
+ auto found = std::find_if(
+ objectTree.begin(), objectTree.end(), [&match](const auto& object) {
+ return (object.first.find(match) != std::string::npos);
+ });
+
+ if (found == objectTree.end())
+ {
+ log<level::ERR>("Failed to find object which matches",
+ entry("MATCH=%s", match.c_str()));
+ elog<InternalFailure>();
+ // elog<> throws an exception.
+ }
+
+ return make_pair(found->first, std::move(found->second.begin()->first));
+}
+
+DbusObjectInfo getIPObject(sdbusplus::bus::bus& bus,
+ const std::string& interface,
+ const std::string& serviceRoot,
+ const std::string& match)
+{
+ auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
+
+ if (objectTree.empty())
+ {
+ log<level::ERR>("No Object has implemented the IP interface",
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+
+ DbusObjectInfo objectInfo;
+
+ for (auto& object : objectTree)
+ {
+ auto variant = ipmi::getDbusProperty(
+ bus, object.second.begin()->first, object.first,
+ ipmi::network::IP_INTERFACE, "Address");
+
+ objectInfo = std::make_pair(object.first, object.second.begin()->first);
+
+ // if LinkLocalIP found look for Non-LinkLocalIP
+ if (ipmi::network::isLinkLocalIP(variant_ns::get<std::string>(variant)))
+ {
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return objectInfo;
+}
+
+Value getDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& property,
+ std::chrono::microseconds timeout)
+{
+
+ Value value;
+
+ auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+ PROP_INTF, METHOD_GET);
+
+ method.append(interface, property);
+
+ auto reply = bus.call(method, timeout.count());
+
+ if (reply.is_method_error())
+ {
+ log<level::ERR>("Failed to get property",
+ entry("PROPERTY=%s", property.c_str()),
+ entry("PATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+
+ reply.read(value);
+
+ return value;
+}
+
+PropertyMap getAllDbusProperties(sdbusplus::bus::bus& bus,
+ const std::string& service,
+ const std::string& objPath,
+ const std::string& interface,
+ std::chrono::microseconds timeout)
+{
+ PropertyMap properties;
+
+ auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+ PROP_INTF, METHOD_GET_ALL);
+
+ method.append(interface);
+
+ auto reply = bus.call(method, timeout.count());
+
+ if (reply.is_method_error())
+ {
+ log<level::ERR>("Failed to get all properties",
+ entry("PATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+
+ reply.read(properties);
+ return properties;
+}
+
+ObjectValueTree getManagedObjects(sdbusplus::bus::bus& bus,
+ const std::string& service,
+ const std::string& objPath)
+{
+ ipmi::ObjectValueTree interfaces;
+
+ auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+ "org.freedesktop.DBus.ObjectManager",
+ "GetManagedObjects");
+
+ auto reply = bus.call(method);
+
+ if (reply.is_method_error())
+ {
+ log<level::ERR>("Failed to get managed objects",
+ entry("PATH=%s", objPath.c_str()));
+ elog<InternalFailure>();
+ }
+
+ reply.read(interfaces);
+ return interfaces;
+}
+
+void setDbusProperty(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& property, const Value& value,
+ std::chrono::microseconds timeout)
+{
+ auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+ PROP_INTF, METHOD_SET);
+
+ method.append(interface, property, value);
+
+ if (!bus.call(method, timeout.count()))
+ {
+ log<level::ERR>("Failed to set property",
+ entry("PROPERTY=%s", property.c_str()),
+ entry("PATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+ServiceCache::ServiceCache(const std::string& intf, const std::string& path) :
+ intf(intf), path(path), cachedService(std::nullopt),
+ cachedBusName(std::nullopt)
+{
+}
+
+ServiceCache::ServiceCache(std::string&& intf, std::string&& path) :
+ intf(std::move(intf)), path(std::move(path)), cachedService(std::nullopt),
+ cachedBusName(std::nullopt)
+{
+}
+
+const std::string& ServiceCache::getService(sdbusplus::bus::bus& bus)
+{
+ if (!isValid(bus))
+ {
+ cachedBusName = bus.get_unique_name();
+ cachedService = ::ipmi::getService(bus, intf, path);
+ }
+ return cachedService.value();
+}
+
+void ServiceCache::invalidate()
+{
+ cachedBusName = std::nullopt;
+ cachedService = std::nullopt;
+}
+
+sdbusplus::message::message
+ ServiceCache::newMethodCall(sdbusplus::bus::bus& bus, const char* intf,
+ const char* method)
+{
+ return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf,
+ method);
+}
+
+bool ServiceCache::isValid(sdbusplus::bus::bus& bus) const
+{
+ return cachedService && cachedBusName == bus.get_unique_name();
+}
+
+std::string getService(sdbusplus::bus::bus& bus, const std::string& intf,
+ const std::string& path)
+{
+ auto mapperCall =
+ bus.new_method_call("xyz.openbmc_project.ObjectMapper",
+ "/xyz/openbmc_project/object_mapper",
+ "xyz.openbmc_project.ObjectMapper", "GetObject");
+
+ mapperCall.append(path);
+ mapperCall.append(std::vector<std::string>({intf}));
+
+ auto mapperResponseMsg = bus.call(mapperCall);
+
+ if (mapperResponseMsg.is_method_error())
+ {
+ throw std::runtime_error("ERROR in mapper call");
+ }
+
+ std::map<std::string, std::vector<std::string>> mapperResponse;
+ mapperResponseMsg.read(mapperResponse);
+
+ if (mapperResponse.begin() == mapperResponse.end())
+ {
+ throw std::runtime_error("ERROR in reading the mapper response");
+ }
+
+ return mapperResponse.begin()->first;
+}
+
+ipmi::ObjectTree getAllDbusObjects(sdbusplus::bus::bus& bus,
+ const std::string& serviceRoot,
+ const std::string& interface,
+ const std::string& match)
+{
+ std::vector<std::string> interfaces;
+ interfaces.emplace_back(interface);
+
+ auto depth = 0;
+
+ auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
+ MAPPER_INTF, "GetSubTree");
+
+ mapperCall.append(serviceRoot, depth, interfaces);
+
+ auto mapperReply = bus.call(mapperCall);
+ if (mapperReply.is_method_error())
+ {
+ log<level::ERR>("Error in mapper call",
+ entry("SERVICEROOT=%s", serviceRoot.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+
+ elog<InternalFailure>();
+ }
+
+ ObjectTree objectTree;
+ mapperReply.read(objectTree);
+
+ for (auto it = objectTree.begin(); it != objectTree.end();)
+ {
+ if (it->first.find(match) == std::string::npos)
+ {
+ it = objectTree.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ return objectTree;
+}
+
+void deleteAllDbusObjects(sdbusplus::bus::bus& bus,
+ const std::string& serviceRoot,
+ const std::string& interface,
+ const std::string& match)
+{
+ try
+ {
+ auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
+
+ for (auto& object : objectTree)
+ {
+ method_no_args::callDbusMethod(bus, object.second.begin()->first,
+ object.first, DELETE_INTERFACE,
+ "Delete");
+ }
+ }
+ catch (InternalFailure& e)
+ {
+ log<level::INFO>("Unable to delete the objects having",
+ entry("INTERFACE=%s", interface.c_str()),
+ entry("SERVICE=%s", serviceRoot.c_str()));
+ }
+}
+
+ObjectTree getAllAncestors(sdbusplus::bus::bus& bus, const std::string& path,
+ InterfaceList&& interfaces)
+{
+ auto convertToString = [](InterfaceList& interfaces) -> std::string {
+ std::string intfStr;
+ for (const auto& intf : interfaces)
+ {
+ intfStr += "," + intf;
+ }
+ return intfStr;
+ };
+
+ auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
+ MAPPER_INTF, "GetAncestors");
+ mapperCall.append(path, interfaces);
+
+ auto mapperReply = bus.call(mapperCall);
+ if (mapperReply.is_method_error())
+ {
+ log<level::ERR>(
+ "Error in mapper call", entry("PATH=%s", path.c_str()),
+ entry("INTERFACES=%s", convertToString(interfaces).c_str()));
+
+ elog<InternalFailure>();
+ }
+
+ ObjectTree objectTree;
+ mapperReply.read(objectTree);
+
+ if (objectTree.empty())
+ {
+ log<level::ERR>(
+ "No Object has implemented the interface",
+ entry("PATH=%s", path.c_str()),
+ entry("INTERFACES=%s", convertToString(interfaces).c_str()));
+ elog<InternalFailure>();
+ }
+
+ return objectTree;
+}
+
+namespace method_no_args
+{
+
+void callDbusMethod(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& interface,
+ const std::string& method)
+
+{
+ auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
+ interface.c_str(), method.c_str());
+
+ auto reply = bus.call(busMethod);
+
+ if (reply.is_method_error())
+ {
+ log<level::ERR>("Failed to execute method",
+ entry("METHOD=%s", method.c_str()),
+ entry("PATH=%s", objPath.c_str()),
+ entry("INTERFACE=%s", interface.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+} // namespace method_no_args
+
+namespace network
+{
+
+bool isLinkLocalIP(const std::string& address)
+{
+ return address.find(IPV4_PREFIX) == 0 || address.find(IPV6_PREFIX) == 0;
+}
+
+void createIP(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& protocolType,
+ const std::string& ipaddress, uint8_t prefix)
+{
+ std::string gateway = "";
+
+ auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
+ IP_CREATE_INTERFACE, "IP");
+
+ busMethod.append(protocolType, ipaddress, prefix, gateway);
+
+ auto reply = bus.call(busMethod);
+
+ if (reply.is_method_error())
+ {
+ log<level::ERR>("Failed to execute method", entry("METHOD=%s", "IP"),
+ entry("PATH=%s", objPath.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+void createVLAN(sdbusplus::bus::bus& bus, const std::string& service,
+ const std::string& objPath, const std::string& interfaceName,
+ uint32_t vlanID)
+{
+ auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
+ VLAN_CREATE_INTERFACE, "VLAN");
+
+ busMethod.append(interfaceName, vlanID);
+
+ auto reply = bus.call(busMethod);
+
+ if (reply.is_method_error())
+ {
+ log<level::ERR>("Failed to execute method", entry("METHOD=%s", "VLAN"),
+ entry("PATH=%s", objPath.c_str()));
+ elog<InternalFailure>();
+ }
+}
+
+uint8_t toPrefix(int addressFamily, const std::string& subnetMask)
+{
+ if (addressFamily == AF_INET6)
+ {
+ return 0;
+ }
+
+ uint32_t buff{};
+
+ auto rc = inet_pton(addressFamily, subnetMask.c_str(), &buff);
+ if (rc <= 0)
+ {
+ log<level::ERR>("inet_pton failed:",
+ entry("SUBNETMASK=%s", subnetMask.c_str()));
+ return 0;
+ }
+
+ buff = be32toh(buff);
+ // total no of bits - total no of leading zero == total no of ones
+ if (((sizeof(buff) * 8) - (__builtin_ctz(buff))) ==
+ __builtin_popcount(buff))
+ {
+ return __builtin_popcount(buff);
+ }
+ else
+ {
+ log<level::ERR>("Invalid Mask",
+ entry("SUBNETMASK=%s", subnetMask.c_str()));
+ return 0;
+ }
+}
+
+uint32_t getVLAN(const std::string& path)
+{
+ // Path would be look like
+ // /xyz/openbmc_project/network/eth0_443/ipv4
+
+ uint32_t vlanID = 0;
+ try
+ {
+ auto intfObjectPath = path.substr(0, path.find(IP_TYPE) - 1);
+
+ auto intfName = intfObjectPath.substr(intfObjectPath.rfind("/") + 1);
+
+ auto index = intfName.find("_");
+ if (index != std::string::npos)
+ {
+ auto str = intfName.substr(index + 1);
+ vlanID = std::stoul(str);
+ }
+ }
+ catch (std::exception& e)
+ {
+ log<level::ERR>("Exception occurred during getVLAN",
+ entry("PATH=%s", path.c_str()),
+ entry("EXCEPTION=%s", e.what()));
+ }
+ return vlanID;
+}
+
+} // namespace network
+} // namespace ipmi
OpenPOWER on IntegriCloud