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