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