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