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 
7a9ac9279SLei YU #include <filesystem>
8f6ed5897SGunnar Mills #include <phosphor-logging/elog-errors.hpp>
9f6ed5897SGunnar Mills #include <phosphor-logging/log.hpp>
102d8fa225SAdriana Kobylak 
112d8fa225SAdriana Kobylak namespace openpower
122d8fa225SAdriana Kobylak {
132d8fa225SAdriana Kobylak namespace software
142d8fa225SAdriana Kobylak {
15befe5ce4SAdriana Kobylak namespace updater
162d8fa225SAdriana Kobylak {
17a9ac9279SLei YU namespace server = sdbusplus::xyz::openbmc_project::Software::server;
18a9ac9279SLei YU namespace fs = std::filesystem;
19a9ac9279SLei YU 
2013fc66adSEddie James using namespace sdbusplus::xyz::openbmc_project::Common::Error;
21b66ac3aeSAdriana Kobylak using namespace phosphor::logging;
22b66ac3aeSAdriana Kobylak 
23a9ac9279SLei YU void ItemUpdater::createActivation(sdbusplus::message::message& m)
24a9ac9279SLei YU {
25a9ac9279SLei YU     using SVersion = server::Version;
26a9ac9279SLei YU     using VersionPurpose = SVersion::VersionPurpose;
27a9ac9279SLei YU 
28a9ac9279SLei YU     sdbusplus::message::object_path objPath;
29*573552aeSPatrick Williams     std::map<std::string, std::map<std::string, std::variant<std::string>>>
30a9ac9279SLei YU         interfaces;
31a9ac9279SLei YU     m.read(objPath, interfaces);
32a9ac9279SLei YU 
33a9ac9279SLei YU     std::string path(std::move(objPath));
34a9ac9279SLei YU     std::string filePath;
35a9ac9279SLei YU     auto purpose = VersionPurpose::Unknown;
36a9ac9279SLei YU     std::string version;
37a9ac9279SLei YU 
38a9ac9279SLei YU     for (const auto& intf : interfaces)
39a9ac9279SLei YU     {
40a9ac9279SLei YU         if (intf.first == VERSION_IFACE)
41a9ac9279SLei YU         {
42a9ac9279SLei YU             for (const auto& property : intf.second)
43a9ac9279SLei YU             {
44a9ac9279SLei YU                 if (property.first == "Purpose")
45a9ac9279SLei YU                 {
46a9ac9279SLei YU                     // Only process the Host and System images
47a9ac9279SLei YU                     auto value = SVersion::convertVersionPurposeFromString(
48550f31b3SPatrick Williams                         std::get<std::string>(property.second));
49a9ac9279SLei YU 
50a9ac9279SLei YU                     if (value == VersionPurpose::Host ||
51a9ac9279SLei YU                         value == VersionPurpose::System)
52a9ac9279SLei YU                     {
53a9ac9279SLei YU                         purpose = value;
54a9ac9279SLei YU                     }
55a9ac9279SLei YU                 }
56a9ac9279SLei YU                 else if (property.first == "Version")
57a9ac9279SLei YU                 {
58550f31b3SPatrick Williams                     version = std::get<std::string>(property.second);
59a9ac9279SLei YU                 }
60a9ac9279SLei YU             }
61a9ac9279SLei YU         }
62a9ac9279SLei YU         else if (intf.first == FILEPATH_IFACE)
63a9ac9279SLei YU         {
64a9ac9279SLei YU             for (const auto& property : intf.second)
65a9ac9279SLei YU             {
66a9ac9279SLei YU                 if (property.first == "Path")
67a9ac9279SLei YU                 {
68550f31b3SPatrick Williams                     filePath = std::get<std::string>(property.second);
69a9ac9279SLei YU                 }
70a9ac9279SLei YU             }
71a9ac9279SLei YU         }
72a9ac9279SLei YU     }
73a9ac9279SLei YU     if ((filePath.empty()) || (purpose == VersionPurpose::Unknown))
74a9ac9279SLei YU     {
75a9ac9279SLei YU         return;
76a9ac9279SLei YU     }
77a9ac9279SLei YU 
78a9ac9279SLei YU     // Version id is the last item in the path
79a9ac9279SLei YU     auto pos = path.rfind("/");
80a9ac9279SLei YU     if (pos == std::string::npos)
81a9ac9279SLei YU     {
82a9ac9279SLei YU         log<level::ERR>("No version id found in object path",
83a9ac9279SLei YU                         entry("OBJPATH=%s", path.c_str()));
84a9ac9279SLei YU         return;
85a9ac9279SLei YU     }
86a9ac9279SLei YU 
87a9ac9279SLei YU     auto versionId = path.substr(pos + 1);
88a9ac9279SLei YU 
89a9ac9279SLei YU     if (activations.find(versionId) == activations.end())
90a9ac9279SLei YU     {
91a9ac9279SLei YU         // Determine the Activation state by processing the given image dir.
92a9ac9279SLei YU         auto activationState = server::Activation::Activations::Invalid;
93a9ac9279SLei YU         AssociationList associations = {};
94a9ac9279SLei YU         if (validateImage(filePath))
95a9ac9279SLei YU         {
96a9ac9279SLei YU             activationState = server::Activation::Activations::Ready;
97a9ac9279SLei YU             // Create an association to the host inventory item
98a9ac9279SLei YU             associations.emplace_back(std::make_tuple(
99a9ac9279SLei YU                 ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
100a9ac9279SLei YU                 HOST_INVENTORY_PATH));
101a9ac9279SLei YU         }
102a9ac9279SLei YU 
103a9ac9279SLei YU         fs::path manifestPath(filePath);
104a9ac9279SLei YU         manifestPath /= MANIFEST_FILE;
105a9ac9279SLei YU         std::string extendedVersion =
106a9ac9279SLei YU             (Version::getValue(
107a9ac9279SLei YU                  manifestPath.string(),
108a9ac9279SLei YU                  std::map<std::string, std::string>{{"extended_version", ""}}))
109a9ac9279SLei YU                 .begin()
110a9ac9279SLei YU                 ->second;
111a9ac9279SLei YU 
112a9ac9279SLei YU         auto activation = createActivationObject(
113a9ac9279SLei YU             path, versionId, extendedVersion, activationState, associations);
114a9ac9279SLei YU         activations.emplace(versionId, std::move(activation));
115a9ac9279SLei YU 
116a9ac9279SLei YU         auto versionPtr =
117a9ac9279SLei YU             createVersionObject(path, versionId, version, purpose, filePath);
118a9ac9279SLei YU         versions.emplace(versionId, std::move(versionPtr));
119a9ac9279SLei YU     }
120a9ac9279SLei YU     return;
121a9ac9279SLei YU }
122a9ac9279SLei YU 
123f3ce4337SLei YU void ItemUpdater::createActiveAssociation(const std::string& path)
1242d8fa225SAdriana Kobylak {
125f3ce4337SLei YU     assocs.emplace_back(
126f3ce4337SLei YU         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
127f3ce4337SLei YU     associations(assocs);
128b66ac3aeSAdriana Kobylak }
129b66ac3aeSAdriana Kobylak 
130f3ce4337SLei YU void ItemUpdater::updateFunctionalAssociation(const std::string& versionId)
131b66ac3aeSAdriana Kobylak {
132f3ce4337SLei YU     std::string path = std::string{SOFTWARE_OBJPATH} + '/' + versionId;
133f3ce4337SLei YU     // remove all functional associations
134f3ce4337SLei YU     for (auto iter = assocs.begin(); iter != assocs.end();)
1352fdb9310SAdriana Kobylak     {
136f3ce4337SLei YU         if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
137a8ade7e4SSaqib Khan         {
138f3ce4337SLei YU             iter = assocs.erase(iter);
139a8ade7e4SSaqib Khan         }
140a8ade7e4SSaqib Khan         else
141a8ade7e4SSaqib Khan         {
142f3ce4337SLei YU             ++iter;
143a8ade7e4SSaqib Khan         }
144a8ade7e4SSaqib Khan     }
145f3ce4337SLei YU     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
146f3ce4337SLei YU                                         FUNCTIONAL_REV_ASSOCIATION, path));
147f3ce4337SLei YU     associations(assocs);
1489c8adfa3SLeonel Gonzalez }
1499c8adfa3SLeonel Gonzalez 
150f3ce4337SLei YU void ItemUpdater::removeAssociation(const std::string& path)
151dd961b6cSMichael Tritz {
152f3ce4337SLei YU     for (auto iter = assocs.begin(); iter != assocs.end();)
153f3ce4337SLei YU     {
154f3ce4337SLei YU         if ((std::get<2>(*iter)).compare(path) == 0)
155f3ce4337SLei YU         {
156f3ce4337SLei YU             iter = assocs.erase(iter);
157f3ce4337SLei YU             associations(assocs);
158dd961b6cSMichael Tritz         }
159f3ce4337SLei YU         else
1609c8adfa3SLeonel Gonzalez         {
161f3ce4337SLei YU             ++iter;
162dd961b6cSMichael Tritz         }
1639c8adfa3SLeonel Gonzalez     }
164ca9ba069SAdriana Kobylak }
165ca9ba069SAdriana Kobylak 
166f3ce4337SLei YU bool ItemUpdater::erase(std::string entryId)
167fa7aa12cSMichael Tritz {
168f3ce4337SLei YU     if (isVersionFunctional(entryId) && isChassisOn())
169ca9ba069SAdriana Kobylak     {
170f3ce4337SLei YU         log<level::ERR>(("Error: Version " + entryId +
171f3ce4337SLei YU                          " is currently active and running on the host."
172f3ce4337SLei YU                          " Unable to remove.")
173f3ce4337SLei YU                             .c_str());
17413fc66adSEddie James         return false;
17513fc66adSEddie James     }
17613fc66adSEddie James 
177f3ce4337SLei YU     // Removing entry in versions map
178f3ce4337SLei YU     auto it = versions.find(entryId);
179f3ce4337SLei YU     if (it == versions.end())
18013fc66adSEddie James     {
181f3ce4337SLei YU         log<level::ERR>(("Error: Failed to find version " + entryId +
182f3ce4337SLei YU                          " in item updater versions map."
183f3ce4337SLei YU                          " Unable to remove.")
184f3ce4337SLei YU                             .c_str());
185f3ce4337SLei YU     }
186f3ce4337SLei YU     else
187f3ce4337SLei YU     {
188f3ce4337SLei YU         versions.erase(entryId);
18913fc66adSEddie James     }
19013fc66adSEddie James 
191f3ce4337SLei YU     // Removing entry in activations map
192f3ce4337SLei YU     auto ita = activations.find(entryId);
193f3ce4337SLei YU     if (ita == activations.end())
19413fc66adSEddie James     {
195f3ce4337SLei YU         log<level::ERR>(("Error: Failed to find version " + entryId +
196f3ce4337SLei YU                          " in item updater activations map."
197f3ce4337SLei YU                          " Unable to remove.")
198f3ce4337SLei YU                             .c_str());
19913fc66adSEddie James     }
200f3ce4337SLei YU     else
201f3ce4337SLei YU     {
202f3ce4337SLei YU         removeAssociation(ita->second->path);
203f3ce4337SLei YU         activations.erase(entryId);
204f3ce4337SLei YU     }
20513fc66adSEddie James     return true;
20613fc66adSEddie James }
20713fc66adSEddie James 
20813fc66adSEddie James bool ItemUpdater::isChassisOn()
20913fc66adSEddie James {
21070dcb63aSAdriana Kobylak     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
21170dcb63aSAdriana Kobylak                                           MAPPER_INTERFACE, "GetObject");
21213fc66adSEddie James 
21313fc66adSEddie James     mapperCall.append(CHASSIS_STATE_PATH,
21413fc66adSEddie James                       std::vector<std::string>({CHASSIS_STATE_OBJ}));
215b8cb0cc9SAdriana Kobylak 
216b8cb0cc9SAdriana Kobylak     std::map<std::string, std::vector<std::string>> mapperResponse;
217b8cb0cc9SAdriana Kobylak 
218b8cb0cc9SAdriana Kobylak     try
21913fc66adSEddie James     {
220b8cb0cc9SAdriana Kobylak         auto mapperResponseMsg = bus.call(mapperCall);
22113fc66adSEddie James         mapperResponseMsg.read(mapperResponse);
22213fc66adSEddie James         if (mapperResponse.empty())
22313fc66adSEddie James         {
22413fc66adSEddie James             log<level::ERR>("Invalid Response from mapper");
22513fc66adSEddie James             elog<InternalFailure>();
22613fc66adSEddie James         }
227b8cb0cc9SAdriana Kobylak     }
228b8cb0cc9SAdriana Kobylak     catch (const sdbusplus::exception::SdBusError& e)
229b8cb0cc9SAdriana Kobylak     {
230b8cb0cc9SAdriana Kobylak         log<level::ERR>("Error in Mapper call");
231b8cb0cc9SAdriana Kobylak         elog<InternalFailure>();
232b8cb0cc9SAdriana Kobylak     }
23313fc66adSEddie James 
23413fc66adSEddie James     auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(),
23513fc66adSEddie James                                       CHASSIS_STATE_PATH,
23670dcb63aSAdriana Kobylak                                       SYSTEMD_PROPERTY_INTERFACE, "Get");
23713fc66adSEddie James     method.append(CHASSIS_STATE_OBJ, "CurrentPowerState");
238b8cb0cc9SAdriana Kobylak 
239212102e6SPatrick Williams     std::variant<std::string> currentChassisState;
240b8cb0cc9SAdriana Kobylak 
241b8cb0cc9SAdriana Kobylak     try
242b8cb0cc9SAdriana Kobylak     {
24313fc66adSEddie James         auto response = bus.call(method);
244b8cb0cc9SAdriana Kobylak         response.read(currentChassisState);
245550f31b3SPatrick Williams         auto strParam = std::get<std::string>(currentChassisState);
246b8cb0cc9SAdriana Kobylak         return (strParam != CHASSIS_STATE_OFF);
247b8cb0cc9SAdriana Kobylak     }
248b8cb0cc9SAdriana Kobylak     catch (const sdbusplus::exception::SdBusError& e)
24913fc66adSEddie James     {
25013fc66adSEddie James         log<level::ERR>("Error in fetching current Chassis State",
251850d5f6bSGunnar Mills                         entry("MAPPERRESPONSE=%s",
25213fc66adSEddie James                               (mapperResponse.begin()->first).c_str()));
25313fc66adSEddie James         elog<InternalFailure>();
25413fc66adSEddie James     }
25513fc66adSEddie James }
25613fc66adSEddie James 
257befe5ce4SAdriana Kobylak } // namespace updater
2582d8fa225SAdriana Kobylak } // namespace software
2592d8fa225SAdriana Kobylak } // namespace openpower
260