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