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::updateFunctionalAssociation(const std::string& versionId)
131 {
132     std::string path = std::string{SOFTWARE_OBJPATH} + '/' + versionId;
133     // remove all functional associations
134     for (auto iter = assocs.begin(); iter != assocs.end();)
135     {
136         if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
137         {
138             iter = assocs.erase(iter);
139         }
140         else
141         {
142             ++iter;
143         }
144     }
145     assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
146                                         FUNCTIONAL_REV_ASSOCIATION, path));
147     associations(assocs);
148 }
149 
150 void ItemUpdater::removeAssociation(const std::string& path)
151 {
152     for (auto iter = assocs.begin(); iter != assocs.end();)
153     {
154         if ((std::get<2>(*iter)).compare(path) == 0)
155         {
156             iter = assocs.erase(iter);
157             associations(assocs);
158         }
159         else
160         {
161             ++iter;
162         }
163     }
164 }
165 
166 bool ItemUpdater::erase(std::string entryId)
167 {
168     if (isVersionFunctional(entryId) && isChassisOn())
169     {
170         log<level::ERR>(("Error: Version " + entryId +
171                          " is currently active and running on the host."
172                          " Unable to remove.")
173                             .c_str());
174         return false;
175     }
176 
177     // Removing entry in versions map
178     auto it = versions.find(entryId);
179     if (it == versions.end())
180     {
181         log<level::ERR>(("Error: Failed to find version " + entryId +
182                          " in item updater versions map."
183                          " Unable to remove.")
184                             .c_str());
185     }
186     else
187     {
188         versions.erase(entryId);
189     }
190 
191     // Removing entry in activations map
192     auto ita = activations.find(entryId);
193     if (ita == activations.end())
194     {
195         log<level::ERR>(("Error: Failed to find version " + entryId +
196                          " in item updater activations map."
197                          " Unable to remove.")
198                             .c_str());
199     }
200     else
201     {
202         removeAssociation(ita->second->path);
203         activations.erase(entryId);
204     }
205     return true;
206 }
207 
208 bool ItemUpdater::isChassisOn()
209 {
210     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
211                                           MAPPER_INTERFACE, "GetObject");
212 
213     mapperCall.append(CHASSIS_STATE_PATH,
214                       std::vector<std::string>({CHASSIS_STATE_OBJ}));
215 
216     std::map<std::string, std::vector<std::string>> mapperResponse;
217 
218     try
219     {
220         auto mapperResponseMsg = bus.call(mapperCall);
221         mapperResponseMsg.read(mapperResponse);
222         if (mapperResponse.empty())
223         {
224             log<level::ERR>("Invalid Response from mapper");
225             elog<InternalFailure>();
226         }
227     }
228     catch (const sdbusplus::exception::SdBusError& e)
229     {
230         log<level::ERR>("Error in Mapper call");
231         elog<InternalFailure>();
232     }
233 
234     auto method = bus.new_method_call((mapperResponse.begin()->first).c_str(),
235                                       CHASSIS_STATE_PATH,
236                                       SYSTEMD_PROPERTY_INTERFACE, "Get");
237     method.append(CHASSIS_STATE_OBJ, "CurrentPowerState");
238 
239     std::variant<std::string> currentChassisState;
240 
241     try
242     {
243         auto response = bus.call(method);
244         response.read(currentChassisState);
245         auto strParam = std::get<std::string>(currentChassisState);
246         return (strParam != CHASSIS_STATE_OFF);
247     }
248     catch (const sdbusplus::exception::SdBusError& e)
249     {
250         log<level::ERR>("Error in fetching current Chassis State",
251                         entry("MAPPERRESPONSE=%s",
252                               (mapperResponse.begin()->first).c_str()));
253         elog<InternalFailure>();
254     }
255 }
256 
257 } // namespace updater
258 } // namespace software
259 } // namespace openpower
260