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