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