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