diff options
author | Zbigniew Lukwinski <zbigniew.lukwinski@linux.intel.com> | 2019-09-04 16:18:57 +0300 |
---|---|---|
committer | Zbigniew Lukwinski <zbigniew.lukwinski@linux.intel.com> | 2019-09-16 12:58:35 +0300 |
commit | 4815936d17aa6223cda31ccce30344069d652a70 (patch) | |
tree | 15c064de8ea4781ceea01bf617d9bf83ff56f59a /src | |
parent | e27f0acd746a61f6e684403a29a6ac8626c14e26 (diff) | |
download | virtual-media-4815936d17aa6223cda31ccce30344069d652a70.tar.xz |
Support for Legacy mode.
Support for Legacy mode added but only for CIFS resources.
DBus "xyz.openbmc_project.VirtualMedia.MountPoint" interface was
extended to support 'ImageUrl'. This property will be used to expose
on DBus HTTPS/CIFS url already mounted in Legacy mode.
Changes does not cover passing credential needed for CIFS
authentication.
Tested:
- CIFS/SMB resource succesfully mounted with DBus call to the
xyz.openbmc_project.VirtualMedia.Legacy:Mount
- CIFS/SMB resource succesfully unmounted with DBus call to the
xyz.openbmc_project.VirtualMedia.Legacy:Unmount
- checked double mount and unmount DBus calls return an error
and put an appropriate trace in logs.
Change-Id: I25b3d11dad6b273e88325beb35580e0baa8568f8
Signed-off-by: Zbigniew Lukwinski <zbigniew.lukwinski@linux.intel.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/main.cpp | 403 |
1 files changed, 384 insertions, 19 deletions
diff --git a/src/main.cpp b/src/main.cpp index 3dfd0ad..6d5ffb6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,3 +1,5 @@ +#include <sys/mount.h> + #include <boost/asio.hpp> #include <boost/asio/buffer.hpp> #include <boost/asio/posix/stream_descriptor.hpp> @@ -338,6 +340,19 @@ class Process : public std::enable_shared_from_this<Process> class Configuration { public: + enum class Mode + { + // Proxy mode - works directly from browser and uses JavaScript/HTML5 + // to communicate over Secure WebSockets directly to HTTPS endpoint + // hosted by bmcweb on BMC + Proxy = 0, + + // Legacy mode - is initiated from browser using Redfish defined + // VirtualMedia schemas, then BMC process connects to external + // CIFS/HTTPS image pointed during initialization. + Legacy = 1, + }; + struct MountPoint { std::string nbdDevice; @@ -345,6 +360,7 @@ class Configuration std::string endPointId; std::optional<int> timeout; std::optional<int> blocksize; + Mode mode; static std::vector<std::string> toArgs(const MountPoint& mp) { @@ -476,6 +492,41 @@ class Configuration std::cout << "BlockSize not set, use default\n"; } } + const auto modeIter = mountpoint.value().find("Mode"); + if (modeIter != mountpoint.value().cend()) + { + const uint64_t* value = + modeIter->get_ptr<const uint64_t*>(); + if (value) + { + if (*value == 0) + { + mp.mode = Configuration::Mode::Proxy; + } + else if (*value == 1) + { + mp.mode = Configuration::Mode::Legacy; + } + else + { + std::cerr << "Incorrect Mode, skip this mount " + "point\n"; + continue; + } + } + else + { + std::cerr + << "Mode not set, skip this mount point\n"; + continue; + } + } + else + { + std::cerr + << "Mode does not exist, skip this mount point\n"; + continue; + } mountPoints[mountpoint.key()] = std::move(mp); } } @@ -484,13 +535,51 @@ class Configuration } }; +class ParametersManager +{ + public: + struct Parameters + { + // Legacy mode specific parameters + std::string imageUrl; + std::string mountDirectoryPath; + }; + + ParametersManager() + { + } + + void addMountPoint(const std::string& name) + { + Parameters parameters; + parameters.imageUrl.clear(); + parameters.mountDirectoryPath.clear(); + mountPoints[name] = std::move(parameters); + } + + Parameters* getMountPoint(const std::string& name) + { + if (mountPoints.find(name) != mountPoints.end()) + { + return &mountPoints[name]; + } + else + { + return nullptr; + } + } + + private: + boost::container::flat_map<std::string, Parameters> mountPoints; +}; + class App { public: App(boost::asio::io_context& ioc, const Configuration& config, sd_bus* custom_bus = nullptr) : ioc(ioc), - devMonitor(ioc), config(config) + devMonitor(ioc), config(config), paramMgr() { if (!custom_bus) { @@ -507,12 +596,25 @@ class App *bus, "/xyz/openbmc_project/VirtualMedia"); for (const auto& entry : config.mountPoints) { - devMonitor.addDevice(entry.second.nbdDevice); - Process::addProcess(entry.first); + if (entry.second.mode == Configuration::Mode::Proxy) + { + devMonitor.addDevice(entry.second.nbdDevice); + Process::addProcess(entry.first); + } addMountPointInterface(entry.first, entry.second); - addProxyInterface(entry.first, entry.second); - addProcessInterface(entry.first); + + if (entry.second.mode == Configuration::Mode::Proxy) + { + addProxyInterface(entry.first, entry.second); + addProcessInterface(entry.first); + } + else + { + addLegacyInterface(entry.first); + } + + paramMgr.addMountPoint(entry.first); } devMonitor.run([this](const std::string& device, StateChange change) { configureUsbGadget(device, change); @@ -530,20 +632,27 @@ class App return true; } - bool configureUsbGadget(const std::string& device, StateChange change) + int configureUsbGadget(const std::string& device, StateChange change, + const std::string& lunFile = "") { - bool success = true; + int result = 0; std::error_code ec; if (change == StateChange::unknown) { std::cerr << "Change to unknown state is not possible\n"; - return false; + return -1; } - StateChange currentState = devMonitor.getState(device); - if (currentState == StateChange::notmonitored) + + // If lun file provided just use it to inject to the lun.0/file + // instead of /dev/<device> + std::string lunFileInternal; + if (lunFile.empty()) { - std::cerr << "Unregistered device\n"; - return false; + lunFileInternal = "/dev/" + device; + } + else + { + lunFileInternal = lunFile; } const fs::path gadgetDir = @@ -574,7 +683,7 @@ class App echoToFile(funcMassStorageDir / "lun.0/removable", "1"); echoToFile(funcMassStorageDir / "lun.0/ro", "1"); echoToFile(funcMassStorageDir / "lun.0/cdrom", "0"); - echoToFile(funcMassStorageDir / "lun.0/file", "/dev/" + device); + echoToFile(funcMassStorageDir / "lun.0/file", lunFileInternal); for (const auto& port : fs::directory_iterator( "/sys/bus/platform/devices/1e6a0000.usb-vhub")) @@ -586,7 +695,7 @@ class App std::cout << "Use port : " << port.path().filename() << '\n'; echoToFile(gadgetDir / "UDC", portId); - return true; + return 0; } } } @@ -594,13 +703,13 @@ class App { // Got error perform cleanup std::cerr << e.what() << '\n'; - success = false; + result = -1; } catch (std::ofstream::failure& e) { // Got error perform cleanup std::cerr << e.what() << '\n'; - success = false; + result = -1; } } // StateChange: unknown, notmonitored, inserted were handler @@ -635,21 +744,50 @@ class App if (ec) { std::cerr << ec.message() << '\n'; + result = -1; } - return (success && !static_cast<bool>(ec)); + + return result; } void addMountPointInterface(const std::string& name, const Configuration::MountPoint& mp) { + std::string objPath; + if (mp.mode == Configuration::Mode::Proxy) + { + objPath = "/xyz/openbmc_project/VirtualMedia/Proxy/"; + } + else + { + objPath = "/xyz/openbmc_project/VirtualMedia/Legacy/"; + } + auto iface = objServer->add_interface( - "/xyz/openbmc_project/VirtualMedia/Proxy/" + name, - "xyz.openbmc_project.VirtualMedia.MountPoint"); + objPath + name, "xyz.openbmc_project.VirtualMedia.MountPoint"); iface->register_property("Device", mp.nbdDevice); iface->register_property("EndpointId", mp.endPointId); iface->register_property("Socket", mp.unixSocket); iface->register_property("Timeout", *mp.timeout); iface->register_property("BlockSize", *mp.blocksize); + iface->register_property( + "ImageUrl", std::string(""), + [](const std::string& req, std::string& property) { + throw sdbusplus::exception::SdBusError( + EPERM, "Setting ImageUrl property is not allowed"); + return -1; + }, + [this, name](const std::string& property) { + auto parameters = paramMgr.getMountPoint(name); + if (parameters == nullptr) + { + return std::string(""); + } + else + { + return parameters->imageUrl; + } + }); iface->initialize(); }; @@ -675,6 +813,232 @@ class App iface->initialize(); } + int prepareTempDirForLegacyMode(std::string& path) + { + int result = -1; + char mountPathTemplate[] = "/tmp/vm_legacy.XXXXXX"; + const char* tmpPath = mkdtemp(mountPathTemplate); + if (tmpPath != nullptr) + { + path = tmpPath; + result = 0; + } + + return result; + } + + bool checkUrl(const std::string& urlScheme, const std::string& imageUrl) + { + if (urlScheme.compare(imageUrl.substr(0, urlScheme.size())) == 0) + { + return true; + } + else + { + return false; + } + } + + bool getImagePathFromUrl(const std::string& urlScheme, + const std::string& imageUrl, + std::string* imagePath) + { + if (checkUrl(urlScheme, imageUrl)) + { + if (imagePath != nullptr) + { + *imagePath = imageUrl.substr(urlScheme.size() - 1); + return true; + } + else + { + std::cerr << "Invalid parameter provied\n"; + return false; + } + } + else + { + std::cerr << "Provied url does not match scheme\n"; + return false; + } + } + + bool checkHttpsUrl(const std::string& imageUrl) + { + return checkUrl("https://", imageUrl); + } + + bool getImagePathFromHttpsUrl(const std::string& imageUrl, + std::string* imagePath) + { + return getImagePathFromUrl("https://", imageUrl, imagePath); + } + + bool checkCifsUrl(const std::string& imageUrl) + { + return checkUrl("smb://", imageUrl); + } + + bool getImagePathFromCifsUrl(const std::string& imageUrl, + std::string* imagePath) + { + return getImagePathFromUrl("smb://", imageUrl, imagePath); + } + + fs::path getImagePath(const std::string& imageUrl) + { + std::string imagePath; + + if (getImagePathFromHttpsUrl(imageUrl, &imagePath)) + { + return fs::path(imagePath); + } + else if (getImagePathFromCifsUrl(imageUrl, &imagePath)) + { + return fs::path(imagePath); + } + else + { + std::cerr << "Unrecognized url's scheme encountered\n"; + return fs::path(""); + } + } + + int mountCifsUrlForLegcyMode(const std::string& name, + const std::string& imageUrl, + ParametersManager::Parameters* parameters) + { + int result = -1; + fs::path imageUrlPath = getImagePath(imageUrl); + const std::string imageUrlParentPath = + "/" + imageUrlPath.parent_path().string(); + std::string mountDirectoryPath; + result = prepareTempDirForLegacyMode(mountDirectoryPath); + if (result != 0) + { + std::cerr << "Failed to create tmp directory\n"; + return result; + } + + result = mount(imageUrlParentPath.c_str(), mountDirectoryPath.c_str(), + "cifs", 0, + "username=,password=,nolock,sec=" + "ntlmsspi,seal,vers=3.0"); + if (result != 0) + { + std::cerr << "Failed to mount the url\n"; + fs::remove_all(fs::path(mountDirectoryPath)); + return result; + } + + const std::string imageMountPath = + mountDirectoryPath + "/" + imageUrlPath.filename().string(); + result = + configureUsbGadget(name, StateChange::inserted, imageMountPath); + if (result != 0) + { + std::cerr << "Failed to run usb gadget\n"; + umount(mountDirectoryPath.c_str()); + fs::remove_all(fs::path(mountDirectoryPath)); + return result; + } + + parameters->mountDirectoryPath = mountDirectoryPath; + + return result; + } + + int umountCifsUrlForLegcyMode(const std::string& name, + ParametersManager::Parameters* parameters) + { + int result = -1; + + result = configureUsbGadget(name, StateChange::removed); + if (result != 0) + { + std::cerr << "Failed to unmount resource\n"; + return result; + } + + umount(parameters->mountDirectoryPath.c_str()); + fs::remove_all(fs::path(parameters->mountDirectoryPath)); + parameters->mountDirectoryPath.clear(); + + return result; + } + + void addLegacyInterface(const std::string& name) + { + auto iface = objServer->add_interface( + "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, + "xyz.openbmc_project.VirtualMedia.Legacy"); + iface->register_method( + "Mount", [this, name](const std::string& imageUrl) { + auto parameters = paramMgr.getMountPoint(name); + if (parameters == nullptr) + { + throw sdbusplus::exception::SdBusError( + ENOENT, "Failed to find the node."); + } + + if (!parameters->imageUrl.empty()) + { + throw sdbusplus::exception::SdBusError( + ETXTBSY, "Node already used and resource mounted."); + } + + if (checkCifsUrl(imageUrl)) + { + int result = + mountCifsUrlForLegcyMode(name, imageUrl, parameters); + if (result != 0) + { + throw sdbusplus::exception::SdBusError( + -result, "Failed to mount cifs url."); + } + } + else + { + throw sdbusplus::exception::SdBusError( + EINVAL, "Not supported url's scheme."); + } + + parameters->imageUrl = imageUrl; + }); + iface->register_method("Unmount", [this, name]() { + auto parameters = paramMgr.getMountPoint(name); + if (parameters == nullptr) + { + throw sdbusplus::exception::SdBusError( + ENOENT, "Failed to find the node."); + } + + if (parameters->imageUrl.empty()) + { + throw sdbusplus::exception::SdBusError( + ENOENT, "Node is not used and no resource mounted."); + } + + if (checkCifsUrl(parameters->imageUrl)) + { + int result = umountCifsUrlForLegcyMode(name, parameters); + if (result != 0) + { + throw sdbusplus::exception::SdBusError( + -result, "Failed to unmount cifs resource."); + } + } + else + { + throw sdbusplus::exception::SdBusError( + EINVAL, "Not supported url's scheme."); + } + + parameters->imageUrl.clear(); + }); + iface->initialize(); + } + void addProcessInterface(const std::string& name) { auto iface = objServer->add_interface( @@ -693,6 +1057,7 @@ class App std::shared_ptr<sdbusplus::asio::object_server> objServer; std::shared_ptr<sdbusplus::server::manager::manager> objManager; const Configuration& config; + ParametersManager paramMgr; }; int main() |