1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5
6 #include "app.hpp"
7 #include "async_resp.hpp"
8 #include "credential_pipe.hpp"
9 #include "dbus_singleton.hpp"
10 #include "dbus_utility.hpp"
11 #include "error_messages.hpp"
12 #include "generated/enums/virtual_media.hpp"
13 #include "http_request.hpp"
14 #include "logging.hpp"
15 #include "query.hpp"
16 #include "registries/privilege_registry.hpp"
17 #include "utils/json_utils.hpp"
18
19 #include <boost/beast/http/status.hpp>
20 #include <boost/beast/http/verb.hpp>
21 #include <boost/system/result.hpp>
22 #include <boost/url/format.hpp>
23 #include <boost/url/parse.hpp>
24 #include <boost/url/url_view.hpp>
25 #include <boost/url/url_view_base.hpp>
26 #include <sdbusplus/message/native_types.hpp>
27
28 #include <cstddef>
29 #include <filesystem>
30 #include <functional>
31 #include <memory>
32 #include <optional>
33 #include <ranges>
34 #include <string>
35 #include <string_view>
36 #include <utility>
37 #include <variant>
38
39 namespace redfish
40 {
41
42 enum class VmMode
43 {
44 Invalid,
45 Legacy,
46 Proxy
47 };
48
parseObjectPathAndGetMode(const sdbusplus::message::object_path & itemPath,const std::string & resName)49 inline VmMode parseObjectPathAndGetMode(
50 const sdbusplus::message::object_path& itemPath, const std::string& resName)
51 {
52 std::string thisPath = itemPath.filename();
53 BMCWEB_LOG_DEBUG("Filename: {}, ThisPath: {}", itemPath.str, thisPath);
54
55 if (thisPath.empty())
56 {
57 return VmMode::Invalid;
58 }
59
60 if (thisPath != resName)
61 {
62 return VmMode::Invalid;
63 }
64
65 auto mode = itemPath.parent_path();
66 auto type = mode.parent_path();
67
68 if (mode.filename().empty() || type.filename().empty())
69 {
70 return VmMode::Invalid;
71 }
72
73 if (type.filename() != "VirtualMedia")
74 {
75 return VmMode::Invalid;
76 }
77 std::string modeStr = mode.filename();
78 if (modeStr == "Legacy")
79 {
80 return VmMode::Legacy;
81 }
82 if (modeStr == "Proxy")
83 {
84 return VmMode::Proxy;
85 }
86 return VmMode::Invalid;
87 }
88
89 using CheckItemHandler =
90 std::function<void(const std::string& service, const std::string& resName,
91 const std::shared_ptr<bmcweb::AsyncResp>&,
92 const std::pair<sdbusplus::message::object_path,
93 dbus::utility::DBusInterfacesMap>&)>;
94
findAndParseObject(const std::string & service,const std::string & resName,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,CheckItemHandler && handler)95 inline void findAndParseObject(
96 const std::string& service, const std::string& resName,
97 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
98 CheckItemHandler&& handler)
99 {
100 sdbusplus::message::object_path path("/xyz/openbmc_project/VirtualMedia");
101 dbus::utility::getManagedObjects(
102 service, path,
103 [service, resName, asyncResp, handler = std::move(handler)](
104 const boost::system::error_code& ec,
105 const dbus::utility::ManagedObjectType& subtree) {
106 if (ec)
107 {
108 BMCWEB_LOG_DEBUG("DBUS response error");
109
110 return;
111 }
112
113 for (const auto& item : subtree)
114 {
115 VmMode mode = parseObjectPathAndGetMode(item.first, resName);
116 if (mode != VmMode::Invalid)
117 {
118 handler(service, resName, asyncResp, item);
119 return;
120 }
121 }
122
123 BMCWEB_LOG_DEBUG("Parent item not found");
124 asyncResp->res.result(boost::beast::http::status::not_found);
125 });
126 }
127
128 /**
129 * @brief Function extracts transfer protocol name from URI.
130 */
getTransferProtocolTypeFromUri(const std::string & imageUri)131 inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri)
132 {
133 boost::system::result<boost::urls::url_view> url =
134 boost::urls::parse_uri(imageUri);
135 if (!url)
136 {
137 return "None";
138 }
139 std::string_view scheme = url->scheme();
140 if (scheme == "smb")
141 {
142 return "CIFS";
143 }
144 if (scheme == "https")
145 {
146 return "HTTPS";
147 }
148
149 return "None";
150 }
151
152 /**
153 * @brief Read all known properties from VM object interfaces
154 */
vmParseInterfaceObject(const dbus::utility::DBusInterfacesMap & interfaces,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)155 inline void vmParseInterfaceObject(
156 const dbus::utility::DBusInterfacesMap& interfaces,
157 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
158 {
159 for (const auto& [interface, values] : interfaces)
160 {
161 if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint")
162 {
163 for (const auto& [property, value] : values)
164 {
165 if (property == "EndpointId")
166 {
167 const std::string* endpointIdValue =
168 std::get_if<std::string>(&value);
169 if (endpointIdValue == nullptr)
170 {
171 continue;
172 }
173 if (!endpointIdValue->empty())
174 {
175 // Proxy mode
176 asyncResp->res
177 .jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
178 *endpointIdValue;
179 asyncResp->res.jsonValue["TransferProtocolType"] =
180 "OEM";
181 }
182 }
183 if (property == "ImageURL")
184 {
185 const std::string* imageUrlValue =
186 std::get_if<std::string>(&value);
187 if (imageUrlValue != nullptr && !imageUrlValue->empty())
188 {
189 std::filesystem::path filePath = *imageUrlValue;
190 if (!filePath.has_filename())
191 {
192 // this will handle https share, which not
193 // necessarily has to have filename given.
194 asyncResp->res.jsonValue["ImageName"] = "";
195 }
196 else
197 {
198 asyncResp->res.jsonValue["ImageName"] =
199 filePath.filename();
200 }
201
202 asyncResp->res.jsonValue["Image"] = *imageUrlValue;
203 asyncResp->res.jsonValue["TransferProtocolType"] =
204 getTransferProtocolTypeFromUri(*imageUrlValue);
205
206 asyncResp->res.jsonValue["ConnectedVia"] =
207 virtual_media::ConnectedVia::URI;
208 }
209 }
210 if (property == "WriteProtected")
211 {
212 const bool* writeProtectedValue = std::get_if<bool>(&value);
213 if (writeProtectedValue != nullptr)
214 {
215 asyncResp->res.jsonValue["WriteProtected"] =
216 *writeProtectedValue;
217 }
218 }
219 }
220 }
221 if (interface == "xyz.openbmc_project.VirtualMedia.Process")
222 {
223 for (const auto& [property, value] : values)
224 {
225 if (property == "Active")
226 {
227 const bool* activeValue = std::get_if<bool>(&value);
228 if (activeValue == nullptr)
229 {
230 BMCWEB_LOG_DEBUG("Value Active not found");
231 return;
232 }
233 asyncResp->res.jsonValue["Inserted"] = *activeValue;
234
235 if (*activeValue)
236 {
237 asyncResp->res.jsonValue["ConnectedVia"] =
238 virtual_media::ConnectedVia::Applet;
239 }
240 }
241 }
242 }
243 }
244 }
245
246 /**
247 * @brief Fill template for Virtual Media Item.
248 */
vmItemTemplate(const std::string & name,const std::string & resName)249 inline nlohmann::json vmItemTemplate(const std::string& name,
250 const std::string& resName)
251 {
252 nlohmann::json item;
253 item["@odata.id"] = boost::urls::format(
254 "/redfish/v1/Managers/{}/VirtualMedia/{}", name, resName);
255
256 item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
257 item["Name"] = "Virtual Removable Media";
258 item["Id"] = resName;
259 item["WriteProtected"] = true;
260 item["ConnectedVia"] = virtual_media::ConnectedVia::NotConnected;
261 item["MediaTypes"] = nlohmann::json::array_t({"CD", "USBStick"});
262 item["TransferMethod"] = virtual_media::TransferMethod::Stream;
263 item["Oem"]["OpenBMC"]["@odata.type"] =
264 "#OpenBMCVirtualMedia.v1_0_0.VirtualMedia";
265 item["Oem"]["OpenBMC"]["@odata.id"] = boost::urls::format(
266 "/redfish/v1/Managers/{}/VirtualMedia/{}#/Oem/OpenBMC", name, resName);
267
268 return item;
269 }
270
271 /**
272 * @brief Fills collection data
273 */
getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & service,const std::string & name)274 inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
275 const std::string& service,
276 const std::string& name)
277 {
278 BMCWEB_LOG_DEBUG("Get available Virtual Media resources.");
279 sdbusplus::message::object_path objPath(
280 "/xyz/openbmc_project/VirtualMedia");
281 dbus::utility::getManagedObjects(
282 service, objPath,
283 [name, asyncResp{std::move(asyncResp)}](
284 const boost::system::error_code& ec,
285 const dbus::utility::ManagedObjectType& subtree) {
286 if (ec)
287 {
288 BMCWEB_LOG_DEBUG("DBUS response error");
289 return;
290 }
291 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
292 members = nlohmann::json::array();
293
294 for (const auto& object : subtree)
295 {
296 nlohmann::json item;
297 std::string path = object.first.filename();
298 if (path.empty())
299 {
300 continue;
301 }
302
303 item["@odata.id"] = boost::urls::format(
304 "/redfish/v1/Managers/{}/VirtualMedia/{}", name, path);
305 members.emplace_back(std::move(item));
306 }
307 asyncResp->res.jsonValue["Members@odata.count"] = members.size();
308 });
309 }
310
afterGetVmData(const std::string & name,const std::string &,const std::string & resName,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::pair<sdbusplus::message::object_path,dbus::utility::DBusInterfacesMap> & item)311 inline void afterGetVmData(
312 const std::string& name, const std::string& /*service*/,
313 const std::string& resName,
314 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
315 const std::pair<sdbusplus::message::object_path,
316 dbus::utility::DBusInterfacesMap>& item)
317 {
318 VmMode mode = parseObjectPathAndGetMode(item.first, resName);
319 if (mode == VmMode::Invalid)
320 {
321 return;
322 }
323
324 asyncResp->res.jsonValue = vmItemTemplate(name, resName);
325
326 // Check if dbus path is Legacy type
327 if (mode == VmMode::Legacy)
328 {
329 asyncResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
330 ["target"] = boost::urls::format(
331 "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.InsertMedia",
332 name, resName);
333 }
334
335 vmParseInterfaceObject(item.second, asyncResp);
336
337 asyncResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"]
338 ["target"] = boost::urls::format(
339 "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.EjectMedia",
340 name, resName);
341 }
342
343 /**
344 * @brief Fills data for specific resource
345 */
getVmData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,const std::string & resName)346 inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
347 const std::string& service, const std::string& name,
348 const std::string& resName)
349 {
350 BMCWEB_LOG_DEBUG("Get Virtual Media resource data.");
351
352 findAndParseObject(service, resName, asyncResp,
353 std::bind_front(afterGetVmData, name));
354 }
355
356 /**
357 * @brief Transfer protocols supported for InsertMedia action.
358 *
359 */
360 enum class TransferProtocol
361 {
362 https,
363 smb,
364 invalid
365 };
366
367 /**
368 * @brief Function extracts transfer protocol type from URI.
369 *
370 */
getTransferProtocolFromUri(const boost::urls::url_view_base & imageUri)371 inline std::optional<TransferProtocol> getTransferProtocolFromUri(
372 const boost::urls::url_view_base& imageUri)
373 {
374 std::string_view scheme = imageUri.scheme();
375 if (scheme == "smb")
376 {
377 return TransferProtocol::smb;
378 }
379 if (scheme == "https")
380 {
381 return TransferProtocol::https;
382 }
383 if (!scheme.empty())
384 {
385 return TransferProtocol::invalid;
386 }
387
388 return {};
389 }
390
391 /**
392 * @brief Function convert transfer protocol from string param.
393 *
394 */
getTransferProtocolFromParam(const std::optional<std::string> & transferProtocolType)395 inline std::optional<TransferProtocol> getTransferProtocolFromParam(
396 const std::optional<std::string>& transferProtocolType)
397 {
398 if (!transferProtocolType)
399 {
400 return {};
401 }
402
403 if (*transferProtocolType == "CIFS")
404 {
405 return TransferProtocol::smb;
406 }
407
408 if (*transferProtocolType == "HTTPS")
409 {
410 return TransferProtocol::https;
411 }
412
413 return TransferProtocol::invalid;
414 }
415
416 /**
417 * @brief Function extends URI with transfer protocol type.
418 *
419 */
getUriWithTransferProtocol(const std::string & imageUri,const TransferProtocol & transferProtocol)420 inline std::string getUriWithTransferProtocol(
421 const std::string& imageUri, const TransferProtocol& transferProtocol)
422 {
423 if (transferProtocol == TransferProtocol::smb)
424 {
425 return "smb://" + imageUri;
426 }
427
428 if (transferProtocol == TransferProtocol::https)
429 {
430 return "https://" + imageUri;
431 }
432
433 return imageUri;
434 }
435
436 struct InsertMediaActionParams
437 {
438 std::optional<std::string> imageUrl;
439 std::optional<std::string> userName;
440 std::optional<std::string> password;
441 std::optional<std::string> transferMethod;
442 std::optional<std::string> transferProtocolType;
443 std::optional<bool> writeProtected = true;
444 std::optional<bool> inserted;
445 };
446
447 /**
448 * @brief Function transceives data with dbus directly.
449 *
450 * All BMC state properties will be retrieved before sending reset request.
451 */
doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,const std::string & imageUrl,bool rw,std::string && userName,std::string && password)452 inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
453 const std::string& service, const std::string& name,
454 const std::string& imageUrl, bool rw,
455 std::string&& userName, std::string&& password)
456 {
457 int fd = -1;
458 std::shared_ptr<CredentialsPipe> secretPipe;
459 if (!userName.empty() || !password.empty())
460 {
461 // Payload must contain data + NULL delimiters
462 constexpr const size_t secretLimit = 1024;
463 if (userName.size() + password.size() + 2 > secretLimit)
464 {
465 BMCWEB_LOG_ERROR("Credentials too long to handle");
466 messages::unrecognizedRequestBody(asyncResp->res);
467 return;
468 }
469
470 // Open pipe
471 secretPipe = std::make_shared<CredentialsPipe>(
472 crow::connections::systemBus->get_io_context());
473 fd = secretPipe->releaseFd();
474
475 // Pass secret over pipe
476 secretPipe->asyncWrite(
477 std::move(userName), std::move(password),
478 [asyncResp,
479 secretPipe](const boost::system::error_code& ec, std::size_t) {
480 if (ec)
481 {
482 BMCWEB_LOG_ERROR("Failed to pass secret: {}", ec);
483 messages::internalError(asyncResp->res);
484 }
485 });
486 }
487
488 std::variant<sdbusplus::message::unix_fd> unixFd(
489 std::in_place_type<sdbusplus::message::unix_fd>, fd);
490
491 sdbusplus::message::object_path path(
492 "/xyz/openbmc_project/VirtualMedia/Legacy");
493 path /= name;
494 crow::connections::systemBus->async_method_call(
495 [asyncResp,
496 secretPipe](const boost::system::error_code& ec, bool success) {
497 if (ec)
498 {
499 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
500 messages::internalError(asyncResp->res);
501 return;
502 }
503 if (!success)
504 {
505 BMCWEB_LOG_ERROR("Service responded with error");
506 messages::internalError(asyncResp->res);
507 }
508 },
509 service, path.str, "xyz.openbmc_project.VirtualMedia.Legacy", "Mount",
510 imageUrl, rw, unixFd);
511 }
512
513 /**
514 * @brief Function validate parameters of insert media request.
515 *
516 */
validateParams(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & resName,InsertMediaActionParams & actionParams)517 inline void validateParams(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
518 const std::string& service,
519 const std::string& resName,
520 InsertMediaActionParams& actionParams)
521 {
522 BMCWEB_LOG_DEBUG("Validation started");
523 // required param imageUrl must not be empty
524 if (!actionParams.imageUrl)
525 {
526 BMCWEB_LOG_ERROR("Request action parameter Image is empty.");
527
528 messages::propertyValueFormatError(asyncResp->res, "<empty>", "Image");
529
530 return;
531 }
532
533 // optional param inserted must be true
534 if (actionParams.inserted && !*actionParams.inserted)
535 {
536 BMCWEB_LOG_ERROR(
537 "Request action optional parameter Inserted must be true.");
538
539 messages::actionParameterNotSupported(asyncResp->res, "Inserted",
540 "InsertMedia");
541
542 return;
543 }
544
545 // optional param transferMethod must be stream
546 if (actionParams.transferMethod &&
547 (*actionParams.transferMethod != "Stream"))
548 {
549 BMCWEB_LOG_ERROR("Request action optional parameter "
550 "TransferMethod must be Stream.");
551
552 messages::actionParameterNotSupported(asyncResp->res, "TransferMethod",
553 "InsertMedia");
554
555 return;
556 }
557 boost::system::result<boost::urls::url_view> url =
558 boost::urls::parse_uri(*actionParams.imageUrl);
559 if (!url)
560 {
561 messages::actionParameterValueFormatError(
562 asyncResp->res, *actionParams.imageUrl, "Image", "InsertMedia");
563 return;
564 }
565 std::optional<TransferProtocol> uriTransferProtocolType =
566 getTransferProtocolFromUri(*url);
567
568 std::optional<TransferProtocol> paramTransferProtocolType =
569 getTransferProtocolFromParam(actionParams.transferProtocolType);
570
571 // ImageUrl does not contain valid protocol type
572 if (uriTransferProtocolType &&
573 *uriTransferProtocolType == TransferProtocol::invalid)
574 {
575 BMCWEB_LOG_ERROR("Request action parameter ImageUrl must "
576 "contain specified protocol type from list: "
577 "(smb, https).");
578
579 messages::resourceAtUriInUnknownFormat(asyncResp->res, *url);
580
581 return;
582 }
583
584 // transferProtocolType should contain value from list
585 if (paramTransferProtocolType &&
586 *paramTransferProtocolType == TransferProtocol::invalid)
587 {
588 BMCWEB_LOG_ERROR("Request action parameter TransferProtocolType "
589 "must be provided with value from list: "
590 "(CIFS, HTTPS).");
591
592 messages::propertyValueNotInList(
593 asyncResp->res, actionParams.transferProtocolType.value_or(""),
594 "TransferProtocolType");
595 return;
596 }
597
598 // valid transfer protocol not provided either with URI nor param
599 if (!uriTransferProtocolType && !paramTransferProtocolType)
600 {
601 BMCWEB_LOG_ERROR("Request action parameter ImageUrl must "
602 "contain specified protocol type or param "
603 "TransferProtocolType must be provided.");
604
605 messages::resourceAtUriInUnknownFormat(asyncResp->res, *url);
606
607 return;
608 }
609
610 // valid transfer protocol provided both with URI and param
611 if (paramTransferProtocolType && uriTransferProtocolType)
612 {
613 // check if protocol is the same for URI and param
614 if (*paramTransferProtocolType != *uriTransferProtocolType)
615 {
616 BMCWEB_LOG_ERROR("Request action parameter "
617 "TransferProtocolType must contain the "
618 "same protocol type as protocol type "
619 "provided with param imageUrl.");
620
621 messages::actionParameterValueTypeError(
622 asyncResp->res, actionParams.transferProtocolType.value_or(""),
623 "TransferProtocolType", "InsertMedia");
624
625 return;
626 }
627 }
628
629 // validation passed, add protocol to URI if needed
630 if (!uriTransferProtocolType && paramTransferProtocolType)
631 {
632 actionParams.imageUrl = getUriWithTransferProtocol(
633 *actionParams.imageUrl, *paramTransferProtocolType);
634 }
635
636 if (!actionParams.userName)
637 {
638 actionParams.userName = "";
639 }
640
641 if (!actionParams.password)
642 {
643 actionParams.password = "";
644 }
645
646 doMountVmLegacy(asyncResp, service, resName, *actionParams.imageUrl,
647 !(actionParams.writeProtected.value_or(false)),
648 std::move(*actionParams.userName),
649 std::move(*actionParams.password));
650 }
651
652 /**
653 * @brief Function transceives data with dbus directly.
654 *
655 * All BMC state properties will be retrieved before sending reset request.
656 */
doEjectAction(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,bool legacy)657 inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
658 const std::string& service, const std::string& name,
659 bool legacy)
660 {
661 // Legacy mount requires parameter with image
662 if (legacy)
663 {
664 crow::connections::systemBus->async_method_call(
665 [asyncResp](const boost::system::error_code& ec) {
666 if (ec)
667 {
668 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
669
670 messages::internalError(asyncResp->res);
671 return;
672 }
673 },
674 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
675 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
676 }
677 else // proxy
678 {
679 crow::connections::systemBus->async_method_call(
680 [asyncResp](const boost::system::error_code& ec) {
681 if (ec)
682 {
683 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
684
685 messages::internalError(asyncResp->res);
686 return;
687 }
688 },
689 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
690 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
691 }
692 }
693
handleManagersVirtualMediaActionInsertPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)694 inline void handleManagersVirtualMediaActionInsertPost(
695 crow::App& app, const crow::Request& req,
696 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
697 const std::string& name, const std::string& resName)
698 {
699 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
700 {
701 return;
702 }
703
704 constexpr std::string_view action = "VirtualMedia.InsertMedia";
705 if (name != "bmc")
706 {
707 messages::resourceNotFound(asyncResp->res, action, resName);
708
709 return;
710 }
711 InsertMediaActionParams actionParams;
712
713 // Read obligatory parameters (url of image)
714 if (!json_util::readJsonAction( //
715 req, asyncResp->res, //
716 "Image", actionParams.imageUrl, //
717 "Inserted", actionParams.inserted, //
718 "Password", actionParams.password, //
719 "TransferMethod", actionParams.transferMethod, //
720 "TransferProtocolType", actionParams.transferProtocolType, //
721 "UserName", actionParams.userName, //
722 "WriteProtected", actionParams.writeProtected //
723 ))
724 {
725 return;
726 }
727
728 dbus::utility::getDbusObject(
729 "/xyz/openbmc_project/VirtualMedia", {},
730 [asyncResp, action, actionParams,
731 resName](const boost::system::error_code& ec,
732 const dbus::utility::MapperGetObject& getObjectType) mutable {
733 if (ec)
734 {
735 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
736 messages::resourceNotFound(asyncResp->res, action, resName);
737
738 return;
739 }
740
741 std::string service = getObjectType.begin()->first;
742 BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
743
744 sdbusplus::message::object_path path(
745 "/xyz/openbmc_project/VirtualMedia");
746 dbus::utility::getManagedObjects(
747 service, path,
748 [service, resName, action, actionParams, asyncResp](
749 const boost::system::error_code& ec2,
750 const dbus::utility::ManagedObjectType& subtree) mutable {
751 if (ec2)
752 {
753 // Not possible in proxy mode
754 BMCWEB_LOG_DEBUG("InsertMedia not "
755 "allowed in proxy mode");
756 messages::resourceNotFound(asyncResp->res, action,
757 resName);
758
759 return;
760 }
761 for (const auto& object : subtree)
762 {
763 VmMode mode =
764 parseObjectPathAndGetMode(object.first, resName);
765 if (mode == VmMode::Legacy)
766 {
767 validateParams(asyncResp, service, resName,
768 actionParams);
769
770 return;
771 }
772 }
773 BMCWEB_LOG_DEBUG("Parent item not found");
774 messages::resourceNotFound(asyncResp->res, "VirtualMedia",
775 resName);
776 });
777 });
778 }
779
handleManagersVirtualMediaActionEject(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerName,const std::string & resName)780 inline void handleManagersVirtualMediaActionEject(
781 crow::App& app, const crow::Request& req,
782 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
783 const std::string& managerName, const std::string& resName)
784 {
785 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
786 {
787 return;
788 }
789
790 constexpr std::string_view action = "VirtualMedia.EjectMedia";
791 if (managerName != "bmc")
792 {
793 messages::resourceNotFound(asyncResp->res, action, resName);
794
795 return;
796 }
797
798 dbus::utility::getDbusObject(
799 "/xyz/openbmc_project/VirtualMedia", {},
800 [asyncResp, action,
801 resName](const boost::system::error_code& ec2,
802 const dbus::utility::MapperGetObject& getObjectType) {
803 if (ec2)
804 {
805 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}",
806 ec2);
807 messages::internalError(asyncResp->res);
808
809 return;
810 }
811 std::string service = getObjectType.begin()->first;
812 BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
813
814 sdbusplus::message::object_path path(
815 "/xyz/openbmc_project/VirtualMedia");
816 dbus::utility::getManagedObjects(
817 service, path,
818 [resName, service, action,
819 asyncResp](const boost::system::error_code& ec,
820 const dbus::utility::ManagedObjectType& subtree) {
821 if (ec)
822 {
823 BMCWEB_LOG_ERROR("ObjectMapper : No Service found");
824 messages::resourceNotFound(asyncResp->res, action,
825 resName);
826 return;
827 }
828
829 for (const auto& object : subtree)
830 {
831 VmMode mode =
832 parseObjectPathAndGetMode(object.first, resName);
833 if (mode != VmMode::Invalid)
834 {
835 doEjectAction(asyncResp, service, resName,
836 mode == VmMode::Legacy);
837 return;
838 }
839 }
840 BMCWEB_LOG_DEBUG("Parent item not found");
841 messages::resourceNotFound(asyncResp->res, "VirtualMedia",
842 resName);
843 });
844 });
845 }
846
handleManagersVirtualMediaCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name)847 inline void handleManagersVirtualMediaCollectionGet(
848 crow::App& app, const crow::Request& req,
849 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
850 const std::string& name)
851 {
852 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
853 {
854 return;
855 }
856 if (name != "bmc")
857 {
858 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
859
860 return;
861 }
862
863 asyncResp->res.jsonValue["@odata.type"] =
864 "#VirtualMediaCollection.VirtualMediaCollection";
865 asyncResp->res.jsonValue["Name"] = "Virtual Media Services";
866 asyncResp->res.jsonValue["@odata.id"] =
867 boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", name);
868
869 dbus::utility::getDbusObject(
870 "/xyz/openbmc_project/VirtualMedia", {},
871 [asyncResp, name](const boost::system::error_code& ec,
872 const dbus::utility::MapperGetObject& getObjectType) {
873 if (ec)
874 {
875 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
876 messages::internalError(asyncResp->res);
877
878 return;
879 }
880 std::string service = getObjectType.begin()->first;
881 BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
882
883 getVmResourceList(asyncResp, service, name);
884 });
885 }
886
handleVirtualMediaGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)887 inline void handleVirtualMediaGet(
888 crow::App& app, const crow::Request& req,
889 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
890 const std::string& name, const std::string& resName)
891 {
892 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
893 {
894 return;
895 }
896 if (name != "bmc")
897 {
898 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
899
900 return;
901 }
902
903 dbus::utility::getDbusObject(
904 "/xyz/openbmc_project/VirtualMedia", {},
905 [asyncResp, name,
906 resName](const boost::system::error_code& ec,
907 const dbus::utility::MapperGetObject& getObjectType) {
908 if (ec)
909 {
910 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
911 messages::internalError(asyncResp->res);
912
913 return;
914 }
915 std::string service = getObjectType.begin()->first;
916 BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
917
918 getVmData(asyncResp, service, name, resName);
919 });
920 }
921
requestNBDVirtualMediaRoutes(App & app)922 inline void requestNBDVirtualMediaRoutes(App& app)
923 {
924 BMCWEB_ROUTE(
925 app,
926 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia")
927 .privileges(redfish::privileges::postVirtualMedia)
928 .methods(boost::beast::http::verb::post)(std::bind_front(
929 handleManagersVirtualMediaActionInsertPost, std::ref(app)));
930
931 BMCWEB_ROUTE(
932 app,
933 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia")
934 .privileges(redfish::privileges::postVirtualMedia)
935 .methods(boost::beast::http::verb::post)(std::bind_front(
936 handleManagersVirtualMediaActionEject, std::ref(app)));
937
938 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/")
939 .privileges(redfish::privileges::getVirtualMediaCollection)
940 .methods(boost::beast::http::verb::get)(std::bind_front(
941 handleManagersVirtualMediaCollectionGet, std::ref(app)));
942
943 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/")
944 .privileges(redfish::privileges::getVirtualMedia)
945 .methods(boost::beast::http::verb::get)(
946 std::bind_front(handleVirtualMediaGet, std::ref(app)));
947 }
948
949 } // namespace redfish
950