Skip to content

Commit 2a41fe2

Browse files
OneBluePooja TrivediptrivediCopilot
authored
Initial implementation for CreateContainer() (#13791)
* Implement CreateContainer method * Build fixes * Implement CreateContainer method * Update src/windows/wslaservice/exe/WSLAContainer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix nerdctl host networking parameter * Prototype CreateContainer() * Format * Format * Start writing tests * Fix various issues * Add new files * Test cleanup * Test cleanup * Prepare for PR * Fix ARM build * Add copyright header * Update cmakelists.txt * Use ifdefs * Format * ifdef ARM64 * Install the test .vhd in the MSI * Update the stdin test * Format * Update src/windows/wslaservice/exe/WSLAContainer.cpp Co-authored-by: Pooja Trivedi <poojatrivedi@gmail.com> * Update src/windows/wslaservice/inc/wslaservice.idl Co-authored-by: Pooja Trivedi <poojatrivedi@gmail.com> * Update test/windows/WSLATests.cpp Co-authored-by: Pooja Trivedi <poojatrivedi@gmail.com> * Update src/windows/common/WSLAContainerLauncher.cpp Co-authored-by: Pooja Trivedi <poojatrivedi@gmail.com> * Apply PR feedback * Format --------- Co-authored-by: Pooja Trivedi <trivedipooja@microsoft.com> Co-authored-by: Pooja Trivedi <poojatrivedi@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 2ab516e commit 2a41fe2

21 files changed

+555
-35
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ if (DEFINED WSL_DEV_BINARY_PATH) # Development shortcut to make the package smal
381381
WSL_GPU_LIB_PATH="${WSL_DEV_BINARY_PATH}/lib")
382382
endif()
383383

384-
if (NOT DEFINED OFFICIAL_BUILD AND ${TARGET_PLATFORM} STREQUAL "x64")
384+
if (NOT OFFICIAL_BUILD AND ${TARGET_PLATFORM} STREQUAL "x64")
385385
add_compile_definitions(WSLA_TEST_DISTRO_PATH="${WSLA_TEST_DISTRO_SOURCE_DIR}/wslatestrootfs.vhd")
386386
endif()
387387

msipackage/package.wix.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
<File Id="system.vhd" Source="${WSLG_SOURCE_DIR}/${TARGET_PLATFORM}/system.vhd"/>
4141
<?endif?>
4242

43+
44+
<!-- Temporary runtime VHD. TODO: Update once the final VHD is available. -->
45+
<?if "${WSL_DEV_BINARY_PATH}" = "" AND "${TARGET_PLATFORM}" = "x64" ?>
46+
<File Id="wslarootfs.vhd" Name="wslarootfs.vhd" Source="${WSLA_TEST_DISTRO_SOURCE_DIR}/wslatestrootfs.vhd"/>
47+
<?endif?>
48+
4349
<!-- Installation folder -->
4450
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\MSI">
4551
<RegistryValue Name="InstallLocation" Value="[INSTALLDIR]" Type="string" />

src/shared/inc/defs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ Module Name:
3030
Type(Type&&) = delete; \
3131
Type& operator=(Type&&) = delete;
3232

33+
#define DEFAULT_MOVABLE(Type) \
34+
Type(Type&&) = default; \
35+
Type& operator=(Type&&) = default;
36+
3337
namespace wsl::shared {
3438

3539
inline constexpr std::uint32_t VersionMajor = WSL_PACKAGE_VERSION_MAJOR;

src/windows/common/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ set(SOURCES
3434
SubProcess.cpp
3535
svccomm.cpp
3636
svccommio.cpp
37+
WSLAContainerLauncher.cpp
3738
VirtioNetworking.cpp
3839
WSLAProcessLauncher.cpp
3940
WslClient.cpp
@@ -111,6 +112,7 @@ set(HEADERS
111112
SubProcess.h
112113
svccomm.hpp
113114
svccommio.hpp
115+
WSLAContainerLauncher.h
114116
VirtioNetworking.h
115117
WSLAProcessLauncher.h
116118
WslClient.h
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*++
2+
3+
Copyright (c) Microsoft. All rights reserved.
4+
5+
Module Name:
6+
7+
WSLAContainerLauncher.cpp
8+
9+
Abstract:
10+
11+
This file contains the implementation for WSLAContainerLauncher.
12+
13+
--*/
14+
#include "WSLAContainerLauncher.h"
15+
16+
using wsl::windows::common::ClientRunningWSLAProcess;
17+
using wsl::windows::common::RunningWSLAContainer;
18+
using wsl::windows::common::WSLAContainerLauncher;
19+
20+
RunningWSLAContainer::RunningWSLAContainer(wil::com_ptr<IWSLAContainer>&& Container, std::vector<WSLA_PROCESS_FD>&& fds) :
21+
m_container(std::move(Container)), m_fds(std::move(fds))
22+
{
23+
}
24+
25+
IWSLAContainer& RunningWSLAContainer::Get()
26+
{
27+
return *m_container;
28+
}
29+
30+
WSLA_CONTAINER_STATE RunningWSLAContainer::State()
31+
{
32+
WSLA_CONTAINER_STATE state{};
33+
THROW_IF_FAILED(m_container->GetState(&state));
34+
return state;
35+
}
36+
37+
ClientRunningWSLAProcess RunningWSLAContainer::GetInitProcess()
38+
{
39+
wil::com_ptr<IWSLAProcess> process;
40+
THROW_IF_FAILED(m_container->GetInitProcess(&process));
41+
42+
return ClientRunningWSLAProcess{std::move(process), std::move(m_fds)};
43+
}
44+
45+
WSLAContainerLauncher::WSLAContainerLauncher(
46+
const std::string& Image,
47+
const std::string& Name,
48+
const std::string& EntryPoint,
49+
const std::vector<std::string>& Arguments,
50+
const std::vector<std::string>& Environment,
51+
ProcessFlags Flags) :
52+
WSLAProcessLauncher(EntryPoint, Arguments, Environment, Flags), m_image(Image), m_name(Name)
53+
{
54+
}
55+
56+
RunningWSLAContainer WSLAContainerLauncher::Launch(IWSLASession& Session)
57+
{
58+
WSLA_CONTAINER_OPTIONS options{};
59+
options.Image = m_image.c_str();
60+
options.Name = m_name.c_str();
61+
auto [processOptions, commandLinePtrs, environmentPtrs] = CreateProcessOptions();
62+
options.InitProcessOptions = processOptions;
63+
64+
if (m_executable.empty())
65+
{
66+
options.InitProcessOptions.Executable = nullptr;
67+
}
68+
69+
// TODO: Support volumes, ports, flags, shm size, container networking mode, etc.
70+
wil::com_ptr<IWSLAContainer> container;
71+
THROW_IF_FAILED(Session.CreateContainer(&options, &container));
72+
73+
return RunningWSLAContainer{std::move(container), std::move(m_fds)};
74+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*++
2+
3+
Copyright (c) Microsoft. All rights reserved.
4+
5+
Module Name:
6+
7+
WSLAContainerLauncher.h
8+
9+
Abstract:
10+
11+
This file contains the definition for WSLAContainerLauncher.
12+
13+
--*/
14+
15+
#pragma once
16+
#include "WSLAProcessLauncher.h"
17+
18+
namespace wsl::windows::common {
19+
20+
class RunningWSLAContainer
21+
{
22+
public:
23+
NON_COPYABLE(RunningWSLAContainer);
24+
DEFAULT_MOVABLE(RunningWSLAContainer);
25+
RunningWSLAContainer(wil::com_ptr<IWSLAContainer>&& Container, std::vector<WSLA_PROCESS_FD>&& fds);
26+
IWSLAContainer& Get();
27+
28+
WSLA_CONTAINER_STATE State();
29+
ClientRunningWSLAProcess GetInitProcess();
30+
31+
private:
32+
wil::com_ptr<IWSLAContainer> m_container;
33+
std::vector<WSLA_PROCESS_FD> m_fds;
34+
};
35+
36+
class WSLAContainerLauncher : public WSLAProcessLauncher
37+
{
38+
public:
39+
NON_COPYABLE(WSLAContainerLauncher);
40+
NON_MOVABLE(WSLAContainerLauncher);
41+
42+
WSLAContainerLauncher(
43+
const std::string& Image,
44+
const std::string& Name,
45+
const std::string& EntryPoint = "",
46+
const std::vector<std::string>& Arguments = {},
47+
const std::vector<std::string>& Environment = {},
48+
ProcessFlags Flags = ProcessFlags::Stdout | ProcessFlags::Stderr);
49+
50+
void AddVolume(const std::string& HostPath, const std::string& ContainerPath, bool ReadOnly);
51+
void AddPort(uint16_t WindowsPort, uint16_t ContainerPort, int Family);
52+
53+
RunningWSLAContainer Launch(IWSLASession& Session);
54+
55+
private:
56+
std::string m_image;
57+
std::string m_name;
58+
};
59+
} // namespace wsl::windows::common

src/windows/common/WSLAProcessLauncher.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ ClientRunningWSLAProcess WSLAProcessLauncher::Launch(IWSLASession& Session)
158158
THROW_HR_MSG(hresult, "Failed to launch process: %hs (commandline: %hs). Errno = %i", m_executable.c_str(), commandLine.c_str(), error);
159159
}
160160

161-
return process.value();
161+
return std::move(process.value());
162162
}
163163

164164
std::tuple<HRESULT, int, std::optional<ClientRunningWSLAProcess>> WSLAProcessLauncher::LaunchNoThrow(IWSLASession& Session)

src/windows/common/WSLAProcessLauncher.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class RunningWSLAProcess
4343
};
4444

4545
RunningWSLAProcess(std::vector<WSLA_PROCESS_FD>&& fds);
46+
NON_COPYABLE(RunningWSLAProcess);
47+
DEFAULT_MOVABLE(RunningWSLAProcess);
48+
4649
ProcessResult WaitAndCaptureOutput(DWORD TimeoutMs = INFINITE, std::vector<std::unique_ptr<relay::OverlappedIOHandle>>&& ExtraHandles = {});
4750
virtual wil::unique_handle GetStdHandle(int Index) = 0;
4851
virtual wil::unique_event GetExitEvent() = 0;
@@ -57,6 +60,9 @@ class RunningWSLAProcess
5760
class ClientRunningWSLAProcess : public RunningWSLAProcess
5861
{
5962
public:
63+
NON_COPYABLE(ClientRunningWSLAProcess);
64+
DEFAULT_MOVABLE(ClientRunningWSLAProcess);
65+
6066
ClientRunningWSLAProcess(wil::com_ptr<IWSLAProcess>&& process, std::vector<WSLA_PROCESS_FD>&& fds);
6167
wil::unique_handle GetStdHandle(int Index) override;
6268
wil::unique_event GetExitEvent() override;
@@ -68,7 +74,6 @@ class ClientRunningWSLAProcess : public RunningWSLAProcess
6874
private:
6975
wil::com_ptr<IWSLAProcess> m_process;
7076
};
71-
7277
class WSLAProcessLauncher
7378
{
7479
public:

src/windows/common/WslClient.cpp

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,7 +1549,9 @@ int WslaShell(_In_ std::wstring_view commandLine)
15491549
settings.BootTimeoutMs = 30000;
15501550
settings.NetworkingMode = WSLANetworkingModeNAT;
15511551
std::wstring containerRootVhd;
1552+
std::string containerImage;
15521553
bool help = false;
1554+
std::wstring debugShell;
15531555

15541556
ArgumentParser parser(std::wstring{commandLine}, WSL_BINARY_NAME);
15551557
parser.AddArgument(vhd, L"--vhd");
@@ -1560,6 +1562,8 @@ int WslaShell(_In_ std::wstring_view commandLine)
15601562
parser.AddArgument(Integer(reinterpret_cast<int&>(settings.NetworkingMode)), L"--networking-mode");
15611563
parser.AddArgument(Utf8String(fsType), L"--fstype");
15621564
parser.AddArgument(containerRootVhd, L"--container-vhd");
1565+
parser.AddArgument(Utf8String(containerImage), L"--image");
1566+
parser.AddArgument(debugShell, L"--debug-shell");
15631567
parser.AddArgument(help, L"--help");
15641568
parser.Parse();
15651569

@@ -1605,18 +1609,28 @@ int WslaShell(_In_ std::wstring_view commandLine)
16051609
wil::com_ptr<IWSLASession> session;
16061610
settings.RootVhd = vhd.c_str();
16071611
settings.RootVhdType = fsType.c_str();
1608-
THROW_IF_FAILED(userSession->CreateSession(&sessionSettings, &settings, &session));
1609-
THROW_IF_FAILED(session->GetVirtualMachine(&virtualMachine));
16101612

1611-
wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());
1612-
1613-
if (!containerRootVhd.empty())
1613+
if (!debugShell.empty())
1614+
{
1615+
THROW_IF_FAILED(userSession->OpenSessionByName(debugShell.c_str(), &session));
1616+
}
1617+
else
16141618
{
1615-
wsl::windows::common::WSLAProcessLauncher initProcessLauncher{shell, {shell, "/etc/lsw-init.sh"}};
1616-
auto initProcess = initProcessLauncher.Launch(*session);
1617-
THROW_HR_IF(E_FAIL, initProcess.WaitAndCaptureOutput().Code != 0);
1619+
THROW_IF_FAILED(userSession->CreateSession(&sessionSettings, &settings, &session));
1620+
THROW_IF_FAILED(session->GetVirtualMachine(&virtualMachine));
1621+
1622+
wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());
1623+
1624+
if (!containerRootVhd.empty())
1625+
{
1626+
wsl::windows::common::WSLAProcessLauncher initProcessLauncher{shell, {shell, "/etc/lsw-init.sh"}};
1627+
auto initProcess = initProcessLauncher.Launch(*session);
1628+
THROW_HR_IF(E_FAIL, initProcess.WaitAndCaptureOutput().Code != 0);
1629+
}
16181630
}
16191631

1632+
std::optional<wil::com_ptr<IWSLAContainer>> container;
1633+
std::optional<wsl::windows::common::ClientRunningWSLAProcess> process;
16201634
// Get the terminal size.
16211635
HANDLE Stdout = GetStdHandle(STD_OUTPUT_HANDLE);
16221636
HANDLE Stdin = GetStdHandle(STD_INPUT_HANDLE);
@@ -1631,7 +1645,36 @@ int WslaShell(_In_ std::wstring_view commandLine)
16311645
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 2, .Type = WSLAFdTypeTerminalControl});
16321646
launcher.SetTtySize(Info.srWindow.Bottom - Info.srWindow.Top + 1, Info.srWindow.Right - Info.srWindow.Left + 1);
16331647

1634-
auto process = launcher.Launch(*session);
1648+
if (containerImage.empty())
1649+
{
1650+
wsl::windows::common::WSLAProcessLauncher launcher{shell, {shell}, {"TERM=xterm-256color"}, ProcessFlags::None};
1651+
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 0, .Type = WSLAFdTypeTerminalInput});
1652+
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 1, .Type = WSLAFdTypeTerminalOutput});
1653+
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 2, .Type = WSLAFdTypeTerminalControl});
1654+
1655+
process = launcher.Launch(*session);
1656+
}
1657+
else
1658+
{
1659+
std::vector<WSLA_PROCESS_FD> fds{
1660+
WSLA_PROCESS_FD{.Fd = 0, .Type = WSLAFdTypeTerminalInput},
1661+
WSLA_PROCESS_FD{.Fd = 1, .Type = WSLAFdTypeTerminalOutput},
1662+
WSLA_PROCESS_FD{.Fd = 2, .Type = WSLAFdTypeTerminalControl},
1663+
};
1664+
1665+
WSLA_CONTAINER_OPTIONS containerOptions{};
1666+
containerOptions.Image = containerImage.c_str();
1667+
containerOptions.Name = "test-container";
1668+
containerOptions.InitProcessOptions.Fds = fds.data();
1669+
containerOptions.InitProcessOptions.FdsCount = static_cast<DWORD>(fds.size());
1670+
1671+
container.emplace();
1672+
THROW_IF_FAILED(session->CreateContainer(&containerOptions, &container.value()));
1673+
1674+
wil::com_ptr<IWSLAProcess> initProcess;
1675+
THROW_IF_FAILED((*container)->GetInitProcess(&initProcess));
1676+
process.emplace(std::move(initProcess), std::move(fds));
1677+
}
16351678

16361679
// Configure console for interactive usage.
16371680
{
@@ -1658,7 +1701,7 @@ int WslaShell(_In_ std::wstring_view commandLine)
16581701
auto exitEvent = wil::unique_event(wil::EventOptions::ManualReset);
16591702

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

16631706
std::thread inputThread([&]() {
16641707
auto updateTerminal = [&controlChannel, &Stdout]() {
@@ -1674,7 +1717,7 @@ int WslaShell(_In_ std::wstring_view commandLine)
16741717
controlChannel.SendMessage(message);
16751718
};
16761719

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

16801723
auto joinThread = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
@@ -1683,12 +1726,12 @@ int WslaShell(_In_ std::wstring_view commandLine)
16831726
});
16841727

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

1689-
process.GetExitEvent().wait();
1732+
process->GetExitEvent().wait();
16901733

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

16941737
return code;

src/windows/common/WslCoreFilesystem.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ wil::unique_hfile wsl::core::filesystem::CreateFile(
2929

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

3537
// Disable creation of sparse VHDs while data corruption is being debugged.
3638
if (sparse)

0 commit comments

Comments
 (0)