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 =
191                 sdbusplus::message::variant_ns::get<std::string>(result);
192             if (applyTime == applyTimeImmediate)
193             {
194                 return true;
195             }
196         }
197         catch (const SdBusError& e)
198         {
199             log<level::ERR>("Error in getting ApplyTime",
200                             entry("ERROR=%s", e.what()));
201         }
202     }
203     return false;
204 }
205 
206 void Activation::rebootHost()
207 {
208     auto service = utils::getService(bus, hostStateObjPath, hostStateIntf);
209     if (service.empty())
210     {
211         log<level::ALERT>("Error in getting the service name to reboot the "
212                           "Host. The Host needs to be manually rebooted to "
213                           "complete the image activation.");
214     }
215 
216     auto method = bus.new_method_call(service.c_str(), hostStateObjPath,
217                                       dbusPropIntf, "Set");
218     sdbusplus::message::variant<std::string> hostReboot = hostStateRebootVal;
219     method.append(hostStateIntf, hostStateRebootProp, hostReboot);
220 
221     try
222     {
223         auto reply = bus.call(method);
224     }
225     catch (const SdBusError& e)
226     {
227         log<level::ALERT>("Error in trying to reboot the Host. "
228                           "The Host needs to be manually rebooted to complete "
229                           "the image activation.",
230                           entry("ERROR=%s", e.what()));
231         report<InternalFailure>();
232     }
233 }
234 
235 uint8_t RedundancyPriority::priority(uint8_t value)
236 {
237     parent.parent.freePriority(value, parent.versionId);
238     return softwareServer::RedundancyPriority::priority(value);
239 }
240 
241 #ifdef WANT_SIGNATURE_VERIFY
242 bool Activation::validateSignature(const std::string& pnorFileName)
243 {
244     using Signature = openpower::software::image::Signature;
245     fs::path imageDir(IMG_DIR);
246 
247     Signature signature(imageDir / versionId, pnorFileName,
248                         PNOR_SIGNED_IMAGE_CONF_PATH);
249 
250     // Validate the signed image.
251     if (signature.verify())
252     {
253         return true;
254     }
255     // Log error and continue activation process, if field mode disabled.
256     log<level::ERR>("Error occurred during image validation");
257     report<InternalFailure>();
258 
259     try
260     {
261         if (!fieldModeEnabled())
262         {
263             return true;
264         }
265     }
266     catch (const InternalFailure& e)
267     {
268         report<InternalFailure>();
269     }
270     return false;
271 }
272 
273 bool Activation::fieldModeEnabled()
274 {
275     auto fieldModeSvc =
276         utils::getService(bus, FIELDMODE_PATH, FIELDMODE_INTERFACE);
277 
278     auto method = bus.new_method_call(fieldModeSvc.c_str(), FIELDMODE_PATH,
279                                       "org.freedesktop.DBus.Properties", "Get");
280 
281     method.append(FIELDMODE_INTERFACE, "FieldModeEnabled");
282 
283     sdbusplus::message::variant<bool> fieldMode;
284 
285     try
286     {
287         auto reply = bus.call(method);
288         reply.read(fieldMode);
289         return sdbusplus::message::variant_ns::get<bool>(fieldMode);
290     }
291     catch (const SdBusError& e)
292     {
293         log<level::ERR>("Error in fieldModeEnabled getValue");
294         elog<InternalFailure>();
295     }
296 }
297 
298 #endif
299 
300 } // namespace updater
301 } // namespace software
302 } // namespace openpower
303