xref: /openbmc/bmcweb/redfish-core/lib/virtual_media.hpp (revision 504af5a0568171b72caf13234cc81380b261fa21)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5 
6 #include "app.hpp"
7 #include "async_resp.hpp"
8 #include "credential_pipe.hpp"
9 #include "dbus_singleton.hpp"
10 #include "dbus_utility.hpp"
11 #include "error_messages.hpp"
12 #include "generated/enums/virtual_media.hpp"
13 #include "http_request.hpp"
14 #include "logging.hpp"
15 #include "query.hpp"
16 #include "registries/privilege_registry.hpp"
17 #include "utils/json_utils.hpp"
18 
19 #include <boost/beast/http/status.hpp>
20 #include <boost/beast/http/verb.hpp>
21 #include <boost/system/result.hpp>
22 #include <boost/url/format.hpp>
23 #include <boost/url/parse.hpp>
24 #include <boost/url/url_view.hpp>
25 #include <boost/url/url_view_base.hpp>
26 #include <sdbusplus/message/native_types.hpp>
27 
28 #include <cstddef>
29 #include <filesystem>
30 #include <functional>
31 #include <memory>
32 #include <optional>
33 #include <ranges>
34 #include <string>
35 #include <string_view>
36 #include <utility>
37 #include <variant>
38 
39 namespace redfish
40 {
41 
42 enum class VmMode
43 {
44     Invalid,
45     Legacy,
46     Proxy
47 };
48 
parseObjectPathAndGetMode(const sdbusplus::message::object_path & itemPath,const std::string & resName)49 inline VmMode parseObjectPathAndGetMode(
50     const sdbusplus::message::object_path& itemPath, const std::string& resName)
51 {
52     std::string thisPath = itemPath.filename();
53     BMCWEB_LOG_DEBUG("Filename: {}, ThisPath: {}", itemPath.str, thisPath);
54 
55     if (thisPath.empty())
56     {
57         return VmMode::Invalid;
58     }
59 
60     if (thisPath != resName)
61     {
62         return VmMode::Invalid;
63     }
64 
65     auto mode = itemPath.parent_path();
66     auto type = mode.parent_path();
67 
68     if (mode.filename().empty() || type.filename().empty())
69     {
70         return VmMode::Invalid;
71     }
72 
73     if (type.filename() != "VirtualMedia")
74     {
75         return VmMode::Invalid;
76     }
77     std::string modeStr = mode.filename();
78     if (modeStr == "Legacy")
79     {
80         return VmMode::Legacy;
81     }
82     if (modeStr == "Proxy")
83     {
84         return VmMode::Proxy;
85     }
86     return VmMode::Invalid;
87 }
88 
89 using CheckItemHandler =
90     std::function<void(const std::string& service, const std::string& resName,
91                        const std::shared_ptr<bmcweb::AsyncResp>&,
92                        const std::pair<sdbusplus::message::object_path,
93                                        dbus::utility::DBusInterfacesMap>&)>;
94 
findAndParseObject(const std::string & service,const std::string & resName,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,CheckItemHandler && handler)95 inline void findAndParseObject(
96     const std::string& service, const std::string& resName,
97     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
98     CheckItemHandler&& handler)
99 {
100     sdbusplus::message::object_path path("/xyz/openbmc_project/VirtualMedia");
101     dbus::utility::getManagedObjects(
102         service, path,
103         [service, resName, asyncResp, handler = std::move(handler)](
104             const boost::system::error_code& ec,
105             const dbus::utility::ManagedObjectType& subtree) {
106             if (ec)
107             {
108                 BMCWEB_LOG_DEBUG("DBUS response error");
109 
110                 return;
111             }
112 
113             for (const auto& item : subtree)
114             {
115                 VmMode mode = parseObjectPathAndGetMode(item.first, resName);
116                 if (mode != VmMode::Invalid)
117                 {
118                     handler(service, resName, asyncResp, item);
119                     return;
120                 }
121             }
122 
123             BMCWEB_LOG_DEBUG("Parent item not found");
124             asyncResp->res.result(boost::beast::http::status::not_found);
125         });
126 }
127 
128 /**
129  * @brief Function extracts transfer protocol name from URI.
130  */
getTransferProtocolTypeFromUri(const std::string & imageUri)131 inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri)
132 {
133     boost::system::result<boost::urls::url_view> url =
134         boost::urls::parse_uri(imageUri);
135     if (!url)
136     {
137         return "None";
138     }
139     std::string_view scheme = url->scheme();
140     if (scheme == "smb")
141     {
142         return "CIFS";
143     }
144     if (scheme == "https")
145     {
146         return "HTTPS";
147     }
148 
149     return "None";
150 }
151 
152 /**
153  * @brief Read all known properties from VM object interfaces
154  */
vmParseInterfaceObject(const dbus::utility::DBusInterfacesMap & interfaces,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)155 inline void vmParseInterfaceObject(
156     const dbus::utility::DBusInterfacesMap& interfaces,
157     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
158 {
159     for (const auto& [interface, values] : interfaces)
160     {
161         if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint")
162         {
163             for (const auto& [property, value] : values)
164             {
165                 if (property == "EndpointId")
166                 {
167                     const std::string* endpointIdValue =
168                         std::get_if<std::string>(&value);
169                     if (endpointIdValue == nullptr)
170                     {
171                         continue;
172                     }
173                     if (!endpointIdValue->empty())
174                     {
175                         // Proxy mode
176                         asyncResp->res
177                             .jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
178                             *endpointIdValue;
179                         asyncResp->res.jsonValue["TransferProtocolType"] =
180                             "OEM";
181                     }
182                 }
183                 if (property == "ImageURL")
184                 {
185                     const std::string* imageUrlValue =
186                         std::get_if<std::string>(&value);
187                     if (imageUrlValue != nullptr && !imageUrlValue->empty())
188                     {
189                         std::filesystem::path filePath = *imageUrlValue;
190                         if (!filePath.has_filename())
191                         {
192                             // this will handle https share, which not
193                             // necessarily has to have filename given.
194                             asyncResp->res.jsonValue["ImageName"] = "";
195                         }
196                         else
197                         {
198                             asyncResp->res.jsonValue["ImageName"] =
199                                 filePath.filename();
200                         }
201 
202                         asyncResp->res.jsonValue["Image"] = *imageUrlValue;
203                         asyncResp->res.jsonValue["TransferProtocolType"] =
204                             getTransferProtocolTypeFromUri(*imageUrlValue);
205 
206                         asyncResp->res.jsonValue["ConnectedVia"] =
207                             virtual_media::ConnectedVia::URI;
208                     }
209                 }
210                 if (property == "WriteProtected")
211                 {
212                     const bool* writeProtectedValue = std::get_if<bool>(&value);
213                     if (writeProtectedValue != nullptr)
214                     {
215                         asyncResp->res.jsonValue["WriteProtected"] =
216                             *writeProtectedValue;
217                     }
218                 }
219             }
220         }
221         if (interface == "xyz.openbmc_project.VirtualMedia.Process")
222         {
223             for (const auto& [property, value] : values)
224             {
225                 if (property == "Active")
226                 {
227                     const bool* activeValue = std::get_if<bool>(&value);
228                     if (activeValue == nullptr)
229                     {
230                         BMCWEB_LOG_DEBUG("Value Active not found");
231                         return;
232                     }
233                     asyncResp->res.jsonValue["Inserted"] = *activeValue;
234 
235                     if (*activeValue)
236                     {
237                         asyncResp->res.jsonValue["ConnectedVia"] =
238                             virtual_media::ConnectedVia::Applet;
239                     }
240                 }
241             }
242         }
243     }
244 }
245 
246 /**
247  * @brief Fill template for Virtual Media Item.
248  */
vmItemTemplate(const std::string & name,const std::string & resName)249 inline nlohmann::json vmItemTemplate(const std::string& name,
250                                      const std::string& resName)
251 {
252     nlohmann::json item;
253     item["@odata.id"] = boost::urls::format(
254         "/redfish/v1/Managers/{}/VirtualMedia/{}", name, resName);
255 
256     item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
257     item["Name"] = "Virtual Removable Media";
258     item["Id"] = resName;
259     item["WriteProtected"] = true;
260     item["ConnectedVia"] = virtual_media::ConnectedVia::NotConnected;
261     item["MediaTypes"] = nlohmann::json::array_t({"CD", "USBStick"});
262     item["TransferMethod"] = virtual_media::TransferMethod::Stream;
263     item["Oem"]["OpenBMC"]["@odata.type"] =
264         "#OpenBMCVirtualMedia.v1_0_0.VirtualMedia";
265     item["Oem"]["OpenBMC"]["@odata.id"] = boost::urls::format(
266         "/redfish/v1/Managers/{}/VirtualMedia/{}#/Oem/OpenBMC", name, resName);
267 
268     return item;
269 }
270 
271 /**
272  *  @brief Fills collection data
273  */
getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> asyncResp,const std::string & service,const std::string & name)274 inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
275                               const std::string& service,
276                               const std::string& name)
277 {
278     BMCWEB_LOG_DEBUG("Get available Virtual Media resources.");
279     sdbusplus::message::object_path objPath(
280         "/xyz/openbmc_project/VirtualMedia");
281     dbus::utility::getManagedObjects(
282         service, objPath,
283         [name, asyncResp{std::move(asyncResp)}](
284             const boost::system::error_code& ec,
285             const dbus::utility::ManagedObjectType& subtree) {
286             if (ec)
287             {
288                 BMCWEB_LOG_DEBUG("DBUS response error");
289                 return;
290             }
291             nlohmann::json& members = asyncResp->res.jsonValue["Members"];
292             members = nlohmann::json::array();
293 
294             for (const auto& object : subtree)
295             {
296                 nlohmann::json item;
297                 std::string path = object.first.filename();
298                 if (path.empty())
299                 {
300                     continue;
301                 }
302 
303                 item["@odata.id"] = boost::urls::format(
304                     "/redfish/v1/Managers/{}/VirtualMedia/{}", name, path);
305                 members.emplace_back(std::move(item));
306             }
307             asyncResp->res.jsonValue["Members@odata.count"] = members.size();
308         });
309 }
310 
afterGetVmData(const std::string & name,const std::string &,const std::string & resName,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::pair<sdbusplus::message::object_path,dbus::utility::DBusInterfacesMap> & item)311 inline void afterGetVmData(
312     const std::string& name, const std::string& /*service*/,
313     const std::string& resName,
314     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
315     const std::pair<sdbusplus::message::object_path,
316                     dbus::utility::DBusInterfacesMap>& item)
317 {
318     VmMode mode = parseObjectPathAndGetMode(item.first, resName);
319     if (mode == VmMode::Invalid)
320     {
321         return;
322     }
323 
324     asyncResp->res.jsonValue = vmItemTemplate(name, resName);
325 
326     // Check if dbus path is Legacy type
327     if (mode == VmMode::Legacy)
328     {
329         asyncResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
330                                 ["target"] = boost::urls::format(
331             "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.InsertMedia",
332             name, resName);
333     }
334 
335     vmParseInterfaceObject(item.second, asyncResp);
336 
337     asyncResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"]
338                             ["target"] = boost::urls::format(
339         "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.EjectMedia",
340         name, resName);
341 }
342 
343 /**
344  *  @brief Fills data for specific resource
345  */
getVmData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,const std::string & resName)346 inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
347                       const std::string& service, const std::string& name,
348                       const std::string& resName)
349 {
350     BMCWEB_LOG_DEBUG("Get Virtual Media resource data.");
351 
352     findAndParseObject(service, resName, asyncResp,
353                        std::bind_front(afterGetVmData, name));
354 }
355 
356 /**
357  * @brief Transfer protocols supported for InsertMedia action.
358  *
359  */
360 enum class TransferProtocol
361 {
362     https,
363     smb,
364     invalid
365 };
366 
367 /**
368  * @brief Function extracts transfer protocol type from URI.
369  *
370  */
getTransferProtocolFromUri(const boost::urls::url_view_base & imageUri)371 inline std::optional<TransferProtocol> getTransferProtocolFromUri(
372     const boost::urls::url_view_base& imageUri)
373 {
374     std::string_view scheme = imageUri.scheme();
375     if (scheme == "smb")
376     {
377         return TransferProtocol::smb;
378     }
379     if (scheme == "https")
380     {
381         return TransferProtocol::https;
382     }
383     if (!scheme.empty())
384     {
385         return TransferProtocol::invalid;
386     }
387 
388     return {};
389 }
390 
391 /**
392  * @brief Function convert transfer protocol from string param.
393  *
394  */
getTransferProtocolFromParam(const std::optional<std::string> & transferProtocolType)395 inline std::optional<TransferProtocol> getTransferProtocolFromParam(
396     const std::optional<std::string>& transferProtocolType)
397 {
398     if (!transferProtocolType)
399     {
400         return {};
401     }
402 
403     if (*transferProtocolType == "CIFS")
404     {
405         return TransferProtocol::smb;
406     }
407 
408     if (*transferProtocolType == "HTTPS")
409     {
410         return TransferProtocol::https;
411     }
412 
413     return TransferProtocol::invalid;
414 }
415 
416 /**
417  * @brief Function extends URI with transfer protocol type.
418  *
419  */
getUriWithTransferProtocol(const std::string & imageUri,const TransferProtocol & transferProtocol)420 inline std::string getUriWithTransferProtocol(
421     const std::string& imageUri, 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  */
doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,const std::string & imageUrl,bool rw,std::string && userName,std::string && password)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->releaseFd();
474 
475         // Pass secret over pipe
476         secretPipe->asyncWrite(
477             std::move(userName), std::move(password),
478             [asyncResp,
479              secretPipe](const boost::system::error_code& ec, 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     std::variant<sdbusplus::message::unix_fd> 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,
496          secretPipe](const boost::system::error_code& ec, 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  */
validateParams(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & resName,InsertMediaActionParams & actionParams)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  */
doEjectAction(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,bool legacy)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 
handleManagersVirtualMediaActionInsertPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)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,                                       //
716             "Image", actionParams.imageUrl,                            //
717             "Inserted", actionParams.inserted,                         //
718             "Password", actionParams.password,                         //
719             "TransferMethod", actionParams.transferMethod,             //
720             "TransferProtocolType", actionParams.transferProtocolType, //
721             "UserName", actionParams.userName,                         //
722             "WriteProtected", actionParams.writeProtected              //
723             ))
724     {
725         return;
726     }
727 
728     dbus::utility::getDbusObject(
729         "/xyz/openbmc_project/VirtualMedia", {},
730         [asyncResp, action, actionParams,
731          resName](const boost::system::error_code& ec,
732                   const dbus::utility::MapperGetObject& getObjectType) mutable {
733             if (ec)
734             {
735                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
736                 messages::resourceNotFound(asyncResp->res, action, resName);
737 
738                 return;
739             }
740 
741             std::string service = getObjectType.begin()->first;
742             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
743 
744             sdbusplus::message::object_path path(
745                 "/xyz/openbmc_project/VirtualMedia");
746             dbus::utility::getManagedObjects(
747                 service, path,
748                 [service, resName, action, actionParams, asyncResp](
749                     const boost::system::error_code& ec2,
750                     const dbus::utility::ManagedObjectType& subtree) mutable {
751                     if (ec2)
752                     {
753                         // Not possible in proxy mode
754                         BMCWEB_LOG_DEBUG("InsertMedia not "
755                                          "allowed in proxy mode");
756                         messages::resourceNotFound(asyncResp->res, action,
757                                                    resName);
758 
759                         return;
760                     }
761                     for (const auto& object : subtree)
762                     {
763                         VmMode mode =
764                             parseObjectPathAndGetMode(object.first, resName);
765                         if (mode == VmMode::Legacy)
766                         {
767                             validateParams(asyncResp, service, resName,
768                                            actionParams);
769 
770                             return;
771                         }
772                     }
773                     BMCWEB_LOG_DEBUG("Parent item not found");
774                     messages::resourceNotFound(asyncResp->res, "VirtualMedia",
775                                                resName);
776                 });
777         });
778 }
779 
handleManagersVirtualMediaActionEject(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerName,const std::string & resName)780 inline void handleManagersVirtualMediaActionEject(
781     crow::App& app, const crow::Request& req,
782     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
783     const std::string& managerName, const std::string& resName)
784 {
785     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
786     {
787         return;
788     }
789 
790     constexpr std::string_view action = "VirtualMedia.EjectMedia";
791     if (managerName != "bmc")
792     {
793         messages::resourceNotFound(asyncResp->res, action, resName);
794 
795         return;
796     }
797 
798     dbus::utility::getDbusObject(
799         "/xyz/openbmc_project/VirtualMedia", {},
800         [asyncResp, action,
801          resName](const boost::system::error_code& ec2,
802                   const dbus::utility::MapperGetObject& getObjectType) {
803             if (ec2)
804             {
805                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}",
806                                  ec2);
807                 messages::internalError(asyncResp->res);
808 
809                 return;
810             }
811             std::string service = getObjectType.begin()->first;
812             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
813 
814             sdbusplus::message::object_path path(
815                 "/xyz/openbmc_project/VirtualMedia");
816             dbus::utility::getManagedObjects(
817                 service, path,
818                 [resName, service, action,
819                  asyncResp](const boost::system::error_code& ec,
820                             const dbus::utility::ManagedObjectType& subtree) {
821                     if (ec)
822                     {
823                         BMCWEB_LOG_ERROR("ObjectMapper : No Service found");
824                         messages::resourceNotFound(asyncResp->res, action,
825                                                    resName);
826                         return;
827                     }
828 
829                     for (const auto& object : subtree)
830                     {
831                         VmMode mode =
832                             parseObjectPathAndGetMode(object.first, resName);
833                         if (mode != VmMode::Invalid)
834                         {
835                             doEjectAction(asyncResp, service, resName,
836                                           mode == VmMode::Legacy);
837                             return;
838                         }
839                     }
840                     BMCWEB_LOG_DEBUG("Parent item not found");
841                     messages::resourceNotFound(asyncResp->res, "VirtualMedia",
842                                                resName);
843                 });
844         });
845 }
846 
handleManagersVirtualMediaCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name)847 inline void handleManagersVirtualMediaCollectionGet(
848     crow::App& app, const crow::Request& req,
849     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
850     const std::string& name)
851 {
852     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
853     {
854         return;
855     }
856     if (name != "bmc")
857     {
858         messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
859 
860         return;
861     }
862 
863     asyncResp->res.jsonValue["@odata.type"] =
864         "#VirtualMediaCollection.VirtualMediaCollection";
865     asyncResp->res.jsonValue["Name"] = "Virtual Media Services";
866     asyncResp->res.jsonValue["@odata.id"] =
867         boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", name);
868 
869     dbus::utility::getDbusObject(
870         "/xyz/openbmc_project/VirtualMedia", {},
871         [asyncResp, name](const boost::system::error_code& ec,
872                           const dbus::utility::MapperGetObject& getObjectType) {
873             if (ec)
874             {
875                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
876                 messages::internalError(asyncResp->res);
877 
878                 return;
879             }
880             std::string service = getObjectType.begin()->first;
881             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
882 
883             getVmResourceList(asyncResp, service, name);
884         });
885 }
886 
handleVirtualMediaGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)887 inline void handleVirtualMediaGet(
888     crow::App& app, const crow::Request& req,
889     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
890     const std::string& name, const std::string& resName)
891 {
892     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
893     {
894         return;
895     }
896     if (name != "bmc")
897     {
898         messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
899 
900         return;
901     }
902 
903     dbus::utility::getDbusObject(
904         "/xyz/openbmc_project/VirtualMedia", {},
905         [asyncResp, name,
906          resName](const boost::system::error_code& ec,
907                   const dbus::utility::MapperGetObject& getObjectType) {
908             if (ec)
909             {
910                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
911                 messages::internalError(asyncResp->res);
912 
913                 return;
914             }
915             std::string service = getObjectType.begin()->first;
916             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
917 
918             getVmData(asyncResp, service, name, resName);
919         });
920 }
921 
requestNBDVirtualMediaRoutes(App & app)922 inline void requestNBDVirtualMediaRoutes(App& app)
923 {
924     BMCWEB_ROUTE(
925         app,
926         "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia")
927         .privileges(redfish::privileges::postVirtualMedia)
928         .methods(boost::beast::http::verb::post)(std::bind_front(
929             handleManagersVirtualMediaActionInsertPost, std::ref(app)));
930 
931     BMCWEB_ROUTE(
932         app,
933         "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia")
934         .privileges(redfish::privileges::postVirtualMedia)
935         .methods(boost::beast::http::verb::post)(std::bind_front(
936             handleManagersVirtualMediaActionEject, std::ref(app)));
937 
938     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/")
939         .privileges(redfish::privileges::getVirtualMediaCollection)
940         .methods(boost::beast::http::verb::get)(std::bind_front(
941             handleManagersVirtualMediaCollectionGet, std::ref(app)));
942 
943     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/")
944         .privileges(redfish::privileges::getVirtualMedia)
945         .methods(boost::beast::http::verb::get)(
946             std::bind_front(handleVirtualMediaGet, std::ref(app)));
947 }
948 
949 } // namespace redfish
950