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