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