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