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