Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
6245ef2
Save state
OneBlue Dec 23, 2025
9da8be2
Save state
OneBlue Dec 23, 2025
d64175b
Save state
OneBlue Dec 23, 2025
30cbabe
Save state
OneBlue Dec 23, 2025
6520087
Interactive shell POC
OneBlue Dec 24, 2025
5b9accd
Save state
OneBlue Dec 26, 2025
74650e5
New process class
OneBlue Dec 26, 2025
364ad34
Wire event tracker
OneBlue Dec 27, 2025
99fd48d
Save state
OneBlue Dec 29, 2025
9825236
Implement non interactive IO relay
OneBlue Dec 30, 2025
f0bd0be
Implement cmd & entrypoint
OneBlue Dec 30, 2025
9a391f1
CreateContainer passing
OneBlue Dec 30, 2025
e1d58e9
ContainerState passing
OneBlue Dec 30, 2025
6d4b941
Format
OneBlue Dec 30, 2025
064dfe9
PortBindings test passing
OneBlue Dec 30, 2025
8dff9c2
ContainerNetwork passing
OneBlue Dec 31, 2025
88c8524
Move test logic to Inspect()
OneBlue Dec 31, 2025
ae4a2c7
PullImage passing
OneBlue Dec 31, 2025
01f6dad
Format
OneBlue Dec 31, 2025
fe0218d
ImportImage passing
OneBlue Dec 31, 2025
ea6116b
Save state
OneBlue Dec 31, 2025
18b7104
Properly implement Import
OneBlue Jan 2, 2026
f927ba4
LoadImage passing
OneBlue Jan 2, 2026
cdd557b
Add docker_schema
OneBlue Jan 2, 2026
c0865cd
Implement exec
OneBlue Jan 3, 2026
05707e8
Format
OneBlue Jan 3, 2026
cfbb0a4
Exec passing
OneBlue Jan 5, 2026
488ce17
Save state
OneBlue Jan 5, 2026
eb9b2da
Prepare for review
OneBlue Jan 5, 2026
5a7de49
Add copyright headers
OneBlue Jan 6, 2026
27244f8
Format
OneBlue Jan 6, 2026
24d054f
Apply PR feedback
OneBlue Jan 6, 2026
d448a71
Apply PR feedback
OneBlue Jan 7, 2026
63201df
Merge remote-tracking branch 'origin/feature/wsl-for-apps' into user/…
OneBlue Jan 7, 2026
d1ef50f
Cleanup diff
OneBlue Jan 7, 2026
ff87084
Apply PR feedback
OneBlue Jan 7, 2026
aad3c02
Skip pmem test
OneBlue Jan 7, 2026
b014574
Revert to cgroupv2
OneBlue Jan 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ FetchContent_Declare(nlohmannjson
FetchContent_MakeAvailable(nlohmannjson)
FetchContent_GetProperties(nlohmannjson SOURCE_DIR NLOHMAN_JSON_SOURCE_DIR)

set(BOOST_VERSION "1.90.0")
set(BOOST_TARBALL "boost_${BOOST_VERSION}")
string(REPLACE "." "_" BOOST_TARBALL "${BOOST_TARBALL}")

FetchContent_Declare(
boost_headers
URL https://archives.boost.io/release/${BOOST_VERSION}/source/${BOOST_TARBALL}.tar.gz
URL_HASH SHA256=5e93d582aff26868d581a52ae78c7d8edf3f3064742c6e77901a1f18a437eea9
)

FetchContent_MakeAvailable(boost_headers)


# Import modules
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
find_package(IDL REQUIRED)
Expand Down Expand Up @@ -383,6 +396,7 @@ endif()

# Common include paths
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/wil/include)
include_directories(${boost_headers_SOURCE_DIR})
include_directories(${WSLDEPS_SOURCE_DIR}/include)
include_directories(${WSLDEPS_SOURCE_DIR}/include/Windows)
include_directories(${WSLDEPS_SOURCE_DIR}/include/schemas)
Expand Down
11 changes: 11 additions & 0 deletions cgmanifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@
"hash": "sha1:918692098b11db61aff23684ab04f375e4a68f69"
}
}
},
{
"component": {
"type": "other",
"other": {
"name": "boost",
"version": "1.90.0",
"downloadUrl": "https://archives.boost.io/release/1.90.0/source/boost_1_90_0.tar.gz",
"hash": "sha1:44500f8d6b279ec314a4cdce1290ddc30f530ed7"
}
}
}
]
}
6 changes: 6 additions & 0 deletions msipackage/package.wix.in
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@
<RegistryValue Value="WSLAProcess" Type="string" />
</RegistryKey>

<!-- WSLAContainerProcess -->
<RegistryKey Root="HKCR" Key="CLSID\{3A5DB29D-6D1D-4619-B89D-578EB34C8E52}">
<RegistryValue Name="AppId" Value="{E9B79997-57E3-4201-AECC-6A464E530DD2}" Type="string" />
<RegistryValue Value="WSLAContainerProcess" Type="string" />
</RegistryKey>

<!-- WSLAUserSession -->
<RegistryKey Root="HKCR" Key="CLSID\{a9b7a1b9-0671-405c-95f1-e0612cb4ce8f}">
<RegistryValue Name="AppId" Value="{E9B79997-57E3-4201-AECC-6A464E530DD2}" Type="string" />
Expand Down
2 changes: 1 addition & 1 deletion packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
<package id="Microsoft.WSL.TestData" version="0.1.0" />
<package id="Microsoft.WSL.TestDistro" version="2.5.7-47" />
<package id="Microsoft.WSL.WslaRootfs" version="0.1.2" />
<package id="Microsoft.WSL.WslaRootfs" version="0.1.3" />
<package id="Microsoft.WSLg" version="1.0.71" />
<package id="Microsoft.Xaml.Behaviors.WinUI.Managed" version="3.0.0" />
<package id="StrawberryPerl" version="5.32.1.1" />
Expand Down
102 changes: 101 additions & 1 deletion src/linux/init/WSLAInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,106 @@ void HandleMessageImpl(wsl::shared::SocketChannel& Channel, const WSLA_OPEN& Mes
result = 0;
}

void HandleMessageImpl(wsl::shared::SocketChannel& Channel, const WSLA_UNIX_CONNECT& Message, const gsl::span<gsl::byte>& Buffer)
{
// Make sure to close the channel since no more messages can be processed after this.
auto closeChannel = wil::scope_exit([&]() { Channel.Close(); });

int result = -1;
auto sendResult = wil::scope_exit([&]() { Channel.SendResultMessage(result); });

const auto* path = wsl::shared::string::FromSpan(Buffer, Message.PathOffset);
THROW_ERRNO_IF(EINVAL, path == nullptr);

wil::unique_fd socket;

try
{
socket = UtilConnectUnix(path);
result = 0;
}
catch (...)
{
result = wil::ResultFromCaughtException();
}

if (result != 0)
{
return;
}

sendResult.reset();

// Relay data between the two sockets.
pollfd pollDescriptors[2];
pollDescriptors[0].fd = socket.get();
pollDescriptors[0].events = POLLIN;
pollDescriptors[1].fd = Channel.Socket();
pollDescriptors[1].events = POLLIN;

std::vector<gsl::byte> relayBuffer;
while (true)
{
auto result = poll(pollDescriptors, COUNT_OF(pollDescriptors), -1);
THROW_LAST_ERROR_IF(result < 0);

if (pollDescriptors[0].revents & (POLLIN | POLLHUP | POLLERR))
{
auto bytesRead = UtilReadBuffer(pollDescriptors[0].fd, relayBuffer);
if (bytesRead < 0)
{
LOG_ERROR("read failed {}", errno);
break;
}
else if (bytesRead == 0)
{
// Unix socket has been closed.
pollDescriptors[0].fd = -1;
break;
}
else
{
auto bytesWritten = write(Channel.Socket(), relayBuffer.data(), bytesRead);
if (bytesWritten < 0)
{
LOG_ERROR("write failed {}", errno);
break;
}
}
}

if (pollDescriptors[1].revents & (POLLIN | POLLHUP | POLLERR))
{
auto bytesRead = UtilReadBuffer(pollDescriptors[1].fd, relayBuffer);
if (bytesRead < 0)
{
LOG_ERROR("read failed {}", errno);
break;
}
else if (bytesRead == 0)
{
// hvsocket has been closed.
pollDescriptors[1].fd = -1;

// Shutdown the write side of the socket. This is required so docker knows when stdin is in EOF for instance.
if (shutdown(socket.get(), SHUT_WR) < 0)
{
LOG_ERROR("shutdown({}, SHUT_WR) failed {}", socket.get(), errno);
}
}
else
{
auto bytesWritten = write(socket.get(), relayBuffer.data(), bytesRead);
if (bytesWritten < 0)
{
LOG_ERROR("write failed {}", errno);
break;
}
}
}
}
}

void HandleMessageImpl(wsl::shared::SocketChannel& Channel, const WSLA_TTY_RELAY& Message, const gsl::span<gsl::byte>&)
{
THROW_LAST_ERROR_IF(fcntl(Message.TtyMaster, F_SETFL, O_NONBLOCK) < 0);
Expand Down Expand Up @@ -766,7 +866,7 @@ void ProcessMessage(wsl::shared::SocketChannel& Channel, LX_MESSAGE_TYPE Type, c
{
try
{
HandleMessage<WSLA_GET_DISK, WSLA_MOUNT, WSLA_EXEC, WSLA_FORK, WSLA_CONNECT, WSLA_WAITPID, WSLA_SIGNAL, WSLA_TTY_RELAY, WSLA_PORT_RELAY, WSLA_OPEN, WSLA_UNMOUNT, WSLA_DETACH, WSLA_ACCEPT, WSLA_WATCH_PROCESSES>(
HandleMessage<WSLA_GET_DISK, WSLA_MOUNT, WSLA_EXEC, WSLA_FORK, WSLA_CONNECT, WSLA_WAITPID, WSLA_SIGNAL, WSLA_TTY_RELAY, WSLA_PORT_RELAY, WSLA_OPEN, WSLA_UNMOUNT, WSLA_DETACH, WSLA_ACCEPT, WSLA_WATCH_PROCESSES, WSLA_UNIX_CONNECT>(
Channel, Type, Buffer);
}
catch (...)
Expand Down
1 change: 1 addition & 0 deletions src/shared/inc/JsonUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ T FromJson(const char* Value)

#ifdef WIN32

WSL_LOG("InvalidJson", TraceLoggingValue(Value, "JsonValue"), TraceLoggingValue(e.what(), "Error"));
THROW_HR_WITH_USER_ERROR(WSL_E_INVALID_JSON, wsl::shared::Localization::MessageInvalidJson(e.what()));

#else
Expand Down
5 changes: 5 additions & 0 deletions src/shared/inc/SocketChannel.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@ class SocketChannel
return m_socket.get();
}

auto Release()
{
return std::move(m_socket);
}

bool Connected() const
{
return m_socket.get() >= 0;
Expand Down
18 changes: 17 additions & 1 deletion src/shared/inc/lxinitshared.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,8 @@ typedef enum _LX_MESSAGE_TYPE
LxMessageWSLADetach,
LxMessageWSLATerminalChanged,
LxMessageWSLAWatchProcesses,
LxMessageWSLAProcessExited
LxMessageWSLAProcessExited,
LxMessageWSLAUnixConnect,
} LX_MESSAGE_TYPE,
*PLX_MESSAGE_TYPE;

Expand Down Expand Up @@ -512,6 +513,7 @@ inline auto ToString(LX_MESSAGE_TYPE messageType)
X(LxMessageWSLATerminalChanged)
X(LxMessageWSLAWatchProcesses)
X(LxMessageWSLAProcessExited)
X(LxMessageWSLAUnixConnect)

default:
return "<unexpected LX_MESSAGE_TYPE>";
Expand Down Expand Up @@ -1884,6 +1886,20 @@ struct WSLA_PROCESS_EXITED
PRETTY_PRINT(FIELD(Header), FIELD(Pid), FIELD(Code), FIELD(Signaled));
};

struct WSLA_UNIX_CONNECT
{
static inline auto Type = LxMessageWSLAUnixConnect;
using TResponse = RESULT_MESSAGE<int32_t>;

DECLARE_MESSAGE_CTOR(WSLA_UNIX_CONNECT);

MESSAGE_HEADER Header;
unsigned int PathOffset;
char Buffer[];

PRETTY_PRINT(FIELD(Header), STRING_FIELD(PathOffset));
};

typedef struct _LX_MINI_INIT_IMPORT_RESULT
{
static inline auto Type = LxMiniInitMessageImportResult;
Expand Down
1 change: 1 addition & 0 deletions src/windows/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ set(SOURCES

set(HEADERS
../../../generated/Localization.h
../inc/docker_schema.h
../inc/lxssbusclient.h
../inc/lxssclient.h
../inc/LxssDynamicFunction.h
Expand Down
6 changes: 5 additions & 1 deletion src/windows/common/Dmesg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ std::pair<std::wstring, std::thread> DmesgCollector::StartDmesgThread(InputSourc
ProcessInput(Source, validBuffer);
}
}
CATCH_LOG()
catch (...)
{
auto error = wil::ResultFromCaughtException();
LOG_HR_IF(error, error != E_ABORT); // E_ABORT is expected during shutdown.
}
});

return std::pair{std::move(pipeName), std::move(workerThread)};
Expand Down
21 changes: 21 additions & 0 deletions src/windows/common/WSLAContainerLauncher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ void wsl::windows::common::WSLAContainerLauncher::AddVolume(const std::wstring&
}

std::pair<HRESULT, std::optional<RunningWSLAContainer>> WSLAContainerLauncher::LaunchNoThrow(IWSLASession& Session)
{
auto [result, container] = CreateNoThrow(Session);
if (FAILED(result))
{
return std::make_pair(result, std::optional<RunningWSLAContainer>{});
}

result = container.value().Get().Start();

return std::make_pair(result, std::move(container));
}

std::pair<HRESULT, std::optional<RunningWSLAContainer>> WSLAContainerLauncher::CreateNoThrow(IWSLASession& Session)
{
WSLA_CONTAINER_OPTIONS options{};
options.Image = m_image.c_str();
Expand Down Expand Up @@ -114,4 +127,12 @@ RunningWSLAContainer WSLAContainerLauncher::Launch(IWSLASession& Session)
THROW_IF_FAILED(result);

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

wsl::windows::common::docker_schema::InspectContainer wsl::windows::common::RunningWSLAContainer::Inspect()
{
wil::unique_cotaskmem_ansistring output;
THROW_IF_FAILED(m_container->Inspect(&output));

return wsl::shared::FromJson<docker_schema::InspectContainer>(output.get());
}
4 changes: 4 additions & 0 deletions src/windows/common/WSLAContainerLauncher.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Module Name:

#pragma once
#include "WSLAProcessLauncher.h"
#include "docker_schema.h"

namespace wsl::windows::common {

Expand All @@ -28,6 +29,7 @@ class RunningWSLAContainer
WSLA_CONTAINER_STATE State();
ClientRunningWSLAProcess GetInitProcess();
void Reset();
docker_schema::InspectContainer Inspect();

private:
wil::com_ptr<IWSLAContainer> m_container;
Expand All @@ -52,6 +54,8 @@ class WSLAContainerLauncher : private WSLAProcessLauncher
void AddVolume(const std::wstring& HostPath, const std::string& ContainerPath, bool ReadOnly);
void AddPort(uint16_t WindowsPort, uint16_t ContainerPort, int Family);

std::pair<HRESULT, std::optional<RunningWSLAContainer>> CreateNoThrow(IWSLASession& Session);

RunningWSLAContainer Launch(IWSLASession& Session);
std::pair<HRESULT, std::optional<RunningWSLAContainer>> LaunchNoThrow(IWSLASession& Session);

Expand Down
Loading