blob: 4f6fad9b46fd18b06c766cdf663119b89478c6ef [file] [log] [blame]
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001/*
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
Ed Tanousb9d36b42022-02-26 21:42:46 -080018#include <account_service.hpp>
John Edward Broadbent7e860f12021-04-08 15:57:16 -070019#include <app.hpp>
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +010020#include <boost/process/async_pipe.hpp>
21#include <boost/type_traits/has_dereference.hpp>
Anna Platash9e319cf2020-11-17 10:18:31 +010022#include <boost/url/url_view.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070023#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070024#include <registries/privilege_registry.hpp>
Ed Tanousb9d36b42022-02-26 21:42:46 -080025#include <utils/json_utils.hpp>
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020026
27namespace redfish
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020028{
Anna Platash9e319cf2020-11-17 10:18:31 +010029/**
30 * @brief Function extracts transfer protocol name from URI.
31 */
Ed Tanous67df0732021-10-26 11:23:56 -070032inline 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}
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020052
53/**
54 * @brief Read all known properties from VM object interfaces
55 */
Ed Tanous22db1722021-06-09 10:53:51 -070056inline void
Ed Tanous8a592812022-06-04 09:06:59 -070057 vmParseInterfaceObject(const dbus::utility::DBusInteracesMap& interfaces,
zhanghch058d1b46d2021-04-01 11:18:24 +080058 const std::shared_ptr<bmcweb::AsyncResp>& aResp)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020059{
Ed Tanous8a592812022-06-04 09:06:59 -070060 for (const auto& [interface, values] : interfaces)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020061 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080062 if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint")
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020063 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080064 for (const auto& [property, value] : values)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020065 {
Ed Tanous711ac7a2021-12-20 09:34:41 -080066 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")
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020084 {
Anna Platash9e319cf2020-11-17 10:18:31 +010085 const std::string* imageUrlValue =
Ed Tanous711ac7a2021-12-20 09:34:41 -080086 std::get_if<std::string>(&value);
Ed Tanous26f69762022-01-25 09:49:11 -080087 if (imageUrlValue != nullptr && !imageUrlValue->empty())
Przemyslaw Czarnowskida4784d2020-11-06 09:58:25 +010088 {
Anna Platash9e319cf2020-11-17 10:18:31 +010089 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 }
Przemyslaw Czarnowskida4784d2020-11-06 09:58:25 +0100101
Anna Platash9e319cf2020-11-17 10:18:31 +0100102 aResp->res.jsonValue["Image"] = *imageUrlValue;
Anna Platash9e319cf2020-11-17 10:18:31 +0100103 aResp->res.jsonValue["TransferProtocolType"] =
104 getTransferProtocolTypeFromUri(*imageUrlValue);
105
Ed Tanous711ac7a2021-12-20 09:34:41 -0800106 aResp->res.jsonValue["ConnectedVia"] = "URI";
Anna Platash9e319cf2020-11-17 10:18:31 +0100107 }
108 }
Ed Tanous711ac7a2021-12-20 09:34:41 -0800109 if (property == "WriteProtected")
Anna Platash9e319cf2020-11-17 10:18:31 +0100110 {
Ed Tanous711ac7a2021-12-20 09:34:41 -0800111 const bool* writeProtectedValue = std::get_if<bool>(&value);
Ed Tanouse662eae2022-01-25 10:39:19 -0800112 if (writeProtectedValue != nullptr)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200113 {
Anna Platash9e319cf2020-11-17 10:18:31 +0100114 aResp->res.jsonValue["WriteProtected"] =
115 *writeProtectedValue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200116 }
117 }
118 }
119 }
Ed Tanous711ac7a2021-12-20 09:34:41 -0800120 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);
Ed Tanouse662eae2022-01-25 10:39:19 -0800127 if (activeValue == nullptr)
Ed Tanous711ac7a2021-12-20 09:34:41 -0800128 {
129 BMCWEB_LOG_DEBUG << "Value Active not found";
130 return;
131 }
132 aResp->res.jsonValue["Inserted"] = *activeValue;
133
Ed Tanouse05aec52022-01-25 10:28:56 -0800134 if (*activeValue)
Ed Tanous711ac7a2021-12-20 09:34:41 -0800135 {
136 aResp->res.jsonValue["ConnectedVia"] = "Applet";
137 }
138 }
139 }
140 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200141 }
142}
143
144/**
145 * @brief Fill template for Virtual Media Item.
146 */
Ed Tanous22db1722021-06-09 10:53:51 -0700147inline nlohmann::json vmItemTemplate(const std::string& name,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500148 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200149{
150 nlohmann::json item;
Ed Tanous22db1722021-06-09 10:53:51 -0700151
152 std::string id = "/redfish/v1/Managers/";
153 id += name;
154 id += "/VirtualMedia/";
155 id += resName;
156 item["@odata.id"] = std::move(id);
157
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100158 item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200159 item["Name"] = "Virtual Removable Media";
160 item["Id"] = resName;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200161 item["WriteProtected"] = true;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200162 item["MediaTypes"] = {"CD", "USBStick"};
163 item["TransferMethod"] = "Stream";
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100164 item["Oem"]["OpenBMC"]["@odata.type"] =
165 "#OemVirtualMedia.v1_0_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200166
167 return item;
168}
169
170/**
171 * @brief Fills collection data
172 */
Ed Tanous22db1722021-06-09 10:53:51 -0700173inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500174 const std::string& service,
175 const std::string& name)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200176{
177 BMCWEB_LOG_DEBUG << "Get available Virtual Media resources.";
178 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800179 [name,
180 aResp{std::move(aResp)}](const boost::system::error_code ec,
181 dbus::utility::ManagedObjectType& subtree) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700182 if (ec)
183 {
184 BMCWEB_LOG_DEBUG << "DBUS response error";
185 return;
186 }
187 nlohmann::json& members = aResp->res.jsonValue["Members"];
188 members = nlohmann::json::array();
189
190 for (const auto& object : subtree)
191 {
192 nlohmann::json item;
193 std::string path = object.first.filename();
194 if (path.empty())
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200195 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700196 continue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200197 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200198
Ed Tanous002d39b2022-05-31 08:59:27 -0700199 std::string id = "/redfish/v1/Managers/";
200 id += name;
201 id += "/VirtualMedia/";
202 id += path;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200203
Ed Tanous002d39b2022-05-31 08:59:27 -0700204 item["@odata.id"] = std::move(id);
205 members.emplace_back(std::move(item));
206 }
207 aResp->res.jsonValue["Members@odata.count"] = members.size();
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200208 },
209 service, "/xyz/openbmc_project/VirtualMedia",
210 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
211}
212
213/**
214 * @brief Fills data for specific resource
215 */
Ed Tanous22db1722021-06-09 10:53:51 -0700216inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500217 const std::string& service, const std::string& name,
218 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200219{
220 BMCWEB_LOG_DEBUG << "Get Virtual Media resource data.";
221
222 crow::connections::systemBus->async_method_call(
Ed Tanous914e2d52022-01-07 11:38:34 -0800223 [resName, name,
224 aResp](const boost::system::error_code ec,
225 const dbus::utility::ManagedObjectType& subtree) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700226 if (ec)
227 {
228 BMCWEB_LOG_DEBUG << "DBUS response error";
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200229
Ed Tanous002d39b2022-05-31 08:59:27 -0700230 return;
231 }
232
233 for (const auto& item : subtree)
234 {
235 std::string thispath = item.first.filename();
236 if (thispath.empty())
237 {
238 continue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200239 }
240
Ed Tanous002d39b2022-05-31 08:59:27 -0700241 if (thispath != resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200242 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700243 continue;
244 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200245
Ed Tanous002d39b2022-05-31 08:59:27 -0700246 // "Legacy"/"Proxy"
247 auto mode = item.first.parent_path();
248 // "VirtualMedia"
249 auto type = mode.parent_path();
250 if (mode.filename().empty() || type.filename().empty())
251 {
252 continue;
253 }
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200254
Ed Tanous002d39b2022-05-31 08:59:27 -0700255 if (type.filename() != "VirtualMedia")
256 {
257 continue;
258 }
Przemyslaw Czarnowski1a6258d2021-04-14 11:02:46 +0200259
Ed Tanous002d39b2022-05-31 08:59:27 -0700260 aResp->res.jsonValue = vmItemTemplate(name, resName);
261 std::string actionsId = "/redfish/v1/Managers/";
262 actionsId += name;
263 actionsId += "/VirtualMedia/";
264 actionsId += resName;
265 actionsId += "/Actions";
Przemyslaw Czarnowski1a6258d2021-04-14 11:02:46 +0200266
Ed Tanous002d39b2022-05-31 08:59:27 -0700267 // Check if dbus path is Legacy type
268 if (mode.filename() == "Legacy")
269 {
270 aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200271 ["target"] =
Ed Tanous002d39b2022-05-31 08:59:27 -0700272 actionsId + "/VirtualMedia.InsertMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200273 }
274
Ed Tanous002d39b2022-05-31 08:59:27 -0700275 vmParseInterfaceObject(item.second, aResp);
276
277 aResp->res
278 .jsonValue["Actions"]["#VirtualMedia.EjectMedia"]["target"] =
279 actionsId + "/VirtualMedia.EjectMedia";
280
281 return;
282 }
283
284 messages::resourceNotFound(
285 aResp->res, "#VirtualMedia.v1_3_0.VirtualMedia", resName);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200286 },
287 service, "/xyz/openbmc_project/VirtualMedia",
288 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
289}
290
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200291/**
Ed Tanous22db1722021-06-09 10:53:51 -0700292 * @brief Transfer protocols supported for InsertMedia action.
293 *
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200294 */
Ed Tanous22db1722021-06-09 10:53:51 -0700295enum class TransferProtocol
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200296{
Ed Tanous22db1722021-06-09 10:53:51 -0700297 https,
298 smb,
299 invalid
300};
301
302/**
303 * @brief Function extracts transfer protocol type from URI.
304 *
305 */
Ed Tanous67df0732021-10-26 11:23:56 -0700306inline std::optional<TransferProtocol>
Ed Tanousace85d62021-10-26 12:45:59 -0700307 getTransferProtocolFromUri(const boost::urls::url_view& imageUri)
Ed Tanous67df0732021-10-26 11:23:56 -0700308{
Ed Tanousace85d62021-10-26 12:45:59 -0700309 boost::string_view scheme = imageUri.scheme();
Ed Tanous67df0732021-10-26 11:23:56 -0700310 if (scheme == "smb")
311 {
312 return TransferProtocol::smb;
313 }
314 if (scheme == "https")
315 {
316 return TransferProtocol::https;
317 }
318 if (!scheme.empty())
319 {
320 return TransferProtocol::invalid;
321 }
322
323 return {};
324}
Ed Tanous22db1722021-06-09 10:53:51 -0700325
326/**
327 * @brief Function convert transfer protocol from string param.
328 *
329 */
330inline std::optional<TransferProtocol> getTransferProtocolFromParam(
331 const std::optional<std::string>& transferProtocolType)
332{
333 if (transferProtocolType == std::nullopt)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100334 {
Ed Tanous22db1722021-06-09 10:53:51 -0700335 return {};
Agata Olenderc6f4e012020-03-11 15:19:07 +0100336 }
337
Ed Tanous22db1722021-06-09 10:53:51 -0700338 if (*transferProtocolType == "CIFS")
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200339 {
Ed Tanous22db1722021-06-09 10:53:51 -0700340 return TransferProtocol::smb;
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200341 }
342
Ed Tanous22db1722021-06-09 10:53:51 -0700343 if (*transferProtocolType == "HTTPS")
344 {
345 return TransferProtocol::https;
346 }
347
348 return TransferProtocol::invalid;
349}
350
351/**
352 * @brief Function extends URI with transfer protocol type.
353 *
354 */
355inline std::string
356 getUriWithTransferProtocol(const std::string& imageUri,
357 const TransferProtocol& transferProtocol)
358{
359 if (transferProtocol == TransferProtocol::smb)
360 {
361 return "smb://" + imageUri;
362 }
363
364 if (transferProtocol == TransferProtocol::https)
365 {
366 return "https://" + imageUri;
367 }
368
369 return imageUri;
370}
371
372/**
373 * @brief Function validate parameters of insert media request.
374 *
375 */
376inline bool
377 validateParams(crow::Response& res, std::string& imageUrl,
378 const std::optional<bool>& inserted,
379 const std::optional<std::string>& transferMethod,
380 const std::optional<std::string>& transferProtocolType)
381{
382 BMCWEB_LOG_DEBUG << "Validation started";
383 // required param imageUrl must not be empty
384 if (imageUrl.empty())
385 {
386 BMCWEB_LOG_ERROR << "Request action parameter Image is empty.";
387
388 messages::propertyValueFormatError(res, "<empty>", "Image");
389
390 return false;
391 }
392
393 // optional param inserted must be true
Ed Tanouse05aec52022-01-25 10:28:56 -0800394 if ((inserted != std::nullopt) && !*inserted)
Ed Tanous22db1722021-06-09 10:53:51 -0700395 {
396 BMCWEB_LOG_ERROR
397 << "Request action optional parameter Inserted must be true.";
398
399 messages::actionParameterNotSupported(res, "Inserted", "InsertMedia");
400
401 return false;
402 }
403
404 // optional param transferMethod must be stream
405 if ((transferMethod != std::nullopt) && (*transferMethod != "Stream"))
406 {
407 BMCWEB_LOG_ERROR << "Request action optional parameter "
408 "TransferMethod must be Stream.";
409
410 messages::actionParameterNotSupported(res, "TransferMethod",
411 "InsertMedia");
412
413 return false;
414 }
Ed Tanousace85d62021-10-26 12:45:59 -0700415 boost::urls::result<boost::urls::url_view> url =
416 boost::urls::parse_uri(boost::string_view(imageUrl));
417 if (!url)
418 {
Michal Orzel9bc55692022-06-14 15:50:50 +0200419 messages::actionParameterValueFormatError(res, imageUrl, "Image",
420 "InsertMedia");
421 return false;
Ed Tanousace85d62021-10-26 12:45:59 -0700422 }
Ed Tanous22db1722021-06-09 10:53:51 -0700423 std::optional<TransferProtocol> uriTransferProtocolType =
Ed Tanousace85d62021-10-26 12:45:59 -0700424 getTransferProtocolFromUri(*url);
Ed Tanous22db1722021-06-09 10:53:51 -0700425
426 std::optional<TransferProtocol> paramTransferProtocolType =
427 getTransferProtocolFromParam(transferProtocolType);
428
429 // ImageUrl does not contain valid protocol type
430 if (*uriTransferProtocolType == TransferProtocol::invalid)
431 {
432 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
433 "contain specified protocol type from list: "
434 "(smb, https).";
435
Ed Tanousace85d62021-10-26 12:45:59 -0700436 messages::resourceAtUriInUnknownFormat(res, *url);
Ed Tanous22db1722021-06-09 10:53:51 -0700437
438 return false;
439 }
440
441 // transferProtocolType should contain value from list
442 if (*paramTransferProtocolType == TransferProtocol::invalid)
443 {
444 BMCWEB_LOG_ERROR << "Request action parameter TransferProtocolType "
445 "must be provided with value from list: "
446 "(CIFS, HTTPS).";
447
448 messages::propertyValueNotInList(res, *transferProtocolType,
449 "TransferProtocolType");
450 return false;
451 }
452
453 // valid transfer protocol not provided either with URI nor param
454 if ((uriTransferProtocolType == std::nullopt) &&
455 (paramTransferProtocolType == std::nullopt))
456 {
457 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
458 "contain specified protocol type or param "
459 "TransferProtocolType must be provided.";
460
Ed Tanousace85d62021-10-26 12:45:59 -0700461 messages::resourceAtUriInUnknownFormat(res, *url);
Ed Tanous22db1722021-06-09 10:53:51 -0700462
463 return false;
464 }
465
466 // valid transfer protocol provided both with URI and param
467 if ((paramTransferProtocolType != std::nullopt) &&
468 (uriTransferProtocolType != std::nullopt))
469 {
470 // check if protocol is the same for URI and param
471 if (*paramTransferProtocolType != *uriTransferProtocolType)
472 {
473 BMCWEB_LOG_ERROR << "Request action parameter "
474 "TransferProtocolType must contain the "
475 "same protocol type as protocol type "
476 "provided with param imageUrl.";
477
478 messages::actionParameterValueTypeError(res, *transferProtocolType,
479 "TransferProtocolType",
480 "InsertMedia");
481
482 return false;
483 }
484 }
485
486 // validation passed
487 // add protocol to URI if needed
488 if (uriTransferProtocolType == std::nullopt)
489 {
490 imageUrl =
491 getUriWithTransferProtocol(imageUrl, *paramTransferProtocolType);
492 }
493
494 return true;
495}
496
497template <typename T>
498static void secureCleanup(T& value)
499{
Ed Tanous4ecc6182022-01-07 09:36:26 -0800500 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
Ed Tanous22db1722021-06-09 10:53:51 -0700501 auto raw = const_cast<typename T::value_type*>(value.data());
502 explicit_bzero(raw, value.size() * sizeof(*raw));
503}
504
505class Credentials
506{
507 public:
508 Credentials(std::string&& user, std::string&& password) :
509 userBuf(std::move(user)), passBuf(std::move(password))
510 {}
511
512 ~Credentials()
513 {
514 secureCleanup(userBuf);
515 secureCleanup(passBuf);
516 }
517
518 const std::string& user()
519 {
520 return userBuf;
521 }
522
523 const std::string& password()
524 {
525 return passBuf;
526 }
527
528 Credentials() = delete;
529 Credentials(const Credentials&) = delete;
530 Credentials& operator=(const Credentials&) = delete;
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800531 Credentials(Credentials&&) = delete;
532 Credentials& operator=(Credentials&&) = delete;
Ed Tanous22db1722021-06-09 10:53:51 -0700533
534 private:
535 std::string userBuf;
536 std::string passBuf;
537};
538
539class CredentialsProvider
540{
541 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500542 template <typename T>
Ed Tanous22db1722021-06-09 10:53:51 -0700543 struct Deleter
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100544 {
Ed Tanous22db1722021-06-09 10:53:51 -0700545 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();
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100569 }
570
Ed Tanous22db1722021-06-09 10:53:51 -0700571 const std::string& password()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100572 {
Ed Tanous22db1722021-06-09 10:53:51 -0700573 return credentials.password();
574 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100575
Ed Tanous22db1722021-06-09 10:53:51 -0700576 SecureBuffer pack(FormatterFunc formatter)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100577 {
Ed Tanous22db1722021-06-09 10:53:51 -0700578 SecureBuffer packed{new Buffer{}};
Ed Tanouse662eae2022-01-25 10:39:19 -0800579 if (formatter != nullptr)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100580 {
Ed Tanous22db1722021-06-09 10:53:51 -0700581 formatter(credentials.user(), credentials.password(), *packed);
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100582 }
583
Ed Tanous22db1722021-06-09 10:53:51 -0700584 return packed;
585 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100586
Ed Tanous22db1722021-06-09 10:53:51 -0700587 private:
588 Credentials credentials;
589};
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100590
Ed Tanous22db1722021-06-09 10:53:51 -0700591// Wrapper for boost::async_pipe ensuring proper pipe cleanup
592template <typename Buffer>
593class Pipe
594{
595 public:
596 using unix_fd = sdbusplus::message::unix_fd;
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100597
Ed Tanous8a592812022-06-04 09:06:59 -0700598 Pipe(boost::asio::io_context& io, Buffer&& bufferIn) :
599 impl(io), buffer{std::move(bufferIn)}
Ed Tanous22db1722021-06-09 10:53:51 -0700600 {}
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100601
Ed Tanous22db1722021-06-09 10:53:51 -0700602 ~Pipe()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100603 {
Ed Tanous22db1722021-06-09 10:53:51 -0700604 // Named pipe needs to be explicitly removed
605 impl.close();
606 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100607
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800608 Pipe(const Pipe&) = delete;
609 Pipe(Pipe&&) = delete;
610 Pipe& operator=(const Pipe&) = delete;
611 Pipe& operator=(Pipe&&) = delete;
612
Ed Tanous22db1722021-06-09 10:53:51 -0700613 unix_fd fd()
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200614 {
Ed Tanous22db1722021-06-09 10:53:51 -0700615 return unix_fd{impl.native_source()};
616 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100617
Ed Tanous22db1722021-06-09 10:53:51 -0700618 template <typename WriteHandler>
619 void asyncWrite(WriteHandler&& handler)
620 {
621 impl.async_write_some(data(), std::forward<WriteHandler>(handler));
622 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100623
Ed Tanous22db1722021-06-09 10:53:51 -0700624 private:
625 // Specialization for pointer types
626 template <typename B = Buffer>
627 typename std::enable_if<boost::has_dereference<B>::value,
628 boost::asio::const_buffer>::type
629 data()
630 {
631 return boost::asio::buffer(*buffer);
632 }
633
634 template <typename B = Buffer>
635 typename std::enable_if<!boost::has_dereference<B>::value,
636 boost::asio::const_buffer>::type
637 data()
638 {
639 return boost::asio::buffer(buffer);
640 }
641
642 const std::string name;
643 boost::process::async_pipe impl;
644 Buffer buffer;
645};
646
647/**
648 * @brief Function transceives data with dbus directly.
649 *
650 * All BMC state properties will be retrieved before sending reset request.
651 */
652inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
653 const std::string& service, const std::string& name,
654 const std::string& imageUrl, const bool rw,
655 std::string&& userName, std::string&& password)
656{
657 using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>;
658 constexpr const size_t secretLimit = 1024;
659
660 std::shared_ptr<SecurePipe> secretPipe;
Ed Tanous168e20c2021-12-13 14:39:53 -0800661 dbus::utility::DbusVariantType unixFd = -1;
Ed Tanous22db1722021-06-09 10:53:51 -0700662
663 if (!userName.empty() || !password.empty())
664 {
665 // Encapsulate in safe buffer
666 CredentialsProvider credentials(std::move(userName),
667 std::move(password));
668
669 // Payload must contain data + NULL delimiters
670 if (credentials.user().size() + credentials.password().size() + 2 >
671 secretLimit)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100672 {
Ed Tanous22db1722021-06-09 10:53:51 -0700673 BMCWEB_LOG_ERROR << "Credentials too long to handle";
674 messages::unrecognizedRequestBody(asyncResp->res);
675 return;
676 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100677
Ed Tanous22db1722021-06-09 10:53:51 -0700678 // Pack secret
679 auto secret = credentials.pack(
680 [](const auto& user, const auto& pass, auto& buff) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700681 std::copy(user.begin(), user.end(), std::back_inserter(buff));
682 buff.push_back('\0');
683 std::copy(pass.begin(), pass.end(), std::back_inserter(buff));
684 buff.push_back('\0');
685 });
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100686
Ed Tanous22db1722021-06-09 10:53:51 -0700687 // Open pipe
688 secretPipe = std::make_shared<SecurePipe>(
689 crow::connections::systemBus->get_io_context(), std::move(secret));
690 unixFd = secretPipe->fd();
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100691
Ed Tanous22db1722021-06-09 10:53:51 -0700692 // Pass secret over pipe
693 secretPipe->asyncWrite(
694 [asyncResp](const boost::system::error_code& ec, std::size_t) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700695 if (ec)
696 {
697 BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec;
698 messages::internalError(asyncResp->res);
699 }
700 });
Ed Tanous22db1722021-06-09 10:53:51 -0700701 }
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100702
Ed Tanous22db1722021-06-09 10:53:51 -0700703 crow::connections::systemBus->async_method_call(
704 [asyncResp, secretPipe](const boost::system::error_code ec,
705 bool success) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700706 if (ec)
707 {
708 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
709 messages::internalError(asyncResp->res);
710 }
711 else if (!success)
712 {
713 BMCWEB_LOG_ERROR << "Service responded with error";
714 messages::generalError(asyncResp->res);
715 }
Ed Tanous22db1722021-06-09 10:53:51 -0700716 },
717 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
718 "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
719 unixFd);
720}
721
722/**
723 * @brief Function transceives data with dbus directly.
724 *
725 * All BMC state properties will be retrieved before sending reset request.
726 */
727inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
728 const std::string& service, const std::string& name,
729 bool legacy)
730{
731
732 // Legacy mount requires parameter with image
733 if (legacy)
734 {
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100735 crow::connections::systemBus->async_method_call(
Ed Tanous22db1722021-06-09 10:53:51 -0700736 [asyncResp](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700737 if (ec)
738 {
739 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
Ed Tanous22db1722021-06-09 10:53:51 -0700740
Ed Tanous002d39b2022-05-31 08:59:27 -0700741 messages::internalError(asyncResp->res);
742 return;
743 }
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100744 },
745 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
Ed Tanous22db1722021-06-09 10:53:51 -0700746 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200747 }
Ed Tanous22db1722021-06-09 10:53:51 -0700748 else // proxy
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200749 {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200750 crow::connections::systemBus->async_method_call(
Ed Tanous22db1722021-06-09 10:53:51 -0700751 [asyncResp](const boost::system::error_code ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700752 if (ec)
753 {
754 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
Ed Tanous22db1722021-06-09 10:53:51 -0700755
Ed Tanous002d39b2022-05-31 08:59:27 -0700756 messages::internalError(asyncResp->res);
757 return;
758 }
Ed Tanous22db1722021-06-09 10:53:51 -0700759 },
760 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
761 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
762 }
763}
764
Ed Tanous98be3e32021-09-16 15:05:36 -0700765struct InsertMediaActionParams
766{
767 std::string imageUrl;
768 std::optional<std::string> userName;
769 std::optional<std::string> password;
770 std::optional<std::string> transferMethod;
771 std::optional<std::string> transferProtocolType;
772 std::optional<bool> writeProtected = true;
773 std::optional<bool> inserted;
774};
775
Ed Tanous96825be2022-06-03 09:43:38 -0700776inline void handleManagersVirtualMediaActionInsertPost(
777 crow::App& app, const crow::Request& req,
778 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
779 const std::string& name, const std::string& resName)
780{
781 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
782 {
783 return;
784 }
785 if (name != "bmc")
786 {
787 messages::resourceNotFound(asyncResp->res, "VirtualMedia.Insert",
788 resName);
789
790 return;
791 }
792 InsertMediaActionParams actionParams;
793
794 // Read obligatory parameters (url of
795 // image)
796 if (!json_util::readJsonAction(
797 req, asyncResp->res, "Image", actionParams.imageUrl,
798 "WriteProtected", actionParams.writeProtected, "UserName",
799 actionParams.userName, "Password", actionParams.password,
800 "Inserted", actionParams.inserted, "TransferMethod",
801 actionParams.transferMethod, "TransferProtocolType",
802 actionParams.transferProtocolType))
803 {
804 BMCWEB_LOG_DEBUG << "Image is not provided";
805 return;
806 }
807
808 bool paramsValid = validateParams(
809 asyncResp->res, actionParams.imageUrl, actionParams.inserted,
810 actionParams.transferMethod, actionParams.transferProtocolType);
811
812 if (!paramsValid)
813 {
814 return;
815 }
816
817 crow::connections::systemBus->async_method_call(
818 [asyncResp, actionParams,
819 resName](const boost::system::error_code ec,
820 const dbus::utility::MapperGetObject& getObjectType) mutable {
821 if (ec)
822 {
823 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << 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,
Ed Tanous8a592812022-06-04 09:06:59 -0700833 asyncResp](const boost::system::error_code ec2,
Ed Tanous96825be2022-06-03 09:43:38 -0700834 dbus::utility::ManagedObjectType& subtree) mutable {
Ed Tanous8a592812022-06-04 09:06:59 -0700835 if (ec2)
Ed Tanous96825be2022-06-03 09:43:38 -0700836 {
837 BMCWEB_LOG_DEBUG << "DBUS response error";
838
839 return;
840 }
841
842 for (const auto& object : subtree)
843 {
844 const std::string& path =
845 static_cast<const std::string&>(object.first);
846
847 std::size_t lastIndex = path.rfind('/');
848 if (lastIndex == std::string::npos)
849 {
850 continue;
851 }
852
853 lastIndex += 1;
854
855 if (path.substr(lastIndex) == resName)
856 {
857 lastIndex = path.rfind("Proxy");
858 if (lastIndex != std::string::npos)
859 {
860 // Not possible in proxy mode
861 BMCWEB_LOG_DEBUG << "InsertMedia not "
862 "allowed in proxy mode";
863 messages::resourceNotFound(asyncResp->res,
864 "VirtualMedia.InsertMedia",
865 resName);
866
867 return;
868 }
869
870 lastIndex = path.rfind("Legacy");
871 if (lastIndex == std::string::npos)
872 {
873 continue;
874 }
875
876 // manager is irrelevant for
877 // VirtualMedia dbus calls
878 doMountVmLegacy(asyncResp, service, resName,
879 actionParams.imageUrl,
880 !(*actionParams.writeProtected),
881 std::move(*actionParams.userName),
882 std::move(*actionParams.password));
883
884 return;
885 }
886 }
887 BMCWEB_LOG_DEBUG << "Parent item not found";
888 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
889 },
890 service, "/xyz/openbmc_project/VirtualMedia",
891 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
892 },
893 "xyz.openbmc_project.ObjectMapper",
894 "/xyz/openbmc_project/object_mapper",
895 "xyz.openbmc_project.ObjectMapper", "GetObject",
896 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
897}
898
899inline void handleManagersVirtualMediaActionEject(
900 crow::App& app, const crow::Request& req,
901 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
902 const std::string& managerName, const std::string& resName)
903{
904 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
905 {
906 return;
907 }
908 if (managerName != "bmc")
909 {
910 messages::resourceNotFound(asyncResp->res, "VirtualMedia.Eject",
911 resName);
912
913 return;
914 }
915
916 crow::connections::systemBus->async_method_call(
917 [asyncResp,
Ed Tanous8a592812022-06-04 09:06:59 -0700918 resName](const boost::system::error_code ec2,
Ed Tanous96825be2022-06-03 09:43:38 -0700919 const dbus::utility::MapperGetObject& getObjectType) {
Ed Tanous8a592812022-06-04 09:06:59 -0700920 if (ec2)
Ed Tanous96825be2022-06-03 09:43:38 -0700921 {
Ed Tanous8a592812022-06-04 09:06:59 -0700922 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec2;
Ed Tanous96825be2022-06-03 09:43:38 -0700923 messages::internalError(asyncResp->res);
924
925 return;
926 }
927 std::string service = getObjectType.begin()->first;
928 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
929
930 crow::connections::systemBus->async_method_call(
931 [resName, service,
932 asyncResp{asyncResp}](const boost::system::error_code ec,
933 dbus::utility::ManagedObjectType& subtree) {
934 if (ec)
935 {
936 BMCWEB_LOG_DEBUG << "DBUS response error";
937
938 return;
939 }
940
941 for (const auto& object : subtree)
942 {
943 const std::string& path =
944 static_cast<const std::string&>(object.first);
945
946 std::size_t lastIndex = path.rfind('/');
947 if (lastIndex == std::string::npos)
948 {
949 continue;
950 }
951
952 lastIndex += 1;
953
954 if (path.substr(lastIndex) == resName)
955 {
956 lastIndex = path.rfind("Proxy");
957 if (lastIndex != std::string::npos)
958 {
959 // Proxy mode
960 doVmAction(asyncResp, service, resName, false);
961 }
962
963 lastIndex = path.rfind("Legacy");
964 if (lastIndex != std::string::npos)
965 {
966 // Legacy mode
967 doVmAction(asyncResp, service, resName, true);
968 }
969
970 return;
971 }
972 }
973 BMCWEB_LOG_DEBUG << "Parent item not found";
974 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
975 },
976 service, "/xyz/openbmc_project/VirtualMedia",
977 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
978 },
979 "xyz.openbmc_project.ObjectMapper",
980 "/xyz/openbmc_project/object_mapper",
981 "xyz.openbmc_project.ObjectMapper", "GetObject",
982 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
983}
984
985inline void handleManagersVirtualMediaCollectionGet(
986 crow::App& app, const crow::Request& req,
987 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
988 const std::string& name)
989{
990 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
991 {
992 return;
993 }
994 if (name != "bmc")
995 {
996 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
997
998 return;
999 }
1000
1001 asyncResp->res.jsonValue["@odata.type"] =
1002 "#VirtualMediaCollection.VirtualMediaCollection";
1003 asyncResp->res.jsonValue["Name"] = "Virtual Media Services";
1004 asyncResp->res.jsonValue["@odata.id"] =
1005 "/redfish/v1/Managers/" + name + "/VirtualMedia";
1006
1007 crow::connections::systemBus->async_method_call(
1008 [asyncResp, name](const boost::system::error_code ec,
1009 const dbus::utility::MapperGetObject& getObjectType) {
1010 if (ec)
1011 {
1012 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec;
1013 messages::internalError(asyncResp->res);
1014
1015 return;
1016 }
1017 std::string service = getObjectType.begin()->first;
1018 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1019
1020 getVmResourceList(asyncResp, service, name);
1021 },
1022 "xyz.openbmc_project.ObjectMapper",
1023 "/xyz/openbmc_project/object_mapper",
1024 "xyz.openbmc_project.ObjectMapper", "GetObject",
1025 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
1026}
1027
1028inline void
1029 handleVirtualMediaGet(crow::App& app, const crow::Request& req,
1030 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1031 const std::string& name, const std::string& resName)
1032{
1033 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1034 {
1035 return;
1036 }
1037 if (name != "bmc")
1038 {
1039 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
1040
1041 return;
1042 }
1043
1044 crow::connections::systemBus->async_method_call(
1045 [asyncResp, name,
1046 resName](const boost::system::error_code ec,
1047 const dbus::utility::MapperGetObject& getObjectType) {
1048 if (ec)
1049 {
1050 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec;
1051 messages::internalError(asyncResp->res);
1052
1053 return;
1054 }
1055 std::string service = getObjectType.begin()->first;
1056 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1057
1058 getVmData(asyncResp, service, name, resName);
1059 },
1060 "xyz.openbmc_project.ObjectMapper",
1061 "/xyz/openbmc_project/object_mapper",
1062 "xyz.openbmc_project.ObjectMapper", "GetObject",
1063 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
1064}
1065
Ed Tanous22db1722021-06-09 10:53:51 -07001066inline void requestNBDVirtualMediaRoutes(App& app)
1067{
George Liu0fda0f12021-11-16 10:06:17 +08001068 BMCWEB_ROUTE(
1069 app,
1070 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia")
Ed Tanoused398212021-06-09 17:05:54 -07001071 .privileges(redfish::privileges::postVirtualMedia)
Ed Tanous96825be2022-06-03 09:43:38 -07001072 .methods(boost::beast::http::verb::post)(std::bind_front(
1073 handleManagersVirtualMediaActionInsertPost, std::ref(app)));
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +02001074
George Liu0fda0f12021-11-16 10:06:17 +08001075 BMCWEB_ROUTE(
1076 app,
1077 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia")
Ed Tanoused398212021-06-09 17:05:54 -07001078 .privileges(redfish::privileges::postVirtualMedia)
Ed Tanous96825be2022-06-03 09:43:38 -07001079 .methods(boost::beast::http::verb::post)(std::bind_front(
1080 handleManagersVirtualMediaActionEject, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001081
Ed Tanous22db1722021-06-09 10:53:51 -07001082 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/")
Ed Tanoused398212021-06-09 17:05:54 -07001083 .privileges(redfish::privileges::getVirtualMediaCollection)
Ed Tanous96825be2022-06-03 09:43:38 -07001084 .methods(boost::beast::http::verb::get)(std::bind_front(
1085 handleManagersVirtualMediaCollectionGet, std::ref(app)));
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001086
Ed Tanous22db1722021-06-09 10:53:51 -07001087 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001088 .privileges(redfish::privileges::getVirtualMedia)
Ed Tanous22db1722021-06-09 10:53:51 -07001089 .methods(boost::beast::http::verb::get)(
Ed Tanous96825be2022-06-03 09:43:38 -07001090 std::bind_front(handleVirtualMediaGet, std::ref(app)));
Ed Tanous22db1722021-06-09 10:53:51 -07001091}
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001092
1093} // namespace redfish