xref: /openbmc/bmcweb/redfish-core/lib/virtual_media.hpp (revision 4bbf2a13c21de665aab76ae3d3a3e2633b667be2)
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     crow::connections::systemBus->async_method_call(
505         [asyncResp](const boost::system::error_code& ec, bool success) {
506             if (ec)
507             {
508                 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
509                 messages::internalError(asyncResp->res);
510                 return;
511             }
512             if (!success)
513             {
514                 BMCWEB_LOG_ERROR("Service responded with error");
515                 messages::internalError(asyncResp->res);
516             }
517         },
518         service, path.str, "xyz.openbmc_project.VirtualMedia.Legacy", "Mount",
519         imageUrl, rw, unixFd);
520 }
521 
522 /**
523  * @brief Function validate parameters of insert media request.
524  *
525  */
validateParams(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & resName,InsertMediaActionParams & actionParams)526 inline void validateParams(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
527                            const std::string& service,
528                            const std::string& resName,
529                            InsertMediaActionParams& actionParams)
530 {
531     BMCWEB_LOG_DEBUG("Validation started");
532     // required param imageUrl must not be empty
533     if (!actionParams.imageUrl)
534     {
535         BMCWEB_LOG_ERROR("Request action parameter Image is empty.");
536 
537         messages::propertyValueFormatError(asyncResp->res, "<empty>", "Image");
538 
539         return;
540     }
541 
542     // optional param inserted must be true
543     if (actionParams.inserted && !*actionParams.inserted)
544     {
545         BMCWEB_LOG_ERROR(
546             "Request action optional parameter Inserted must be true.");
547 
548         messages::actionParameterNotSupported(asyncResp->res, "Inserted",
549                                               "InsertMedia");
550 
551         return;
552     }
553 
554     // optional param transferMethod must be stream
555     if (actionParams.transferMethod &&
556         (*actionParams.transferMethod != "Stream"))
557     {
558         BMCWEB_LOG_ERROR("Request action optional parameter "
559                          "TransferMethod must be Stream.");
560 
561         messages::actionParameterNotSupported(asyncResp->res, "TransferMethod",
562                                               "InsertMedia");
563 
564         return;
565     }
566     boost::system::result<boost::urls::url_view> url =
567         boost::urls::parse_uri(*actionParams.imageUrl);
568     if (!url)
569     {
570         messages::actionParameterValueFormatError(
571             asyncResp->res, *actionParams.imageUrl, "Image", "InsertMedia");
572         return;
573     }
574     std::optional<TransferProtocol> uriTransferProtocolType =
575         getTransferProtocolFromUri(*url);
576 
577     std::optional<TransferProtocol> paramTransferProtocolType =
578         getTransferProtocolFromParam(actionParams.transferProtocolType);
579 
580     // ImageUrl does not contain valid protocol type
581     if (uriTransferProtocolType &&
582         *uriTransferProtocolType == TransferProtocol::invalid)
583     {
584         BMCWEB_LOG_ERROR("Request action parameter ImageUrl must "
585                          "contain specified protocol type from list: "
586                          "(smb, https).");
587 
588         messages::resourceAtUriInUnknownFormat(asyncResp->res, *url);
589 
590         return;
591     }
592 
593     // transferProtocolType should contain value from list
594     if (paramTransferProtocolType &&
595         *paramTransferProtocolType == TransferProtocol::invalid)
596     {
597         BMCWEB_LOG_ERROR("Request action parameter TransferProtocolType "
598                          "must be provided with value from list: "
599                          "(CIFS, HTTPS).");
600 
601         messages::propertyValueNotInList(
602             asyncResp->res, actionParams.transferProtocolType.value_or(""),
603             "TransferProtocolType");
604         return;
605     }
606 
607     // valid transfer protocol not provided either with URI nor param
608     if (!uriTransferProtocolType && !paramTransferProtocolType)
609     {
610         BMCWEB_LOG_ERROR("Request action parameter ImageUrl must "
611                          "contain specified protocol type or param "
612                          "TransferProtocolType must be provided.");
613 
614         messages::resourceAtUriInUnknownFormat(asyncResp->res, *url);
615 
616         return;
617     }
618 
619     // valid transfer protocol provided both with URI and param
620     if (paramTransferProtocolType && uriTransferProtocolType)
621     {
622         // check if protocol is the same for URI and param
623         if (*paramTransferProtocolType != *uriTransferProtocolType)
624         {
625             BMCWEB_LOG_ERROR("Request action parameter "
626                              "TransferProtocolType must  contain the "
627                              "same protocol type as protocol type "
628                              "provided with param imageUrl.");
629 
630             messages::actionParameterValueTypeError(
631                 asyncResp->res, actionParams.transferProtocolType.value_or(""),
632                 "TransferProtocolType", "InsertMedia");
633 
634             return;
635         }
636     }
637 
638     // validation passed, add protocol to URI if needed
639     if (!uriTransferProtocolType && paramTransferProtocolType)
640     {
641         actionParams.imageUrl = getUriWithTransferProtocol(
642             *actionParams.imageUrl, *paramTransferProtocolType);
643     }
644 
645     if (!actionParams.userName)
646     {
647         actionParams.userName = "";
648     }
649 
650     if (!actionParams.password)
651     {
652         actionParams.password = "";
653     }
654 
655     doMountVmLegacy(asyncResp, service, resName, *actionParams.imageUrl,
656                     !(actionParams.writeProtected.value_or(false)),
657                     std::move(*actionParams.userName),
658                     std::move(*actionParams.password));
659 }
660 
661 /**
662  * @brief Function transceives data with dbus directly.
663  *
664  * All BMC state properties will be retrieved before sending reset request.
665  */
doEjectAction(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const std::string & name,bool legacy)666 inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
667                           const std::string& service, const std::string& name,
668                           bool legacy)
669 {
670     // Legacy mount requires parameter with image
671     if (legacy)
672     {
673         crow::connections::systemBus->async_method_call(
674             [asyncResp](const boost::system::error_code& ec) {
675                 if (ec)
676                 {
677                     BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
678 
679                     messages::internalError(asyncResp->res);
680                     return;
681                 }
682             },
683             service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
684             "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
685     }
686     else // proxy
687     {
688         crow::connections::systemBus->async_method_call(
689             [asyncResp](const boost::system::error_code& ec) {
690                 if (ec)
691                 {
692                     BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
693 
694                     messages::internalError(asyncResp->res);
695                     return;
696                 }
697             },
698             service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
699             "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
700     }
701 }
702 
handleManagersVirtualMediaActionInsertPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)703 inline void handleManagersVirtualMediaActionInsertPost(
704     crow::App& app, const crow::Request& req,
705     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
706     const std::string& name, const std::string& resName)
707 {
708     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
709     {
710         return;
711     }
712 
713     constexpr std::string_view action = "VirtualMedia.InsertMedia";
714     if (name != "bmc")
715     {
716         messages::resourceNotFound(asyncResp->res, action, resName);
717 
718         return;
719     }
720     InsertMediaActionParams actionParams;
721 
722     // Read obligatory parameters (url of image)
723     if (!json_util::readJsonAction(                                    //
724             req, asyncResp->res,                                       //
725             "Image", actionParams.imageUrl,                            //
726             "Inserted", actionParams.inserted,                         //
727             "Password", actionParams.password,                         //
728             "TransferMethod", actionParams.transferMethod,             //
729             "TransferProtocolType", actionParams.transferProtocolType, //
730             "UserName", actionParams.userName,                         //
731             "WriteProtected", actionParams.writeProtected              //
732             ))
733     {
734         return;
735     }
736 
737     dbus::utility::getDbusObject(
738         "/xyz/openbmc_project/VirtualMedia", {},
739         [asyncResp, action, actionParams,
740          resName](const boost::system::error_code& ec,
741                   const dbus::utility::MapperGetObject& getObjectType) mutable {
742             if (ec)
743             {
744                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
745                 messages::resourceNotFound(asyncResp->res, action, resName);
746 
747                 return;
748             }
749 
750             std::string service = getObjectType.begin()->first;
751             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
752 
753             sdbusplus::message::object_path path(
754                 "/xyz/openbmc_project/VirtualMedia");
755             dbus::utility::getManagedObjects(
756                 service, path,
757                 [service, resName, action, actionParams, asyncResp](
758                     const boost::system::error_code& ec2,
759                     const dbus::utility::ManagedObjectType& subtree) mutable {
760                     if (ec2)
761                     {
762                         // Not possible in proxy mode
763                         BMCWEB_LOG_DEBUG("InsertMedia not "
764                                          "allowed in proxy mode");
765                         messages::resourceNotFound(asyncResp->res, action,
766                                                    resName);
767 
768                         return;
769                     }
770                     for (const auto& object : subtree)
771                     {
772                         VmMode mode =
773                             parseObjectPathAndGetMode(object.first, resName);
774                         if (mode == VmMode::Legacy)
775                         {
776                             validateParams(asyncResp, service, resName,
777                                            actionParams);
778 
779                             return;
780                         }
781                     }
782                     BMCWEB_LOG_DEBUG("Parent item not found");
783                     messages::resourceNotFound(asyncResp->res, "VirtualMedia",
784                                                resName);
785                 });
786         });
787 }
788 
handleManagersVirtualMediaActionEject(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerName,const std::string & resName)789 inline void handleManagersVirtualMediaActionEject(
790     crow::App& app, const crow::Request& req,
791     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
792     const std::string& managerName, const std::string& resName)
793 {
794     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
795     {
796         return;
797     }
798 
799     constexpr std::string_view action = "VirtualMedia.EjectMedia";
800     if (managerName != "bmc")
801     {
802         messages::resourceNotFound(asyncResp->res, action, resName);
803 
804         return;
805     }
806 
807     dbus::utility::getDbusObject(
808         "/xyz/openbmc_project/VirtualMedia", {},
809         [asyncResp, action,
810          resName](const boost::system::error_code& ec2,
811                   const dbus::utility::MapperGetObject& getObjectType) {
812             if (ec2)
813             {
814                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}",
815                                  ec2);
816                 messages::internalError(asyncResp->res);
817 
818                 return;
819             }
820             std::string service = getObjectType.begin()->first;
821             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
822 
823             sdbusplus::message::object_path path(
824                 "/xyz/openbmc_project/VirtualMedia");
825             dbus::utility::getManagedObjects(
826                 service, path,
827                 [resName, service, action,
828                  asyncResp](const boost::system::error_code& ec,
829                             const dbus::utility::ManagedObjectType& subtree) {
830                     if (ec)
831                     {
832                         BMCWEB_LOG_ERROR("ObjectMapper : No Service found");
833                         messages::resourceNotFound(asyncResp->res, action,
834                                                    resName);
835                         return;
836                     }
837 
838                     for (const auto& object : subtree)
839                     {
840                         VmMode mode =
841                             parseObjectPathAndGetMode(object.first, resName);
842                         if (mode != VmMode::Invalid)
843                         {
844                             doEjectAction(asyncResp, service, resName,
845                                           mode == VmMode::Legacy);
846                             return;
847                         }
848                     }
849                     BMCWEB_LOG_DEBUG("Parent item not found");
850                     messages::resourceNotFound(asyncResp->res, "VirtualMedia",
851                                                resName);
852                 });
853         });
854 }
855 
handleManagersVirtualMediaCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name)856 inline void handleManagersVirtualMediaCollectionGet(
857     crow::App& app, const crow::Request& req,
858     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
859     const std::string& name)
860 {
861     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
862     {
863         return;
864     }
865     if (name != "bmc")
866     {
867         messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
868 
869         return;
870     }
871 
872     asyncResp->res.jsonValue["@odata.type"] =
873         "#VirtualMediaCollection.VirtualMediaCollection";
874     asyncResp->res.jsonValue["Name"] = "Virtual Media Services";
875     asyncResp->res.jsonValue["@odata.id"] =
876         boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", name);
877 
878     dbus::utility::getDbusObject(
879         "/xyz/openbmc_project/VirtualMedia", {},
880         [asyncResp, name](const boost::system::error_code& ec,
881                           const dbus::utility::MapperGetObject& getObjectType) {
882             if (ec)
883             {
884                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
885                 messages::internalError(asyncResp->res);
886 
887                 return;
888             }
889             std::string service = getObjectType.begin()->first;
890             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
891 
892             getVmResourceList(asyncResp, service, name);
893         });
894 }
895 
handleVirtualMediaGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & name,const std::string & resName)896 inline void handleVirtualMediaGet(
897     crow::App& app, const crow::Request& req,
898     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
899     const std::string& name, const std::string& resName)
900 {
901     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
902     {
903         return;
904     }
905     if (name != "bmc")
906     {
907         messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
908 
909         return;
910     }
911 
912     dbus::utility::getDbusObject(
913         "/xyz/openbmc_project/VirtualMedia", {},
914         [asyncResp, name,
915          resName](const boost::system::error_code& ec,
916                   const dbus::utility::MapperGetObject& getObjectType) {
917             if (ec)
918             {
919                 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec);
920                 messages::internalError(asyncResp->res);
921 
922                 return;
923             }
924             std::string service = getObjectType.begin()->first;
925             BMCWEB_LOG_DEBUG("GetObjectType: {}", service);
926 
927             getVmData(asyncResp, service, name, resName);
928         });
929 }
930 
requestNBDVirtualMediaRoutes(App & app)931 inline void requestNBDVirtualMediaRoutes(App& app)
932 {
933     BMCWEB_ROUTE(
934         app,
935         "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia")
936         .privileges(redfish::privileges::postVirtualMedia)
937         .methods(boost::beast::http::verb::post)(std::bind_front(
938             handleManagersVirtualMediaActionInsertPost, std::ref(app)));
939 
940     BMCWEB_ROUTE(
941         app,
942         "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia")
943         .privileges(redfish::privileges::postVirtualMedia)
944         .methods(boost::beast::http::verb::post)(std::bind_front(
945             handleManagersVirtualMediaActionEject, std::ref(app)));
946 
947     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/")
948         .privileges(redfish::privileges::getVirtualMediaCollection)
949         .methods(boost::beast::http::verb::get)(std::bind_front(
950             handleManagersVirtualMediaCollectionGet, std::ref(app)));
951 
952     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/")
953         .privileges(redfish::privileges::getVirtualMedia)
954         .methods(boost::beast::http::verb::get)(
955             std::bind_front(handleVirtualMediaGet, std::ref(app)));
956 }
957 
958 } // namespace redfish
959