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/process/async_pipe.hpp>
29 #include <boost/url/format.hpp>
30 #include <boost/url/url_view.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
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
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,
102          handler](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  */
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
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  */
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  */
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
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  */
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>
370     getTransferProtocolFromUri(boost::urls::url_view 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  */
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
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  */
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->fd();
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  */
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  */
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 
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 
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 
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
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 
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