1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "node.hpp" 19 20 #include <boost/container/flat_map.hpp> 21 22 namespace redfish 23 { 24 static std::unique_ptr<sdbusplus::bus::match::match> fwUpdateMatcher; 25 26 class UpdateService : public Node 27 { 28 public: 29 UpdateService(CrowApp &app) : Node(app, "/redfish/v1/UpdateService/") 30 { 31 Node::json["@odata.type"] = "#UpdateService.v1_2_0.UpdateService"; 32 Node::json["@odata.id"] = "/redfish/v1/UpdateService"; 33 Node::json["@odata.context"] = 34 "/redfish/v1/$metadata#UpdateService.UpdateService"; 35 Node::json["Id"] = "UpdateService"; 36 Node::json["Description"] = "Service for Software Update"; 37 Node::json["Name"] = "Update Service"; 38 Node::json["HttpPushUri"] = "/redfish/v1/UpdateService"; 39 // UpdateService cannot be disabled 40 Node::json["ServiceEnabled"] = true; 41 Node::json["FirmwareInventory"] = { 42 {"@odata.id", "/redfish/v1/UpdateService/FirmwareInventory"}}; 43 44 entityPrivileges = { 45 {boost::beast::http::verb::get, {{"Login"}}}, 46 {boost::beast::http::verb::head, {{"Login"}}}, 47 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 48 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 49 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 50 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 51 } 52 53 private: 54 void doGet(crow::Response &res, const crow::Request &req, 55 const std::vector<std::string> ¶ms) override 56 { 57 res.jsonValue = Node::json; 58 res.end(); 59 } 60 static void activateImage(const std::string &objPath) 61 { 62 crow::connections::systemBus->async_method_call( 63 [objPath](const boost::system::error_code error_code) { 64 if (error_code) 65 { 66 BMCWEB_LOG_DEBUG << "error_code = " << error_code; 67 BMCWEB_LOG_DEBUG << "error msg = " << error_code.message(); 68 } 69 }, 70 "xyz.openbmc_project.Software.BMC.Updater", objPath, 71 "org.freedesktop.DBus.Properties", "Set", 72 "xyz.openbmc_project.Software.Activation", "RequestedActivation", 73 sdbusplus::message::variant<std::string>( 74 "xyz.openbmc_project.Software.Activation.RequestedActivations." 75 "Active")); 76 } 77 void doPost(crow::Response &res, const crow::Request &req, 78 const std::vector<std::string> ¶ms) override 79 { 80 BMCWEB_LOG_DEBUG << "doPost..."; 81 82 // Only allow one FW update at a time 83 if (fwUpdateMatcher != nullptr) 84 { 85 res.addHeader("Retry-After", "30"); 86 res.result(boost::beast::http::status::service_unavailable); 87 res.jsonValue = messages::serviceTemporarilyUnavailable("3"); 88 res.end(); 89 return; 90 } 91 // Make this const static so it survives outside this method 92 static boost::asio::deadline_timer timeout( 93 *req.ioService, boost::posix_time::seconds(5)); 94 95 timeout.expires_from_now(boost::posix_time::seconds(5)); 96 97 timeout.async_wait([&res](const boost::system::error_code &ec) { 98 fwUpdateMatcher = nullptr; 99 if (ec == boost::asio::error::operation_aborted) 100 { 101 // expected, we were canceled before the timer completed. 102 return; 103 } 104 BMCWEB_LOG_ERROR 105 << "Timed out waiting for firmware object being created"; 106 BMCWEB_LOG_ERROR 107 << "FW image may has already been uploaded to server"; 108 if (ec) 109 { 110 BMCWEB_LOG_ERROR << "Async_wait failed" << ec; 111 return; 112 } 113 114 res.result(boost::beast::http::status::internal_server_error); 115 res.jsonValue = redfish::messages::internalError(); 116 res.end(); 117 }); 118 119 auto callback = [&res](sdbusplus::message::message &m) { 120 BMCWEB_LOG_DEBUG << "Match fired"; 121 bool flag = false; 122 123 if (m.is_method_error()) 124 { 125 BMCWEB_LOG_DEBUG << "Dbus method error!!!"; 126 res.end(); 127 return; 128 } 129 std::vector<std::pair< 130 std::string, 131 std::vector<std::pair< 132 std::string, sdbusplus::message::variant<std::string>>>>> 133 interfacesProperties; 134 135 sdbusplus::message::object_path objPath; 136 137 m.read(objPath, interfacesProperties); // Read in the object path 138 // that was just created 139 // std::string str_objpath = objPath.str; // keep a copy for 140 // constructing response message 141 BMCWEB_LOG_DEBUG << "obj path = " << objPath.str; // str_objpath; 142 for (auto &interface : interfacesProperties) 143 { 144 BMCWEB_LOG_DEBUG << "interface = " << interface.first; 145 146 if (interface.first == 147 "xyz.openbmc_project.Software.Activation") 148 { 149 // cancel timer only when 150 // xyz.openbmc_project.Software.Activation interface is 151 // added 152 boost::system::error_code ec; 153 timeout.cancel(ec); 154 if (ec) 155 { 156 BMCWEB_LOG_ERROR << "error canceling timer " << ec; 157 } 158 UpdateService::activateImage(objPath.str); // str_objpath); 159 res.jsonValue = redfish::messages::success(); 160 BMCWEB_LOG_DEBUG << "ending response"; 161 res.end(); 162 fwUpdateMatcher = nullptr; 163 } 164 } 165 }; 166 167 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match::match>( 168 *crow::connections::systemBus, 169 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 170 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 171 callback); 172 173 std::string filepath( 174 "/tmp/images/" + 175 boost::uuids::to_string(boost::uuids::random_generator()())); 176 BMCWEB_LOG_DEBUG << "Writing file to " << filepath; 177 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary | 178 std::ofstream::trunc); 179 out << req.body; 180 out.close(); 181 BMCWEB_LOG_DEBUG << "file upload complete!!"; 182 } 183 }; 184 185 class SoftwareInventoryCollection : public Node 186 { 187 public: 188 template <typename CrowApp> 189 SoftwareInventoryCollection(CrowApp &app) : 190 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/") 191 { 192 Node::json["@odata.type"] = 193 "#SoftwareInventoryCollection.SoftwareInventoryCollection"; 194 Node::json["@odata.id"] = "/redfish/v1/UpdateService/FirmwareInventory"; 195 Node::json["@odata.context"] = 196 "/redfish/v1/" 197 "$metadata#SoftwareInventoryCollection.SoftwareInventoryCollection"; 198 Node::json["Name"] = "Software Inventory Collection"; 199 200 entityPrivileges = { 201 {boost::beast::http::verb::get, {{"Login"}}}, 202 {boost::beast::http::verb::head, {{"Login"}}}, 203 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 204 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 205 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 206 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 207 } 208 209 private: 210 void doGet(crow::Response &res, const crow::Request &req, 211 const std::vector<std::string> ¶ms) override 212 { 213 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 214 res.jsonValue = Node::json; 215 216 crow::connections::systemBus->async_method_call( 217 [asyncResp]( 218 const boost::system::error_code ec, 219 const std::vector<std::pair< 220 std::string, std::vector<std::pair< 221 std::string, std::vector<std::string>>>>> 222 &subtree) { 223 if (ec) 224 { 225 asyncResp->res.result( 226 boost::beast::http::status::internal_server_error); 227 return; 228 } 229 asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 230 asyncResp->res.jsonValue["Members@odata.count"] = 0; 231 232 for (auto &obj : subtree) 233 { 234 const std::vector< 235 std::pair<std::string, std::vector<std::string>>> 236 &connections = obj.second; 237 238 // if can't parse fw id then return 239 std::size_t idPos = obj.first.rfind("/"); 240 if (idPos == std::string::npos || 241 idPos + 1 == obj.first.size()) 242 { 243 asyncResp->res.result( 244 boost::beast::http::status::internal_server_error); 245 asyncResp->res.jsonValue = messages::internalError(); 246 BMCWEB_LOG_DEBUG << "Can't parse firmware ID!!"; 247 return; 248 } 249 250 std::string swId = obj.first.substr(idPos + 1); 251 252 for (auto &conn : connections) 253 { 254 const std::string &connectionName = conn.first; 255 BMCWEB_LOG_DEBUG << "connectionName = " 256 << connectionName; 257 BMCWEB_LOG_DEBUG << "obj.first = " << obj.first; 258 259 crow::connections::systemBus->async_method_call( 260 [asyncResp, 261 swId](const boost::system::error_code error_code, 262 const VariantType &activation) { 263 BMCWEB_LOG_DEBUG 264 << "safe returned in lambda function"; 265 if (error_code) 266 { 267 asyncResp->res.result( 268 boost::beast::http::status:: 269 internal_server_error); 270 return; 271 } 272 273 const std::string *swActivationStatus = 274 mapbox::getPtr<const std::string>( 275 activation); 276 if (swActivationStatus == nullptr) 277 { 278 asyncResp->res.result( 279 boost::beast::http::status:: 280 internal_server_error); 281 return; 282 } 283 if (swActivationStatus != nullptr && 284 *swActivationStatus != 285 "xyz.openbmc_project.Software." 286 "Activation." 287 "Activations.Active") 288 { 289 // The activation status of this software is 290 // not currently active, so does not need to 291 // be listed in the response 292 return; 293 } 294 nlohmann::json &members = 295 asyncResp->res.jsonValue["Members"]; 296 members.push_back( 297 {{"@odata.id", "/redfish/v1/UpdateService/" 298 "FirmwareInventory/" + 299 swId}}); 300 asyncResp->res 301 .jsonValue["Members@odata.count"] = 302 members.size(); 303 }, 304 connectionName, obj.first, 305 "org.freedesktop.DBus.Properties", "Get", 306 "xyz.openbmc_project.Software.Activation", 307 "Activation"); 308 } 309 } 310 }, 311 "xyz.openbmc_project.ObjectMapper", 312 "/xyz/openbmc_project/object_mapper", 313 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 314 "/xyz/openbmc_project/software", int32_t(1), 315 std::array<const char *, 1>{ 316 "xyz.openbmc_project.Software.Version"}); 317 } 318 }; 319 320 class SoftwareInventory : public Node 321 { 322 public: 323 template <typename CrowApp> 324 SoftwareInventory(CrowApp &app) : 325 Node(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/", 326 std::string()) 327 { 328 Node::json["@odata.type"] = 329 "#SoftwareInventory.v1_1_0.SoftwareInventory"; 330 Node::json["@odata.context"] = 331 "/redfish/v1/$metadata#SoftwareInventory.SoftwareInventory"; 332 Node::json["Name"] = "Software Inventory"; 333 Node::json["Updateable"] = false; 334 Node::json["Status"]["Health"] = "OK"; 335 Node::json["Status"]["HealthRollup"] = "OK"; 336 Node::json["Status"]["State"] = "Enabled"; 337 entityPrivileges = { 338 {boost::beast::http::verb::get, {{"Login"}}}, 339 {boost::beast::http::verb::head, {{"Login"}}}, 340 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 341 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 342 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 343 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 344 } 345 346 private: 347 void doGet(crow::Response &res, const crow::Request &req, 348 const std::vector<std::string> ¶ms) override 349 { 350 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 351 res.jsonValue = Node::json; 352 353 if (params.size() != 1) 354 { 355 res.result(boost::beast::http::status::internal_server_error); 356 res.jsonValue = messages::internalError(); 357 res.end(); 358 return; 359 } 360 361 std::shared_ptr<std::string> swId = 362 std::make_shared<std::string>(params[0]); 363 364 res.jsonValue["@odata.id"] = 365 "/redfish/v1/UpdateService/FirmwareInventory/" + *swId; 366 367 crow::connections::systemBus->async_method_call( 368 [asyncResp, swId]( 369 const boost::system::error_code ec, 370 const std::vector<std::pair< 371 std::string, std::vector<std::pair< 372 std::string, std::vector<std::string>>>>> 373 &subtree) { 374 BMCWEB_LOG_DEBUG << "doGet callback..."; 375 if (ec) 376 { 377 asyncResp->res.result( 378 boost::beast::http::status::internal_server_error); 379 return; 380 } 381 382 for (const std::pair< 383 std::string, 384 std::vector< 385 std::pair<std::string, std::vector<std::string>>>> 386 &obj : subtree) 387 { 388 if (boost::ends_with(obj.first, *swId) != true) 389 { 390 continue; 391 } 392 393 if (obj.second.size() < 1) 394 { 395 continue; 396 } 397 398 crow::connections::systemBus->async_method_call( 399 [asyncResp, 400 swId](const boost::system::error_code error_code, 401 const boost::container::flat_map< 402 std::string, VariantType> &propertiesList) { 403 if (error_code) 404 { 405 asyncResp->res.result( 406 boost::beast::http::status:: 407 internal_server_error); 408 return; 409 } 410 boost::container::flat_map< 411 std::string, VariantType>::const_iterator it = 412 propertiesList.find("Purpose"); 413 if (it == propertiesList.end()) 414 { 415 BMCWEB_LOG_DEBUG 416 << "Can't find property \"Purpose\"!"; 417 asyncResp->res.result( 418 boost::beast::http::status:: 419 internal_server_error); 420 return; 421 } 422 const std::string *swInvPurpose = 423 mapbox::getPtr<const std::string>(it->second); 424 if (swInvPurpose == nullptr) 425 { 426 BMCWEB_LOG_DEBUG 427 << "wrong types for property\"Purpose\"!"; 428 asyncResp->res.result( 429 boost::beast::http::status:: 430 internal_server_error); 431 return; 432 } 433 434 BMCWEB_LOG_DEBUG << "swInvPurpose = " 435 << *swInvPurpose; 436 it = propertiesList.find("Version"); 437 if (it == propertiesList.end()) 438 { 439 BMCWEB_LOG_DEBUG 440 << "Can't find property \"Version\"!"; 441 asyncResp->res.result( 442 boost::beast::http::status:: 443 internal_server_error); 444 return; 445 } 446 447 BMCWEB_LOG_DEBUG << "Version found!"; 448 449 const std::string *version = 450 mapbox::getPtr<const std::string>(it->second); 451 452 if (version == nullptr) 453 { 454 BMCWEB_LOG_DEBUG 455 << "Can't find property \"Version\"!"; 456 asyncResp->res.result( 457 boost::beast::http::status:: 458 internal_server_error); 459 return; 460 } 461 asyncResp->res.jsonValue["Version"] = *version; 462 asyncResp->res.jsonValue["Id"] = *swId; 463 }, 464 obj.second[0].first, obj.first, 465 "org.freedesktop.DBus.Properties", "GetAll", 466 "xyz.openbmc_project.Software.Version"); 467 } 468 }, 469 "xyz.openbmc_project.ObjectMapper", 470 "/xyz/openbmc_project/object_mapper", 471 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 472 "/xyz/openbmc_project/software", int32_t(1), 473 std::array<const char *, 1>{ 474 "xyz.openbmc_project.Software.Version"}); 475 } 476 }; 477 478 } // namespace redfish 479