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 dbus::utility::async_method_call(
505 asyncResp,
506 [asyncResp](const boost::system::error_code& ec, bool success) {
507 if (ec)
508 {
509 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
510 messages::internalError(asyncResp->res);
511 return;
512 }
513 if (!success)
514 {
515 BMCWEB_LOG_ERROR("Service responded with error");
516 messages::internalError(asyncResp->res);
517 }
518 },
519 service, path.str, "xyz.openbmc_project.VirtualMedia.Legacy", "Mount",
520 imageUrl, rw, unixFd);
521 }
522
523 /**
524 * @brief Function validate parameters of insert media request.
525 *
526 */
validateParams(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & resName,InsertMediaActionParams & actionParams)527 inline void validateParams(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
528 const std::string& service,
529 const std::string& resName,
530 InsertMediaActionParams& actionParams)
531 {
532 BMCWEB_LOG_DEBUG("Validation started");
533 // required param imageUrl must not be empty
534 if (!actionParams.imageUrl)
535 {
536 BMCWEB_LOG_ERROR("Request action parameter Image is empty.");
537
538 messages::propertyValueFormatError(asyncResp->res, "<empty>", "Image");
539
540 return;
541 }
542
543 // optional param inserted must be true
544 if (actionParams.inserted && !*actionParams.inserted)
545 {
546 BMCWEB_LOG_ERROR(
547 "Request action optional parameter Inserted must be true.");
548
549 messages::actionParameterNotSupported(asyncResp->res, "Inserted",
550 "InsertMedia");
551
552 return;
553 }
554
555 // optional param transferMethod must be stream
556 if (actionParams.transferMethod &&
557 (*actionParams.transferMethod != "Stream"))
558 {
559 BMCWEB_LOG_ERROR("Request action optional parameter "
560 "TransferMethod must be Stream.");
561
562 messages::actionParameterNotSupported(asyncResp->res, "TransferMethod",
563 "InsertMedia");
564
565 return;
566 }
567 boost::system::result<boost::urls::url_view> url =
568 boost::urls::parse_uri(*actionParams.imageUrl);
569 if (!url)
570 {
571 messages::actionParameterValueFormatError(
572 asyncResp->res, *actionParams.imageUrl, "Image", "InsertMedia");
573 return;
574 }
575 std::optional<TransferProtocol> uriTransferProtocolType =
576 getTransferProtocolFromUri(*url);
577
578 std::optional<TransferProtocol> paramTransferProtocolType =
579 getTransferProtocolFromParam(actionParams.transferProtocolType);
580
581 // ImageUrl does not contain valid protocol type
582 if (uriTransferProtocolType &&
583 *uriTransferProtocolType == TransferProtocol::invalid)
584 {
585 BMCWEB_LOG_ERROR("Request action parameter ImageUrl must "
586 "contain specified protocol type from list: "
587 "(smb, https).");
588
589 messages::resourceAtUriInUnknownFormat(asyncResp->res, *url);
590
591 return;
592 }
593
594 // transferProtocolType should contain value from list
595 if (paramTransferProtocolType &&
596 *paramTransferProtocolType == TransferProtocol::invalid)
597 {
598 BMCWEB_LOG_ERROR("Request action parameter TransferProtocolType "
599 "must be provided with value from list: "
600 "(CIFS, HTTPS).");
601
602 messages::propertyValueNotInList(
603 asyncResp->res, actionParams.transferProtocolType.value_or(""),
604 "TransferProtocolType");
605 return;
606 }
607
608 // valid transfer protocol not provided either with URI nor param
609 if (!uriTransferProtocolType && !paramTransferProtocolType)
610 {
611 BMCWEB_LOG_ERROR("Request action parameter ImageUrl must "
612 "contain specified protocol type or param "
613 "TransferProtocolType must be provided.");
614
615 messages::resourceAtUriInUnknownFormat(asyncResp->res, *url);
616
617 return;
618 }
619
620 // valid transfer protocol provided both with URI and param
621 if (paramTransferProtocolType && uriTransferProtocolType)
622 {
623 // check if protocol is the same for URI and param
624 if (*paramTransferProtocolType != *uriTransferProtocolType)
625 {
626 BMCWEB_LOG_ERROR("Request action parameter "
627 "TransferProtocolType must contain the "
628 "same protocol type as protocol type "
629 "provided with param imageUrl.");
630
631 messages::actionParameterValueTypeError(
632 asyncResp->res, actionParams.transferProtocolType.value_or(""),
633 "TransferProtocolType", "InsertMedia");
634
635 return;
636 }
637 }
638
639 // validation passed, add protocol to URI if needed
640 if (!uriTransferProtocolType && paramTransferProtocolType)
641 {
642 actionParams.imageUrl = getUriWithTransferProtocol(
643 *actionParams.imageUrl, *paramTransferProtocolType);
644 }
645
646 if (!actionParams.userName)
647 {
648 actionParams.userName = "";
649 }
650
651 if (!actionParams.password)
652 {
653 actionParams.password = "";
654 }
655
656 doMountVmLegacy(asyncResp, service, resName, *actionParams.imageUrl,
657 !(actionParams.writeProtected.value_or(false)),
658 std::move(*actionParams.userName),
659 std::move(*actionParams.password));
660 }
661
662 /**
663 * @brief Function transceives data with dbus directly.
664 *
665 * All BMC state properties will be retrieved before sending reset request.
666 */
doEjectAction(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,bool legacy)667 inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
668 const std::string& service, const std::string& name,
669 bool legacy)
670 {
671 // Legacy mount requires parameter with image
672 if (legacy)
673 {
674 dbus::utility::async_method_call(
675 asyncResp,
676 [asyncResp](const boost::system::error_code& ec) {
677 if (ec)
678 {
679 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
680
681 messages::internalError(asyncResp->res);
682 return;
683 }
684 },
685 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
686 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
687 }
688 else // proxy
689 {
690 dbus::utility::async_method_call(
691 asyncResp,
692 [asyncResp](const boost::system::error_code& ec) {
693 if (ec)
694 {
695 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
696
697 messages::internalError(asyncResp->res);
698 return;
699 }
700 },
701 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
702 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
703 }
704 }
705
handleManagersVirtualMediaActionInsertPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)706 inline void handleManagersVirtualMediaActionInsertPost(
707 crow::App& app, const crow::Request& req,
708 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
709 const std::string& name, const std::string& resName)
710 {
711 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
712 {
713 return;
714 }
715
716 constexpr std::string_view action = "VirtualMedia.InsertMedia";
717 if (name != "bmc")
718 {
719 messages::resourceNotFound(asyncResp->res, action, resName);
720
721 return;
722 }
723 InsertMediaActionParams actionParams;
724
725 // Read obligatory parameters (url of image)
726 if (!json_util::readJsonAction( //
727 req, asyncResp->res, //
728 "Image", actionParams.imageUrl, //
729 "Inserted", actionParams.inserted, //
730 "Password", actionParams.password, //
731 "TransferMethod", actionParams.transferMethod, //
732 "TransferProtocolType", actionParams.transferProtocolType, //
733 "UserName", actionParams.userName, //
734 "WriteProtected", actionParams.writeProtected //
735 ))
736 {
737 return;
738 }
739
740 dbus::utility::getDbusObject(
741 "/xyz/openbmc_project/VirtualMedia", {},
742 [asyncResp, action, actionParams,
743 resName](const boost::system::error_code& ec,
744 const dbus::utility::MapperGetObject& getObjectType) mutable {
745 if (ec)
746 {
747 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
748 messages::resourceNotFound(asyncResp->res, action, resName);
749
750 return;
751 }
752
753 std::string service = getObjectType.begin()->first;
754 BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
755
756 sdbusplus::message::object_path path(
757 "/xyz/openbmc_project/VirtualMedia");
758 dbus::utility::getManagedObjects(
759 service, path,
760 [service, resName, action, actionParams, asyncResp](
761 const boost::system::error_code& ec2,
762 const dbus::utility::ManagedObjectType& subtree) mutable {
763 if (ec2)
764 {
765 // Not possible in proxy mode
766 BMCWEB_LOG_DEBUG("InsertMedia not "
767 "allowed in proxy mode");
768 messages::resourceNotFound(asyncResp->res, action,
769 resName);
770
771 return;
772 }
773 for (const auto& object : subtree)
774 {
775 VmMode mode =
776 parseObjectPathAndGetMode(object.first, resName);
777 if (mode == VmMode::Legacy)
778 {
779 validateParams(asyncResp, service, resName,
780 actionParams);
781
782 return;
783 }
784 }
785 BMCWEB_LOG_DEBUG("Parent item not found");
786 messages::resourceNotFound(asyncResp->res, "VirtualMedia",
787 resName);
788 });
789 });
790 }
791
handleManagersVirtualMediaActionEject(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerName,const std::string & resName)792 inline void handleManagersVirtualMediaActionEject(
793 crow::App& app, const crow::Request& req,
794 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
795 const std::string& managerName, const std::string& resName)
796 {
797 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
798 {
799 return;
800 }
801
802 constexpr std::string_view action = "VirtualMedia.EjectMedia";
803 if (managerName != "bmc")
804 {
805 messages::resourceNotFound(asyncResp->res, action, resName);
806
807 return;
808 }
809
810 dbus::utility::getDbusObject(
811 "/xyz/openbmc_project/VirtualMedia", {},
812 [asyncResp, action,
813 resName](const boost::system::error_code& ec2,
814 const dbus::utility::MapperGetObject& getObjectType) {
815 if (ec2)
816 {
817 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}",
818 ec2);
819 messages::internalError(asyncResp->res);
820
821 return;
822 }
823 std::string service = getObjectType.begin()->first;
824 BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
825
826 sdbusplus::message::object_path path(
827 "/xyz/openbmc_project/VirtualMedia");
828 dbus::utility::getManagedObjects(
829 service, path,
830 [resName, service, action,
831 asyncResp](const boost::system::error_code& ec,
832 const dbus::utility::ManagedObjectType& subtree) {
833 if (ec)
834 {
835 BMCWEB_LOG_ERROR("ObjectMapper : No Service found");
836 messages::resourceNotFound(asyncResp->res, action,
837 resName);
838 return;
839 }
840
841 for (const auto& object : subtree)
842 {
843 VmMode mode =
844 parseObjectPathAndGetMode(object.first, resName);
845 if (mode != VmMode::Invalid)
846 {
847 doEjectAction(asyncResp, service, resName,
848 mode == VmMode::Legacy);
849 return;
850 }
851 }
852 BMCWEB_LOG_DEBUG("Parent item not found");
853 messages::resourceNotFound(asyncResp->res, "VirtualMedia",
854 resName);
855 });
856 });
857 }
858
handleManagersVirtualMediaCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name)859 inline void handleManagersVirtualMediaCollectionGet(
860 crow::App& app, const crow::Request& req,
861 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
862 const std::string& name)
863 {
864 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
865 {
866 return;
867 }
868 if (name != "bmc")
869 {
870 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
871
872 return;
873 }
874
875 asyncResp->res.jsonValue["@odata.type"] =
876 "#VirtualMediaCollection.VirtualMediaCollection";
877 asyncResp->res.jsonValue["Name"] = "Virtual Media Services";
878 asyncResp->res.jsonValue["@odata.id"] =
879 boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", name);
880
881 dbus::utility::getDbusObject(
882 "/xyz/openbmc_project/VirtualMedia", {},
883 [asyncResp, name](const boost::system::error_code& ec,
884 const dbus::utility::MapperGetObject& getObjectType) {
885 if (ec)
886 {
887 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
888 messages::internalError(asyncResp->res);
889
890 return;
891 }
892 std::string service = getObjectType.begin()->first;
893 BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
894
895 getVmResourceList(asyncResp, service, name);
896 });
897 }
898
handleVirtualMediaGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)899 inline void handleVirtualMediaGet(
900 crow::App& app, const crow::Request& req,
901 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
902 const std::string& name, const std::string& resName)
903 {
904 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
905 {
906 return;
907 }
908 if (name != "bmc")
909 {
910 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
911
912 return;
913 }
914
915 dbus::utility::getDbusObject(
916 "/xyz/openbmc_project/VirtualMedia", {},
917 [asyncResp, name,
918 resName](const boost::system::error_code& ec,
919 const dbus::utility::MapperGetObject& getObjectType) {
920 if (ec)
921 {
922 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
923 messages::internalError(asyncResp->res);
924
925 return;
926 }
927 std::string service = getObjectType.begin()->first;
928 BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
929
930 getVmData(asyncResp, service, name, resName);
931 });
932 }
933
requestNBDVirtualMediaRoutes(App & app)934 inline void requestNBDVirtualMediaRoutes(App& app)
935 {
936 BMCWEB_ROUTE(
937 app,
938 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia")
939 .privileges(redfish::privileges::postVirtualMedia)
940 .methods(boost::beast::http::verb::post)(std::bind_front(
941 handleManagersVirtualMediaActionInsertPost, std::ref(app)));
942
943 BMCWEB_ROUTE(
944 app,
945 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia")
946 .privileges(redfish::privileges::postVirtualMedia)
947 .methods(boost::beast::http::verb::post)(std::bind_front(
948 handleManagersVirtualMediaActionEject, std::ref(app)));
949
950 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/")
951 .privileges(redfish::privileges::getVirtualMediaCollection)
952 .methods(boost::beast::http::verb::get)(std::bind_front(
953 handleManagersVirtualMediaCollectionGet, std::ref(app)));
954
955 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/")
956 .privileges(redfish::privileges::getVirtualMedia)
957 .methods(boost::beast::http::verb::get)(
958 std::bind_front(handleVirtualMediaGet, std::ref(app)));
959 }
960
961 } // namespace redfish
962