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