12d8fa225SAdriana Kobylak #include "config.h"
2f6ed5897SGunnar Mills 
32d8fa225SAdriana Kobylak #include "item_updater.hpp"
4f6ed5897SGunnar Mills 
5f6ed5897SGunnar Mills #include "xyz/openbmc_project/Common/error.hpp"
6f6ed5897SGunnar Mills 
7f6ed5897SGunnar Mills #include <phosphor-logging/elog-errors.hpp>
8f6ed5897SGunnar Mills #include <phosphor-logging/log.hpp>
92d8fa225SAdriana Kobylak 
108facccfaSBrad Bishop #include <filesystem>
118facccfaSBrad Bishop 
122d8fa225SAdriana Kobylak namespace openpower
132d8fa225SAdriana Kobylak {
142d8fa225SAdriana Kobylak namespace software
152d8fa225SAdriana Kobylak {
16befe5ce4SAdriana Kobylak namespace updater
172d8fa225SAdriana Kobylak {
18a9ac9279SLei YU namespace server = sdbusplus::xyz::openbmc_project::Software::server;
19a9ac9279SLei YU namespace fs = std::filesystem;
20a9ac9279SLei YU 
2113fc66adSEddie James using namespace sdbusplus::xyz::openbmc_project::Common::Error;
22b66ac3aeSAdriana Kobylak using namespace phosphor::logging;
23b66ac3aeSAdriana Kobylak 
createActivation(sdbusplus::message_t & m)240dea1992SPatrick Williams void ItemUpdater::createActivation(sdbusplus::message_t& m)
25a9ac9279SLei YU {
26a9ac9279SLei YU     using SVersion = server::Version;
27a9ac9279SLei YU     using VersionPurpose = SVersion::VersionPurpose;
28a9ac9279SLei YU 
29a9ac9279SLei YU     sdbusplus::message::object_path objPath;
30573552aeSPatrick Williams     std::map<std::string, std::map<std::string, std::variant<std::string>>>
31a9ac9279SLei YU         interfaces;
32a9ac9279SLei YU     m.read(objPath, interfaces);
33a9ac9279SLei YU 
34a9ac9279SLei YU     std::string path(std::move(objPath));
35a9ac9279SLei YU     std::string filePath;
36a9ac9279SLei YU     auto purpose = VersionPurpose::Unknown;
37a9ac9279SLei YU     std::string version;
38a9ac9279SLei YU 
39a9ac9279SLei YU     for (const auto& intf : interfaces)
40a9ac9279SLei YU     {
41a9ac9279SLei YU         if (intf.first == VERSION_IFACE)
42a9ac9279SLei YU         {
43a9ac9279SLei YU             for (const auto& property : intf.second)
44a9ac9279SLei YU             {
45a9ac9279SLei YU                 if (property.first == "Purpose")
46a9ac9279SLei YU                 {
47a9ac9279SLei YU                     // Only process the Host and System images
48a9ac9279SLei YU                     auto value = SVersion::convertVersionPurposeFromString(
49550f31b3SPatrick Williams                         std::get<std::string>(property.second));
50a9ac9279SLei YU 
51a9ac9279SLei YU                     if (value == VersionPurpose::Host ||
52a9ac9279SLei YU                         value == VersionPurpose::System)
53a9ac9279SLei YU                     {
54a9ac9279SLei YU                         purpose = value;
55a9ac9279SLei YU                     }
56a9ac9279SLei YU                 }
57a9ac9279SLei YU                 else if (property.first == "Version")
58a9ac9279SLei YU                 {
59550f31b3SPatrick Williams                     version = std::get<std::string>(property.second);
60a9ac9279SLei YU                 }
61a9ac9279SLei YU             }
62a9ac9279SLei YU         }
63a9ac9279SLei YU         else if (intf.first == FILEPATH_IFACE)
64a9ac9279SLei YU         {
65a9ac9279SLei YU             for (const auto& property : intf.second)
66a9ac9279SLei YU             {
67a9ac9279SLei YU                 if (property.first == "Path")
68a9ac9279SLei YU                 {
69550f31b3SPatrick Williams                     filePath = std::get<std::string>(property.second);
70a9ac9279SLei YU                 }
71a9ac9279SLei YU             }
72a9ac9279SLei YU         }
73a9ac9279SLei YU     }
74a9ac9279SLei YU     if ((filePath.empty()) || (purpose == VersionPurpose::Unknown))
75a9ac9279SLei YU     {
76a9ac9279SLei YU         return;
77a9ac9279SLei YU     }
78a9ac9279SLei YU 
79a9ac9279SLei YU     // Version id is the last item in the path
80a9ac9279SLei YU     auto pos = path.rfind("/");
81a9ac9279SLei YU     if (pos == std::string::npos)
82a9ac9279SLei YU     {
83a9ac9279SLei YU         log<level::ERR>("No version id found in object path",
84a9ac9279SLei YU                         entry("OBJPATH=%s", path.c_str()));
85a9ac9279SLei YU         return;
86a9ac9279SLei YU     }
87a9ac9279SLei YU 
88a9ac9279SLei YU     auto versionId = path.substr(pos + 1);
89a9ac9279SLei YU 
90a9ac9279SLei YU     if (activations.find(versionId) == activations.end())
91a9ac9279SLei YU     {
92a9ac9279SLei YU         // Determine the Activation state by processing the given image dir.
93a9ac9279SLei YU         auto activationState = server::Activation::Activations::Invalid;
94a9ac9279SLei YU         AssociationList associations = {};
95a9ac9279SLei YU         if (validateImage(filePath))
96a9ac9279SLei YU         {
97a9ac9279SLei YU             activationState = server::Activation::Activations::Ready;
98a9ac9279SLei YU             // Create an association to the host inventory item
99a9ac9279SLei YU             associations.emplace_back(std::make_tuple(
100a9ac9279SLei YU                 ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
101a9ac9279SLei YU                 HOST_INVENTORY_PATH));
102a9ac9279SLei YU         }
103a9ac9279SLei YU 
104a9ac9279SLei YU         fs::path manifestPath(filePath);
105a9ac9279SLei YU         manifestPath /= MANIFEST_FILE;
106a9ac9279SLei YU         std::string extendedVersion =
107a9ac9279SLei YU             (Version::getValue(
108a9ac9279SLei YU                  manifestPath.string(),
109a9ac9279SLei YU                  std::map<std::string, std::string>{{"extended_version", ""}}))
110a9ac9279SLei YU                 .begin()
111a9ac9279SLei YU                 ->second;
112a9ac9279SLei YU 
113a9ac9279SLei YU         auto activation = createActivationObject(
114a9ac9279SLei YU             path, versionId, extendedVersion, activationState, associations);
115a9ac9279SLei YU         activations.emplace(versionId, std::move(activation));
116a9ac9279SLei YU 
117*7fb6c346SPatrick Williams         auto versionPtr = createVersionObject(path, versionId, version, purpose,
118*7fb6c346SPatrick Williams                                               filePath);
119a9ac9279SLei YU         versions.emplace(versionId, std::move(versionPtr));
120a9ac9279SLei YU     }
121a9ac9279SLei YU     return;
122a9ac9279SLei YU }
123a9ac9279SLei YU 
createActiveAssociation(const std::string & path)124f3ce4337SLei YU void ItemUpdater::createActiveAssociation(const std::string& path)
1252d8fa225SAdriana Kobylak {
126f3ce4337SLei YU     assocs.emplace_back(
127f3ce4337SLei YU         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
128f3ce4337SLei YU     associations(assocs);
129b66ac3aeSAdriana Kobylak }
130b66ac3aeSAdriana Kobylak 
createUpdateableAssociation(const std::string & path)1313c81037eSAdriana Kobylak void ItemUpdater::createUpdateableAssociation(const std::string& path)
1323c81037eSAdriana Kobylak {
1333c81037eSAdriana Kobylak     assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION,
1343c81037eSAdriana Kobylak                                         UPDATEABLE_REV_ASSOCIATION, path));
1353c81037eSAdriana Kobylak     associations(assocs);
1363c81037eSAdriana Kobylak }
1373c81037eSAdriana Kobylak 
updateFunctionalAssociation(const std::string & versionId)138f3ce4337SLei YU void ItemUpdater::updateFunctionalAssociation(const std::string& versionId)
139b66ac3aeSAdriana Kobylak {
140f3ce4337SLei YU     std::string path = std::string{SOFTWARE_OBJPATH} + '/' + versionId;
141f3ce4337SLei YU     // remove all functional associations
142f3ce4337SLei YU     for (auto iter = assocs.begin(); iter != assocs.end();)
1432fdb9310SAdriana Kobylak     {
144f3ce4337SLei YU         if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
145a8ade7e4SSaqib Khan         {
146f3ce4337SLei YU             iter = assocs.erase(iter);
147a8ade7e4SSaqib Khan         }
148a8ade7e4SSaqib Khan         else
149a8ade7e4SSaqib Khan         {
150f3ce4337SLei YU             ++iter;
151a8ade7e4SSaqib Khan         }
152a8ade7e4SSaqib Khan     }
153f3ce4337SLei YU     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
154f3ce4337SLei YU                                         FUNCTIONAL_REV_ASSOCIATION, path));
155f3ce4337SLei YU     associations(assocs);
1569c8adfa3SLeonel Gonzalez }
1579c8adfa3SLeonel Gonzalez 
removeAssociation(const std::string & path)158f3ce4337SLei YU void ItemUpdater::removeAssociation(const std::string& path)
159dd961b6cSMichael Tritz {
160f3ce4337SLei YU     for (auto iter = assocs.begin(); iter != assocs.end();)
161f3ce4337SLei YU     {
162f3ce4337SLei YU         if ((std::get<2>(*iter)).compare(path) == 0)
163f3ce4337SLei YU         {
164f3ce4337SLei YU             iter = assocs.erase(iter);
165f3ce4337SLei YU             associations(assocs);
166dd961b6cSMichael Tritz         }
167f3ce4337SLei YU         else
1689c8adfa3SLeonel Gonzalez         {
169f3ce4337SLei YU             ++iter;
170dd961b6cSMichael Tritz         }
1719c8adfa3SLeonel Gonzalez     }
172ca9ba069SAdriana Kobylak }
173ca9ba069SAdriana Kobylak 
erase(std::string entryId)174f3ce4337SLei YU bool ItemUpdater::erase(std::string entryId)
175fa7aa12cSMichael Tritz {
176f3ce4337SLei YU     if (isVersionFunctional(entryId) && isChassisOn())
177ca9ba069SAdriana Kobylak     {
178f3ce4337SLei YU         log<level::ERR>(("Error: Version " + entryId +
179f3ce4337SLei YU                          " is currently active and running on the host."
180f3ce4337SLei YU                          " Unable to remove.")
181f3ce4337SLei YU                             .c_str());
18213fc66adSEddie James         return false;
18313fc66adSEddie James     }
18413fc66adSEddie James 
185f3ce4337SLei YU     // Removing entry in versions map
186f3ce4337SLei YU     auto it = versions.find(entryId);
187f3ce4337SLei YU     if (it == versions.end())
18813fc66adSEddie James     {
189f3ce4337SLei YU         log<level::ERR>(("Error: Failed to find version " + entryId +
190f3ce4337SLei YU                          " in item updater versions map."
191f3ce4337SLei YU                          " Unable to remove.")
192f3ce4337SLei YU                             .c_str());
193f3ce4337SLei YU     }
194f3ce4337SLei YU     else
195f3ce4337SLei YU     {
196f3ce4337SLei YU         versions.erase(entryId);
19713fc66adSEddie James     }
19813fc66adSEddie James 
199f3ce4337SLei YU     // Removing entry in activations map
200f3ce4337SLei YU     auto ita = activations.find(entryId);
201f3ce4337SLei YU     if (ita == activations.end())
20213fc66adSEddie James     {
203f3ce4337SLei YU         log<level::ERR>(("Error: Failed to find version " + entryId +
204f3ce4337SLei YU                          " in item updater activations map."
205f3ce4337SLei YU                          " Unable to remove.")
206f3ce4337SLei YU                             .c_str());
20713fc66adSEddie James     }
208f3ce4337SLei YU     else
209f3ce4337SLei YU     {
210f3ce4337SLei YU         removeAssociation(ita->second->path);
211f3ce4337SLei YU         activations.erase(entryId);
212f3ce4337SLei YU     }
21313fc66adSEddie James     return true;
21413fc66adSEddie James }
21513fc66adSEddie James 
isChassisOn()21613fc66adSEddie James bool ItemUpdater::isChassisOn()
21713fc66adSEddie James {
21870dcb63aSAdriana Kobylak     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
21970dcb63aSAdriana Kobylak                                           MAPPER_INTERFACE, "GetObject");
22013fc66adSEddie James 
22113fc66adSEddie James     mapperCall.append(CHASSIS_STATE_PATH,
22213fc66adSEddie James                       std::vector<std::string>({CHASSIS_STATE_OBJ}));
223b8cb0cc9SAdriana Kobylak 
224b8cb0cc9SAdriana Kobylak     std::map<std::string, std::vector<std::string>> mapperResponse;
225b8cb0cc9SAdriana Kobylak 
226b8cb0cc9SAdriana Kobylak     try
22713fc66adSEddie James     {
228b8cb0cc9SAdriana Kobylak         auto mapperResponseMsg = bus.call(mapperCall);
22913fc66adSEddie James         mapperResponseMsg.read(mapperResponse);
23013fc66adSEddie James         if (mapperResponse.empty())
23113fc66adSEddie James         {
23213fc66adSEddie James             log<level::ERR>("Invalid Response from mapper");
23313fc66adSEddie James             elog<InternalFailure>();
23413fc66adSEddie James         }
235b8cb0cc9SAdriana Kobylak     }
2360dea1992SPatrick Williams     catch (const sdbusplus::exception_t& e)
237b8cb0cc9SAdriana Kobylak     {
238b8cb0cc9SAdriana Kobylak         log<level::ERR>("Error in Mapper call");
239b8cb0cc9SAdriana Kobylak         elog<InternalFailure>();
240b8cb0cc9SAdriana Kobylak     }
24113fc66adSEddie James 
24213fc66adSEddie James     auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(),
24313fc66adSEddie James                                       CHASSIS_STATE_PATH,
24470dcb63aSAdriana Kobylak                                       SYSTEMD_PROPERTY_INTERFACE, "Get");
24513fc66adSEddie James     method.append(CHASSIS_STATE_OBJ, "CurrentPowerState");
246b8cb0cc9SAdriana Kobylak 
247212102e6SPatrick Williams     std::variant<std::string> currentChassisState;
248b8cb0cc9SAdriana Kobylak 
249b8cb0cc9SAdriana Kobylak     try
250b8cb0cc9SAdriana Kobylak     {
25113fc66adSEddie James         auto response = bus.call(method);
252b8cb0cc9SAdriana Kobylak         response.read(currentChassisState);
253550f31b3SPatrick Williams         auto strParam = std::get<std::string>(currentChassisState);
254b8cb0cc9SAdriana Kobylak         return (strParam != CHASSIS_STATE_OFF);
255b8cb0cc9SAdriana Kobylak     }
2560dea1992SPatrick Williams     catch (const sdbusplus::exception_t& e)
25713fc66adSEddie James     {
25813fc66adSEddie James         log<level::ERR>("Error in fetching current Chassis State",
259850d5f6bSGunnar Mills                         entry("MAPPERRESPONSE=%s",
26013fc66adSEddie James                               (mapperResponse.begin()->first).c_str()));
26113fc66adSEddie James         elog<InternalFailure>();
26213fc66adSEddie James     }
26313fc66adSEddie James }
26413fc66adSEddie James 
265befe5ce4SAdriana Kobylak } // namespace updater
2662d8fa225SAdriana Kobylak } // namespace software
2672d8fa225SAdriana Kobylak } // namespace openpower
268