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