blob: 13361904cb397585d96621b5fba10081afe05066 [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
18#include <boost/container/flat_map.hpp>
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +010019#include <boost/process/async_pipe.hpp>
20#include <boost/type_traits/has_dereference.hpp>
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020021#include <node.hpp>
22#include <utils/json_utils.hpp>
23// for GetObjectType and ManagedObjectType
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +020024#include <account_service.hpp>
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020025
26namespace redfish
27
28{
29
30/**
31 * @brief Read all known properties from VM object interfaces
32 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -050033static void vmParseInterfaceObject(const DbusInterfaceType& interface,
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020034 std::shared_ptr<AsyncResp> aResp)
35{
36 const auto mountPointIface =
37 interface.find("xyz.openbmc_project.VirtualMedia.MountPoint");
38 if (mountPointIface == interface.cend())
39 {
40 BMCWEB_LOG_DEBUG << "Interface MountPoint not found";
41 return;
42 }
43
44 const auto processIface =
45 interface.find("xyz.openbmc_project.VirtualMedia.Process");
46 if (processIface == interface.cend())
47 {
48 BMCWEB_LOG_DEBUG << "Interface Process not found";
49 return;
50 }
51
52 const auto endpointIdProperty = mountPointIface->second.find("EndpointId");
53 if (endpointIdProperty == mountPointIface->second.cend())
54 {
55 BMCWEB_LOG_DEBUG << "Property EndpointId not found";
56 return;
57 }
58
59 const auto activeProperty = processIface->second.find("Active");
60 if (activeProperty == processIface->second.cend())
61 {
62 BMCWEB_LOG_DEBUG << "Property Active not found";
63 return;
64 }
65
Gunnar Mills1214b7e2020-06-04 10:11:30 -050066 const bool* activeValue = std::get_if<bool>(&activeProperty->second);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020067 if (!activeValue)
68 {
69 BMCWEB_LOG_DEBUG << "Value Active not found";
70 return;
71 }
72
Gunnar Mills1214b7e2020-06-04 10:11:30 -050073 const std::string* endpointIdValue =
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020074 std::get_if<std::string>(&endpointIdProperty->second);
75 if (endpointIdValue)
76 {
77 if (!endpointIdValue->empty())
78 {
79 // Proxy mode
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +010080 aResp->res.jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
81 *endpointIdValue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020082 aResp->res.jsonValue["TransferProtocolType"] = "OEM";
83 aResp->res.jsonValue["Inserted"] = *activeValue;
84 if (*activeValue == true)
85 {
86 aResp->res.jsonValue["ConnectedVia"] = "Applet";
87 }
88 }
89 else
90 {
91 // Legacy mode
92 const auto imageUrlProperty =
93 mountPointIface->second.find("ImageURL");
94 if (imageUrlProperty != processIface->second.cend())
95 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -050096 const std::string* imageUrlValue =
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +020097 std::get_if<std::string>(&imageUrlProperty->second);
98 if (imageUrlValue && !imageUrlValue->empty())
99 {
Przemyslaw Czarnowskida4784d2020-11-06 09:58:25 +0100100 std::filesystem::path filePath = *imageUrlValue;
101 if (!filePath.has_filename())
102 {
103 // this will handle https share, which not necessarily
104 // has to have filename given.
105 aResp->res.jsonValue["ImageName"] = "";
106 }
107 else
108 {
109 aResp->res.jsonValue["ImageName"] = filePath.filename();
110 }
111
112 aResp->res.jsonValue["Image"] = *imageUrlValue;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200113 aResp->res.jsonValue["Inserted"] = *activeValue;
114 if (*activeValue == true)
115 {
116 aResp->res.jsonValue["ConnectedVia"] = "URI";
117 }
118 }
119 }
120 }
121 }
122}
123
124/**
125 * @brief Fill template for Virtual Media Item.
126 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500127static nlohmann::json vmItemTemplate(const std::string& name,
128 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200129{
130 nlohmann::json item;
131 item["@odata.id"] =
132 "/redfish/v1/Managers/" + name + "/VirtualMedia/" + resName;
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100133 item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200134 item["Name"] = "Virtual Removable Media";
135 item["Id"] = resName;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200136 item["WriteProtected"] = true;
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200137 item["MediaTypes"] = {"CD", "USBStick"};
138 item["TransferMethod"] = "Stream";
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100139 item["Oem"]["OpenBMC"]["@odata.type"] =
140 "#OemVirtualMedia.v1_0_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200141
142 return item;
143}
144
145/**
146 * @brief Fills collection data
147 */
148static void getVmResourceList(std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500149 const std::string& service,
150 const std::string& name)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200151{
152 BMCWEB_LOG_DEBUG << "Get available Virtual Media resources.";
153 crow::connections::systemBus->async_method_call(
154 [name, aResp{std::move(aResp)}](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500155 ManagedObjectType& subtree) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200156 if (ec)
157 {
158 BMCWEB_LOG_DEBUG << "DBUS response error";
159 return;
160 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500161 nlohmann::json& members = aResp->res.jsonValue["Members"];
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200162 members = nlohmann::json::array();
163
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500164 for (const auto& object : subtree)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200165 {
166 nlohmann::json item;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500167 const std::string& path =
168 static_cast<const std::string&>(object.first);
Ed Tanousf23b7292020-10-15 09:41:17 -0700169 std::size_t lastIndex = path.rfind('/');
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200170 if (lastIndex == std::string::npos)
171 {
172 continue;
173 }
174
175 lastIndex += 1;
176
177 item["@odata.id"] = "/redfish/v1/Managers/" + name +
178 "/VirtualMedia/" + path.substr(lastIndex);
179
180 members.emplace_back(std::move(item));
181 }
182 aResp->res.jsonValue["Members@odata.count"] = members.size();
183 },
184 service, "/xyz/openbmc_project/VirtualMedia",
185 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
186}
187
188/**
189 * @brief Fills data for specific resource
190 */
191static void getVmData(std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500192 const std::string& service, const std::string& name,
193 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200194{
195 BMCWEB_LOG_DEBUG << "Get Virtual Media resource data.";
196
197 crow::connections::systemBus->async_method_call(
198 [resName, name, aResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500199 ManagedObjectType& subtree) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200200 if (ec)
201 {
202 BMCWEB_LOG_DEBUG << "DBUS response error";
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200203
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200204 return;
205 }
206
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500207 for (auto& item : subtree)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200208 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500209 const std::string& path =
210 static_cast<const std::string&>(item.first);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200211
Ed Tanousf23b7292020-10-15 09:41:17 -0700212 std::size_t lastItem = path.rfind('/');
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200213 if (lastItem == std::string::npos)
214 {
215 continue;
216 }
217
218 if (path.substr(lastItem + 1) != resName)
219 {
220 continue;
221 }
222
223 aResp->res.jsonValue = vmItemTemplate(name, resName);
224
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200225 // Check if dbus path is Legacy type
226 if (path.find("VirtualMedia/Legacy") != std::string::npos)
227 {
228 aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
229 ["target"] =
230 "/redfish/v1/Managers/" + name + "/VirtualMedia/" +
231 resName + "/Actions/VirtualMedia.InsertMedia";
232 }
233
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200234 vmParseInterfaceObject(item.second, aResp);
235
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200236 aResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"]
237 ["target"] =
238 "/redfish/v1/Managers/" + name + "/VirtualMedia/" +
239 resName + "/Actions/VirtualMedia.EjectMedia";
240
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200241 return;
242 }
243
244 messages::resourceNotFound(
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100245 aResp->res, "#VirtualMedia.v1_3_0.VirtualMedia", resName);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200246 },
247 service, "/xyz/openbmc_project/VirtualMedia",
248 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
249}
250
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200251/**
252 @brief InsertMedia action class
253 */
254class VirtualMediaActionInsertMedia : public Node
255{
256 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700257 VirtualMediaActionInsertMedia(App& app) :
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200258 Node(app,
259 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
260 "VirtualMedia.InsertMedia",
261 std::string(), std::string())
262 {
263 entityPrivileges = {
264 {boost::beast::http::verb::get, {{"Login"}}},
265 {boost::beast::http::verb::head, {{"Login"}}},
266 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
267 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
268 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
269 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
270 }
271
272 private:
273 /**
Agata Olenderc6f4e012020-03-11 15:19:07 +0100274 * @brief Transfer protocols supported for InsertMedia action.
275 *
276 */
277 enum class TransferProtocol
278 {
279 https,
280 smb,
281 invalid
282 };
283
284 /**
285 * @brief Function extracts transfer protocol type from URI.
286 *
287 */
288 std::optional<TransferProtocol>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500289 getTransferProtocolFromUri(const std::string& imageUri)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100290 {
291 if (imageUri.find("smb://") != std::string::npos)
292 {
293 return TransferProtocol::smb;
294 }
295 else if (imageUri.find("https://") != std::string::npos)
296 {
297 return TransferProtocol::https;
298 }
299 else if (imageUri.find("://") != std::string::npos)
300 {
301 return TransferProtocol::invalid;
302 }
303 else
304 {
305 return {};
306 }
307 }
308
309 /**
310 * @brief Function convert transfer protocol from string param.
311 *
312 */
313 std::optional<TransferProtocol> getTransferProtocolFromParam(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500314 const std::optional<std::string>& transferProtocolType)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100315 {
316 if (transferProtocolType == std::nullopt)
317 {
318 return {};
319 }
320
321 if (*transferProtocolType == "CIFS")
322 {
323 return TransferProtocol::smb;
324 }
325
326 if (*transferProtocolType == "HTTPS")
327 {
328 return TransferProtocol::https;
329 }
330
331 return TransferProtocol::invalid;
332 }
333
334 /**
335 * @brief Function extends URI with transfer protocol type.
336 *
337 */
338 const std::string
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500339 getUriWithTransferProtocol(const std::string& imageUri,
340 const TransferProtocol& transferProtocol)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100341 {
342 if (transferProtocol == TransferProtocol::smb)
343 {
344 return "smb://" + imageUri;
345 }
346
347 if (transferProtocol == TransferProtocol::https)
348 {
349 return "https://" + imageUri;
350 }
351
352 return imageUri;
353 }
354
355 /**
356 * @brief Function validate parameters of insert media request.
357 *
358 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500359 bool validateParams(crow::Response& res, std::string& imageUrl,
360 const std::optional<bool>& inserted,
361 const std::optional<std::string>& transferMethod,
362 const std::optional<std::string>& transferProtocolType)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100363 {
364 BMCWEB_LOG_DEBUG << "Validation started";
365 // required param imageUrl must not be empty
366 if (imageUrl.empty())
367 {
368 BMCWEB_LOG_ERROR << "Request action parameter Image is empty.";
369
370 messages::propertyValueFormatError(res, "<empty>", "Image");
371
372 return false;
373 }
374
375 // optional param inserted must be true
376 if ((inserted != std::nullopt) && (*inserted != true))
377 {
378 BMCWEB_LOG_ERROR
379 << "Request action optional parameter Inserted must be true.";
380
381 messages::actionParameterNotSupported(res, "Inserted",
382 "InsertMedia");
383
384 return false;
385 }
386
387 // optional param transferMethod must be stream
388 if ((transferMethod != std::nullopt) && (*transferMethod != "Stream"))
389 {
390 BMCWEB_LOG_ERROR << "Request action optional parameter "
391 "TransferMethod must be Stream.";
392
393 messages::actionParameterNotSupported(res, "TransferMethod",
394 "InsertMedia");
395
396 return false;
397 }
398
399 std::optional<TransferProtocol> uriTransferProtocolType =
400 getTransferProtocolFromUri(imageUrl);
401
402 std::optional<TransferProtocol> paramTransferProtocolType =
403 getTransferProtocolFromParam(transferProtocolType);
404
405 // ImageUrl does not contain valid protocol type
406 if (*uriTransferProtocolType == TransferProtocol::invalid)
407 {
408 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
409 "contain specified protocol type from list: "
410 "(smb, https).";
411
412 messages::resourceAtUriInUnknownFormat(res, imageUrl);
413
414 return false;
415 }
416
417 // transferProtocolType should contain value from list
418 if (*paramTransferProtocolType == TransferProtocol::invalid)
419 {
420 BMCWEB_LOG_ERROR << "Request action parameter TransferProtocolType "
421 "must be provided with value from list: "
422 "(CIFS, HTTPS).";
423
424 messages::propertyValueNotInList(res, *transferProtocolType,
425 "TransferProtocolType");
426 return false;
427 }
428
429 // valid transfer protocol not provided either with URI nor param
430 if ((uriTransferProtocolType == std::nullopt) &&
431 (paramTransferProtocolType == std::nullopt))
432 {
433 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
434 "contain specified protocol type or param "
435 "TransferProtocolType must be provided.";
436
437 messages::resourceAtUriInUnknownFormat(res, imageUrl);
438
439 return false;
440 }
441
442 // valid transfer protocol provided both with URI and param
443 if ((paramTransferProtocolType != std::nullopt) &&
444 (uriTransferProtocolType != std::nullopt))
445 {
446 // check if protocol is the same for URI and param
447 if (*paramTransferProtocolType != *uriTransferProtocolType)
448 {
449 BMCWEB_LOG_ERROR << "Request action parameter "
450 "TransferProtocolType must contain the "
451 "same protocol type as protocol type "
452 "provided with param imageUrl.";
453
454 messages::actionParameterValueTypeError(
455 res, *transferProtocolType, "TransferProtocolType",
456 "InsertMedia");
457
458 return false;
459 }
460 }
461
462 // validation passed
463 // add protocol to URI if needed
464 if (uriTransferProtocolType == std::nullopt)
465 {
466 imageUrl = getUriWithTransferProtocol(imageUrl,
467 *paramTransferProtocolType);
468 }
469
470 return true;
471 }
472
473 /**
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200474 * @brief Function handles POST method request.
475 *
476 * Analyzes POST body message before sends Reset request data to dbus.
477 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500478 void doPost(crow::Response& res, const crow::Request& req,
479 const std::vector<std::string>& params) override
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200480 {
481 auto aResp = std::make_shared<AsyncResp>(res);
482
483 if (params.size() != 2)
484 {
485 messages::internalError(res);
486 return;
487 }
488
489 // take resource name from URL
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500490 const std::string& resName = params[1];
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200491
492 if (params[0] != "bmc")
493 {
494 messages::resourceNotFound(res, "VirtualMedia.Insert", resName);
495
496 return;
497 }
498
499 crow::connections::systemBus->async_method_call(
500 [this, aResp{std::move(aResp)}, req,
501 resName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500502 const GetObjectType& getObjectType) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200503 if (ec)
504 {
505 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
506 << ec;
507 messages::internalError(aResp->res);
508
509 return;
510 }
511 std::string service = getObjectType.begin()->first;
512 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
513
514 crow::connections::systemBus->async_method_call(
515 [this, service, resName, req, aResp{std::move(aResp)}](
516 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500517 ManagedObjectType& subtree) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200518 if (ec)
519 {
520 BMCWEB_LOG_DEBUG << "DBUS response error";
521
522 return;
523 }
524
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500525 for (const auto& object : subtree)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200526 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500527 const std::string& path =
528 static_cast<const std::string&>(object.first);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200529
Ed Tanousf23b7292020-10-15 09:41:17 -0700530 std::size_t lastIndex = path.rfind('/');
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200531 if (lastIndex == std::string::npos)
532 {
533 continue;
534 }
535
536 lastIndex += 1;
537
538 if (path.substr(lastIndex) == resName)
539 {
540 lastIndex = path.rfind("Proxy");
541 if (lastIndex != std::string::npos)
542 {
543 // Not possible in proxy mode
544 BMCWEB_LOG_DEBUG << "InsertMedia not "
545 "allowed in proxy mode";
546 messages::resourceNotFound(
547 aResp->res, "VirtualMedia.InsertMedia",
548 resName);
549
550 return;
551 }
552
553 lastIndex = path.rfind("Legacy");
Agata Olenderc6f4e012020-03-11 15:19:07 +0100554 if (lastIndex == std::string::npos)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200555 {
Agata Olenderc6f4e012020-03-11 15:19:07 +0100556 continue;
557 }
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200558
Agata Olenderc6f4e012020-03-11 15:19:07 +0100559 // Legacy mode
560 std::string imageUrl;
561 std::optional<std::string> userName;
562 std::optional<std::string> password;
563 std::optional<std::string> transferMethod;
564 std::optional<std::string> transferProtocolType;
565 std::optional<bool> writeProtected = true;
566 std::optional<bool> inserted;
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100567
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500568 // Read obligatory parameters (url of image)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100569 if (!json_util::readJson(
570 req, aResp->res, "Image", imageUrl,
571 "WriteProtected", writeProtected,
572 "UserName", userName, "Password",
573 password, "Inserted", inserted,
574 "TransferMethod", transferMethod,
575 "TransferProtocolType",
576 transferProtocolType))
577 {
578 BMCWEB_LOG_DEBUG << "Image is not provided";
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200579 return;
580 }
Agata Olenderc6f4e012020-03-11 15:19:07 +0100581
582 bool paramsValid = validateParams(
583 aResp->res, imageUrl, inserted,
584 transferMethod, transferProtocolType);
585
586 if (paramsValid == false)
587 {
588 return;
589 }
590
591 // manager is irrelevant for VirtualMedia dbus
592 // calls
593 doMountVmLegacy(
594 std::move(aResp), service, resName,
595 imageUrl, !(*writeProtected),
596 std::move(*userName), std::move(*password));
597
598 return;
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200599 }
600 }
601 BMCWEB_LOG_DEBUG << "Parent item not found";
602 messages::resourceNotFound(aResp->res, "VirtualMedia",
603 resName);
604 },
605 service, "/xyz/openbmc_project/VirtualMedia",
606 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
607 },
608 "xyz.openbmc_project.ObjectMapper",
609 "/xyz/openbmc_project/object_mapper",
610 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500611 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200612 }
613
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500614 template <typename T>
615 static void secureCleanup(T& value)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100616 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500617 auto raw = const_cast<typename T::value_type*>(value.data());
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100618 explicit_bzero(raw, value.size() * sizeof(*raw));
619 }
620
621 class Credentials
622 {
623 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500624 Credentials(std::string&& user, std::string&& password) :
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100625 userBuf(std::move(user)), passBuf(std::move(password))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500626 {}
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100627
628 ~Credentials()
629 {
630 secureCleanup(userBuf);
631 secureCleanup(passBuf);
632 }
633
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500634 const std::string& user()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100635 {
636 return userBuf;
637 }
638
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500639 const std::string& password()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100640 {
641 return passBuf;
642 }
643
644 private:
645 Credentials() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500646 Credentials(const Credentials&) = delete;
647 Credentials& operator=(const Credentials&) = delete;
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100648
649 std::string userBuf;
650 std::string passBuf;
651 };
652
653 class CredentialsProvider
654 {
655 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500656 template <typename T>
657 struct Deleter
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100658 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500659 void operator()(T* buff) const
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100660 {
661 if (buff)
662 {
663 secureCleanup(*buff);
664 delete buff;
665 }
666 }
667 };
668
669 using Buffer = std::vector<char>;
670 using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>;
671 // Using explicit definition instead of std::function to avoid implicit
672 // conversions eg. stack copy instead of reference
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500673 using FormatterFunc = void(const std::string& username,
674 const std::string& password, Buffer& dest);
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100675
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500676 CredentialsProvider(std::string&& user, std::string&& password) :
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100677 credentials(std::move(user), std::move(password))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500678 {}
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100679
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500680 const std::string& user()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100681 {
682 return credentials.user();
683 }
684
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500685 const std::string& password()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100686 {
687 return credentials.password();
688 }
689
690 SecureBuffer pack(const FormatterFunc formatter)
691 {
692 SecureBuffer packed{new Buffer{}};
693 if (formatter)
694 {
695 formatter(credentials.user(), credentials.password(), *packed);
696 }
697
698 return packed;
699 }
700
701 private:
702 Credentials credentials;
703 };
704
705 // Wrapper for boost::async_pipe ensuring proper pipe cleanup
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500706 template <typename Buffer>
707 class Pipe
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100708 {
709 public:
710 using unix_fd = sdbusplus::message::unix_fd;
711
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500712 Pipe(boost::asio::io_context& io, Buffer&& buffer) :
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100713 impl(io), buffer{std::move(buffer)}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500714 {}
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100715
716 ~Pipe()
717 {
718 // Named pipe needs to be explicitly removed
719 impl.close();
720 }
721
722 unix_fd fd()
723 {
724 return unix_fd{impl.native_source()};
725 }
726
727 template <typename WriteHandler>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500728 void async_write(WriteHandler&& handler)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100729 {
730 impl.async_write_some(data(), std::forward<WriteHandler>(handler));
731 }
732
733 private:
734 // Specialization for pointer types
735 template <typename B = Buffer>
736 typename std::enable_if<boost::has_dereference<B>::value,
737 boost::asio::const_buffer>::type
738 data()
739 {
740 return boost::asio::buffer(*buffer);
741 }
742
743 template <typename B = Buffer>
744 typename std::enable_if<!boost::has_dereference<B>::value,
745 boost::asio::const_buffer>::type
746 data()
747 {
748 return boost::asio::buffer(buffer);
749 }
750
751 const std::string name;
752 boost::process::async_pipe impl;
753 Buffer buffer;
754 };
755
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200756 /**
757 * @brief Function transceives data with dbus directly.
758 *
759 * All BMC state properties will be retrieved before sending reset request.
760 */
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100761 void doMountVmLegacy(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500762 const std::string& service, const std::string& name,
763 const std::string& imageUrl, const bool rw,
764 std::string&& userName, std::string&& password)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200765 {
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100766 using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>;
767 constexpr const size_t secretLimit = 1024;
768
769 std::shared_ptr<SecurePipe> secretPipe;
770 std::variant<int, SecurePipe::unix_fd> unixFd = -1;
771
772 if (!userName.empty() || !password.empty())
773 {
774 // Encapsulate in safe buffer
775 CredentialsProvider credentials(std::move(userName),
776 std::move(password));
777
778 // Payload must contain data + NULL delimiters
779 if (credentials.user().size() + credentials.password().size() + 2 >
780 secretLimit)
781 {
782 BMCWEB_LOG_ERROR << "Credentials too long to handle";
783 messages::unrecognizedRequestBody(asyncResp->res);
784 return;
785 }
786
787 // Pack secret
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500788 auto secret = credentials.pack([](const auto& user,
789 const auto& pass, auto& buff) {
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100790 std::copy(user.begin(), user.end(), std::back_inserter(buff));
791 buff.push_back('\0');
792 std::copy(pass.begin(), pass.end(), std::back_inserter(buff));
793 buff.push_back('\0');
794 });
795
796 // Open pipe
797 secretPipe = std::make_shared<SecurePipe>(
798 crow::connections::systemBus->get_io_context(),
799 std::move(secret));
800 unixFd = secretPipe->fd();
801
802 // Pass secret over pipe
803 secretPipe->async_write(
Vikram Bodireddyf5b16f02020-08-26 14:54:51 +0530804 [asyncResp](const boost::system::error_code& ec, std::size_t) {
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100805 if (ec)
806 {
807 BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec;
808 messages::internalError(asyncResp->res);
809 }
810 });
811 }
812
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100813 crow::connections::systemBus->async_method_call(
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100814 [asyncResp, secretPipe](const boost::system::error_code ec,
815 bool success) {
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100816 if (ec)
817 {
818 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
819 messages::internalError(asyncResp->res);
820 }
821 else if (!success)
822 {
823 BMCWEB_LOG_ERROR << "Service responded with error";
824 messages::generalError(asyncResp->res);
825 }
826 },
827 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100828 "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
829 unixFd);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200830 }
831};
832
833/**
834 @brief EjectMedia action class
835 */
836class VirtualMediaActionEjectMedia : public Node
837{
838 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700839 VirtualMediaActionEjectMedia(App& app) :
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200840 Node(app,
841 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
842 "VirtualMedia.EjectMedia",
843 std::string(), std::string())
844 {
845 entityPrivileges = {
846 {boost::beast::http::verb::get, {{"Login"}}},
847 {boost::beast::http::verb::head, {{"Login"}}},
848 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
849 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
850 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
851 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
852 }
853
854 private:
855 /**
856 * @brief Function handles POST method request.
857 *
858 * Analyzes POST body message before sends Reset request data to dbus.
859 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500860 void doPost(crow::Response& res, const crow::Request& req,
861 const std::vector<std::string>& params) override
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200862 {
863 auto aResp = std::make_shared<AsyncResp>(res);
864
865 if (params.size() != 2)
866 {
867 messages::internalError(res);
868 return;
869 }
870
871 // take resource name from URL
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500872 const std::string& resName = params[1];
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200873
874 if (params[0] != "bmc")
875 {
876 messages::resourceNotFound(res, "VirtualMedia.Eject", resName);
877
878 return;
879 }
880
881 crow::connections::systemBus->async_method_call(
882 [this, aResp{std::move(aResp)}, req,
883 resName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500884 const GetObjectType& getObjectType) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200885 if (ec)
886 {
887 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
888 << ec;
889 messages::internalError(aResp->res);
890
891 return;
892 }
893 std::string service = getObjectType.begin()->first;
894 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
895
896 crow::connections::systemBus->async_method_call(
897 [this, resName, service, req, aResp{std::move(aResp)}](
898 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500899 ManagedObjectType& subtree) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200900 if (ec)
901 {
902 BMCWEB_LOG_DEBUG << "DBUS response error";
903
904 return;
905 }
906
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500907 for (const auto& object : subtree)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200908 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500909 const std::string& path =
910 static_cast<const std::string&>(object.first);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200911
Ed Tanousf23b7292020-10-15 09:41:17 -0700912 std::size_t lastIndex = path.rfind('/');
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200913 if (lastIndex == std::string::npos)
914 {
915 continue;
916 }
917
918 lastIndex += 1;
919
920 if (path.substr(lastIndex) == resName)
921 {
922 lastIndex = path.rfind("Proxy");
923 if (lastIndex != std::string::npos)
924 {
925 // Proxy mode
926 doVmAction(std::move(aResp), service,
927 resName, false);
928 }
929
930 lastIndex = path.rfind("Legacy");
931 if (lastIndex != std::string::npos)
932 {
933 // Legacy mode
934 doVmAction(std::move(aResp), service,
935 resName, true);
936 }
937
938 return;
939 }
940 }
941 BMCWEB_LOG_DEBUG << "Parent item not found";
942 messages::resourceNotFound(aResp->res, "VirtualMedia",
943 resName);
944 },
945 service, "/xyz/openbmc_project/VirtualMedia",
946 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
947 },
948 "xyz.openbmc_project.ObjectMapper",
949 "/xyz/openbmc_project/object_mapper",
950 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500951 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200952 }
953
954 /**
955 * @brief Function transceives data with dbus directly.
956 *
957 * All BMC state properties will be retrieved before sending reset request.
958 */
959 void doVmAction(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500960 const std::string& service, const std::string& name,
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200961 bool legacy)
962 {
963
964 // Legacy mount requires parameter with image
965 if (legacy)
966 {
967 crow::connections::systemBus->async_method_call(
968 [asyncResp](const boost::system::error_code ec) {
969 if (ec)
970 {
971 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
972
973 messages::internalError(asyncResp->res);
974 return;
975 }
976 },
977 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
978 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
979 }
980 else // proxy
981 {
982 crow::connections::systemBus->async_method_call(
983 [asyncResp](const boost::system::error_code ec) {
984 if (ec)
985 {
986 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
987
988 messages::internalError(asyncResp->res);
989 return;
990 }
991 },
992 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
993 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
994 }
995 }
996};
997
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200998class VirtualMediaCollection : public Node
999{
1000 public:
1001 /*
1002 * Default Constructor
1003 */
Ed Tanous52cc1122020-07-18 13:51:21 -07001004 VirtualMediaCollection(App& app) :
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001005 Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/", std::string())
1006 {
1007 entityPrivileges = {
1008 {boost::beast::http::verb::get, {{"Login"}}},
1009 {boost::beast::http::verb::head, {{"Login"}}},
1010 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1011 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1012 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1013 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1014 }
1015
1016 private:
1017 /**
1018 * Functions triggers appropriate requests on DBus
1019 */
Vikram Bodireddyf5b16f02020-08-26 14:54:51 +05301020 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001021 const std::vector<std::string>& params) override
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001022 {
1023 auto asyncResp = std::make_shared<AsyncResp>(res);
1024
1025 // Check if there is required param, truly entering this shall be
1026 // impossible
1027 if (params.size() != 1)
1028 {
1029 messages::internalError(res);
1030
1031 return;
1032 }
1033
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001034 const std::string& name = params[0];
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001035
1036 if (name != "bmc")
1037 {
1038 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
1039
1040 return;
1041 }
1042
1043 res.jsonValue["@odata.type"] =
1044 "#VirtualMediaCollection.VirtualMediaCollection";
1045 res.jsonValue["Name"] = "Virtual Media Services";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001046 res.jsonValue["@odata.id"] =
Przemyslaw Czarnowskid6c414f2020-07-08 15:17:31 +02001047 "/redfish/v1/Managers/" + name + "/VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001048
1049 crow::connections::systemBus->async_method_call(
1050 [asyncResp, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001051 const GetObjectType& getObjectType) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001052 if (ec)
1053 {
1054 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
1055 << ec;
1056 messages::internalError(asyncResp->res);
1057
1058 return;
1059 }
1060 std::string service = getObjectType.begin()->first;
1061 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1062
1063 getVmResourceList(asyncResp, service, name);
1064 },
1065 "xyz.openbmc_project.ObjectMapper",
1066 "/xyz/openbmc_project/object_mapper",
1067 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001068 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001069 }
1070};
1071
1072class VirtualMedia : public Node
1073{
1074 public:
1075 /*
1076 * Default Constructor
1077 */
Ed Tanous52cc1122020-07-18 13:51:21 -07001078 VirtualMedia(App& app) :
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001079 Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/",
1080 std::string(), std::string())
1081 {
1082 entityPrivileges = {
1083 {boost::beast::http::verb::get, {{"Login"}}},
1084 {boost::beast::http::verb::head, {{"Login"}}},
1085 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1086 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1087 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1088 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1089 }
1090
1091 private:
1092 /**
1093 * Functions triggers appropriate requests on DBus
1094 */
Vikram Bodireddyf5b16f02020-08-26 14:54:51 +05301095 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001096 const std::vector<std::string>& params) override
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001097 {
1098 // Check if there is required param, truly entering this shall be
1099 // impossible
1100 if (params.size() != 2)
1101 {
1102 messages::internalError(res);
1103
1104 res.end();
1105 return;
1106 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001107 const std::string& name = params[0];
1108 const std::string& resName = params[1];
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001109
1110 auto asyncResp = std::make_shared<AsyncResp>(res);
1111
1112 if (name != "bmc")
1113 {
1114 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
1115
1116 return;
1117 }
1118
1119 crow::connections::systemBus->async_method_call(
1120 [asyncResp, name, resName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001121 const GetObjectType& getObjectType) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001122 if (ec)
1123 {
1124 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
1125 << ec;
1126 messages::internalError(asyncResp->res);
1127
1128 return;
1129 }
1130 std::string service = getObjectType.begin()->first;
1131 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1132
1133 getVmData(asyncResp, service, name, resName);
1134 },
1135 "xyz.openbmc_project.ObjectMapper",
1136 "/xyz/openbmc_project/object_mapper",
1137 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001138 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001139 }
1140};
1141
1142} // namespace redfish