summaryrefslogtreecommitdiff
path: root/src/state_machine.hpp
diff options
context:
space:
mode:
authorAgata Olender <agata.olender@intel.com>2020-01-13 17:57:37 +0300
committerOlender, Agata <agata.olender@intel.com>2020-02-06 12:07:57 +0300
commitbb43fd537ccb83e3188717ea77a58e3929a5445c (patch)
tree498fa5f08089f9adcbb1e89bf2ef4db4e355b6e2 /src/state_machine.hpp
parentb9a4ef7fd96aeda3e311993810c68bbb6553f3c2 (diff)
downloadvirtual-media-bb43fd537ccb83e3188717ea77a58e3929a5445c.tar.xz
Integration with NBDKit for Legacy mode
This change introduces integration of virtual-media application with NBDKit. NBDKit is used here to connect to externally provided image on web and expose NBD device internally in BMC for NBD subsystem (already implemented in Proxy mode) to use. 'Mount' D-Bus call accepts 's imgUrl' and 'b rw'. Based on 's imgUrl' prefix (https:// or smb://) proper mount type is attempted. 'b rw' determines Read-Only mode for both USB Gadget and NBD stack. When 'Mount' is called, virtual-media parses arguments, determines mounting options and attempts to mount external share. For SMB protocol native CIFS Linux module is used: 1) mount(8) call is used to mound provided CIFS share 2) NBDKit loads file on mounted filesystem and exposes NBD Server on internal unix socket 3) Pre-existing code takes care of mouting gadget automatically (connecting socket to /dev/nbdX and then /dev/nbdX to USB Gadget) For HTTPS protocol provisioning is performed by NBDKit: 1) NBDKit connects to provided resource and exposes NBD Server on internal unix socket 2) Pre-existing code takes care of mouting gadget automatically (connecting socket to /dev/nbdX and then /dev/nbdX to USB Gadget) Tested: Manual and automated tests on WilsonCity platform: - mounting and unmounting images over CIFS and HTTPS (single, multiple at the same time etc) - positive and negative tests for D-Bus calls - ensuring proper information is exposed on D-Bus Change-Id: Ia2b6e8c13603521063f5c94cdfdb06f2e872e9e7 Signed-off-by: Adrian Ambrożewicz <adrian.ambrozewicz@linux.intel.com> Signed-off-by: Agata Olender <agata.olender@intel.com>
Diffstat (limited to 'src/state_machine.hpp')
-rw-r--r--src/state_machine.hpp180
1 files changed, 163 insertions, 17 deletions
diff --git a/src/state_machine.hpp b/src/state_machine.hpp
index d6ec1b4..9e011b9 100644
--- a/src/state_machine.hpp
+++ b/src/state_machine.hpp
@@ -2,6 +2,7 @@
#include "configuration.hpp"
#include "logger.hpp"
+#include "smb.hpp"
#include "system.hpp"
#include <sys/mount.h>
@@ -78,6 +79,12 @@ struct MountPointStateMachine
{
if (machine.target)
{
+ // Cleanup after previously mounted device
+ if (machine.target->mountDir)
+ {
+ SmbShare::unmount(*machine.target->mountDir);
+ }
+
machine.target.reset();
}
@@ -431,7 +438,7 @@ struct MountPointStateMachine
LogMsg(Logger::Info, "[App]: Mount called on ",
getObjectPath(machine), machine.name);
- machine.target = {imgUrl};
+ machine.target = {imgUrl, rw};
return handleMount(yield, machine);
});
}
@@ -551,7 +558,7 @@ struct MountPointStateMachine
{
auto process = std::make_shared<Process>(
state.machine.ioc.get(), state.machine.name,
- state.machine.config.nbdDevice);
+ "/usr/sbin/nbd-client", state.machine.config.nbdDevice);
if (!process)
{
LogMsg(Logger::Error, state.machine.name,
@@ -576,32 +583,168 @@ struct MountPointStateMachine
State activateLegacyMode(const ActivatingState& state)
{
- // Check if imgUrl is not emptry
+ LogMsg(
+ Logger::Debug, state.machine.name,
+ " Mount requested on address: ", state.machine.target->imgUrl,
+ " ; RW: ", state.machine.target->rw);
+
if (isCifsUrl(state.machine.target->imgUrl))
{
- auto newState = ActiveState(state);
+ return mountSmbShare(state);
+ }
+ else if (isHttpsUrl(state.machine.target->imgUrl))
+ {
+ return mountHttpsShare(state);
+ }
+
+ LogMsg(Logger::Error, state.machine.name, " URL not recognized");
+ return ReadyState(state);
+ }
- return newState;
+ State mountSmbShare(const ActivatingState& state)
+ {
+ auto mountDir = SmbShare::createMountDir(state.machine.name);
+ if (!mountDir)
+ {
+ return ReadyState(state);
}
- else
+
+ SmbShare smb(*mountDir);
+ fs::path remote = getImagePath(state.machine.target->imgUrl);
+ auto remoteParent = "/" + remote.parent_path().string();
+ auto localFile = *mountDir / remote.filename();
+
+ LogMsg(Logger::Debug, state.machine.name, " Remote name: ", remote,
+ "\n Remote parent: ", remoteParent,
+ "\n Local file: ", localFile);
+
+ if (!smb.mount(remoteParent, state.machine.target->rw))
+ {
+ fs::remove_all(*mountDir);
+ return ReadyState(state);
+ }
+
+ auto process = spawnNbdKit(state.machine, localFile);
+ if (!process)
{
- throw sdbusplus::exception::SdBusError(
- EINVAL, "Not supported url's scheme.");
+ SmbShare::unmount(*mountDir);
+ return ReadyState(state);
}
+
+ auto newState = WaitingForGadgetState(state);
+ newState.process = process;
+ newState.machine.target->mountDir = *mountDir;
+
+ return newState;
}
- int prepareTempDirForLegacyMode(std::string& path)
+ State mountHttpsShare(const ActivatingState& state)
{
- int result = -1;
- char mountPathTemplate[] = "/tmp/vm_legacy.XXXXXX";
- const char* tmpPath = mkdtemp(mountPathTemplate);
- if (tmpPath != nullptr)
+ auto& machine = state.machine;
+
+ auto process = spawnNbdKit(machine, machine.target->imgUrl);
+ if (!process)
{
- path = tmpPath;
- result = 0;
+ return ReadyState(state);
}
- return result;
+ auto newState = WaitingForGadgetState(state);
+ newState.process = process;
+ return newState;
+ }
+
+ static std::shared_ptr<Process>
+ spawnNbdKit(MountPointStateMachine& machine,
+ const std::vector<std::string>& params)
+ {
+ // Investigate
+ auto process = std::make_shared<Process>(
+ machine.ioc.get(), machine.name, "/usr/sbin/nbdkit",
+ machine.config.nbdDevice);
+ if (!process)
+ {
+ LogMsg(Logger::Error, machine.name,
+ " Failed to create Process for: ", machine.name);
+ return {};
+ }
+
+ // Cleanup of previous socket
+ if (fs::exists(machine.config.unixSocket))
+ {
+ LogMsg(Logger::Debug, machine.name,
+ " Removing previously mounted socket: ",
+ machine.config.unixSocket);
+ if (!fs::remove(machine.config.unixSocket))
+ {
+ LogMsg(Logger::Error, machine.name,
+ " Unable to remove pre-existing socket :",
+ machine.config.unixSocket);
+ return {};
+ }
+ }
+
+ std::string nbd_client =
+ "/usr/sbin/nbd-client " +
+ boost::algorithm::join(
+ Configuration::MountPoint::toArgs(machine.config), " ");
+
+ std::vector<std::string> args = {
+ // Listen for client on this unix socket...
+ "--unix",
+ machine.config.unixSocket,
+
+ // ... then connect nbd-client to served image
+ "--run",
+ nbd_client,
+
+#if VM_VERBOSE_NBDKIT_LOGS
+ "--verbose", // swarm of debug logs - only for brave souls
+#endif
+ };
+
+ if (!machine.target->rw)
+ {
+ args.push_back("--readonly");
+ }
+
+ // Insert extra params
+ args.insert(args.end(), params.begin(), params.end());
+
+ if (!process->spawn(
+ args, [& machine = machine](int exitCode, bool isReady) {
+ LogMsg(Logger::Info, machine.name, " process ended.");
+ machine.emitSubprocessStoppedEvent();
+ }))
+ {
+ LogMsg(Logger::Error, machine.name,
+ " Failed to spawn Process for: ", machine.name);
+ return {};
+ }
+
+ return process;
+ }
+
+ static std::shared_ptr<Process>
+ spawnNbdKit(MountPointStateMachine& machine, const fs::path& file)
+ {
+ return spawnNbdKit(machine, {// Use file plugin ...
+ "file",
+ // ... to mount file at this location
+ "file=" + file.string()});
+ }
+
+ static std::shared_ptr<Process>
+ spawnNbdKit(MountPointStateMachine& machine, const std::string& url)
+ {
+ std::vector<std::string> params = {
+ // Use curl plugin ...
+ "curl",
+ // ... to mount http resource at url
+ "url=" + url};
+
+ // TODO: Authorization support in future patch
+
+ return spawnNbdKit(machine, params);
}
bool checkUrl(const std::string& urlScheme, const std::string& imageUrl)
@@ -688,7 +831,8 @@ struct MountPointStateMachine
{
int32_t ret = UsbGadget::configure(
state.machine.name, state.machine.config.nbdDevice,
- devState);
+ devState,
+ state.machine.target ? state.machine.target->rw : false);
if (ret == 0)
{
return ActiveState(state);
@@ -821,6 +965,8 @@ struct MountPointStateMachine
struct Target
{
std::string imgUrl;
+ bool rw;
+ std::optional<fs::path> mountDir;
};
std::reference_wrapper<boost::asio::io_context> ioc;