Skip to content
4 changes: 4 additions & 0 deletions src/shared/inc/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Module Name:
Type(Type&&) = delete; \
Type& operator=(Type&&) = delete;

#define DEFAULT_MOVABLE(Type) \
Type(Type&&) = default; \
Type& operator=(Type&&) = default;

namespace wsl::shared {

inline constexpr std::uint32_t VersionMajor = WSL_PACKAGE_VERSION_MAJOR;
Expand Down
2 changes: 2 additions & 0 deletions src/windows/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ set(SOURCES
SubProcess.cpp
svccomm.cpp
svccommio.cpp
WSLAContainerLauncher.cpp
WSLAProcessLauncher.cpp
WslClient.cpp
WslCoreConfig.cpp
Expand Down Expand Up @@ -106,6 +107,7 @@ set(HEADERS
SubProcess.h
svccomm.hpp
svccommio.hpp
WSLAContainerLauncher.h
WSLAProcessLauncher.h
WslClient.h
WslCoreConfig.h
Expand Down
61 changes: 61 additions & 0 deletions src/windows/common/WSLAContainerLauncher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include "WSLAContainerLauncher.h"

using wsl::windows::common::ClientRunningWSLAProcess;
using wsl::windows::common::RunningWSLAContainer;
using wsl::windows::common::WSLAContainerLauncher;

RunningWSLAContainer::RunningWSLAContainer(wil::com_ptr<IWSLAContainer>&& Container, std::vector<WSLA_PROCESS_FD>&& fds) :
m_container(std::move(Container)), m_fds(std::move(fds))
{
}

IWSLAContainer& RunningWSLAContainer::Get()
{
return *m_container;
}

WSLA_CONTAINER_STATE RunningWSLAContainer::State()
{
WSLA_CONTAINER_STATE state{};
THROW_IF_FAILED(m_container->GetState(&state));
return state;
}

ClientRunningWSLAProcess RunningWSLAContainer::GetInitProcess()
{
wil::com_ptr<IWSLAProcess> process;
THROW_IF_FAILED(m_container->GetInitProcess(&process));

return ClientRunningWSLAProcess{std::move(process), std::move(m_fds)};
}

WSLAContainerLauncher::WSLAContainerLauncher(
const std::string& Image,
const std::string& Name,
const std::string& EntryPoint,
const std::vector<std::string>& Arguments,
const std::vector<std::string>& Environment,
ProcessFlags Flags) :
WSLAProcessLauncher(EntryPoint, Arguments, Environment, Flags), m_image(Image), m_name(Name)
{
}

RunningWSLAContainer WSLAContainerLauncher::Launch(IWSLASession& Session)
{
WSLA_CONTAINER_OPTIONS options{};
options.Image = m_image.c_str();
options.Name = m_name.c_str();
auto [processOptions, commandLinePtrs, environmentPtrs] = CreateProcessOptions();
options.InitProcessOptions = processOptions;

if (m_executable.empty())
{
options.InitProcessOptions.Executable = nullptr;
}

// TODO: Support volumes, ports, flags, shm size, etc.
wil::com_ptr<IWSLAContainer> container;
THROW_IF_FAILED(Session.CreateContainer(&options, &container));

return RunningWSLAContainer{std::move(container), std::move(m_fds)};
}
44 changes: 44 additions & 0 deletions src/windows/common/WSLAContainerLauncher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "WSLAprocessLauncher.h"

namespace wsl::windows::common {

class RunningWSLAContainer
{
public:
NON_COPYABLE(RunningWSLAContainer);
DEFAULT_MOVABLE(RunningWSLAContainer);
RunningWSLAContainer(wil::com_ptr<IWSLAContainer>&& Container, std::vector<WSLA_PROCESS_FD>&& fds);
IWSLAContainer& Get();

WSLA_CONTAINER_STATE State();
ClientRunningWSLAProcess GetInitProcess();

private:
wil::com_ptr<IWSLAContainer> m_container;
std::vector<WSLA_PROCESS_FD> m_fds;
};

class WSLAContainerLauncher : public WSLAProcessLauncher
{
public:
NON_COPYABLE(WSLAContainerLauncher);
NON_MOVABLE(WSLAContainerLauncher);

WSLAContainerLauncher(
const std::string& Image,
const std::string& Name,
const std::string& EntryPoint = "",
const std::vector<std::string>& Arguments = {},
const std::vector<std::string>& Environment = {},
ProcessFlags Flags = ProcessFlags::Stdout | ProcessFlags::Stderr);

void AddVolume(const std::string& HostPath, const std::string& ContainerPath, bool ReadOnly);
void AddPort(uint16_t WindowsPort, uint16_t ContainerPort, int Family);

RunningWSLAContainer Launch(IWSLASession& Session);

private:
std::string m_image;
std::string m_name;
};
} // namespace wsl::windows::common
2 changes: 1 addition & 1 deletion src/windows/common/WSLAProcessLauncher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ ClientRunningWSLAProcess WSLAProcessLauncher::Launch(IWSLASession& Session)
THROW_HR_MSG(hresult, "Failed to launch process: %hs (commandline: %hs). Errno = %i", m_executable.c_str(), commandLine.c_str(), error);
}

return process.value();
return std::move(process.value());
}

std::tuple<HRESULT, int, std::optional<ClientRunningWSLAProcess>> WSLAProcessLauncher::LaunchNoThrow(IWSLASession& Session)
Expand Down
7 changes: 6 additions & 1 deletion src/windows/common/WSLAProcessLauncher.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class RunningWSLAProcess
};

RunningWSLAProcess(std::vector<WSLA_PROCESS_FD>&& fds);
NON_COPYABLE(RunningWSLAProcess);
DEFAULT_MOVABLE(RunningWSLAProcess);

ProcessResult WaitAndCaptureOutput(DWORD TimeoutMs = INFINITE, std::vector<std::unique_ptr<relay::OverlappedIOHandle>>&& ExtraHandles = {});
virtual wil::unique_handle GetStdHandle(int Index) = 0;
virtual wil::unique_event GetExitEvent() = 0;
Expand All @@ -57,6 +60,9 @@ class RunningWSLAProcess
class ClientRunningWSLAProcess : public RunningWSLAProcess
{
public:
NON_COPYABLE(ClientRunningWSLAProcess);
DEFAULT_MOVABLE(ClientRunningWSLAProcess);

ClientRunningWSLAProcess(wil::com_ptr<IWSLAProcess>&& process, std::vector<WSLA_PROCESS_FD>&& fds);
wil::unique_handle GetStdHandle(int Index) override;
wil::unique_event GetExitEvent() override;
Expand All @@ -68,7 +74,6 @@ class ClientRunningWSLAProcess : public RunningWSLAProcess
private:
wil::com_ptr<IWSLAProcess> m_process;
};

class WSLAProcessLauncher
{
public:
Expand Down
73 changes: 56 additions & 17 deletions src/windows/common/WslClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1549,7 +1549,9 @@ int WslaShell(_In_ std::wstring_view commandLine)
settings.BootTimeoutMs = 30000;
settings.NetworkingMode = WSLANetworkingModeNAT;
std::wstring containerRootVhd;
std::string containerImage;
bool help = false;
std::wstring debugShell;

ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);
parser.AddArgument(vhd, L"--vhd");
Expand All @@ -1559,6 +1561,8 @@ int WslaShell(_In_ std::wstring_view commandLine)
parser.AddArgument(Integer(settings.CpuCount), L"--cpu");
parser.AddArgument(Utf8String(fsType), L"--fstype");
parser.AddArgument(containerRootVhd, L"--container-vhd");
parser.AddArgument(Utf8String(containerImage), L"--image");
parser.AddArgument(debugShell, L"--debug-shell");
parser.AddArgument(help, L"--help");
parser.Parse();

Expand Down Expand Up @@ -1594,24 +1598,59 @@ int WslaShell(_In_ std::wstring_view commandLine)
wil::com_ptr<IWSLASession> session;
settings.RootVhd = vhd.c_str();
settings.RootVhdType = fsType.c_str();
THROW_IF_FAILED(userSession->CreateSession(&sessionSettings, &settings, &session));
THROW_IF_FAILED(session->GetVirtualMachine(&virtualMachine));

wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());
if (!debugShell.empty())
{
THROW_IF_FAILED(userSession->OpenSessionByName(debugShell.c_str(), &session));
}
else
{
THROW_IF_FAILED(userSession->CreateSession(&sessionSettings, &settings, &session));
THROW_IF_FAILED(session->GetVirtualMachine(&virtualMachine));

if (!containerRootVhd.empty())
wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());

if (!containerRootVhd.empty())
{
wsl::windows::common::WSLAProcessLauncher initProcessLauncher{shell, {shell, "/etc/lsw-init.sh"}};
auto initProcess = initProcessLauncher.Launch(*session);
THROW_HR_IF(E_FAIL, initProcess.WaitAndCaptureOutput().Code != 0);
}
}

std::optional<wil::com_ptr<IWSLAContainer>> container;
std::optional<wsl::windows::common::ClientRunningWSLAProcess> process;

if (containerImage.empty())
{
wsl::windows::common::WSLAProcessLauncher initProcessLauncher{shell, {shell, "/etc/lsw-init.sh"}};
auto initProcess = initProcessLauncher.Launch(*session);
THROW_HR_IF(E_FAIL, initProcess.WaitAndCaptureOutput().Code != 0);
wsl::windows::common::WSLAProcessLauncher launcher{shell, {shell}, {"TERM=xterm-256color"}, ProcessFlags::None};
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 0, .Type = WSLAFdTypeTerminalInput});
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 1, .Type = WSLAFdTypeTerminalOutput});
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 2, .Type = WSLAFdTypeTerminalControl});

process = launcher.Launch(*session);
}
else
{
std::vector<WSLA_PROCESS_FD> fds{
WSLA_PROCESS_FD{.Fd = 0, .Type = WSLAFdTypeTerminalInput},
WSLA_PROCESS_FD{.Fd = 1, .Type = WSLAFdTypeTerminalOutput},
WSLA_PROCESS_FD{.Fd = 2, .Type = WSLAFdTypeTerminalControl},
};

wsl::windows::common::WSLAProcessLauncher launcher{shell, {shell}, {"TERM=xterm-256color"}, ProcessFlags::None};
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 0, .Type = WSLAFdTypeTerminalInput});
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 1, .Type = WSLAFdTypeTerminalOutput});
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 2, .Type = WSLAFdTypeTerminalControl});
WSLA_CONTAINER_OPTIONS containerOptions{};
containerOptions.Image = containerImage.c_str();
containerOptions.Name = "test-container";
containerOptions.InitProcessOptions.Fds = fds.data();
containerOptions.InitProcessOptions.FdsCount = static_cast<DWORD>(fds.size());

auto process = launcher.Launch(*session);
container.emplace();
THROW_IF_FAILED(session->CreateContainer(&containerOptions, &container.value()));

wil::com_ptr<IWSLAProcess> initProcess;
THROW_IF_FAILED((*container)->GetInitProcess(&initProcess));
process.emplace(std::move(initProcess), std::move(fds));
}

// Configure console for interactive usage.

Expand Down Expand Up @@ -1641,7 +1680,7 @@ int WslaShell(_In_ std::wstring_view commandLine)
auto exitEvent = wil::unique_event(wil::EventOptions::ManualReset);

wsl::shared::SocketChannel controlChannel{
wil::unique_socket{(SOCKET)process.GetStdHandle(2).release()}, "TerminalControl", exitEvent.get()};
wil::unique_socket{(SOCKET)process->GetStdHandle(2).release()}, "TerminalControl", exitEvent.get()};

std::thread inputThread([&]() {
auto updateTerminal = [&controlChannel, &Stdout]() {
Expand All @@ -1657,7 +1696,7 @@ int WslaShell(_In_ std::wstring_view commandLine)
controlChannel.SendMessage(message);
};

wsl::windows::common::relay::StandardInputRelay(Stdin, process.GetStdHandle(0).get(), updateTerminal, exitEvent.get());
wsl::windows::common::relay::StandardInputRelay(Stdin, process->GetStdHandle(0).get(), updateTerminal, exitEvent.get());
});

auto joinThread = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
Expand All @@ -1666,12 +1705,12 @@ int WslaShell(_In_ std::wstring_view commandLine)
});

// Relay the contents of the pipe to stdout.
wsl::windows::common::relay::InterruptableRelay(process.GetStdHandle(1).get(), Stdout);
wsl::windows::common::relay::InterruptableRelay(process->GetStdHandle(1).get(), Stdout);
}

process.GetExitEvent().wait();
process->GetExitEvent().wait();

auto [code, signalled] = process.GetExitState();
auto [code, signalled] = process->GetExitState();
wprintf(L"%hs exited with: %i%hs", shell.c_str(), code, signalled ? " (signalled)" : "");

return code;
Expand Down
6 changes: 4 additions & 2 deletions src/windows/common/WslCoreFilesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ wil::unique_hfile wsl::core::filesystem::CreateFile(

void wsl::core::filesystem::CreateVhd(_In_ LPCWSTR target, _In_ ULONGLONG maximumSize, _In_ PSID userSid, _In_ BOOL sparse, _In_ BOOL fixed)
{
WI_ASSERT(wsl::windows::common::string::IsPathComponentEqual(
std::filesystem::path{target}.extension().native(), windows::common::wslutil::c_vhdxFileExtension));
THROW_HR_IF(
E_INVALIDARG,
!wsl::windows::common::string::IsPathComponentEqual(
std::filesystem::path{target}.extension().native(), windows::common::wslutil::c_vhdxFileExtension));

// Disable creation of sparse VHDs while data corruption is being debugged.
if (sparse)
Expand Down
3 changes: 2 additions & 1 deletion src/windows/common/wslutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ static const std::map<HRESULT, LPCWSTR> g_commonErrors{
X_WIN32(ERROR_OPERATION_ABORTED),
X_WIN32(WSAECONNREFUSED),
X_WIN32(ERROR_BAD_PATHNAME),
X(WININET_E_TIMEOUT)};
X(WININET_E_TIMEOUT),
X_WIN32(ERROR_INVALID_SID)};

#undef X

Expand Down
2 changes: 1 addition & 1 deletion src/windows/wslaservice/exe/ServiceProcessLauncher.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ServiceRunningProcess : public common::RunningWSLAProcess
{
public:
NON_COPYABLE(ServiceRunningProcess);
NON_MOVABLE(ServiceRunningProcess);
DEFAULT_MOVABLE(ServiceRunningProcess);

ServiceRunningProcess(const Microsoft::WRL::ComPtr<WSLAProcess>& process, std::vector<WSLA_PROCESS_FD>&& fds);
wil::unique_handle GetStdHandle(int Index) override;
Expand Down
Loading