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
createActivation(sdbusplus::message_t & m)24 void ItemUpdater::createActivation(sdbusplus::message_t& 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
createActiveAssociation(const std::string & path)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
createUpdateableAssociation(const std::string & path)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
updateFunctionalAssociation(const std::string & versionId)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
removeAssociation(const std::string & path)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
erase(std::string entryId)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
isChassisOn()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_t& 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_t& 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