blob: 88fd765b2ecc860a27751dfb932f284a864db4e1 [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 */
33static void vmParseInterfaceObject(const DbusInterfaceType &interface,
34 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
66 const bool *activeValue = std::get_if<bool>(&activeProperty->second);
67 if (!activeValue)
68 {
69 BMCWEB_LOG_DEBUG << "Value Active not found";
70 return;
71 }
72
73 const std::string *endpointIdValue =
74 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 {
96 const std::string *imageUrlValue =
97 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 */
115static nlohmann::json vmItemTemplate(const std::string &name,
116 const std::string &resName)
117{
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,
143 const std::string &service,
144 const std::string &name)
145{
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,
149 ManagedObjectType &subtree) {
150 if (ec)
151 {
152 BMCWEB_LOG_DEBUG << "DBUS response error";
153 return;
154 }
155 nlohmann::json &members = aResp->res.jsonValue["Members"];
156 members = nlohmann::json::array();
157
158 for (const auto &object : subtree)
159 {
160 nlohmann::json item;
161 const std::string &path =
162 static_cast<const std::string &>(object.first);
163 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,
186 const std::string &service, const std::string &name,
187 const std::string &resName)
188{
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,
193 ManagedObjectType &subtree) {
194 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
201 for (auto &item : subtree)
202 {
203 const std::string &path =
204 static_cast<const std::string &>(item.first);
205
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:
251 VirtualMediaActionInsertMedia(CrowApp &app) :
252 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 /**
268 * @brief Function handles POST method request.
269 *
270 * Analyzes POST body message before sends Reset request data to dbus.
271 */
272 void doPost(crow::Response &res, const crow::Request &req,
273 const std::vector<std::string> &params) override
274 {
275 auto aResp = std::make_shared<AsyncResp>(res);
276
277 if (params.size() != 2)
278 {
279 messages::internalError(res);
280 return;
281 }
282
283 // take resource name from URL
284 const std::string &resName = params[1];
285
286 if (params[0] != "bmc")
287 {
288 messages::resourceNotFound(res, "VirtualMedia.Insert", resName);
289
290 return;
291 }
292
293 crow::connections::systemBus->async_method_call(
294 [this, aResp{std::move(aResp)}, req,
295 resName](const boost::system::error_code ec,
296 const GetObjectType &getObjectType) {
297 if (ec)
298 {
299 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
300 << ec;
301 messages::internalError(aResp->res);
302
303 return;
304 }
305 std::string service = getObjectType.begin()->first;
306 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
307
308 crow::connections::systemBus->async_method_call(
309 [this, service, resName, req, aResp{std::move(aResp)}](
310 const boost::system::error_code ec,
311 ManagedObjectType &subtree) {
312 if (ec)
313 {
314 BMCWEB_LOG_DEBUG << "DBUS response error";
315
316 return;
317 }
318
319 for (const auto &object : subtree)
320 {
321 const std::string &path =
322 static_cast<const std::string &>(object.first);
323
324 std::size_t lastIndex = path.rfind("/");
325 if (lastIndex == std::string::npos)
326 {
327 continue;
328 }
329
330 lastIndex += 1;
331
332 if (path.substr(lastIndex) == resName)
333 {
334 lastIndex = path.rfind("Proxy");
335 if (lastIndex != std::string::npos)
336 {
337 // Not possible in proxy mode
338 BMCWEB_LOG_DEBUG << "InsertMedia not "
339 "allowed in proxy mode";
340 messages::resourceNotFound(
341 aResp->res, "VirtualMedia.InsertMedia",
342 resName);
343
344 return;
345 }
346
347 lastIndex = path.rfind("Legacy");
348 if (lastIndex != std::string::npos)
349 {
350 // Legacy mode
351 std::string imageUrl;
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100352 std::string userName;
353 std::string password;
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100354 bool writeProtected;
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200355
356 // Read obligatory paramters (url of image)
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100357 if (!json_util::readJson(
358 req, aResp->res, "Image", imageUrl,
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100359 "WriteProtected", writeProtected,
360 "UserName", userName, "Password",
361 password))
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100362
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200363 {
364 BMCWEB_LOG_DEBUG
365 << "Image is not provided";
366 return;
367 }
368
369 // must not be empty
370 if (imageUrl.size() == 0)
371 {
372 BMCWEB_LOG_ERROR
373 << "Request action parameter "
374 "Image is empty.";
375 messages::propertyValueFormatError(
376 aResp->res, "<empty>", "Image");
377
378 return;
379 }
380
381 // manager is irrelevant for VirtualMedia
382 // dbus calls
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100383 doMountVmLegacy(std::move(aResp), service,
384 resName, imageUrl,
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100385 !writeProtected,
386 std::move(userName),
387 std::move(password));
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200388
389 return;
390 }
391 }
392 }
393 BMCWEB_LOG_DEBUG << "Parent item not found";
394 messages::resourceNotFound(aResp->res, "VirtualMedia",
395 resName);
396 },
397 service, "/xyz/openbmc_project/VirtualMedia",
398 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
399 },
400 "xyz.openbmc_project.ObjectMapper",
401 "/xyz/openbmc_project/object_mapper",
402 "xyz.openbmc_project.ObjectMapper", "GetObject",
403 "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
404 }
405
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100406 template <typename T> static void secureCleanup(T &value)
407 {
408 auto raw = const_cast<typename T::value_type *>(value.data());
409 explicit_bzero(raw, value.size() * sizeof(*raw));
410 }
411
412 class Credentials
413 {
414 public:
415 Credentials(std::string &&user, std::string &&password) :
416 userBuf(std::move(user)), passBuf(std::move(password))
417 {
418 }
419
420 ~Credentials()
421 {
422 secureCleanup(userBuf);
423 secureCleanup(passBuf);
424 }
425
426 const std::string &user()
427 {
428 return userBuf;
429 }
430
431 const std::string &password()
432 {
433 return passBuf;
434 }
435
436 private:
437 Credentials() = delete;
438 Credentials(const Credentials &) = delete;
439 Credentials &operator=(const Credentials &) = delete;
440
441 std::string userBuf;
442 std::string passBuf;
443 };
444
445 class CredentialsProvider
446 {
447 public:
448 template <typename T> struct Deleter
449 {
450 void operator()(T *buff) const
451 {
452 if (buff)
453 {
454 secureCleanup(*buff);
455 delete buff;
456 }
457 }
458 };
459
460 using Buffer = std::vector<char>;
461 using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>;
462 // Using explicit definition instead of std::function to avoid implicit
463 // conversions eg. stack copy instead of reference
464 using FormatterFunc = void(const std::string &username,
465 const std::string &password, Buffer &dest);
466
467 CredentialsProvider(std::string &&user, std::string &&password) :
468 credentials(std::move(user), std::move(password))
469 {
470 }
471
472 const std::string &user()
473 {
474 return credentials.user();
475 }
476
477 const std::string &password()
478 {
479 return credentials.password();
480 }
481
482 SecureBuffer pack(const FormatterFunc formatter)
483 {
484 SecureBuffer packed{new Buffer{}};
485 if (formatter)
486 {
487 formatter(credentials.user(), credentials.password(), *packed);
488 }
489
490 return packed;
491 }
492
493 private:
494 Credentials credentials;
495 };
496
497 // Wrapper for boost::async_pipe ensuring proper pipe cleanup
498 template <typename Buffer> class Pipe
499 {
500 public:
501 using unix_fd = sdbusplus::message::unix_fd;
502
503 Pipe(boost::asio::io_context &io, Buffer &&buffer) :
504 impl(io), buffer{std::move(buffer)}
505 {
506 }
507
508 ~Pipe()
509 {
510 // Named pipe needs to be explicitly removed
511 impl.close();
512 }
513
514 unix_fd fd()
515 {
516 return unix_fd{impl.native_source()};
517 }
518
519 template <typename WriteHandler>
520 void async_write(WriteHandler &&handler)
521 {
522 impl.async_write_some(data(), std::forward<WriteHandler>(handler));
523 }
524
525 private:
526 // Specialization for pointer types
527 template <typename B = Buffer>
528 typename std::enable_if<boost::has_dereference<B>::value,
529 boost::asio::const_buffer>::type
530 data()
531 {
532 return boost::asio::buffer(*buffer);
533 }
534
535 template <typename B = Buffer>
536 typename std::enable_if<!boost::has_dereference<B>::value,
537 boost::asio::const_buffer>::type
538 data()
539 {
540 return boost::asio::buffer(buffer);
541 }
542
543 const std::string name;
544 boost::process::async_pipe impl;
545 Buffer buffer;
546 };
547
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200548 /**
549 * @brief Function transceives data with dbus directly.
550 *
551 * All BMC state properties will be retrieved before sending reset request.
552 */
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100553 void doMountVmLegacy(std::shared_ptr<AsyncResp> asyncResp,
554 const std::string &service, const std::string &name,
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100555 const std::string &imageUrl, const bool rw,
556 std::string &&userName, std::string &&password)
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200557 {
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100558 using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>;
559 constexpr const size_t secretLimit = 1024;
560
561 std::shared_ptr<SecurePipe> secretPipe;
562 std::variant<int, SecurePipe::unix_fd> unixFd = -1;
563
564 if (!userName.empty() || !password.empty())
565 {
566 // Encapsulate in safe buffer
567 CredentialsProvider credentials(std::move(userName),
568 std::move(password));
569
570 // Payload must contain data + NULL delimiters
571 if (credentials.user().size() + credentials.password().size() + 2 >
572 secretLimit)
573 {
574 BMCWEB_LOG_ERROR << "Credentials too long to handle";
575 messages::unrecognizedRequestBody(asyncResp->res);
576 return;
577 }
578
579 // Pack secret
580 auto secret = credentials.pack([](const auto &user,
581 const auto &pass, auto &buff) {
582 std::copy(user.begin(), user.end(), std::back_inserter(buff));
583 buff.push_back('\0');
584 std::copy(pass.begin(), pass.end(), std::back_inserter(buff));
585 buff.push_back('\0');
586 });
587
588 // Open pipe
589 secretPipe = std::make_shared<SecurePipe>(
590 crow::connections::systemBus->get_io_context(),
591 std::move(secret));
592 unixFd = secretPipe->fd();
593
594 // Pass secret over pipe
595 secretPipe->async_write(
596 [asyncResp](const boost::system::error_code &ec,
597 std::size_t size) {
598 if (ec)
599 {
600 BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec;
601 messages::internalError(asyncResp->res);
602 }
603 });
604 }
605
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100606 crow::connections::systemBus->async_method_call(
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100607 [asyncResp, secretPipe](const boost::system::error_code ec,
608 bool success) {
Adrian Ambrożewiczd6da5be2020-01-13 18:31:01 +0100609 if (ec)
610 {
611 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
612 messages::internalError(asyncResp->res);
613 }
614 else if (!success)
615 {
616 BMCWEB_LOG_ERROR << "Service responded with error";
617 messages::generalError(asyncResp->res);
618 }
619 },
620 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
Adrian Ambrożewicz988fb7b2020-01-13 18:52:46 +0100621 "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw,
622 unixFd);
Przemyslaw Czarnowskie13c2762019-09-02 17:32:43 +0200623 }
624};
625
626/**
627 @brief EjectMedia action class
628 */
629class VirtualMediaActionEjectMedia : public Node
630{
631 public:
632 VirtualMediaActionEjectMedia(CrowApp &app) :
633 Node(app,
634 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
635 "VirtualMedia.EjectMedia",
636 std::string(), std::string())
637 {
638 entityPrivileges = {
639 {boost::beast::http::verb::get, {{"Login"}}},
640 {boost::beast::http::verb::head, {{"Login"}}},
641 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
642 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
643 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
644 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
645 }
646
647 private:
648 /**
649 * @brief Function handles POST method request.
650 *
651 * Analyzes POST body message before sends Reset request data to dbus.
652 */
653 void doPost(crow::Response &res, const crow::Request &req,
654 const std::vector<std::string> &params) override
655 {
656 auto aResp = std::make_shared<AsyncResp>(res);
657
658 if (params.size() != 2)
659 {
660 messages::internalError(res);
661 return;
662 }
663
664 // take resource name from URL
665 const std::string &resName = params[1];
666
667 if (params[0] != "bmc")
668 {
669 messages::resourceNotFound(res, "VirtualMedia.Eject", resName);
670
671 return;
672 }
673
674 crow::connections::systemBus->async_method_call(
675 [this, aResp{std::move(aResp)}, req,
676 resName](const boost::system::error_code ec,
677 const GetObjectType &getObjectType) {
678 if (ec)
679 {
680 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
681 << ec;
682 messages::internalError(aResp->res);
683
684 return;
685 }
686 std::string service = getObjectType.begin()->first;
687 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
688
689 crow::connections::systemBus->async_method_call(
690 [this, resName, service, req, aResp{std::move(aResp)}](
691 const boost::system::error_code ec,
692 ManagedObjectType &subtree) {
693 if (ec)
694 {
695 BMCWEB_LOG_DEBUG << "DBUS response error";
696
697 return;
698 }
699
700 for (const auto &object : subtree)
701 {
702 const std::string &path =
703 static_cast<const std::string &>(object.first);
704
705 std::size_t lastIndex = path.rfind("/");
706 if (lastIndex == std::string::npos)
707 {
708 continue;
709 }
710
711 lastIndex += 1;
712
713 if (path.substr(lastIndex) == resName)
714 {
715 lastIndex = path.rfind("Proxy");
716 if (lastIndex != std::string::npos)
717 {
718 // Proxy mode
719 doVmAction(std::move(aResp), service,
720 resName, false);
721 }
722
723 lastIndex = path.rfind("Legacy");
724 if (lastIndex != std::string::npos)
725 {
726 // Legacy mode
727 doVmAction(std::move(aResp), service,
728 resName, true);
729 }
730
731 return;
732 }
733 }
734 BMCWEB_LOG_DEBUG << "Parent item not found";
735 messages::resourceNotFound(aResp->res, "VirtualMedia",
736 resName);
737 },
738 service, "/xyz/openbmc_project/VirtualMedia",
739 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
740 },
741 "xyz.openbmc_project.ObjectMapper",
742 "/xyz/openbmc_project/object_mapper",
743 "xyz.openbmc_project.ObjectMapper", "GetObject",
744 "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
745 }
746
747 /**
748 * @brief Function transceives data with dbus directly.
749 *
750 * All BMC state properties will be retrieved before sending reset request.
751 */
752 void doVmAction(std::shared_ptr<AsyncResp> asyncResp,
753 const std::string &service, const std::string &name,
754 bool legacy)
755 {
756
757 // Legacy mount requires parameter with image
758 if (legacy)
759 {
760 crow::connections::systemBus->async_method_call(
761 [asyncResp](const boost::system::error_code ec) {
762 if (ec)
763 {
764 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
765
766 messages::internalError(asyncResp->res);
767 return;
768 }
769 },
770 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
771 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
772 }
773 else // proxy
774 {
775 crow::connections::systemBus->async_method_call(
776 [asyncResp](const boost::system::error_code ec) {
777 if (ec)
778 {
779 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
780
781 messages::internalError(asyncResp->res);
782 return;
783 }
784 },
785 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
786 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
787 }
788 }
789};
790
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200791class VirtualMediaCollection : public Node
792{
793 public:
794 /*
795 * Default Constructor
796 */
797 VirtualMediaCollection(CrowApp &app) :
798 Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/", std::string())
799 {
800 entityPrivileges = {
801 {boost::beast::http::verb::get, {{"Login"}}},
802 {boost::beast::http::verb::head, {{"Login"}}},
803 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
804 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
805 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
806 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
807 }
808
809 private:
810 /**
811 * Functions triggers appropriate requests on DBus
812 */
813 void doGet(crow::Response &res, const crow::Request &req,
814 const std::vector<std::string> &params) override
815 {
816 auto asyncResp = std::make_shared<AsyncResp>(res);
817
818 // Check if there is required param, truly entering this shall be
819 // impossible
820 if (params.size() != 1)
821 {
822 messages::internalError(res);
823
824 return;
825 }
826
827 const std::string &name = params[0];
828
829 if (name != "bmc")
830 {
831 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
832
833 return;
834 }
835
836 res.jsonValue["@odata.type"] =
837 "#VirtualMediaCollection.VirtualMediaCollection";
838 res.jsonValue["Name"] = "Virtual Media Services";
Przemyslaw Czarnowski107077d2019-07-11 10:16:43 +0200839 res.jsonValue["@odata.id"] =
840 "/redfish/v1/Managers/" + name + "/VirtualMedia/";
841
842 crow::connections::systemBus->async_method_call(
843 [asyncResp, name](const boost::system::error_code ec,
844 const GetObjectType &getObjectType) {
845 if (ec)
846 {
847 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
848 << ec;
849 messages::internalError(asyncResp->res);
850
851 return;
852 }
853 std::string service = getObjectType.begin()->first;
854 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
855
856 getVmResourceList(asyncResp, service, name);
857 },
858 "xyz.openbmc_project.ObjectMapper",
859 "/xyz/openbmc_project/object_mapper",
860 "xyz.openbmc_project.ObjectMapper", "GetObject",
861 "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
862 }
863};
864
865class VirtualMedia : public Node
866{
867 public:
868 /*
869 * Default Constructor
870 */
871 VirtualMedia(CrowApp &app) :
872 Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/",
873 std::string(), std::string())
874 {
875 entityPrivileges = {
876 {boost::beast::http::verb::get, {{"Login"}}},
877 {boost::beast::http::verb::head, {{"Login"}}},
878 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
879 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
880 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
881 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
882 }
883
884 private:
885 /**
886 * Functions triggers appropriate requests on DBus
887 */
888 void doGet(crow::Response &res, const crow::Request &req,
889 const std::vector<std::string> &params) override
890 {
891 // Check if there is required param, truly entering this shall be
892 // impossible
893 if (params.size() != 2)
894 {
895 messages::internalError(res);
896
897 res.end();
898 return;
899 }
900 const std::string &name = params[0];
901 const std::string &resName = params[1];
902
903 auto asyncResp = std::make_shared<AsyncResp>(res);
904
905 if (name != "bmc")
906 {
907 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
908
909 return;
910 }
911
912 crow::connections::systemBus->async_method_call(
913 [asyncResp, name, resName](const boost::system::error_code ec,
914 const GetObjectType &getObjectType) {
915 if (ec)
916 {
917 BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
918 << ec;
919 messages::internalError(asyncResp->res);
920
921 return;
922 }
923 std::string service = getObjectType.begin()->first;
924 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
925
926 getVmData(asyncResp, service, name, resName);
927 },
928 "xyz.openbmc_project.ObjectMapper",
929 "/xyz/openbmc_project/object_mapper",
930 "xyz.openbmc_project.ObjectMapper", "GetObject",
931 "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
932 }
933};
934
935} // namespace redfish