xref: /openbmc/bmcweb/features/redfish/lib/virtual_media.hpp (revision 711ac7a931dd3f151fc4064063b5ea90404b9054)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include <app.hpp>
19 #include <boost/container/flat_map.hpp>
20 #include <boost/process/async_pipe.hpp>
21 #include <boost/type_traits/has_dereference.hpp>
22 #include <utils/json_utils.hpp>
23 // for GetObjectType and ManagedObjectType
24 
25 #include <account_service.hpp>
26 #include <boost/url/url_view.hpp>
27 #include <registries/privilege_registry.hpp>
28 
29 namespace redfish
30 {
31 /**
32  * @brief Function extracts transfer protocol name from URI.
33  */
34 inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri)
35 {
36     boost::urls::error_code ec;
37     boost::urls::url_view url =
38         boost::urls::parse_uri(boost::string_view(imageUri), ec);
39     if (ec)
40     {
41         return "None";
42     }
43     boost::string_view scheme = url.scheme();
44     if (scheme == "smb")
45     {
46         return "CIFS";
47     }
48     if (scheme == "https")
49     {
50         return "HTTPS";
51     }
52 
53     return "None";
54 }
55 
56 /**
57  * @brief Read all known properties from VM object interfaces
58  */
59 inline void
60     vmParseInterfaceObject(const dbus::utility::DBusInteracesMap& interface,
61                            const std::shared_ptr<bmcweb::AsyncResp>& aResp)
62 {
63     for (const auto& [interface, values] : interface)
64     {
65         if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint")
66         {
67             for (const auto& [property, value] : values)
68             {
69                 if (property == "EndpointId")
70                 {
71                     const std::string* endpointIdValue =
72                         std::get_if<std::string>(&value);
73                     if (endpointIdValue == nullptr)
74                     {
75                         continue;
76                     }
77                     if (!endpointIdValue->empty())
78                     {
79                         // Proxy mode
80                         aResp->res
81                             .jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
82                             *endpointIdValue;
83                         aResp->res.jsonValue["TransferProtocolType"] = "OEM";
84                     }
85                 }
86                 if (property == "ImageURL")
87                 {
88                     const std::string* imageUrlValue =
89                         std::get_if<std::string>(&value);
90                     if (imageUrlValue && !imageUrlValue->empty())
91                     {
92                         std::filesystem::path filePath = *imageUrlValue;
93                         if (!filePath.has_filename())
94                         {
95                             // this will handle https share, which not
96                             // necessarily has to have filename given.
97                             aResp->res.jsonValue["ImageName"] = "";
98                         }
99                         else
100                         {
101                             aResp->res.jsonValue["ImageName"] =
102                                 filePath.filename();
103                         }
104 
105                         aResp->res.jsonValue["Image"] = *imageUrlValue;
106                         aResp->res.jsonValue["TransferProtocolType"] =
107                             getTransferProtocolTypeFromUri(*imageUrlValue);
108 
109                         aResp->res.jsonValue["ConnectedVia"] = "URI";
110                     }
111                 }
112                 if (property == "WriteProtected")
113                 {
114                     const bool* writeProtectedValue = std::get_if<bool>(&value);
115                     if (writeProtectedValue)
116                     {
117                         aResp->res.jsonValue["WriteProtected"] =
118                             *writeProtectedValue;
119                     }
120                 }
121             }
122         }
123         if (interface == "xyz.openbmc_project.VirtualMedia.Process")
124         {
125             for (const auto& [property, value] : values)
126             {
127                 if (property == "Active")
128                 {
129                     const bool* activeValue = std::get_if<bool>(&value);
130                     if (!activeValue)
131                     {
132                         BMCWEB_LOG_DEBUG << "Value Active not found";
133                         return;
134                     }
135                     aResp->res.jsonValue["Inserted"] = *activeValue;
136 
137                     if (*activeValue == true)
138                     {
139                         aResp->res.jsonValue["ConnectedVia"] = "Applet";
140                     }
141                 }
142             }
143         }
144     }
145 }
146 
147 /**
148  * @brief Fill template for Virtual Media Item.
149  */
150 inline nlohmann::json vmItemTemplate(const std::string& name,
151                                      const std::string& resName)
152 {
153     nlohmann::json item;
154 
155     std::string id = "/redfish/v1/Managers/";
156     id += name;
157     id += "/VirtualMedia/";
158     id += resName;
159     item["@odata.id"] = std::move(id);
160 
161     item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
162     item["Name"] = "Virtual Removable Media";
163     item["Id"] = resName;
164     item["WriteProtected"] = true;
165     item["MediaTypes"] = {"CD", "USBStick"};
166     item["TransferMethod"] = "Stream";
167     item["Oem"]["OpenBMC"]["@odata.type"] =
168         "#OemVirtualMedia.v1_0_0.VirtualMedia";
169 
170     return item;
171 }
172 
173 /**
174  *  @brief Fills collection data
175  */
176 inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> aResp,
177                               const std::string& service,
178                               const std::string& name)
179 {
180     BMCWEB_LOG_DEBUG << "Get available Virtual Media resources.";
181     crow::connections::systemBus->async_method_call(
182         [name,
183          aResp{std::move(aResp)}](const boost::system::error_code ec,
184                                   dbus::utility::ManagedObjectType& subtree) {
185             if (ec)
186             {
187                 BMCWEB_LOG_DEBUG << "DBUS response error";
188                 return;
189             }
190             nlohmann::json& members = aResp->res.jsonValue["Members"];
191             members = nlohmann::json::array();
192 
193             for (const auto& object : subtree)
194             {
195                 nlohmann::json item;
196                 std::string path = object.first.filename();
197                 if (path.empty())
198                 {
199                     continue;
200                 }
201 
202                 std::string id = "/redfish/v1/Managers/";
203                 id += name;
204                 id += "/VirtualMedia/";
205                 id += path;
206 
207                 item["@odata.id"] = std::move(id);
208                 members.emplace_back(std::move(item));
209             }
210             aResp->res.jsonValue["Members@odata.count"] = members.size();
211         },
212         service, "/xyz/openbmc_project/VirtualMedia",
213         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
214 }
215 
216 /**
217  *  @brief Fills data for specific resource
218  */
219 inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
220                       const std::string& service, const std::string& name,
221                       const std::string& resName)
222 {
223     BMCWEB_LOG_DEBUG << "Get Virtual Media resource data.";
224 
225     crow::connections::systemBus->async_method_call(
226         [resName, name, aResp](const boost::system::error_code ec,
227                                dbus::utility::ManagedObjectType& subtree) {
228             if (ec)
229             {
230                 BMCWEB_LOG_DEBUG << "DBUS response error";
231 
232                 return;
233             }
234 
235             for (auto& item : subtree)
236             {
237                 std::string thispath = item.first.filename();
238                 if (thispath.empty())
239                 {
240                     continue;
241                 }
242 
243                 if (thispath != resName)
244                 {
245                     continue;
246                 }
247 
248                 // "Legacy"/"Proxy"
249                 auto mode = item.first.parent_path();
250                 // "VirtualMedia"
251                 auto type = mode.parent_path();
252                 if (mode.filename().empty() || type.filename().empty())
253                 {
254                     continue;
255                 }
256 
257                 if (type.filename() != "VirtualMedia")
258                 {
259                     continue;
260                 }
261 
262                 aResp->res.jsonValue = vmItemTemplate(name, resName);
263                 std::string actionsId = "/redfish/v1/Managers/";
264                 actionsId += name;
265                 actionsId += "/VirtualMedia/";
266                 actionsId += resName;
267                 actionsId += "/Actions";
268 
269                 // Check if dbus path is Legacy type
270                 if (mode.filename() == "Legacy")
271                 {
272                     aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
273                                         ["target"] =
274                         actionsId + "/VirtualMedia.InsertMedia";
275                 }
276 
277                 vmParseInterfaceObject(item.second, aResp);
278 
279                 aResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"]
280                                     ["target"] =
281                     actionsId + "/VirtualMedia.EjectMedia";
282 
283                 return;
284             }
285 
286             messages::resourceNotFound(
287                 aResp->res, "#VirtualMedia.v1_3_0.VirtualMedia", resName);
288         },
289         service, "/xyz/openbmc_project/VirtualMedia",
290         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
291 }
292 
293 /**
294  * @brief Transfer protocols supported for InsertMedia action.
295  *
296  */
297 enum class TransferProtocol
298 {
299     https,
300     smb,
301     invalid
302 };
303 
304 /**
305  * @brief Function extracts transfer protocol type from URI.
306  *
307  */
308 inline std::optional<TransferProtocol>
309     getTransferProtocolFromUri(const std::string& imageUri)
310 {
311     boost::urls::error_code ec;
312     boost::urls::url_view url =
313         boost::urls::parse_uri(boost::string_view(imageUri), ec);
314     if (ec)
315     {
316         return {};
317     }
318 
319     boost::string_view scheme = url.scheme();
320     if (scheme == "smb")
321     {
322         return TransferProtocol::smb;
323     }
324     if (scheme == "https")
325     {
326         return TransferProtocol::https;
327     }
328     if (!scheme.empty())
329     {
330         return TransferProtocol::invalid;
331     }
332 
333     return {};
334 }
335 
336 /**
337  * @brief Function convert transfer protocol from string param.
338  *
339  */
340 inline std::optional<TransferProtocol> getTransferProtocolFromParam(
341     const std::optional<std::string>& transferProtocolType)
342 {
343     if (transferProtocolType == std::nullopt)
344     {
345         return {};
346     }
347 
348     if (*transferProtocolType == "CIFS")
349     {
350         return TransferProtocol::smb;
351     }
352 
353     if (*transferProtocolType == "HTTPS")
354     {
355         return TransferProtocol::https;
356     }
357 
358     return TransferProtocol::invalid;
359 }
360 
361 /**
362  * @brief Function extends URI with transfer protocol type.
363  *
364  */
365 inline std::string
366     getUriWithTransferProtocol(const std::string& imageUri,
367                                const TransferProtocol& transferProtocol)
368 {
369     if (transferProtocol == TransferProtocol::smb)
370     {
371         return "smb://" + imageUri;
372     }
373 
374     if (transferProtocol == TransferProtocol::https)
375     {
376         return "https://" + imageUri;
377     }
378 
379     return imageUri;
380 }
381 
382 /**
383  * @brief Function validate parameters of insert media request.
384  *
385  */
386 inline bool
387     validateParams(crow::Response& res, std::string& imageUrl,
388                    const std::optional<bool>& inserted,
389                    const std::optional<std::string>& transferMethod,
390                    const std::optional<std::string>& transferProtocolType)
391 {
392     BMCWEB_LOG_DEBUG << "Validation started";
393     // required param imageUrl must not be empty
394     if (imageUrl.empty())
395     {
396         BMCWEB_LOG_ERROR << "Request action parameter Image is empty.";
397 
398         messages::propertyValueFormatError(res, "<empty>", "Image");
399 
400         return false;
401     }
402 
403     // optional param inserted must be true
404     if ((inserted != std::nullopt) && (*inserted != true))
405     {
406         BMCWEB_LOG_ERROR
407             << "Request action optional parameter Inserted must be true.";
408 
409         messages::actionParameterNotSupported(res, "Inserted", "InsertMedia");
410 
411         return false;
412     }
413 
414     // optional param transferMethod must be stream
415     if ((transferMethod != std::nullopt) && (*transferMethod != "Stream"))
416     {
417         BMCWEB_LOG_ERROR << "Request action optional parameter "
418                             "TransferMethod must be Stream.";
419 
420         messages::actionParameterNotSupported(res, "TransferMethod",
421                                               "InsertMedia");
422 
423         return false;
424     }
425 
426     std::optional<TransferProtocol> uriTransferProtocolType =
427         getTransferProtocolFromUri(imageUrl);
428 
429     std::optional<TransferProtocol> paramTransferProtocolType =
430         getTransferProtocolFromParam(transferProtocolType);
431 
432     // ImageUrl does not contain valid protocol type
433     if (*uriTransferProtocolType == TransferProtocol::invalid)
434     {
435         BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
436                             "contain specified protocol type from list: "
437                             "(smb, https).";
438 
439         messages::resourceAtUriInUnknownFormat(res, imageUrl);
440 
441         return false;
442     }
443 
444     // transferProtocolType should contain value from list
445     if (*paramTransferProtocolType == TransferProtocol::invalid)
446     {
447         BMCWEB_LOG_ERROR << "Request action parameter TransferProtocolType "
448                             "must be provided with value from list: "
449                             "(CIFS, HTTPS).";
450 
451         messages::propertyValueNotInList(res, *transferProtocolType,
452                                          "TransferProtocolType");
453         return false;
454     }
455 
456     // valid transfer protocol not provided either with URI nor param
457     if ((uriTransferProtocolType == std::nullopt) &&
458         (paramTransferProtocolType == std::nullopt))
459     {
460         BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
461                             "contain specified protocol type or param "
462                             "TransferProtocolType must be provided.";
463 
464         messages::resourceAtUriInUnknownFormat(res, imageUrl);
465 
466         return false;
467     }
468 
469     // valid transfer protocol provided both with URI and param
470     if ((paramTransferProtocolType != std::nullopt) &&
471         (uriTransferProtocolType != std::nullopt))
472     {
473         // check if protocol is the same for URI and param
474         if (*paramTransferProtocolType != *uriTransferProtocolType)
475         {
476             BMCWEB_LOG_ERROR << "Request action parameter "
477                                 "TransferProtocolType must  contain the "
478                                 "same protocol type as protocol type "
479                                 "provided with param imageUrl.";
480 
481             messages::actionParameterValueTypeError(res, *transferProtocolType,
482                                                     "TransferProtocolType",
483                                                     "InsertMedia");
484 
485             return false;
486         }
487     }
488 
489     // validation passed
490     // add protocol to URI if needed
491     if (uriTransferProtocolType == std::nullopt)
492     {
493         imageUrl =
494             getUriWithTransferProtocol(imageUrl, *paramTransferProtocolType);
495     }
496 
497     return true;
498 }
499 
500 template <typename T>
501 static void secureCleanup(T& value)
502 {
503     auto raw = const_cast<typename T::value_type*>(value.data());
504     explicit_bzero(raw, value.size() * sizeof(*raw));
505 }
506 
507 class Credentials
508 {
509   public:
510     Credentials(std::string&& user, std::string&& password) :
511         userBuf(std::move(user)), passBuf(std::move(password))
512     {}
513 
514     ~Credentials()
515     {
516         secureCleanup(userBuf);
517         secureCleanup(passBuf);
518     }
519 
520     const std::string& user()
521     {
522         return userBuf;
523     }
524 
525     const std::string& password()
526     {
527         return passBuf;
528     }
529 
530     Credentials() = delete;
531     Credentials(const Credentials&) = delete;
532     Credentials& operator=(const Credentials&) = delete;
533 
534   private:
535     std::string userBuf;
536     std::string passBuf;
537 };
538 
539 class CredentialsProvider
540 {
541   public:
542     template <typename T>
543     struct Deleter
544     {
545         void operator()(T* buff) const
546         {
547             if (buff)
548             {
549                 secureCleanup(*buff);
550                 delete buff;
551             }
552         }
553     };
554 
555     using Buffer = std::vector<char>;
556     using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>;
557     // Using explicit definition instead of std::function to avoid implicit
558     // conversions eg. stack copy instead of reference
559     using FormatterFunc = void(const std::string& username,
560                                const std::string& password, Buffer& dest);
561 
562     CredentialsProvider(std::string&& user, std::string&& password) :
563         credentials(std::move(user), std::move(password))
564     {}
565 
566     const std::string& user()
567     {
568         return credentials.user();
569     }
570 
571     const std::string& password()
572     {
573         return credentials.password();
574     }
575 
576     SecureBuffer pack(FormatterFunc formatter)
577     {
578         SecureBuffer packed{new Buffer{}};
579         if (formatter)
580         {
581             formatter(credentials.user(), credentials.password(), *packed);
582         }
583 
584         return packed;
585     }
586 
587   private:
588     Credentials credentials;
589 };
590 
591 // Wrapper for boost::async_pipe ensuring proper pipe cleanup
592 template <typename Buffer>
593 class Pipe
594 {
595   public:
596     using unix_fd = sdbusplus::message::unix_fd;
597 
598     Pipe(boost::asio::io_context& io, Buffer&& buffer) :
599         impl(io), buffer{std::move(buffer)}
600     {}
601 
602     ~Pipe()
603     {
604         // Named pipe needs to be explicitly removed
605         impl.close();
606     }
607 
608     unix_fd fd()
609     {
610         return unix_fd{impl.native_source()};
611     }
612 
613     template <typename WriteHandler>
614     void asyncWrite(WriteHandler&& handler)
615     {
616         impl.async_write_some(data(), std::forward<WriteHandler>(handler));
617     }
618 
619   private:
620     // Specialization for pointer types
621     template <typename B = Buffer>
622     typename std::enable_if<boost::has_dereference<B>::value,
623                             boost::asio::const_buffer>::type
624         data()
625     {
626         return boost::asio::buffer(*buffer);
627     }
628 
629     template <typename B = Buffer>
630     typename std::enable_if<!boost::has_dereference<B>::value,
631                             boost::asio::const_buffer>::type
632         data()
633     {
634         return boost::asio::buffer(buffer);
635     }
636 
637     const std::string name;
638     boost::process::async_pipe impl;
639     Buffer buffer;
640 };
641 
642 /**
643  * @brief Function transceives data with dbus directly.
644  *
645  * All BMC state properties will be retrieved before sending reset request.
646  */
647 inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
648                             const std::string& service, const std::string& name,
649                             const std::string& imageUrl, const bool rw,
650                             std::string&& userName, std::string&& password)
651 {
652     using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>;
653     constexpr const size_t secretLimit = 1024;
654 
655     std::shared_ptr<SecurePipe> secretPipe;
656     dbus::utility::DbusVariantType unixFd = -1;
657 
658     if (!userName.empty() || !password.empty())
659     {
660         // Encapsulate in safe buffer
661         CredentialsProvider credentials(std::move(userName),
662                                         std::move(password));
663 
664         // Payload must contain data + NULL delimiters
665         if (credentials.user().size() + credentials.password().size() + 2 >
666             secretLimit)
667         {
668             BMCWEB_LOG_ERROR << "Credentials too long to handle";
669             messages::unrecognizedRequestBody(asyncResp->res);
670             return;
671         }
672 
673         // Pack secret
674         auto secret = credentials.pack(
675             [](const auto& user, const auto& pass, auto& buff) {
676                 std::copy(user.begin(), user.end(), std::back_inserter(buff));
677                 buff.push_back('\0');
678                 std::copy(pass.begin(), pass.end(), std::back_inserter(buff));
679                 buff.push_back('\0');
680             });
681 
682         // Open pipe
683         secretPipe = std::make_shared<SecurePipe>(
684             crow::connections::systemBus->get_io_context(), std::move(secret));
685         unixFd = secretPipe->fd();
686 
687         // Pass secret over pipe
688         secretPipe->asyncWrite(
689             [asyncResp](const boost::system::error_code& ec, std::size_t) {
690                 if (ec)
691                 {
692                     BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec;
693                     messages::internalError(asyncResp->res);
694                 }
695             });
696     }
697 
698     crow::connections::systemBus->async_method_call(
699         [asyncResp, secretPipe](const boost::system::error_code ec,
700                                 bool success) {
701             if (ec)
702             {
703                 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
704                 messages::internalError(asyncResp->res);
705             }
706             else if (!success)
707             {
708                 BMCWEB_LOG_ERROR << "Service responded with error";
709                 messages::generalError(asyncResp->res);
710             }
711         },
712         service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
713         "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
714         unixFd);
715 }
716 
717 /**
718  * @brief Function transceives data with dbus directly.
719  *
720  * All BMC state properties will be retrieved before sending reset request.
721  */
722 inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
723                        const std::string& service, const std::string& name,
724                        bool legacy)
725 {
726 
727     // Legacy mount requires parameter with image
728     if (legacy)
729     {
730         crow::connections::systemBus->async_method_call(
731             [asyncResp](const boost::system::error_code ec) {
732                 if (ec)
733                 {
734                     BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
735 
736                     messages::internalError(asyncResp->res);
737                     return;
738                 }
739             },
740             service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
741             "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
742     }
743     else // proxy
744     {
745         crow::connections::systemBus->async_method_call(
746             [asyncResp](const boost::system::error_code ec) {
747                 if (ec)
748                 {
749                     BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
750 
751                     messages::internalError(asyncResp->res);
752                     return;
753                 }
754             },
755             service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
756             "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
757     }
758 }
759 
760 struct InsertMediaActionParams
761 {
762     std::string imageUrl;
763     std::optional<std::string> userName;
764     std::optional<std::string> password;
765     std::optional<std::string> transferMethod;
766     std::optional<std::string> transferProtocolType;
767     std::optional<bool> writeProtected = true;
768     std::optional<bool> inserted;
769 };
770 
771 inline void requestNBDVirtualMediaRoutes(App& app)
772 {
773     BMCWEB_ROUTE(
774         app,
775         "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia")
776         .privileges(redfish::privileges::postVirtualMedia)
777         .methods(boost::beast::http::verb::post)(
778             [](const crow::Request& req,
779                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
780                const std::string& name, const std::string& resName) {
781                 if (name != "bmc")
782                 {
783                     messages::resourceNotFound(asyncResp->res,
784                                                "VirtualMedia.Insert", resName);
785 
786                     return;
787                 }
788                 InsertMediaActionParams actionParams;
789 
790                 // Read obligatory parameters (url of
791                 // image)
792                 if (!json_util::readJson(
793                         req, asyncResp->res, "Image", actionParams.imageUrl,
794                         "WriteProtected", actionParams.writeProtected,
795                         "UserName", actionParams.userName, "Password",
796                         actionParams.password, "Inserted",
797                         actionParams.inserted, "TransferMethod",
798                         actionParams.transferMethod, "TransferProtocolType",
799                         actionParams.transferProtocolType))
800                 {
801                     BMCWEB_LOG_DEBUG << "Image is not provided";
802                     return;
803                 }
804 
805                 bool paramsValid = validateParams(
806                     asyncResp->res, actionParams.imageUrl,
807                     actionParams.inserted, actionParams.transferMethod,
808                     actionParams.transferProtocolType);
809 
810                 if (paramsValid == false)
811                 {
812                     return;
813                 }
814 
815                 crow::connections::systemBus->async_method_call(
816                     [asyncResp, actionParams,
817                      resName](const boost::system::error_code ec,
818                               const GetObjectType& getObjectType) mutable {
819                         if (ec)
820                         {
821                             BMCWEB_LOG_ERROR
822                                 << "ObjectMapper::GetObject call failed: "
823                                 << ec;
824                             messages::internalError(asyncResp->res);
825 
826                             return;
827                         }
828                         std::string service = getObjectType.begin()->first;
829                         BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
830 
831                         crow::connections::systemBus->async_method_call(
832                             [service, resName, actionParams,
833                              asyncResp](const boost::system::error_code ec,
834                                         dbus::utility::ManagedObjectType&
835                                             subtree) mutable {
836                                 if (ec)
837                                 {
838                                     BMCWEB_LOG_DEBUG << "DBUS response error";
839 
840                                     return;
841                                 }
842 
843                                 for (const auto& object : subtree)
844                                 {
845                                     const std::string& path =
846                                         static_cast<const std::string&>(
847                                             object.first);
848 
849                                     std::size_t lastIndex = path.rfind('/');
850                                     if (lastIndex == std::string::npos)
851                                     {
852                                         continue;
853                                     }
854 
855                                     lastIndex += 1;
856 
857                                     if (path.substr(lastIndex) == resName)
858                                     {
859                                         lastIndex = path.rfind("Proxy");
860                                         if (lastIndex != std::string::npos)
861                                         {
862                                             // Not possible in proxy mode
863                                             BMCWEB_LOG_DEBUG
864                                                 << "InsertMedia not "
865                                                    "allowed in proxy mode";
866                                             messages::resourceNotFound(
867                                                 asyncResp->res,
868                                                 "VirtualMedia.InsertMedia",
869                                                 resName);
870 
871                                             return;
872                                         }
873 
874                                         lastIndex = path.rfind("Legacy");
875                                         if (lastIndex == std::string::npos)
876                                         {
877                                             continue;
878                                         }
879 
880                                         // manager is irrelevant for
881                                         // VirtualMedia dbus calls
882                                         doMountVmLegacy(
883                                             asyncResp, service, resName,
884                                             actionParams.imageUrl,
885                                             !(*actionParams.writeProtected),
886                                             std::move(*actionParams.userName),
887                                             std::move(*actionParams.password));
888 
889                                         return;
890                                     }
891                                 }
892                                 BMCWEB_LOG_DEBUG << "Parent item not found";
893                                 messages::resourceNotFound(
894                                     asyncResp->res, "VirtualMedia", resName);
895                             },
896                             service, "/xyz/openbmc_project/VirtualMedia",
897                             "org.freedesktop.DBus.ObjectManager",
898                             "GetManagedObjects");
899                     },
900                     "xyz.openbmc_project.ObjectMapper",
901                     "/xyz/openbmc_project/object_mapper",
902                     "xyz.openbmc_project.ObjectMapper", "GetObject",
903                     "/xyz/openbmc_project/VirtualMedia",
904                     std::array<const char*, 0>());
905             });
906 
907     BMCWEB_ROUTE(
908         app,
909         "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia")
910         .privileges(redfish::privileges::postVirtualMedia)
911         .methods(boost::beast::http::verb::post)(
912             [](const crow::Request&,
913                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
914                const std::string& name, const std::string& resName) {
915                 if (name != "bmc")
916                 {
917                     messages::resourceNotFound(asyncResp->res,
918                                                "VirtualMedia.Eject", resName);
919 
920                     return;
921                 }
922 
923                 crow::connections::systemBus->async_method_call(
924                     [asyncResp, resName](const boost::system::error_code ec,
925                                          const GetObjectType& getObjectType) {
926                         if (ec)
927                         {
928                             BMCWEB_LOG_ERROR
929                                 << "ObjectMapper::GetObject call failed: "
930                                 << ec;
931                             messages::internalError(asyncResp->res);
932 
933                             return;
934                         }
935                         std::string service = getObjectType.begin()->first;
936                         BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
937 
938                         crow::connections::systemBus->async_method_call(
939                             [resName, service, asyncResp{asyncResp}](
940                                 const boost::system::error_code ec,
941                                 dbus::utility::ManagedObjectType& subtree) {
942                                 if (ec)
943                                 {
944                                     BMCWEB_LOG_DEBUG << "DBUS response error";
945 
946                                     return;
947                                 }
948 
949                                 for (const auto& object : subtree)
950                                 {
951                                     const std::string& path =
952                                         static_cast<const std::string&>(
953                                             object.first);
954 
955                                     std::size_t lastIndex = path.rfind('/');
956                                     if (lastIndex == std::string::npos)
957                                     {
958                                         continue;
959                                     }
960 
961                                     lastIndex += 1;
962 
963                                     if (path.substr(lastIndex) == resName)
964                                     {
965                                         lastIndex = path.rfind("Proxy");
966                                         if (lastIndex != std::string::npos)
967                                         {
968                                             // Proxy mode
969                                             doVmAction(asyncResp, service,
970                                                        resName, false);
971                                         }
972 
973                                         lastIndex = path.rfind("Legacy");
974                                         if (lastIndex != std::string::npos)
975                                         {
976                                             // Legacy mode
977                                             doVmAction(asyncResp, service,
978                                                        resName, true);
979                                         }
980 
981                                         return;
982                                     }
983                                 }
984                                 BMCWEB_LOG_DEBUG << "Parent item not found";
985                                 messages::resourceNotFound(
986                                     asyncResp->res, "VirtualMedia", resName);
987                             },
988                             service, "/xyz/openbmc_project/VirtualMedia",
989                             "org.freedesktop.DBus.ObjectManager",
990                             "GetManagedObjects");
991                     },
992                     "xyz.openbmc_project.ObjectMapper",
993                     "/xyz/openbmc_project/object_mapper",
994                     "xyz.openbmc_project.ObjectMapper", "GetObject",
995                     "/xyz/openbmc_project/VirtualMedia",
996                     std::array<const char*, 0>());
997             });
998     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/")
999         .privileges(redfish::privileges::getVirtualMediaCollection)
1000         .methods(boost::beast::http::verb::get)(
1001             [](const crow::Request& /* req */,
1002                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1003                const std::string& name) {
1004                 if (name != "bmc")
1005                 {
1006                     messages::resourceNotFound(asyncResp->res, "VirtualMedia",
1007                                                name);
1008 
1009                     return;
1010                 }
1011 
1012                 asyncResp->res.jsonValue["@odata.type"] =
1013                     "#VirtualMediaCollection.VirtualMediaCollection";
1014                 asyncResp->res.jsonValue["Name"] = "Virtual Media Services";
1015                 asyncResp->res.jsonValue["@odata.id"] =
1016                     "/redfish/v1/Managers/" + name + "/VirtualMedia";
1017 
1018                 crow::connections::systemBus->async_method_call(
1019                     [asyncResp, name](const boost::system::error_code ec,
1020                                       const GetObjectType& getObjectType) {
1021                         if (ec)
1022                         {
1023                             BMCWEB_LOG_ERROR
1024                                 << "ObjectMapper::GetObject call failed: "
1025                                 << ec;
1026                             messages::internalError(asyncResp->res);
1027 
1028                             return;
1029                         }
1030                         std::string service = getObjectType.begin()->first;
1031                         BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1032 
1033                         getVmResourceList(asyncResp, service, name);
1034                     },
1035                     "xyz.openbmc_project.ObjectMapper",
1036                     "/xyz/openbmc_project/object_mapper",
1037                     "xyz.openbmc_project.ObjectMapper", "GetObject",
1038                     "/xyz/openbmc_project/VirtualMedia",
1039                     std::array<const char*, 0>());
1040             });
1041 
1042     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/")
1043         .privileges(redfish::privileges::getVirtualMedia)
1044         .methods(boost::beast::http::verb::get)(
1045             [](const crow::Request& /* req */,
1046                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1047                const std::string& name, const std::string& resName) {
1048                 if (name != "bmc")
1049                 {
1050                     messages::resourceNotFound(asyncResp->res, "VirtualMedia",
1051                                                resName);
1052 
1053                     return;
1054                 }
1055 
1056                 crow::connections::systemBus->async_method_call(
1057                     [asyncResp, name,
1058                      resName](const boost::system::error_code ec,
1059                               const GetObjectType& getObjectType) {
1060                         if (ec)
1061                         {
1062                             BMCWEB_LOG_ERROR
1063                                 << "ObjectMapper::GetObject call failed: "
1064                                 << ec;
1065                             messages::internalError(asyncResp->res);
1066 
1067                             return;
1068                         }
1069                         std::string service = getObjectType.begin()->first;
1070                         BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1071 
1072                         getVmData(asyncResp, service, name, resName);
1073                     },
1074                     "xyz.openbmc_project.ObjectMapper",
1075                     "/xyz/openbmc_project/object_mapper",
1076                     "xyz.openbmc_project.ObjectMapper", "GetObject",
1077                     "/xyz/openbmc_project/VirtualMedia",
1078                     std::array<const char*, 0>());
1079             });
1080 }
1081 
1082 } // namespace redfish
1083