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 "bmcweb_config.h"
19
20 #include "app.hpp"
21 #include "dbus_utility.hpp"
22 #include "error_messages.hpp"
23 #include "generated/enums/update_service.hpp"
24 #include "multipart_parser.hpp"
25 #include "ossl_random.hpp"
26 #include "query.hpp"
27 #include "registries/privilege_registry.hpp"
28 #include "task.hpp"
29 #include "task_messages.hpp"
30 #include "utils/collection.hpp"
31 #include "utils/dbus_utils.hpp"
32 #include "utils/json_utils.hpp"
33 #include "utils/sw_utils.hpp"
34
35 #include <sys/mman.h>
36
37 #include <boost/system/error_code.hpp>
38 #include <boost/url/format.hpp>
39 #include <sdbusplus/asio/property.hpp>
40 #include <sdbusplus/bus/match.hpp>
41 #include <sdbusplus/unpack_properties.hpp>
42
43 #include <array>
44 #include <cstddef>
45 #include <filesystem>
46 #include <functional>
47 #include <iterator>
48 #include <memory>
49 #include <optional>
50 #include <string>
51 #include <string_view>
52 #include <unordered_map>
53 #include <vector>
54
55 namespace redfish
56 {
57
58 // Match signals added on software path
59 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
60 static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateMatcher;
61 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
62 static std::unique_ptr<sdbusplus::bus::match_t> fwUpdateErrorMatcher;
63 // Only allow one update at a time
64 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
65 static bool fwUpdateInProgress = false;
66 // Timer for software available
67 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
68 static std::unique_ptr<boost::asio::steady_timer> fwAvailableTimer;
69
70 struct MemoryFileDescriptor
71 {
72 int fd = -1;
73
MemoryFileDescriptorredfish::MemoryFileDescriptor74 explicit MemoryFileDescriptor(const std::string& filename) :
75 fd(memfd_create(filename.c_str(), 0))
76 {}
77
78 MemoryFileDescriptor(const MemoryFileDescriptor&) = default;
MemoryFileDescriptorredfish::MemoryFileDescriptor79 MemoryFileDescriptor(MemoryFileDescriptor&& other) noexcept : fd(other.fd)
80 {
81 other.fd = -1;
82 }
83 MemoryFileDescriptor& operator=(const MemoryFileDescriptor&) = delete;
84 MemoryFileDescriptor& operator=(MemoryFileDescriptor&&) = default;
85
~MemoryFileDescriptorredfish::MemoryFileDescriptor86 ~MemoryFileDescriptor()
87 {
88 if (fd != -1)
89 {
90 close(fd);
91 }
92 }
93
rewindredfish::MemoryFileDescriptor94 bool rewind() const
95 {
96 if (lseek(fd, 0, SEEK_SET) == -1)
97 {
98 BMCWEB_LOG_ERROR("Failed to seek to beginning of image memfd");
99 return false;
100 }
101 return true;
102 }
103 };
104
cleanUp()105 inline void cleanUp()
106 {
107 fwUpdateInProgress = false;
108 fwUpdateMatcher = nullptr;
109 fwUpdateErrorMatcher = nullptr;
110 }
111
activateImage(const std::string & objPath,const std::string & service)112 inline void activateImage(const std::string& objPath,
113 const std::string& service)
114 {
115 BMCWEB_LOG_DEBUG("Activate image for {} {}", objPath, service);
116 sdbusplus::asio::setProperty(
117 *crow::connections::systemBus, service, objPath,
118 "xyz.openbmc_project.Software.Activation", "RequestedActivation",
119 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active",
120 [](const boost::system::error_code& ec) {
121 if (ec)
122 {
123 BMCWEB_LOG_DEBUG("error_code = {}", ec);
124 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
125 }
126 });
127 }
128
handleCreateTask(const boost::system::error_code & ec2,sdbusplus::message_t & msg,const std::shared_ptr<task::TaskData> & taskData)129 inline bool handleCreateTask(const boost::system::error_code& ec2,
130 sdbusplus::message_t& msg,
131 const std::shared_ptr<task::TaskData>& taskData)
132 {
133 if (ec2)
134 {
135 return task::completed;
136 }
137
138 std::string iface;
139 dbus::utility::DBusPropertiesMap values;
140
141 std::string index = std::to_string(taskData->index);
142 msg.read(iface, values);
143
144 if (iface == "xyz.openbmc_project.Software.Activation")
145 {
146 const std::string* state = nullptr;
147 for (const auto& property : values)
148 {
149 if (property.first == "Activation")
150 {
151 state = std::get_if<std::string>(&property.second);
152 if (state == nullptr)
153 {
154 taskData->messages.emplace_back(messages::internalError());
155 return task::completed;
156 }
157 }
158 }
159
160 if (state == nullptr)
161 {
162 return !task::completed;
163 }
164
165 if (state->ends_with("Invalid") || state->ends_with("Failed"))
166 {
167 taskData->state = "Exception";
168 taskData->status = "Warning";
169 taskData->messages.emplace_back(messages::taskAborted(index));
170 return task::completed;
171 }
172
173 if (state->ends_with("Staged"))
174 {
175 taskData->state = "Stopping";
176 taskData->messages.emplace_back(messages::taskPaused(index));
177
178 // its staged, set a long timer to
179 // allow them time to complete the
180 // update (probably cycle the
181 // system) if this expires then
182 // task will be canceled
183 taskData->extendTimer(std::chrono::hours(5));
184 return !task::completed;
185 }
186
187 if (state->ends_with("Active"))
188 {
189 taskData->messages.emplace_back(messages::taskCompletedOK(index));
190 taskData->state = "Completed";
191 return task::completed;
192 }
193 }
194 else if (iface == "xyz.openbmc_project.Software.ActivationProgress")
195 {
196 const uint8_t* progress = nullptr;
197 for (const auto& property : values)
198 {
199 if (property.first == "Progress")
200 {
201 progress = std::get_if<uint8_t>(&property.second);
202 if (progress == nullptr)
203 {
204 taskData->messages.emplace_back(messages::internalError());
205 return task::completed;
206 }
207 }
208 }
209
210 if (progress == nullptr)
211 {
212 return !task::completed;
213 }
214 taskData->percentComplete = *progress;
215 taskData->messages.emplace_back(
216 messages::taskProgressChanged(index, *progress));
217
218 // if we're getting status updates it's
219 // still alive, update timer
220 taskData->extendTimer(std::chrono::minutes(5));
221 }
222
223 // as firmware update often results in a
224 // reboot, the task may never "complete"
225 // unless it is an error
226
227 return !task::completed;
228 }
229
createTask(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload && payload,const sdbusplus::message::object_path & objPath)230 inline void createTask(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
231 task::Payload&& payload,
232 const sdbusplus::message::object_path& objPath)
233 {
234 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
235 std::bind_front(handleCreateTask),
236 "type='signal',interface='org.freedesktop.DBus.Properties',"
237 "member='PropertiesChanged',path='" +
238 objPath.str + "'");
239 task->startTimer(std::chrono::minutes(5));
240 task->populateResp(asyncResp->res);
241 task->payload.emplace(std::move(payload));
242 }
243
244 // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
245 // then no asyncResp updates will occur
246 inline void
softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,sdbusplus::message_t & m,task::Payload && payload)247 softwareInterfaceAdded(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
248 sdbusplus::message_t& m, task::Payload&& payload)
249 {
250 dbus::utility::DBusInterfacesMap interfacesProperties;
251
252 sdbusplus::message::object_path objPath;
253
254 m.read(objPath, interfacesProperties);
255
256 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
257 for (const auto& interface : interfacesProperties)
258 {
259 BMCWEB_LOG_DEBUG("interface = {}", interface.first);
260
261 if (interface.first == "xyz.openbmc_project.Software.Activation")
262 {
263 // Retrieve service and activate
264 constexpr std::array<std::string_view, 1> interfaces = {
265 "xyz.openbmc_project.Software.Activation"};
266 dbus::utility::getDbusObject(
267 objPath.str, interfaces,
268 [objPath, asyncResp, payload(std::move(payload))](
269 const boost::system::error_code& ec,
270 const std::vector<
271 std::pair<std::string, std::vector<std::string>>>&
272 objInfo) mutable {
273 if (ec)
274 {
275 BMCWEB_LOG_DEBUG("error_code = {}", ec);
276 BMCWEB_LOG_DEBUG("error msg = {}", ec.message());
277 if (asyncResp)
278 {
279 messages::internalError(asyncResp->res);
280 }
281 cleanUp();
282 return;
283 }
284 // Ensure we only got one service back
285 if (objInfo.size() != 1)
286 {
287 BMCWEB_LOG_ERROR("Invalid Object Size {}",
288 objInfo.size());
289 if (asyncResp)
290 {
291 messages::internalError(asyncResp->res);
292 }
293 cleanUp();
294 return;
295 }
296 // cancel timer only when
297 // xyz.openbmc_project.Software.Activation interface
298 // is added
299 fwAvailableTimer = nullptr;
300
301 activateImage(objPath.str, objInfo[0].first);
302 if (asyncResp)
303 {
304 createTask(asyncResp, std::move(payload), objPath);
305 }
306 fwUpdateInProgress = false;
307 });
308
309 break;
310 }
311 }
312 }
313
afterAvailbleTimerAsyncWait(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec)314 inline void afterAvailbleTimerAsyncWait(
315 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
316 const boost::system::error_code& ec)
317 {
318 cleanUp();
319 if (ec == boost::asio::error::operation_aborted)
320 {
321 // expected, we were canceled before the timer completed.
322 return;
323 }
324 BMCWEB_LOG_ERROR("Timed out waiting for firmware object being created");
325 BMCWEB_LOG_ERROR("FW image may has already been uploaded to server");
326 if (ec)
327 {
328 BMCWEB_LOG_ERROR("Async_wait failed{}", ec);
329 return;
330 }
331 if (asyncResp)
332 {
333 redfish::messages::internalError(asyncResp->res);
334 }
335 }
336
337 inline void
handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & url,const std::string & type)338 handleUpdateErrorType(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
339 const std::string& url, const std::string& type)
340 {
341 // NOLINTBEGIN(bugprone-branch-clone)
342 if (type == "xyz.openbmc_project.Software.Image.Error.UnTarFailure")
343 {
344 messages::missingOrMalformedPart(asyncResp->res);
345 }
346 else if (type ==
347 "xyz.openbmc_project.Software.Image.Error.ManifestFileFailure")
348 {
349 messages::missingOrMalformedPart(asyncResp->res);
350 }
351 else if (type == "xyz.openbmc_project.Software.Image.Error.ImageFailure")
352 {
353 messages::missingOrMalformedPart(asyncResp->res);
354 }
355 else if (type == "xyz.openbmc_project.Software.Version.Error.AlreadyExists")
356 {
357 messages::resourceAlreadyExists(asyncResp->res, "UpdateService",
358 "Version", "uploaded version");
359 }
360 else if (type == "xyz.openbmc_project.Software.Image.Error.BusyFailure")
361 {
362 messages::serviceTemporarilyUnavailable(asyncResp->res, url);
363 }
364 else if (type == "xyz.openbmc_project.Software.Version.Error.Incompatible")
365 {
366 messages::internalError(asyncResp->res);
367 }
368 else if (type ==
369 "xyz.openbmc_project.Software.Version.Error.ExpiredAccessKey")
370 {
371 messages::internalError(asyncResp->res);
372 }
373 else if (type ==
374 "xyz.openbmc_project.Software.Version.Error.InvalidSignature")
375 {
376 messages::missingOrMalformedPart(asyncResp->res);
377 }
378 else if (type ==
379 "xyz.openbmc_project.Software.Image.Error.InternalFailure" ||
380 type == "xyz.openbmc_project.Software.Version.Error.HostFile")
381 {
382 BMCWEB_LOG_ERROR("Software Image Error type={}", type);
383 messages::internalError(asyncResp->res);
384 }
385 else
386 {
387 // Unrelated error types. Ignored
388 BMCWEB_LOG_INFO("Non-Software-related Error type={}. Ignored", type);
389 return;
390 }
391 // NOLINTEND(bugprone-branch-clone)
392 // Clear the timer
393 fwAvailableTimer = nullptr;
394 }
395
396 inline void
afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & url,sdbusplus::message_t & m)397 afterUpdateErrorMatcher(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
398 const std::string& url, sdbusplus::message_t& m)
399 {
400 dbus::utility::DBusInterfacesMap interfacesProperties;
401 sdbusplus::message::object_path objPath;
402 m.read(objPath, interfacesProperties);
403 BMCWEB_LOG_DEBUG("obj path = {}", objPath.str);
404 for (const std::pair<std::string, dbus::utility::DBusPropertiesMap>&
405 interface : interfacesProperties)
406 {
407 if (interface.first == "xyz.openbmc_project.Logging.Entry")
408 {
409 for (const std::pair<std::string, dbus::utility::DbusVariantType>&
410 value : interface.second)
411 {
412 if (value.first != "Message")
413 {
414 continue;
415 }
416 const std::string* type =
417 std::get_if<std::string>(&value.second);
418 if (type == nullptr)
419 {
420 // if this was our message, timeout will cover it
421 return;
422 }
423 handleUpdateErrorType(asyncResp, url, *type);
424 }
425 }
426 }
427 }
428
429 // Note that asyncResp can be either a valid pointer or nullptr. If nullptr
430 // then no asyncResp updates will occur
monitorForSoftwareAvailable(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req,const std::string & url,int timeoutTimeSeconds=25)431 inline void monitorForSoftwareAvailable(
432 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
433 const crow::Request& req, const std::string& url,
434 int timeoutTimeSeconds = 25)
435 {
436 // Only allow one FW update at a time
437 if (fwUpdateInProgress)
438 {
439 if (asyncResp)
440 {
441 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
442 }
443 return;
444 }
445
446 if (req.ioService == nullptr)
447 {
448 messages::internalError(asyncResp->res);
449 return;
450 }
451
452 fwAvailableTimer =
453 std::make_unique<boost::asio::steady_timer>(*req.ioService);
454
455 fwAvailableTimer->expires_after(std::chrono::seconds(timeoutTimeSeconds));
456
457 fwAvailableTimer->async_wait(
458 std::bind_front(afterAvailbleTimerAsyncWait, asyncResp));
459
460 task::Payload payload(req);
461 auto callback = [asyncResp, payload](sdbusplus::message_t& m) mutable {
462 BMCWEB_LOG_DEBUG("Match fired");
463 softwareInterfaceAdded(asyncResp, m, std::move(payload));
464 };
465
466 fwUpdateInProgress = true;
467
468 fwUpdateMatcher = std::make_unique<sdbusplus::bus::match_t>(
469 *crow::connections::systemBus,
470 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
471 "member='InterfacesAdded',path='/xyz/openbmc_project/software'",
472 callback);
473
474 fwUpdateErrorMatcher = std::make_unique<sdbusplus::bus::match_t>(
475 *crow::connections::systemBus,
476 "interface='org.freedesktop.DBus.ObjectManager',type='signal',"
477 "member='InterfacesAdded',"
478 "path='/xyz/openbmc_project/logging'",
479 std::bind_front(afterUpdateErrorMatcher, asyncResp, url));
480 }
481
parseSimpleUpdateUrl(std::string imageURI,std::optional<std::string> transferProtocol,crow::Response & res)482 inline std::optional<boost::urls::url> parseSimpleUpdateUrl(
483 std::string imageURI, std::optional<std::string> transferProtocol,
484 crow::Response& res)
485 {
486 if (imageURI.find("://") == std::string::npos)
487 {
488 if (imageURI.starts_with("/"))
489 {
490 messages::actionParameterValueTypeError(
491 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
492 return std::nullopt;
493 }
494 if (!transferProtocol)
495 {
496 messages::actionParameterValueTypeError(
497 res, imageURI, "ImageURI", "UpdateService.SimpleUpdate");
498 return std::nullopt;
499 }
500 // OpenBMC currently only supports TFTP or HTTPS
501 if (*transferProtocol == "TFTP")
502 {
503 imageURI = "tftp://" + imageURI;
504 }
505 else if (*transferProtocol == "HTTPS")
506 {
507 imageURI = "https://" + imageURI;
508 }
509 else
510 {
511 messages::actionParameterNotSupported(res, "TransferProtocol",
512 *transferProtocol);
513 BMCWEB_LOG_ERROR("Request incorrect protocol parameter: {}",
514 *transferProtocol);
515 return std::nullopt;
516 }
517 }
518
519 boost::system::result<boost::urls::url> url =
520 boost::urls::parse_absolute_uri(imageURI);
521 if (!url)
522 {
523 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
524 "UpdateService.SimpleUpdate");
525
526 return std::nullopt;
527 }
528 url->normalize();
529
530 if (url->scheme() == "tftp")
531 {
532 if (url->encoded_path().size() < 2)
533 {
534 messages::actionParameterNotSupported(res, "ImageURI",
535 url->buffer());
536 return std::nullopt;
537 }
538 }
539 else if (url->scheme() == "https")
540 {
541 // Empty paths default to "/"
542 if (url->encoded_path().empty())
543 {
544 url->set_encoded_path("/");
545 }
546 }
547 else
548 {
549 messages::actionParameterNotSupported(res, "ImageURI", imageURI);
550 return std::nullopt;
551 }
552
553 if (url->encoded_path().empty())
554 {
555 messages::actionParameterValueTypeError(res, imageURI, "ImageURI",
556 "UpdateService.SimpleUpdate");
557 return std::nullopt;
558 }
559
560 return *url;
561 }
562
doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::urls::url_view_base & url)563 inline void doHttpsUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
564 const boost::urls::url_view_base& url)
565 {
566 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
567 url.buffer());
568 }
569
handleUpdateServiceSimpleUpdateAction(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)570 inline void handleUpdateServiceSimpleUpdateAction(
571 crow::App& app, const crow::Request& req,
572 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
573 {
574 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
575 {
576 return;
577 }
578
579 std::optional<std::string> transferProtocol;
580 std::string imageURI;
581
582 BMCWEB_LOG_DEBUG("Enter UpdateService.SimpleUpdate doPost");
583
584 // User can pass in both TransferProtocol and ImageURI parameters or
585 // they can pass in just the ImageURI with the transfer protocol
586 // embedded within it.
587 // 1) TransferProtocol:TFTP ImageURI:1.1.1.1/myfile.bin
588 // 2) ImageURI:tftp://1.1.1.1/myfile.bin
589
590 if (!json_util::readJsonAction( //
591 req, asyncResp->res, //
592 "ImageURI", imageURI, //
593 "TransferProtocol", transferProtocol //
594 ))
595 {
596 BMCWEB_LOG_DEBUG("Missing TransferProtocol or ImageURI parameter");
597 return;
598 }
599
600 std::optional<boost::urls::url> url =
601 parseSimpleUpdateUrl(imageURI, transferProtocol, asyncResp->res);
602 if (!url)
603 {
604 return;
605 }
606 if (url->scheme() == "https")
607 {
608 doHttpsUpdate(asyncResp, *url);
609 }
610 else
611 {
612 messages::actionParameterNotSupported(asyncResp->res, "ImageURI",
613 url->buffer());
614 return;
615 }
616
617 BMCWEB_LOG_DEBUG("Exit UpdateService.SimpleUpdate doPost");
618 }
619
uploadImageFile(crow::Response & res,std::string_view body)620 inline void uploadImageFile(crow::Response& res, std::string_view body)
621 {
622 std::filesystem::path filepath("/tmp/images/" + bmcweb::getRandomUUID());
623
624 BMCWEB_LOG_DEBUG("Writing file to {}", filepath.string());
625 std::ofstream out(filepath, std::ofstream::out | std::ofstream::binary |
626 std::ofstream::trunc);
627 // set the permission of the file to 640
628 std::filesystem::perms permission =
629 std::filesystem::perms::owner_read | std::filesystem::perms::group_read;
630 std::filesystem::permissions(filepath, permission);
631 out << body;
632
633 if (out.bad())
634 {
635 messages::internalError(res);
636 cleanUp();
637 }
638 }
639
640 // Convert the Request Apply Time to the D-Bus value
convertApplyTime(crow::Response & res,const std::string & applyTime,std::string & applyTimeNewVal)641 inline bool convertApplyTime(crow::Response& res, const std::string& applyTime,
642 std::string& applyTimeNewVal)
643 {
644 if (applyTime == "Immediate")
645 {
646 applyTimeNewVal =
647 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate";
648 }
649 else if (applyTime == "OnReset")
650 {
651 applyTimeNewVal =
652 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset";
653 }
654 else
655 {
656 BMCWEB_LOG_WARNING(
657 "ApplyTime value {} is not in the list of acceptable values",
658 applyTime);
659 messages::propertyValueNotInList(res, applyTime, "ApplyTime");
660 return false;
661 }
662 return true;
663 }
664
setApplyTime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & applyTime)665 inline void setApplyTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
666 const std::string& applyTime)
667 {
668 std::string applyTimeNewVal;
669 if (!convertApplyTime(asyncResp->res, applyTime, applyTimeNewVal))
670 {
671 return;
672 }
673
674 setDbusProperty(asyncResp, "ApplyTime", "xyz.openbmc_project.Settings",
675 sdbusplus::message::object_path(
676 "/xyz/openbmc_project/software/apply_time"),
677 "xyz.openbmc_project.Software.ApplyTime",
678 "RequestedApplyTime", applyTimeNewVal);
679 }
680
681 struct MultiPartUpdateParameters
682 {
683 std::optional<std::string> applyTime;
684 std::string uploadData;
685 std::vector<std::string> targets;
686 };
687
688 inline std::optional<std::string>
processUrl(boost::system::result<boost::urls::url_view> & url)689 processUrl(boost::system::result<boost::urls::url_view>& url)
690 {
691 if (!url)
692 {
693 return std::nullopt;
694 }
695 if (crow::utility::readUrlSegments(*url, "redfish", "v1", "Managers",
696 BMCWEB_REDFISH_MANAGER_URI_NAME))
697 {
698 return std::make_optional(std::string(BMCWEB_REDFISH_MANAGER_URI_NAME));
699 }
700 if constexpr (!BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
701 {
702 return std::nullopt;
703 }
704 std::string firmwareId;
705 if (!crow::utility::readUrlSegments(*url, "redfish", "v1", "UpdateService",
706 "FirmwareInventory",
707 std::ref(firmwareId)))
708 {
709 return std::nullopt;
710 }
711
712 return std::make_optional(firmwareId);
713 }
714
715 inline std::optional<MultiPartUpdateParameters>
extractMultipartUpdateParameters(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,MultipartParser parser)716 extractMultipartUpdateParameters(
717 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
718 MultipartParser parser)
719 {
720 MultiPartUpdateParameters multiRet;
721 for (FormPart& formpart : parser.mime_fields)
722 {
723 boost::beast::http::fields::const_iterator it =
724 formpart.fields.find("Content-Disposition");
725 if (it == formpart.fields.end())
726 {
727 BMCWEB_LOG_ERROR("Couldn't find Content-Disposition");
728 return std::nullopt;
729 }
730 BMCWEB_LOG_INFO("Parsing value {}", it->value());
731
732 // The construction parameters of param_list must start with `;`
733 size_t index = it->value().find(';');
734 if (index == std::string::npos)
735 {
736 continue;
737 }
738
739 for (const auto& param :
740 boost::beast::http::param_list{it->value().substr(index)})
741 {
742 if (param.first != "name" || param.second.empty())
743 {
744 continue;
745 }
746
747 if (param.second == "UpdateParameters")
748 {
749 std::vector<std::string> tempTargets;
750 nlohmann::json content =
751 nlohmann::json::parse(formpart.content, nullptr, false);
752 if (content.is_discarded())
753 {
754 return std::nullopt;
755 }
756 nlohmann::json::object_t* obj =
757 content.get_ptr<nlohmann::json::object_t*>();
758 if (obj == nullptr)
759 {
760 messages::propertyValueTypeError(
761 asyncResp->res, formpart.content, "UpdateParameters");
762 return std::nullopt;
763 }
764
765 if (!json_util::readJsonObject( //
766 *obj, asyncResp->res, //
767 "@Redfish.OperationApplyTime", multiRet.applyTime, //
768 "Targets", tempTargets //
769 ))
770 {
771 return std::nullopt;
772 }
773
774 for (size_t urlIndex = 0; urlIndex < tempTargets.size();
775 urlIndex++)
776 {
777 const std::string& target = tempTargets[urlIndex];
778 boost::system::result<boost::urls::url_view> url =
779 boost::urls::parse_origin_form(target);
780 auto res = processUrl(url);
781 if (!res.has_value())
782 {
783 messages::propertyValueFormatError(
784 asyncResp->res, target,
785 std::format("Targets/{}", urlIndex));
786 return std::nullopt;
787 }
788 multiRet.targets.emplace_back(res.value());
789 }
790 if (multiRet.targets.size() != 1)
791 {
792 messages::propertyValueFormatError(
793 asyncResp->res, multiRet.targets, "Targets");
794 return std::nullopt;
795 }
796 }
797 else if (param.second == "UpdateFile")
798 {
799 multiRet.uploadData = std::move(formpart.content);
800 }
801 }
802 }
803
804 if (multiRet.uploadData.empty())
805 {
806 BMCWEB_LOG_ERROR("Upload data is NULL");
807 messages::propertyMissing(asyncResp->res, "UpdateFile");
808 return std::nullopt;
809 }
810 if (multiRet.targets.empty())
811 {
812 messages::propertyMissing(asyncResp->res, "Targets");
813 return std::nullopt;
814 }
815 return multiRet;
816 }
817
handleStartUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload payload,const std::string & objectPath,const boost::system::error_code & ec,const sdbusplus::message::object_path & retPath)818 inline void handleStartUpdate(
819 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
820 const std::string& objectPath, const boost::system::error_code& ec,
821 const sdbusplus::message::object_path& retPath)
822 {
823 if (ec)
824 {
825 BMCWEB_LOG_ERROR("error_code = {}", ec);
826 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
827 messages::internalError(asyncResp->res);
828 return;
829 }
830
831 BMCWEB_LOG_INFO("Call to StartUpdate on {} Success, retPath = {}",
832 objectPath, retPath.str);
833 createTask(asyncResp, std::move(payload), retPath);
834 }
835
startUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload payload,const MemoryFileDescriptor & memfd,const std::string & applyTime,const std::string & objectPath,const std::string & serviceName)836 inline void startUpdate(
837 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
838 const MemoryFileDescriptor& memfd, const std::string& applyTime,
839 const std::string& objectPath, const std::string& serviceName)
840 {
841 crow::connections::systemBus->async_method_call(
842 [asyncResp, payload = std::move(payload),
843 objectPath](const boost::system::error_code& ec1,
844 const sdbusplus::message::object_path& retPath) mutable {
845 handleStartUpdate(asyncResp, std::move(payload), objectPath, ec1,
846 retPath);
847 },
848 serviceName, objectPath, "xyz.openbmc_project.Software.Update",
849 "StartUpdate", sdbusplus::message::unix_fd(memfd.fd), applyTime);
850 }
851
getSwInfo(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload payload,const MemoryFileDescriptor & memfd,const std::string & applyTime,const std::string & target,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)852 inline void getSwInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
853 task::Payload payload, const MemoryFileDescriptor& memfd,
854 const std::string& applyTime, const std::string& target,
855 const boost::system::error_code& ec,
856 const dbus::utility::MapperGetSubTreeResponse& subtree)
857 {
858 using SwInfoMap = std::unordered_map<
859 std::string, std::pair<sdbusplus::message::object_path, std::string>>;
860 SwInfoMap swInfoMap;
861
862 if (ec)
863 {
864 BMCWEB_LOG_ERROR("error_code = {}", ec);
865 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
866 messages::internalError(asyncResp->res);
867 return;
868 }
869 BMCWEB_LOG_DEBUG("Found {} software version paths", subtree.size());
870
871 for (const auto& entry : subtree)
872 {
873 sdbusplus::message::object_path path(entry.first);
874 std::string swId = path.filename();
875 swInfoMap.emplace(swId, make_pair(path, entry.second[0].first));
876 }
877
878 auto swEntry = swInfoMap.find(target);
879 if (swEntry == swInfoMap.end())
880 {
881 BMCWEB_LOG_WARNING("No valid DBus path for Target URI {}", target);
882 messages::propertyValueFormatError(asyncResp->res, target, "Targets");
883 return;
884 }
885
886 BMCWEB_LOG_DEBUG("Found software version path {} serviceName {}",
887 swEntry->second.first.str, swEntry->second.second);
888
889 startUpdate(asyncResp, std::move(payload), memfd, applyTime,
890 swEntry->second.first.str, swEntry->second.second);
891 }
892
handleBMCUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload payload,const MemoryFileDescriptor & memfd,const std::string & applyTime,const boost::system::error_code & ec,const dbus::utility::MapperEndPoints & functionalSoftware)893 inline void handleBMCUpdate(
894 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, task::Payload payload,
895 const MemoryFileDescriptor& memfd, const std::string& applyTime,
896 const boost::system::error_code& ec,
897 const dbus::utility::MapperEndPoints& functionalSoftware)
898 {
899 if (ec)
900 {
901 BMCWEB_LOG_ERROR("error_code = {}", ec);
902 BMCWEB_LOG_ERROR("error msg = {}", ec.message());
903 messages::internalError(asyncResp->res);
904 return;
905 }
906 if (functionalSoftware.size() != 1)
907 {
908 BMCWEB_LOG_ERROR("Found {} functional software endpoints",
909 functionalSoftware.size());
910 messages::internalError(asyncResp->res);
911 return;
912 }
913
914 startUpdate(asyncResp, std::move(payload), memfd, applyTime,
915 functionalSoftware[0], "xyz.openbmc_project.Software.Manager");
916 }
917
processUpdateRequest(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,task::Payload && payload,std::string_view body,const std::string & applyTime,std::vector<std::string> & targets)918 inline void processUpdateRequest(
919 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
920 task::Payload&& payload, std::string_view body,
921 const std::string& applyTime, std::vector<std::string>& targets)
922 {
923 MemoryFileDescriptor memfd("update-image");
924 if (memfd.fd == -1)
925 {
926 BMCWEB_LOG_ERROR("Failed to create image memfd");
927 messages::internalError(asyncResp->res);
928 return;
929 }
930 if (write(memfd.fd, body.data(), body.length()) !=
931 static_cast<ssize_t>(body.length()))
932 {
933 BMCWEB_LOG_ERROR("Failed to write to image memfd");
934 messages::internalError(asyncResp->res);
935 return;
936 }
937 if (!memfd.rewind())
938 {
939 messages::internalError(asyncResp->res);
940 return;
941 }
942
943 if (!targets.empty() && targets[0] == BMCWEB_REDFISH_MANAGER_URI_NAME)
944 {
945 dbus::utility::getAssociationEndPoints(
946 "/xyz/openbmc_project/software/bmc/updateable",
947 [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
948 applyTime](
949 const boost::system::error_code& ec,
950 const dbus::utility::MapperEndPoints& objectPaths) mutable {
951 handleBMCUpdate(asyncResp, std::move(payload), memfd, applyTime,
952 ec, objectPaths);
953 });
954 }
955 else
956 {
957 constexpr std::array<std::string_view, 1> interfaces = {
958 "xyz.openbmc_project.Software.Version"};
959 dbus::utility::getSubTree(
960 "/xyz/openbmc_project/software", 1, interfaces,
961 [asyncResp, payload = std::move(payload), memfd = std::move(memfd),
962 applyTime, targets](const boost::system::error_code& ec,
963 const dbus::utility::MapperGetSubTreeResponse&
964 subtree) mutable {
965 getSwInfo(asyncResp, std::move(payload), memfd, applyTime,
966 targets[0], ec, subtree);
967 });
968 }
969 }
970
971 inline void
updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req,MultipartParser && parser)972 updateMultipartContext(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
973 const crow::Request& req, MultipartParser&& parser)
974 {
975 std::optional<MultiPartUpdateParameters> multipart =
976 extractMultipartUpdateParameters(asyncResp, std::move(parser));
977 if (!multipart)
978 {
979 return;
980 }
981 if (!multipart->applyTime)
982 {
983 multipart->applyTime = "OnReset";
984 }
985
986 if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
987 {
988 std::string applyTimeNewVal;
989 if (!convertApplyTime(asyncResp->res, *multipart->applyTime,
990 applyTimeNewVal))
991 {
992 return;
993 }
994 task::Payload payload(req);
995
996 processUpdateRequest(asyncResp, std::move(payload),
997 multipart->uploadData, applyTimeNewVal,
998 multipart->targets);
999 }
1000 else
1001 {
1002 setApplyTime(asyncResp, *multipart->applyTime);
1003
1004 // Setup callback for when new software detected
1005 monitorForSoftwareAvailable(asyncResp, req,
1006 "/redfish/v1/UpdateService");
1007
1008 uploadImageFile(asyncResp->res, multipart->uploadData);
1009 }
1010 }
1011
doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req)1012 inline void doHTTPUpdate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1013 const crow::Request& req)
1014 {
1015 if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
1016 {
1017 task::Payload payload(req);
1018 // HTTP push only supports BMC updates (with ApplyTime as immediate) for
1019 // backwards compatibility. Specific component updates will be handled
1020 // through Multipart form HTTP push.
1021 std::vector<std::string> targets;
1022 targets.emplace_back(BMCWEB_REDFISH_MANAGER_URI_NAME);
1023
1024 processUpdateRequest(
1025 asyncResp, std::move(payload), req.body(),
1026 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.Immediate",
1027 targets);
1028 }
1029 else
1030 {
1031 // Setup callback for when new software detected
1032 monitorForSoftwareAvailable(asyncResp, req,
1033 "/redfish/v1/UpdateService");
1034
1035 uploadImageFile(asyncResp->res, req.body());
1036 }
1037 }
1038
1039 inline void
handleUpdateServicePost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1040 handleUpdateServicePost(App& app, const crow::Request& req,
1041 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1042 {
1043 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1044 {
1045 return;
1046 }
1047 std::string_view contentType = req.getHeaderValue("Content-Type");
1048
1049 BMCWEB_LOG_DEBUG("doPost: contentType={}", contentType);
1050
1051 // Make sure that content type is application/octet-stream or
1052 // multipart/form-data
1053 if (bmcweb::asciiIEquals(contentType, "application/octet-stream"))
1054 {
1055 doHTTPUpdate(asyncResp, req);
1056 }
1057 else if (contentType.starts_with("multipart/form-data"))
1058 {
1059 MultipartParser parser;
1060
1061 ParserError ec = parser.parse(req);
1062 if (ec != ParserError::PARSER_SUCCESS)
1063 {
1064 // handle error
1065 BMCWEB_LOG_ERROR("MIME parse failed, ec : {}",
1066 static_cast<int>(ec));
1067 messages::internalError(asyncResp->res);
1068 return;
1069 }
1070
1071 updateMultipartContext(asyncResp, req, std::move(parser));
1072 }
1073 else
1074 {
1075 BMCWEB_LOG_DEBUG("Bad content type specified:{}", contentType);
1076 asyncResp->res.result(boost::beast::http::status::bad_request);
1077 }
1078 }
1079
1080 inline void
handleUpdateServiceGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1081 handleUpdateServiceGet(App& app, const crow::Request& req,
1082 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1083 {
1084 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1085 {
1086 return;
1087 }
1088 asyncResp->res.jsonValue["@odata.type"] =
1089 "#UpdateService.v1_11_1.UpdateService";
1090 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/UpdateService";
1091 asyncResp->res.jsonValue["Id"] = "UpdateService";
1092 asyncResp->res.jsonValue["Description"] = "Service for Software Update";
1093 asyncResp->res.jsonValue["Name"] = "Update Service";
1094
1095 asyncResp->res.jsonValue["HttpPushUri"] =
1096 "/redfish/v1/UpdateService/update";
1097 asyncResp->res.jsonValue["MultipartHttpPushUri"] =
1098 "/redfish/v1/UpdateService/update";
1099
1100 // UpdateService cannot be disabled
1101 asyncResp->res.jsonValue["ServiceEnabled"] = true;
1102 asyncResp->res.jsonValue["FirmwareInventory"]["@odata.id"] =
1103 "/redfish/v1/UpdateService/FirmwareInventory";
1104 // Get the MaxImageSizeBytes
1105 asyncResp->res.jsonValue["MaxImageSizeBytes"] =
1106 BMCWEB_HTTP_BODY_LIMIT * 1024 * 1024;
1107
1108 // Update Actions object.
1109 nlohmann::json& updateSvcSimpleUpdate =
1110 asyncResp->res.jsonValue["Actions"]["#UpdateService.SimpleUpdate"];
1111 updateSvcSimpleUpdate["target"] =
1112 "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate";
1113
1114 nlohmann::json::array_t allowed;
1115 allowed.emplace_back(update_service::TransferProtocolType::HTTPS);
1116
1117 if constexpr (BMCWEB_INSECURE_PUSH_STYLE_NOTIFICATION)
1118 {
1119 allowed.emplace_back(update_service::TransferProtocolType::TFTP);
1120 }
1121
1122 updateSvcSimpleUpdate["TransferProtocol@Redfish.AllowableValues"] =
1123 std::move(allowed);
1124
1125 asyncResp->res
1126 .jsonValue["HttpPushUriOptions"]["HttpPushUriApplyTime"]["ApplyTime"] =
1127 update_service::ApplyTime::Immediate;
1128 }
1129
handleUpdateServiceFirmwareInventoryCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1130 inline void handleUpdateServiceFirmwareInventoryCollectionGet(
1131 App& app, const crow::Request& req,
1132 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1133 {
1134 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1135 {
1136 return;
1137 }
1138 asyncResp->res.jsonValue["@odata.type"] =
1139 "#SoftwareInventoryCollection.SoftwareInventoryCollection";
1140 asyncResp->res.jsonValue["@odata.id"] =
1141 "/redfish/v1/UpdateService/FirmwareInventory";
1142 asyncResp->res.jsonValue["Name"] = "Software Inventory Collection";
1143 const std::array<const std::string_view, 1> iface = {
1144 "xyz.openbmc_project.Software.Version"};
1145
1146 redfish::collection_util::getCollectionMembers(
1147 asyncResp,
1148 boost::urls::url("/redfish/v1/UpdateService/FirmwareInventory"), iface,
1149 "/xyz/openbmc_project/software");
1150 }
1151
1152 /* Fill related item links (i.e. bmc, bios) in for inventory */
getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & purpose)1153 inline void getRelatedItems(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1154 const std::string& purpose)
1155 {
1156 if (purpose == sw_util::bmcPurpose)
1157 {
1158 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
1159 nlohmann::json::object_t item;
1160 item["@odata.id"] = boost::urls::format(
1161 "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
1162 relatedItem.emplace_back(std::move(item));
1163 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1164 relatedItem.size();
1165 }
1166 else if (purpose == sw_util::biosPurpose)
1167 {
1168 nlohmann::json& relatedItem = asyncResp->res.jsonValue["RelatedItem"];
1169 nlohmann::json::object_t item;
1170 item["@odata.id"] = std::format("/redfish/v1/Systems/{}/Bios",
1171 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1172 relatedItem.emplace_back(std::move(item));
1173 asyncResp->res.jsonValue["RelatedItem@odata.count"] =
1174 relatedItem.size();
1175 }
1176 else
1177 {
1178 BMCWEB_LOG_DEBUG("Unknown software purpose {}", purpose);
1179 }
1180 }
1181
1182 inline void
getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & path,const std::string & swId)1183 getSoftwareVersion(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1184 const std::string& service, const std::string& path,
1185 const std::string& swId)
1186 {
1187 sdbusplus::asio::getAllProperties(
1188 *crow::connections::systemBus, service, path,
1189 "xyz.openbmc_project.Software.Version",
1190 [asyncResp,
1191 swId](const boost::system::error_code& ec,
1192 const dbus::utility::DBusPropertiesMap& propertiesList) {
1193 if (ec)
1194 {
1195 messages::internalError(asyncResp->res);
1196 return;
1197 }
1198
1199 const std::string* swInvPurpose = nullptr;
1200 const std::string* version = nullptr;
1201
1202 const bool success = sdbusplus::unpackPropertiesNoThrow(
1203 dbus_utils::UnpackErrorPrinter(), propertiesList, "Purpose",
1204 swInvPurpose, "Version", version);
1205
1206 if (!success)
1207 {
1208 messages::internalError(asyncResp->res);
1209 return;
1210 }
1211
1212 if (swInvPurpose == nullptr)
1213 {
1214 BMCWEB_LOG_DEBUG("Can't find property \"Purpose\"!");
1215 messages::internalError(asyncResp->res);
1216 return;
1217 }
1218
1219 BMCWEB_LOG_DEBUG("swInvPurpose = {}", *swInvPurpose);
1220
1221 if (version == nullptr)
1222 {
1223 BMCWEB_LOG_DEBUG("Can't find property \"Version\"!");
1224
1225 messages::internalError(asyncResp->res);
1226
1227 return;
1228 }
1229 asyncResp->res.jsonValue["Version"] = *version;
1230 asyncResp->res.jsonValue["Id"] = swId;
1231
1232 // swInvPurpose is of format:
1233 // xyz.openbmc_project.Software.Version.VersionPurpose.ABC
1234 // Translate this to "ABC image"
1235 size_t endDesc = swInvPurpose->rfind('.');
1236 if (endDesc == std::string::npos)
1237 {
1238 messages::internalError(asyncResp->res);
1239 return;
1240 }
1241 endDesc++;
1242 if (endDesc >= swInvPurpose->size())
1243 {
1244 messages::internalError(asyncResp->res);
1245 return;
1246 }
1247
1248 std::string formatDesc = swInvPurpose->substr(endDesc);
1249 asyncResp->res.jsonValue["Description"] = formatDesc + " image";
1250 getRelatedItems(asyncResp, *swInvPurpose);
1251 });
1252 }
1253
handleUpdateServiceFirmwareInventoryGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & param)1254 inline void handleUpdateServiceFirmwareInventoryGet(
1255 App& app, const crow::Request& req,
1256 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1257 const std::string& param)
1258 {
1259 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1260 {
1261 return;
1262 }
1263 std::shared_ptr<std::string> swId = std::make_shared<std::string>(param);
1264
1265 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1266 "/redfish/v1/UpdateService/FirmwareInventory/{}", *swId);
1267
1268 constexpr std::array<std::string_view, 1> interfaces = {
1269 "xyz.openbmc_project.Software.Version"};
1270 dbus::utility::getSubTree(
1271 "/", 0, interfaces,
1272 [asyncResp,
1273 swId](const boost::system::error_code& ec,
1274 const dbus::utility::MapperGetSubTreeResponse& subtree) {
1275 BMCWEB_LOG_DEBUG("doGet callback...");
1276 if (ec)
1277 {
1278 messages::internalError(asyncResp->res);
1279 return;
1280 }
1281
1282 // Ensure we find our input swId, otherwise return an error
1283 bool found = false;
1284 for (const std::pair<std::string,
1285 std::vector<std::pair<
1286 std::string, std::vector<std::string>>>>&
1287 obj : subtree)
1288 {
1289 if (!obj.first.ends_with(*swId))
1290 {
1291 continue;
1292 }
1293
1294 if (obj.second.empty())
1295 {
1296 continue;
1297 }
1298
1299 found = true;
1300 sw_util::getSwStatus(asyncResp, swId, obj.second[0].first);
1301 getSoftwareVersion(asyncResp, obj.second[0].first, obj.first,
1302 *swId);
1303 }
1304 if (!found)
1305 {
1306 BMCWEB_LOG_WARNING("Input swID {} not found!", *swId);
1307 messages::resourceMissingAtURI(
1308 asyncResp->res,
1309 boost::urls::format(
1310 "/redfish/v1/UpdateService/FirmwareInventory/{}",
1311 *swId));
1312 return;
1313 }
1314 asyncResp->res.jsonValue["@odata.type"] =
1315 "#SoftwareInventory.v1_1_0.SoftwareInventory";
1316 asyncResp->res.jsonValue["Name"] = "Software Inventory";
1317 asyncResp->res.jsonValue["Status"]["HealthRollup"] =
1318 resource::Health::OK;
1319
1320 asyncResp->res.jsonValue["Updateable"] = false;
1321 sw_util::getSwUpdatableStatus(asyncResp, swId);
1322 });
1323 }
1324
requestRoutesUpdateService(App & app)1325 inline void requestRoutesUpdateService(App& app)
1326 {
1327 BMCWEB_ROUTE(
1328 app, "/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate/")
1329 .privileges(redfish::privileges::postUpdateService)
1330 .methods(boost::beast::http::verb::post)(std::bind_front(
1331 handleUpdateServiceSimpleUpdateAction, std::ref(app)));
1332
1333 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/<str>/")
1334 .privileges(redfish::privileges::getSoftwareInventory)
1335 .methods(boost::beast::http::verb::get)(std::bind_front(
1336 handleUpdateServiceFirmwareInventoryGet, std::ref(app)));
1337
1338 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/")
1339 .privileges(redfish::privileges::getUpdateService)
1340 .methods(boost::beast::http::verb::get)(
1341 std::bind_front(handleUpdateServiceGet, std::ref(app)));
1342
1343 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/update/")
1344 .privileges(redfish::privileges::postUpdateService)
1345 .methods(boost::beast::http::verb::post)(
1346 std::bind_front(handleUpdateServicePost, std::ref(app)));
1347
1348 BMCWEB_ROUTE(app, "/redfish/v1/UpdateService/FirmwareInventory/")
1349 .privileges(redfish::privileges::getSoftwareInventoryCollection)
1350 .methods(boost::beast::http::verb::get)(std::bind_front(
1351 handleUpdateServiceFirmwareInventoryCollectionGet, std::ref(app)));
1352 }
1353
1354 } // namespace redfish
1355