1 #include "config.h"
2 
3 #include "item_updater.hpp"
4 
5 #include "xyz/openbmc_project/Common/error.hpp"
6 
7 #include <phosphor-logging/elog-errors.hpp>
8 #include <phosphor-logging/log.hpp>
9 
10 #include <filesystem>
11 
12 namespace openpower
13 {
14 namespace software
15 {
16 namespace updater
17 {
18 namespace server = sdbusplus::xyz::openbmc_project::Software::server;
19 namespace fs = std::filesystem;
20 
21 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
22 using namespace phosphor::logging;
23 
24 void ItemUpdater::createActivation(sdbusplus::message::message& m)
25 {
26     using SVersion = server::Version;
27     using VersionPurpose = SVersion::VersionPurpose;
28 
29     sdbusplus::message::object_path objPath;
30     std::map<std::string, std::map<std::string, std::variant<std::string>>>
31         interfaces;
32     m.read(objPath, interfaces);
33 
34     std::string path(std::move(objPath));
35     std::string filePath;
36     auto purpose = VersionPurpose::Unknown;
37     std::string version;
38 
39     for (const auto& intf : interfaces)
40     {
41         if (intf.first == VERSION_IFACE)
42         {
43             for (const auto& property : intf.second)
44             {
45                 if (property.first == "Purpose")
46                 {
47                     // Only process the Host and System images
48                     auto value = SVersion::convertVersionPurposeFromString(
49                         std::get<std::string>(property.second));
50 
51                     if (value == VersionPurpose::Host ||
52                         value == VersionPurpose::System)
53                     {
54                         purpose = value;
55                     }
56                 }
57                 else if (property.first == "Version")
58                 {
59                     version = std::get<std::string>(property.second);
60                 }
61             }
62         }
63         else if (intf.first == FILEPATH_IFACE)
64         {
65             for (const auto& property : intf.second)
66             {
67                 if (property.first == "Path")
68                 {
69                     filePath = std::get<std::string>(property.second);
70                 }
71             }
72         }
73     }
74     if ((filePath.empty()) || (purpose == VersionPurpose::Unknown))
75     {
76         return;
77     }
78 
79     // Version id is the last item in the path
80     auto pos = path.rfind("/");
81     if (pos == std::string::npos)
82     {
83         log<level::ERR>("No version id found in object path",
84                         entry("OBJPATH=%s", path.c_str()));
85         return;
86     }
87 
88     auto versionId = path.substr(pos + 1);
89 
90     if (activations.find(versionId) == activations.end())
91     {
92         // Determine the Activation state by processing the given image dir.
93         auto activationState = server::Activation::Activations::Invalid;
94         AssociationList associations = {};
95         if (validateImage(filePath))
96         {
97             activationState = server::Activation::Activations::Ready;
98             // Create an association to the host inventory item
99             associations.emplace_back(std::make_tuple(
100                 ACTIVATION_FWD_ASSOCIATION, ACTIVATION_REV_ASSOCIATION,
101                 HOST_INVENTORY_PATH));
102         }
103 
104         fs::path manifestPath(filePath);
105         manifestPath /= MANIFEST_FILE;
106         std::string extendedVersion =
107             (Version::getValue(
108                  manifestPath.string(),
109                  std::map<std::string, std::string>{{"extended_version", ""}}))
110                 .begin()
111                 ->second;
112 
113         auto activation = createActivationObject(
114             path, versionId, extendedVersion, activationState, associations);
115         activations.emplace(versionId, std::move(activation));
116 
117         auto versionPtr =
118             createVersionObject(path, versionId, version, purpose, filePath);
119         versions.emplace(versionId, std::move(versionPtr));
120     }
121     return;
122 }
123 
124 void ItemUpdater::createActiveAssociation(const std::string& path)
125 {
126     assocs.emplace_back(
127         std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
128     associations(assocs);
129 }
130 
131 void ItemUpdater::createUpdateableAssociation(const std::string& path)
132 {
133     assocs.emplace_back(std::make_tuple(UPDATEABLE_FWD_ASSOCIATION,
134                                         UPDATEABLE_REV_ASSOCIATION, path));
135     associations(assocs);
136 }
137 
138 void ItemUpdater::updateFunctionalAssociation(const std::string& versionId)
139 {
140     std::string path = std::string{SOFTWARE_OBJPATH} + '/' + versionId;
141     // remove all functional associations
142     for (auto iter = assocs.begin(); iter != assocs.end();)
143     {
144         if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
145         {
146             iter = assocs.erase(iter);
147         }
148         else
149         {
150             ++iter;
151         }
152     }
153     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
154                                         FUNCTIONAL_REV_ASSOCIATION, path));
155     associations(assocs);
156 }
157 
158 void ItemUpdater::removeAssociation(const std::string& path)
159 {
160     for (auto iter = assocs.begin(); iter != assocs.end();)
161     {
162         if ((std::get<2>(*iter)).compare(path) == 0)
163         {
164             iter = assocs.erase(iter);
165             associations(assocs);
166         }
167         else
168         {
169             ++iter;
170         }
171     }
172 }
173 
174 bool ItemUpdater::erase(std::string entryId)
175 {
176     if (isVersionFunctional(entryId) && isChassisOn())
177     {
178         log<level::ERR>(("Error: Version " + entryId +
179                          " is currently active and running on the host."
180                          " Unable to remove.")
181                             .c_str());
182         return false;
183     }
184 
185     // Removing entry in versions map
186     auto it = versions.find(entryId);
187     if (it == versions.end())
188     {
189         log<level::ERR>(("Error: Failed to find version " + entryId +
190                          " in item updater versions map."
191                          " Unable to remove.")
192                             .c_str());
193     }
194     else
195     {
196         versions.erase(entryId);
197     }
198 
199     // Removing entry in activations map
200     auto ita = activations.find(entryId);
201     if (ita == activations.end())
202     {
203         log<level::ERR>(("Error: Failed to find version " + entryId +
204                          " in item updater activations map."
205                          " Unable to remove.")
206                             .c_str());
207     }
208     else
209     {
210         removeAssociation(ita->second->path);
211         activations.erase(entryId);
212     }
213     return true;
214 }
215 
216 bool ItemUpdater::isChassisOn()
217 {
218     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
219                                           MAPPER_INTERFACE, "GetObject");
220 
221     mapperCall.append(CHASSIS_STATE_PATH,
222                       std::vector<std::string>({CHASSIS_STATE_OBJ}));
223 
224     std::map<std::string, std::vector<std::string>> mapperResponse;
225 
226     try
227     {
228         auto mapperResponseMsg = bus.call(mapperCall);
229         mapperResponseMsg.read(mapperResponse);
230         if (mapperResponse.empty())
231         {
232             log<level::ERR>("Invalid Response from mapper");
233             elog<InternalFailure>();
234         }
235     }
236     catch (const sdbusplus::exception::exception& e)
237     {
238         log<level::ERR>("Error in Mapper call");
239         elog<InternalFailure>();
240     }
241 
242     auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(),
243                                       CHASSIS_STATE_PATH,
244                                       SYSTEMD_PROPERTY_INTERFACE, "Get");
245     method.append(CHASSIS_STATE_OBJ, "CurrentPowerState");
246 
247     std::variant<std::string> currentChassisState;
248 
249     try
250     {
251         auto response = bus.call(method);
252         response.read(currentChassisState);
253         auto strParam = std::get<std::string>(currentChassisState);
254         return (strParam != CHASSIS_STATE_OFF);
255     }
256     catch (const sdbusplus::exception::exception& e)
257     {
258         log<level::ERR>("Error in fetching current Chassis State",
259                         entry("MAPPERRESPONSE=%s",
260                               (mapperResponse.begin()->first).c_str()));
261         elog<InternalFailure>();
262     }
263 }
264 
265 } // namespace updater
266 } // namespace software
267 } // namespace openpower
268