blob: 552e2552c841490bb40d927c5e1863081ddb2d02 [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 {
100 aResp->res.jsonValue["ImageName"] = *imageUrlValue;
101 aResp->res.jsonValue["Inserted"] = *activeValue;
102 if (*activeValue == true)
103 {
104 aResp->res.jsonValue["ConnectedVia"] = "URI";
105 }
106 }
107 }
108 }
109 }
110}
111
112/**
113 * @brief Fill template for Virtual Media Item.
114 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500115static nlohmann::json vmItemTemplate(const std::string& name,
116 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200117{
118 nlohmann::json item;
119 item["@odata.id"] =
120 "/redfish/v1/Managers/" + name + "/VirtualMedia/" + resName;
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100121 item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200122 item["Name"] = "Virtual Removable Media";
123 item["Id"] = resName;
124 item["Image"] = nullptr;
125 item["Inserted"] = nullptr;
126 item["ImageName"] = nullptr;
127 item["WriteProtected"] = true;
128 item["ConnectedVia"] = "NotConnected";
129 item["MediaTypes"] = {"CD", "USBStick"};
130 item["TransferMethod"] = "Stream";
131 item["TransferProtocolType"] = nullptr;
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100132 item["Oem"]["OpenBmc"]["WebSocketEndpoint"] = nullptr;
133 item["Oem"]["OpenBMC"]["@odata.type"] =
134 "#OemVirtualMedia.v1_0_0.VirtualMedia";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200135
136 return item;
137}
138
139/**
140 * @brief Fills collection data
141 */
142static void getVmResourceList(std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500143 const std::string& service,
144 const std::string& name)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200145{
146 BMCWEB_LOG_DEBUG << "Get available Virtual Media resources.";
147 crow::connections::systemBus->async_method_call(
148 [name, aResp{std::move(aResp)}](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500149 ManagedObjectType& subtree) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200150 if (ec)
151 {
152 BMCWEB_LOG_DEBUG << "DBUS response error";
153 return;
154 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500155 nlohmann::json& members = aResp->res.jsonValue["Members"];
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200156 members = nlohmann::json::array();
157
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500158 for (const auto& object : subtree)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200159 {
160 nlohmann::json item;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500161 const std::string& path =
162 static_cast<const std::string&>(object.first);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200163 std::size_t lastIndex = path.rfind("/");
164 if (lastIndex == std::string::npos)
165 {
166 continue;
167 }
168
169 lastIndex += 1;
170
171 item["@odata.id"] = "/redfish/v1/Managers/" + name +
172 "/VirtualMedia/" + path.substr(lastIndex);
173
174 members.emplace_back(std::move(item));
175 }
176 aResp->res.jsonValue["Members@odata.count"] = members.size();
177 },
178 service, "/xyz/openbmc_project/VirtualMedia",
179 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
180}
181
182/**
183 * @brief Fills data for specific resource
184 */
185static void getVmData(std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500186 const std::string& service, const std::string& name,
187 const std::string& resName)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200188{
189 BMCWEB_LOG_DEBUG << "Get Virtual Media resource data.";
190
191 crow::connections::systemBus->async_method_call(
192 [resName, name, aResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500193 ManagedObjectType& subtree) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200194 if (ec)
195 {
196 BMCWEB_LOG_DEBUG << "DBUS response error";
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200197
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200198 return;
199 }
200
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500201 for (auto& item : subtree)
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200202 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500203 const std::string& path =
204 static_cast<const std::string&>(item.first);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200205
206 std::size_t lastItem = path.rfind("/");
207 if (lastItem == std::string::npos)
208 {
209 continue;
210 }
211
212 if (path.substr(lastItem + 1) != resName)
213 {
214 continue;
215 }
216
217 aResp->res.jsonValue = vmItemTemplate(name, resName);
218
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200219 // Check if dbus path is Legacy type
220 if (path.find("VirtualMedia/Legacy") != std::string::npos)
221 {
222 aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
223 ["target"] =
224 "/redfish/v1/Managers/" + name + "/VirtualMedia/" +
225 resName + "/Actions/VirtualMedia.InsertMedia";
226 }
227
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200228 vmParseInterfaceObject(item.second, aResp);
229
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200230 aResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"]
231 ["target"] =
232 "/redfish/v1/Managers/" + name + "/VirtualMedia/" +
233 resName + "/Actions/VirtualMedia.EjectMedia";
234
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200235 return;
236 }
237
238 messages::resourceNotFound(
Przemyslaw Czarnowskid04ba322020-01-21 12:41:56 +0100239 aResp->res, "#VirtualMedia.v1_3_0.VirtualMedia", resName);
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200240 },
241 service, "/xyz/openbmc_project/VirtualMedia",
242 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
243}
244
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200245/**
246 @brief InsertMedia action class
247 */
248class VirtualMediaActionInsertMedia : public Node
249{
250 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500251 VirtualMediaActionInsertMedia(CrowApp& app) :
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200252 Node(app,
253 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
254 "VirtualMedia.InsertMedia",
255 std::string(), std::string())
256 {
257 entityPrivileges = {
258 {boost::beast::http::verb::get, {{"Login"}}},
259 {boost::beast::http::verb::head, {{"Login"}}},
260 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
261 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
262 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
263 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
264 }
265
266 private:
267 /**
Agata Olenderc6f4e012020-03-11 15:19:07 +0100268 * @brief Transfer protocols supported for InsertMedia action.
269 *
270 */
271 enum class TransferProtocol
272 {
273 https,
274 smb,
275 invalid
276 };
277
278 /**
279 * @brief Function extracts transfer protocol type from URI.
280 *
281 */
282 std::optional<TransferProtocol>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500283 getTransferProtocolFromUri(const std::string& imageUri)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100284 {
285 if (imageUri.find("smb://") != std::string::npos)
286 {
287 return TransferProtocol::smb;
288 }
289 else if (imageUri.find("https://") != std::string::npos)
290 {
291 return TransferProtocol::https;
292 }
293 else if (imageUri.find("://") != std::string::npos)
294 {
295 return TransferProtocol::invalid;
296 }
297 else
298 {
299 return {};
300 }
301 }
302
303 /**
304 * @brief Function convert transfer protocol from string param.
305 *
306 */
307 std::optional<TransferProtocol> getTransferProtocolFromParam(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500308 const std::optional<std::string>& transferProtocolType)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100309 {
310 if (transferProtocolType == std::nullopt)
311 {
312 return {};
313 }
314
315 if (*transferProtocolType == "CIFS")
316 {
317 return TransferProtocol::smb;
318 }
319
320 if (*transferProtocolType == "HTTPS")
321 {
322 return TransferProtocol::https;
323 }
324
325 return TransferProtocol::invalid;
326 }
327
328 /**
329 * @brief Function extends URI with transfer protocol type.
330 *
331 */
332 const std::string
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500333 getUriWithTransferProtocol(const std::string& imageUri,
334 const TransferProtocol& transferProtocol)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100335 {
336 if (transferProtocol == TransferProtocol::smb)
337 {
338 return "smb://" + imageUri;
339 }
340
341 if (transferProtocol == TransferProtocol::https)
342 {
343 return "https://" + imageUri;
344 }
345
346 return imageUri;
347 }
348
349 /**
350 * @brief Function validate parameters of insert media request.
351 *
352 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500353 bool validateParams(crow::Response& res, std::string& imageUrl,
354 const std::optional<bool>& inserted,
355 const std::optional<std::string>& transferMethod,
356 const std::optional<std::string>& transferProtocolType)
Agata Olenderc6f4e012020-03-11 15:19:07 +0100357 {
358 BMCWEB_LOG_DEBUG << "Validation started";
359 // required param imageUrl must not be empty
360 if (imageUrl.empty())
361 {
362 BMCWEB_LOG_ERROR << "Request action parameter Image is empty.";
363
364 messages::propertyValueFormatError(res, "<empty>", "Image");
365
366 return false;
367 }
368
369 // optional param inserted must be true
370 if ((inserted != std::nullopt) && (*inserted != true))
371 {
372 BMCWEB_LOG_ERROR
373 << "Request action optional parameter Inserted must be true.";
374
375 messages::actionParameterNotSupported(res, "Inserted",
376 "InsertMedia");
377
378 return false;
379 }
380
381 // optional param transferMethod must be stream
382 if ((transferMethod != std::nullopt) && (*transferMethod != "Stream"))
383 {
384 BMCWEB_LOG_ERROR << "Request action optional parameter "
385 "TransferMethod must be Stream.";
386
387 messages::actionParameterNotSupported(res, "TransferMethod",
388 "InsertMedia");
389
390 return false;
391 }
392
393 std::optional<TransferProtocol> uriTransferProtocolType =
394 getTransferProtocolFromUri(imageUrl);
395
396 std::optional<TransferProtocol> paramTransferProtocolType =
397 getTransferProtocolFromParam(transferProtocolType);
398
399 // ImageUrl does not contain valid protocol type
400 if (*uriTransferProtocolType == TransferProtocol::invalid)
401 {
402 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
403 "contain specified protocol type from list: "
404 "(smb, https).";
405
406 messages::resourceAtUriInUnknownFormat(res, imageUrl);
407
408 return false;
409 }
410
411 // transferProtocolType should contain value from list
412 if (*paramTransferProtocolType == TransferProtocol::invalid)
413 {
414 BMCWEB_LOG_ERROR << "Request action parameter TransferProtocolType "
415 "must be provided with value from list: "
416 "(CIFS, HTTPS).";
417
418 messages::propertyValueNotInList(res, *transferProtocolType,
419 "TransferProtocolType");
420 return false;
421 }
422
423 // valid transfer protocol not provided either with URI nor param
424 if ((uriTransferProtocolType == std::nullopt) &&
425 (paramTransferProtocolType == std::nullopt))
426 {
427 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must "
428 "contain specified protocol type or param "
429 "TransferProtocolType must be provided.";
430
431 messages::resourceAtUriInUnknownFormat(res, imageUrl);
432
433 return false;
434 }
435
436 // valid transfer protocol provided both with URI and param
437 if ((paramTransferProtocolType != std::nullopt) &&
438 (uriTransferProtocolType != std::nullopt))
439 {
440 // check if protocol is the same for URI and param
441 if (*paramTransferProtocolType != *uriTransferProtocolType)
442 {
443 BMCWEB_LOG_ERROR << "Request action parameter "
444 "TransferProtocolType must contain the "
445 "same protocol type as protocol type "
446 "provided with param imageUrl.";
447
448 messages::actionParameterValueTypeError(
449 res, *transferProtocolType, "TransferProtocolType",
450 "InsertMedia");
451
452 return false;
453 }
454 }
455
456 // validation passed
457 // add protocol to URI if needed
458 if (uriTransferProtocolType == std::nullopt)
459 {
460 imageUrl = getUriWithTransferProtocol(imageUrl,
461 *paramTransferProtocolType);
462 }
463
464 return true;
465 }
466
467 /**
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200468 * @brief Function handles POST method request.
469 *
470 * Analyzes POST body message before sends Reset request data to dbus.
471 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500472 void doPost(crow::Response& res, const crow::Request& req,
473 const std::vector<std::string>& params) override
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200474 {
475 auto aResp = std::make_shared<AsyncResp>(res);
476
477 if (params.size() != 2)
478 {
479 messages::internalError(res);
480 return;
481 }
482
483 // take resource name from URL
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500484 const std::string& resName = params[1];
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200485
486 if (params[0] != "bmc")
487 {
488 messages::resourceNotFound(res, "VirtualMedia.Insert", resName);
489
490 return;
491 }
492
493 crow::connections::systemBus->async_method_call(
494 [this, aResp{std::move(aResp)}, req,
495 resName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500496 const GetObjectType& getObjectType) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200497 if (ec)
498 {
499 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
500 << ec;
501 messages::internalError(aResp->res);
502
503 return;
504 }
505 std::string service = getObjectType.begin()->first;
506 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
507
508 crow::connections::systemBus->async_method_call(
509 [this, service, resName, req, aResp{std::move(aResp)}](
510 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500511 ManagedObjectType& subtree) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200512 if (ec)
513 {
514 BMCWEB_LOG_DEBUG << "DBUS response error";
515
516 return;
517 }
518
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500519 for (const auto& object : subtree)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200520 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500521 const std::string& path =
522 static_cast<const std::string&>(object.first);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200523
524 std::size_t lastIndex = path.rfind("/");
525 if (lastIndex == std::string::npos)
526 {
527 continue;
528 }
529
530 lastIndex += 1;
531
532 if (path.substr(lastIndex) == resName)
533 {
534 lastIndex = path.rfind("Proxy");
535 if (lastIndex != std::string::npos)
536 {
537 // Not possible in proxy mode
538 BMCWEB_LOG_DEBUG << "InsertMedia not "
539 "allowed in proxy mode";
540 messages::resourceNotFound(
541 aResp->res, "VirtualMedia.InsertMedia",
542 resName);
543
544 return;
545 }
546
547 lastIndex = path.rfind("Legacy");
Agata Olenderc6f4e012020-03-11 15:19:07 +0100548 if (lastIndex == std::string::npos)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200549 {
Agata Olenderc6f4e012020-03-11 15:19:07 +0100550 continue;
551 }
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200552
Agata Olenderc6f4e012020-03-11 15:19:07 +0100553 // Legacy mode
554 std::string imageUrl;
555 std::optional<std::string> userName;
556 std::optional<std::string> password;
557 std::optional<std::string> transferMethod;
558 std::optional<std::string> transferProtocolType;
559 std::optional<bool> writeProtected = true;
560 std::optional<bool> inserted;
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100561
Agata Olenderc6f4e012020-03-11 15:19:07 +0100562 // Read obligatory paramters (url of image)
563 if (!json_util::readJson(
564 req, aResp->res, "Image", imageUrl,
565 "WriteProtected", writeProtected,
566 "UserName", userName, "Password",
567 password, "Inserted", inserted,
568 "TransferMethod", transferMethod,
569 "TransferProtocolType",
570 transferProtocolType))
571 {
572 BMCWEB_LOG_DEBUG << "Image is not provided";
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200573 return;
574 }
Agata Olenderc6f4e012020-03-11 15:19:07 +0100575
576 bool paramsValid = validateParams(
577 aResp->res, imageUrl, inserted,
578 transferMethod, transferProtocolType);
579
580 if (paramsValid == false)
581 {
582 return;
583 }
584
585 // manager is irrelevant for VirtualMedia dbus
586 // calls
587 doMountVmLegacy(
588 std::move(aResp), service, resName,
589 imageUrl, !(*writeProtected),
590 std::move(*userName), std::move(*password));
591
592 return;
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200593 }
594 }
595 BMCWEB_LOG_DEBUG << "Parent item not found";
596 messages::resourceNotFound(aResp->res, "VirtualMedia",
597 resName);
598 },
599 service, "/xyz/openbmc_project/VirtualMedia",
600 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
601 },
602 "xyz.openbmc_project.ObjectMapper",
603 "/xyz/openbmc_project/object_mapper",
604 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500605 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200606 }
607
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500608 template <typename T>
609 static void secureCleanup(T& value)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100610 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500611 auto raw = const_cast<typename T::value_type*>(value.data());
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100612 explicit_bzero(raw, value.size() * sizeof(*raw));
613 }
614
615 class Credentials
616 {
617 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500618 Credentials(std::string&& user, std::string&& password) :
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100619 userBuf(std::move(user)), passBuf(std::move(password))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500620 {}
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100621
622 ~Credentials()
623 {
624 secureCleanup(userBuf);
625 secureCleanup(passBuf);
626 }
627
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500628 const std::string& user()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100629 {
630 return userBuf;
631 }
632
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500633 const std::string& password()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100634 {
635 return passBuf;
636 }
637
638 private:
639 Credentials() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500640 Credentials(const Credentials&) = delete;
641 Credentials& operator=(const Credentials&) = delete;
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100642
643 std::string userBuf;
644 std::string passBuf;
645 };
646
647 class CredentialsProvider
648 {
649 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500650 template <typename T>
651 struct Deleter
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100652 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500653 void operator()(T* buff) const
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100654 {
655 if (buff)
656 {
657 secureCleanup(*buff);
658 delete buff;
659 }
660 }
661 };
662
663 using Buffer = std::vector<char>;
664 using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>;
665 // Using explicit definition instead of std::function to avoid implicit
666 // conversions eg. stack copy instead of reference
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500667 using FormatterFunc = void(const std::string& username,
668 const std::string& password, Buffer& dest);
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100669
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500670 CredentialsProvider(std::string&& user, std::string&& password) :
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100671 credentials(std::move(user), std::move(password))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500672 {}
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100673
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500674 const std::string& user()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100675 {
676 return credentials.user();
677 }
678
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500679 const std::string& password()
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100680 {
681 return credentials.password();
682 }
683
684 SecureBuffer pack(const FormatterFunc formatter)
685 {
686 SecureBuffer packed{new Buffer{}};
687 if (formatter)
688 {
689 formatter(credentials.user(), credentials.password(), *packed);
690 }
691
692 return packed;
693 }
694
695 private:
696 Credentials credentials;
697 };
698
699 // Wrapper for boost::async_pipe ensuring proper pipe cleanup
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500700 template <typename Buffer>
701 class Pipe
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100702 {
703 public:
704 using unix_fd = sdbusplus::message::unix_fd;
705
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500706 Pipe(boost::asio::io_context& io, Buffer&& buffer) :
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100707 impl(io), buffer{std::move(buffer)}
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500708 {}
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100709
710 ~Pipe()
711 {
712 // Named pipe needs to be explicitly removed
713 impl.close();
714 }
715
716 unix_fd fd()
717 {
718 return unix_fd{impl.native_source()};
719 }
720
721 template <typename WriteHandler>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500722 void async_write(WriteHandler&& handler)
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100723 {
724 impl.async_write_some(data(), std::forward<WriteHandler>(handler));
725 }
726
727 private:
728 // Specialization for pointer types
729 template <typename B = Buffer>
730 typename std::enable_if<boost::has_dereference<B>::value,
731 boost::asio::const_buffer>::type
732 data()
733 {
734 return boost::asio::buffer(*buffer);
735 }
736
737 template <typename B = Buffer>
738 typename std::enable_if<!boost::has_dereference<B>::value,
739 boost::asio::const_buffer>::type
740 data()
741 {
742 return boost::asio::buffer(buffer);
743 }
744
745 const std::string name;
746 boost::process::async_pipe impl;
747 Buffer buffer;
748 };
749
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200750 /**
751 * @brief Function transceives data with dbus directly.
752 *
753 * All BMC state properties will be retrieved before sending reset request.
754 */
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100755 void doMountVmLegacy(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500756 const std::string& service, const std::string& name,
757 const std::string& imageUrl, const bool rw,
758 std::string&& userName, std::string&& password)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200759 {
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100760 using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>;
761 constexpr const size_t secretLimit = 1024;
762
763 std::shared_ptr<SecurePipe> secretPipe;
764 std::variant<int, SecurePipe::unix_fd> unixFd = -1;
765
766 if (!userName.empty() || !password.empty())
767 {
768 // Encapsulate in safe buffer
769 CredentialsProvider credentials(std::move(userName),
770 std::move(password));
771
772 // Payload must contain data + NULL delimiters
773 if (credentials.user().size() + credentials.password().size() + 2 >
774 secretLimit)
775 {
776 BMCWEB_LOG_ERROR << "Credentials too long to handle";
777 messages::unrecognizedRequestBody(asyncResp->res);
778 return;
779 }
780
781 // Pack secret
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500782 auto secret = credentials.pack([](const auto& user,
783 const auto& pass, auto& buff) {
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100784 std::copy(user.begin(), user.end(), std::back_inserter(buff));
785 buff.push_back('\0');
786 std::copy(pass.begin(), pass.end(), std::back_inserter(buff));
787 buff.push_back('\0');
788 });
789
790 // Open pipe
791 secretPipe = std::make_shared<SecurePipe>(
792 crow::connections::systemBus->get_io_context(),
793 std::move(secret));
794 unixFd = secretPipe->fd();
795
796 // Pass secret over pipe
797 secretPipe->async_write(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500798 [asyncResp](const boost::system::error_code& ec,
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100799 std::size_t size) {
800 if (ec)
801 {
802 BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec;
803 messages::internalError(asyncResp->res);
804 }
805 });
806 }
807
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100808 crow::connections::systemBus->async_method_call(
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100809 [asyncResp, secretPipe](const boost::system::error_code ec,
810 bool success) {
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100811 if (ec)
812 {
813 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
814 messages::internalError(asyncResp->res);
815 }
816 else if (!success)
817 {
818 BMCWEB_LOG_ERROR << "Service responded with error";
819 messages::generalError(asyncResp->res);
820 }
821 },
822 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100823 "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
824 unixFd);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200825 }
826};
827
828/**
829 @brief EjectMedia action class
830 */
831class VirtualMediaActionEjectMedia : public Node
832{
833 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500834 VirtualMediaActionEjectMedia(CrowApp& app) :
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200835 Node(app,
836 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
837 "VirtualMedia.EjectMedia",
838 std::string(), std::string())
839 {
840 entityPrivileges = {
841 {boost::beast::http::verb::get, {{"Login"}}},
842 {boost::beast::http::verb::head, {{"Login"}}},
843 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
844 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
845 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
846 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
847 }
848
849 private:
850 /**
851 * @brief Function handles POST method request.
852 *
853 * Analyzes POST body message before sends Reset request data to dbus.
854 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500855 void doPost(crow::Response& res, const crow::Request& req,
856 const std::vector<std::string>& params) override
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200857 {
858 auto aResp = std::make_shared<AsyncResp>(res);
859
860 if (params.size() != 2)
861 {
862 messages::internalError(res);
863 return;
864 }
865
866 // take resource name from URL
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500867 const std::string& resName = params[1];
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200868
869 if (params[0] != "bmc")
870 {
871 messages::resourceNotFound(res, "VirtualMedia.Eject", resName);
872
873 return;
874 }
875
876 crow::connections::systemBus->async_method_call(
877 [this, aResp{std::move(aResp)}, req,
878 resName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500879 const GetObjectType& getObjectType) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200880 if (ec)
881 {
882 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
883 << ec;
884 messages::internalError(aResp->res);
885
886 return;
887 }
888 std::string service = getObjectType.begin()->first;
889 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
890
891 crow::connections::systemBus->async_method_call(
892 [this, resName, service, req, aResp{std::move(aResp)}](
893 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500894 ManagedObjectType& subtree) {
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200895 if (ec)
896 {
897 BMCWEB_LOG_DEBUG << "DBUS response error";
898
899 return;
900 }
901
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500902 for (const auto& object : subtree)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200903 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500904 const std::string& path =
905 static_cast<const std::string&>(object.first);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200906
907 std::size_t lastIndex = path.rfind("/");
908 if (lastIndex == std::string::npos)
909 {
910 continue;
911 }
912
913 lastIndex += 1;
914
915 if (path.substr(lastIndex) == resName)
916 {
917 lastIndex = path.rfind("Proxy");
918 if (lastIndex != std::string::npos)
919 {
920 // Proxy mode
921 doVmAction(std::move(aResp), service,
922 resName, false);
923 }
924
925 lastIndex = path.rfind("Legacy");
926 if (lastIndex != std::string::npos)
927 {
928 // Legacy mode
929 doVmAction(std::move(aResp), service,
930 resName, true);
931 }
932
933 return;
934 }
935 }
936 BMCWEB_LOG_DEBUG << "Parent item not found";
937 messages::resourceNotFound(aResp->res, "VirtualMedia",
938 resName);
939 },
940 service, "/xyz/openbmc_project/VirtualMedia",
941 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
942 },
943 "xyz.openbmc_project.ObjectMapper",
944 "/xyz/openbmc_project/object_mapper",
945 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500946 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200947 }
948
949 /**
950 * @brief Function transceives data with dbus directly.
951 *
952 * All BMC state properties will be retrieved before sending reset request.
953 */
954 void doVmAction(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500955 const std::string& service, const std::string& name,
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200956 bool legacy)
957 {
958
959 // Legacy mount requires parameter with image
960 if (legacy)
961 {
962 crow::connections::systemBus->async_method_call(
963 [asyncResp](const boost::system::error_code ec) {
964 if (ec)
965 {
966 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
967
968 messages::internalError(asyncResp->res);
969 return;
970 }
971 },
972 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
973 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
974 }
975 else // proxy
976 {
977 crow::connections::systemBus->async_method_call(
978 [asyncResp](const boost::system::error_code ec) {
979 if (ec)
980 {
981 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
982
983 messages::internalError(asyncResp->res);
984 return;
985 }
986 },
987 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
988 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
989 }
990 }
991};
992
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200993class VirtualMediaCollection : public Node
994{
995 public:
996 /*
997 * Default Constructor
998 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500999 VirtualMediaCollection(CrowApp& app) :
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001000 Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/", std::string())
1001 {
1002 entityPrivileges = {
1003 {boost::beast::http::verb::get, {{"Login"}}},
1004 {boost::beast::http::verb::head, {{"Login"}}},
1005 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1006 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1007 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1008 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1009 }
1010
1011 private:
1012 /**
1013 * Functions triggers appropriate requests on DBus
1014 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001015 void doGet(crow::Response& res, const crow::Request& req,
1016 const std::vector<std::string>& params) override
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001017 {
1018 auto asyncResp = std::make_shared<AsyncResp>(res);
1019
1020 // Check if there is required param, truly entering this shall be
1021 // impossible
1022 if (params.size() != 1)
1023 {
1024 messages::internalError(res);
1025
1026 return;
1027 }
1028
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001029 const std::string& name = params[0];
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001030
1031 if (name != "bmc")
1032 {
1033 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
1034
1035 return;
1036 }
1037
1038 res.jsonValue["@odata.type"] =
1039 "#VirtualMediaCollection.VirtualMediaCollection";
1040 res.jsonValue["Name"] = "Virtual Media Services";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001041 res.jsonValue["@odata.id"] =
1042 "/redfish/v1/Managers/" + name + "/VirtualMedia/";
1043
1044 crow::connections::systemBus->async_method_call(
1045 [asyncResp, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001046 const GetObjectType& getObjectType) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001047 if (ec)
1048 {
1049 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
1050 << 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 getVmResourceList(asyncResp, service, name);
1059 },
1060 "xyz.openbmc_project.ObjectMapper",
1061 "/xyz/openbmc_project/object_mapper",
1062 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001063 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001064 }
1065};
1066
1067class VirtualMedia : public Node
1068{
1069 public:
1070 /*
1071 * Default Constructor
1072 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001073 VirtualMedia(CrowApp& app) :
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001074 Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/",
1075 std::string(), std::string())
1076 {
1077 entityPrivileges = {
1078 {boost::beast::http::verb::get, {{"Login"}}},
1079 {boost::beast::http::verb::head, {{"Login"}}},
1080 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1081 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1082 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1083 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1084 }
1085
1086 private:
1087 /**
1088 * Functions triggers appropriate requests on DBus
1089 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001090 void doGet(crow::Response& res, const crow::Request& req,
1091 const std::vector<std::string>& params) override
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001092 {
1093 // Check if there is required param, truly entering this shall be
1094 // impossible
1095 if (params.size() != 2)
1096 {
1097 messages::internalError(res);
1098
1099 res.end();
1100 return;
1101 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001102 const std::string& name = params[0];
1103 const std::string& resName = params[1];
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001104
1105 auto asyncResp = std::make_shared<AsyncResp>(res);
1106
1107 if (name != "bmc")
1108 {
1109 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
1110
1111 return;
1112 }
1113
1114 crow::connections::systemBus->async_method_call(
1115 [asyncResp, name, resName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001116 const GetObjectType& getObjectType) {
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001117 if (ec)
1118 {
1119 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
1120 << ec;
1121 messages::internalError(asyncResp->res);
1122
1123 return;
1124 }
1125 std::string service = getObjectType.begin()->first;
1126 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
1127
1128 getVmData(asyncResp, service, name, resName);
1129 },
1130 "xyz.openbmc_project.ObjectMapper",
1131 "/xyz/openbmc_project/object_mapper",
1132 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001133 "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>());
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +02001134 }
1135};
1136
1137} // namespace redfish