1 #include "guid.hpp"
2 
3 #include <ipmid/api.h>
4 
5 #include <ipmid/utils.hpp>
6 #include <phosphor-logging/elog-errors.hpp>
7 #include <phosphor-logging/lg2.hpp>
8 #include <xyz/openbmc_project/Common/error.hpp>
9 
10 #include <sstream>
11 #include <string>
12 
13 using namespace phosphor::logging;
14 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
15 
16 static std::optional<command::Guid> guid;
17 
18 namespace command
19 {
20 
21 std::unique_ptr<sdbusplus::bus::match_t> matchPtr(nullptr);
22 
23 static constexpr auto propInterface = "xyz.openbmc_project.Common.UUID";
24 static constexpr auto uuidProperty = "UUID";
25 static constexpr auto subtreePath = "/xyz/openbmc_project/inventory";
26 
rfcToGuid(std::string rfc4122,Guid & uuid)27 static void rfcToGuid(std::string rfc4122, Guid& uuid)
28 {
29     using Argument = xyz::openbmc_project::Common::InvalidArgument;
30     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
31     // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte
32     // order
33     // Ex: 0x2332fc2c40e66298e511f2782395a361
34     constexpr size_t uuidHexLength = (2 * BMC_GUID_LEN);
35     constexpr size_t uuidRfc4122Length = (uuidHexLength + 4);
36 
37     if (rfc4122.size() == uuidRfc4122Length)
38     {
39         rfc4122.erase(std::remove(rfc4122.begin(), rfc4122.end(), '-'),
40                       rfc4122.end());
41     }
42     if (rfc4122.size() != uuidHexLength)
43     {
44         elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
45                               Argument::ARGUMENT_VALUE(rfc4122.c_str()));
46     }
47     for (size_t ind = 0; ind < uuidHexLength; ind += 2)
48     {
49         long b;
50         try
51         {
52             b = std::stoul(rfc4122.substr(ind, 2), nullptr, 16);
53         }
54         catch (const std::exception& e)
55         {
56             elog<InvalidArgument>(Argument::ARGUMENT_NAME("rfc4122"),
57                                   Argument::ARGUMENT_VALUE(rfc4122.c_str()));
58         }
59 
60         uuid[BMC_GUID_LEN - (ind / 2) - 1] = static_cast<uint8_t>(b);
61     }
62     return;
63 }
64 
65 // Canned System GUID for when the Chassis DBUS object is not populated
66 static constexpr Guid fakeGuid = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
67                                   0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
68                                   0x0D, 0x0E, 0x0F, 0x10};
getSystemGUID()69 const Guid& getSystemGUID()
70 {
71     if (guid.has_value())
72     {
73         return guid.value();
74     }
75 
76     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
77 
78     ipmi::Value propValue;
79     try
80     {
81         const auto& [objPath, service] =
82             ipmi::getDbusObject(bus, propInterface, subtreePath);
83         // Read UUID property value from bmcObject
84         // UUID is in RFC4122 format Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
85         propValue = ipmi::getDbusProperty(bus, service, objPath, propInterface,
86                                           uuidProperty);
87     }
88     catch (const sdbusplus::exception_t& e)
89     {
90         lg2::error("Failed in reading BMC UUID property: {ERROR}", "ERROR", e);
91         return fakeGuid;
92     }
93 
94     std::string rfc4122Uuid = std::get<std::string>(propValue);
95     try
96     {
97         // convert to IPMI format
98         Guid tmpGuid{};
99         rfcToGuid(rfc4122Uuid, tmpGuid);
100         guid = tmpGuid;
101     }
102     catch (const InvalidArgument& e)
103     {
104         lg2::error("Failed in parsing BMC UUID property: {VALUE}", "VALUE",
105                    rfc4122Uuid.c_str());
106         return fakeGuid;
107     }
108     return guid.value();
109 }
110 
registerGUIDChangeCallback()111 void registerGUIDChangeCallback()
112 {
113     if (matchPtr == nullptr)
114     {
115         using namespace sdbusplus::bus::match::rules;
116         sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
117 
118         try
119         {
120             matchPtr = std::make_unique<sdbusplus::bus::match_t>(
121                 bus, propertiesChangedNamespace(subtreePath, propInterface),
122                 [](sdbusplus::message_t& m) {
123                     try
124                     {
125                         std::string iface{};
126                         std::map<std::string, ipmi::Value> pdict{};
127                         m.read(iface, pdict);
128                         if (iface != propInterface)
129                         {
130                             return;
131                         }
132                         auto guidStr = std::get<std::string>(pdict.at("UUID"));
133                         Guid tmpGuid{};
134                         rfcToGuid(guidStr, tmpGuid);
135                         guid = tmpGuid;
136                     }
137                     catch (const std::exception& e)
138                     {
139                         // signal contained invalid guid; ignore it
140                         lg2::error(
141                             "Failed to parse propertiesChanged signal: {ERROR}",
142                             "ERROR", e);
143                     }
144                 });
145         }
146         catch (const std::exception& e)
147         {
148             lg2::error("Failed to create dbus match: {ERROR}", "ERROR", e);
149         }
150     }
151 }
152 
153 } // namespace command
154