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