1 #include "config.h"
2 
3 #include "activation.hpp"
4 
5 #include "item_updater.hpp"
6 
7 #include <experimental/filesystem>
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/elog.hpp>
10 #include <phosphor-logging/log.hpp>
11 #include <sdbusplus/exception.hpp>
12 #include <sdbusplus/server.hpp>
13 #include <xyz/openbmc_project/Common/error.hpp>
14 
15 #ifdef WANT_SIGNATURE_VERIFY
16 #include "image_verify.hpp"
17 #endif
18 
19 namespace openpower
20 {
21 namespace software
22 {
23 namespace updater
24 {
25 
26 namespace fs = std::experimental::filesystem;
27 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
28 
29 using namespace phosphor::logging;
30 using sdbusplus::exception::SdBusError;
31 using InternalFailure =
32     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
33 
34 #ifdef WANT_SIGNATURE_VERIFY
35 // Field mode path and interface.
36 constexpr auto FIELDMODE_PATH("/xyz/openbmc_project/software");
37 constexpr auto FIELDMODE_INTERFACE("xyz.openbmc_project.Control.FieldMode");
38 #endif
39 
40 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
41 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
42 
43 void Activation::subscribeToSystemdSignals()
44 {
45     auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
46                                             SYSTEMD_INTERFACE, "Subscribe");
47     try
48     {
49         this->bus.call_noreply(method);
50     }
51     catch (const SdBusError& e)
52     {
53         if (e.name() != nullptr &&
54             strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0)
55         {
56             // If an Activation attempt fails, the Unsubscribe method is not
57             // called. This may lead to an AlreadySubscribed error if the
58             // Activation is re-attempted.
59         }
60         else
61         {
62             log<level::ERR>("Error subscribing to systemd",
63                             entry("ERROR=%s", e.what()));
64         }
65     }
66     return;
67 }
68 
69 void Activation::unsubscribeFromSystemdSignals()
70 {
71     auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
72                                             SYSTEMD_INTERFACE, "Unsubscribe");
73     this->bus.call_noreply(method);
74 
75     return;
76 }
77 
78 auto Activation::requestedActivation(RequestedActivations value)
79     -> RequestedActivations
80 {
81     if ((value == softwareServer::Activation::RequestedActivations::Active) &&
82         (softwareServer::Activation::requestedActivation() !=
83          softwareServer::Activation::RequestedActivations::Active))
84     {
85         if ((softwareServer::Activation::activation() ==
86              softwareServer::Activation::Activations::Ready) ||
87             (softwareServer::Activation::activation() ==
88              softwareServer::Activation::Activations::Failed))
89         {
90             activation(softwareServer::Activation::Activations::Activating);
91         }
92     }
93     return softwareServer::Activation::requestedActivation(value);
94 }
95 
96 void Activation::deleteImageManagerObject()
97 {
98     // Get the Delete object for <versionID> inside image_manager
99     constexpr auto versionServiceStr = "xyz.openbmc_project.Software.Version";
100     constexpr auto deleteInterface = "xyz.openbmc_project.Object.Delete";
101     std::string versionService;
102     auto method = this->bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
103                                             MAPPER_INTERFACE, "GetObject");
104 
105     method.append(path);
106     method.append(std::vector<std::string>({deleteInterface}));
107 
108     std::map<std::string, std::vector<std::string>> mapperResponse;
109 
110     try
111     {
112         auto mapperResponseMsg = bus.call(method);
113         mapperResponseMsg.read(mapperResponse);
114         if (mapperResponse.begin() == mapperResponse.end())
115         {
116             log<level::ERR>("ERROR in reading the mapper response",
117                             entry("VERSIONPATH=%s", path.c_str()));
118             return;
119         }
120     }
121     catch (const SdBusError& e)
122     {
123         log<level::ERR>("Error in Get Delete Object",
124                         entry("VERSIONPATH=%s", path.c_str()));
125         return;
126     }
127 
128     // We need to find the phosphor-software-manager's version service
129     // to invoke the delete interface
130     for (auto resp : mapperResponse)
131     {
132         if (resp.first.find(versionServiceStr) != std::string::npos)
133         {
134             versionService = resp.first;
135         }
136     }
137 
138     if (versionService.empty())
139     {
140         log<level::ERR>("Error finding version service");
141         return;
142     }
143 
144     // Call the Delete object for <versionID> inside image_manager
145     method = this->bus.new_method_call(versionService.c_str(), path.c_str(),
146                                        deleteInterface, "Delete");
147     try
148     {
149         bus.call(method);
150     }
151     catch (const SdBusError& e)
152     {
153         if (e.name() != nullptr && strcmp("System.Error.ELOOP", e.name()) == 0)
154         {
155             // TODO: Error being tracked with openbmc/openbmc#3311
156         }
157         else
158         {
159             log<level::ERR>("Error performing call to Delete object path",
160                             entry("ERROR=%s", e.what()),
161                             entry("PATH=%s", path.c_str()));
162         }
163         return;
164     }
165 }
166 
167 bool Activation::checkApplyTimeImmediate()
168 {
169     auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf);
170     if (service.empty())
171     {
172         log<level::INFO>("Error getting the service name for Host image "
173                          "ApplyTime. The Host needs to be manually rebooted to "
174                          "complete the image activation if needed "
175                          "immediately.");
176     }
177     else
178     {
179 
180         auto method = bus.new_method_call(service.c_str(), applyTimeObjPath,
181                                           dbusPropIntf, "Get");
182         method.append(applyTimeIntf, applyTimeProp);
183 
184         try
185         {
186             auto reply = bus.call(method);
187 
188             sdbusplus::message::variant<std::string> result;
189             reply.read(result);
190             auto applyTime = std::get<std::string>(result);
191             if (applyTime == applyTimeImmediate)
192             {
193                 return true;
194             }
195         }
196         catch (const SdBusError& e)
197         {
198             log<level::ERR>("Error in getting ApplyTime",
199                             entry("ERROR=%s", e.what()));
200         }
201     }
202     return false;
203 }
204 
205 void Activation::rebootHost()
206 {
207     auto service = utils::getService(bus, hostStateObjPath, hostStateIntf);
208     if (service.empty())
209     {
210         log<level::ALERT>("Error in getting the service name to reboot the "
211                           "Host. The Host needs to be manually rebooted to "
212                           "complete the image activation.");
213     }
214 
215     auto method = bus.new_method_call(service.c_str(), hostStateObjPath,
216                                       dbusPropIntf, "Set");
217     sdbusplus::message::variant<std::string> hostReboot = hostStateRebootVal;
218     method.append(hostStateIntf, hostStateRebootProp, hostReboot);
219 
220     try
221     {
222         auto reply = bus.call(method);
223     }
224     catch (const SdBusError& e)
225     {
226         log<level::ALERT>("Error in trying to reboot the Host. "
227                           "The Host needs to be manually rebooted to complete "
228                           "the image activation.",
229                           entry("ERROR=%s", e.what()));
230         report<InternalFailure>();
231     }
232 }
233 
234 uint8_t RedundancyPriority::priority(uint8_t value)
235 {
236     parent.parent.freePriority(value, parent.versionId);
237     return softwareServer::RedundancyPriority::priority(value);
238 }
239 
240 #ifdef WANT_SIGNATURE_VERIFY
241 bool Activation::validateSignature(const std::string& pnorFileName)
242 {
243     using Signature = openpower::software::image::Signature;
244     fs::path imageDir(IMG_DIR);
245 
246     Signature signature(imageDir / versionId, pnorFileName,
247                         PNOR_SIGNED_IMAGE_CONF_PATH);
248 
249     // Validate the signed image.
250     if (signature.verify())
251     {
252         return true;
253     }
254     // Log error and continue activation process, if field mode disabled.
255     log<level::ERR>("Error occurred during image validation");
256     report<InternalFailure>();
257 
258     try
259     {
260         if (!fieldModeEnabled())
261         {
262             return true;
263         }
264     }
265     catch (const InternalFailure& e)
266     {
267         report<InternalFailure>();
268     }
269     return false;
270 }
271 
272 bool Activation::fieldModeEnabled()
273 {
274     auto fieldModeSvc =
275         utils::getService(bus, FIELDMODE_PATH, FIELDMODE_INTERFACE);
276 
277     auto method = bus.new_method_call(fieldModeSvc.c_str(), FIELDMODE_PATH,
278                                       "org.freedesktop.DBus.Properties", "Get");
279 
280     method.append(FIELDMODE_INTERFACE, "FieldModeEnabled");
281 
282     sdbusplus::message::variant<bool> fieldMode;
283 
284     try
285     {
286         auto reply = bus.call(method);
287         reply.read(fieldMode);
288         return std::get<bool>(fieldMode);
289     }
290     catch (const SdBusError& e)
291     {
292         log<level::ERR>("Error in fieldModeEnabled getValue");
293         elog<InternalFailure>();
294     }
295 }
296 
297 #endif
298 
299 } // namespace updater
300 } // namespace software
301 } // namespace openpower
302