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