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