Skip to content
6 changes: 3 additions & 3 deletions src/windows/WslcSDK/IOCallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ bool IOCallback::HasIOCallback(const WslcContainerProcessIOCallbackOptions& opti

wil::unique_handle IOCallback::GetIOHandle(IWSLCProcess* process, WslcProcessIOHandle ioHandle)
{
ULONG ulongHandle = 0;
wsl::windows::common::wslutil::COMOutputHandle handle;

THROW_IF_FAILED(process->GetStdHandle(static_cast<ULONG>(static_cast<std::underlying_type_t<WslcProcessIOHandle>>(ioHandle)), &ulongHandle));
THROW_IF_FAILED(process->GetStdHandle(static_cast<WSLCFD>(static_cast<std::underlying_type_t<WslcProcessIOHandle>>(ioHandle)), &handle));

return wil::unique_handle{ULongToHandle(ulongHandle)};
return handle.Release();
}
5 changes: 3 additions & 2 deletions src/windows/WslcSDK/wslcsdk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Module Name:
#include "wslutil.h"

using namespace std::string_view_literals;
using namespace wsl::windows::common::wslutil;

namespace {
constexpr uint32_t s_DefaultCPUCount = 2;
Expand Down Expand Up @@ -1129,7 +1130,7 @@ static HRESULT WslcImportSessionImageImpl(
auto progressCallback = ProgressCallback::CreateIf(options);

return errorInfoWrapper.CaptureResult(internalSession->session->ImportImage(
HandleToULong(imageFile.Handle()), imageName, progressCallback.get(), imageFile.Length()));
ToCOMInputHandle(imageFile.Handle()), imageName, progressCallback.get(), imageFile.Length()));
}

STDAPI WslcImportSessionImage(
Expand Down Expand Up @@ -1167,7 +1168,7 @@ static HRESULT WslcLoadSessionImageImpl(
auto progressCallback = ProgressCallback::CreateIf(options);

return errorInfoWrapper.CaptureResult(
internalSession->session->LoadImage(HandleToULong(imageFile.Handle()), progressCallback.get(), imageFile.Length()));
internalSession->session->LoadImage(ToCOMInputHandle(imageFile.Handle()), progressCallback.get(), imageFile.Length()));
}

STDAPI WslcLoadSessionImage(
Expand Down
6 changes: 3 additions & 3 deletions src/windows/common/WSLCProcessLauncher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,10 @@ ClientRunningWSLCProcess::ClientRunningWSLCProcess(wil::com_ptr<IWSLCProcess>&&

wil::unique_handle ClientRunningWSLCProcess::GetStdHandle(int Index)
{
ULONG handle{};
THROW_IF_FAILED_MSG(m_process->GetStdHandle(Index, &handle), "Failed to get handle: %i", Index);
wslutil::COMOutputHandle handle;
THROW_IF_FAILED_MSG(m_process->GetStdHandle(static_cast<WSLCFD>(Index), &handle), "Failed to get handle: %i", Index);

return wil::unique_handle{ULongToHandle(handle)};
return handle.Release();
}

wil::unique_event ClientRunningWSLCProcess::GetExitEvent()
Expand Down
62 changes: 62 additions & 0 deletions src/windows/common/wslutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,23 @@ wsl::windows::common::ErrorStrings wsl::windows::common::wslutil::ErrorToString(
return errorStrings;
}

[[nodiscard]] HANDLE wsl::windows::common::wslutil::FromCOMInputHandle(WSLCHandle Handle)
{
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE), Handle.Handle.File == nullptr || Handle.Handle.File == INVALID_HANDLE_VALUE);

switch (Handle.Type)
{
case WSLCHandleTypeFile:
return Handle.Handle.File;
case WSLCHandleTypePipe:
return Handle.Handle.Pipe;
case WSLCHandleTypeSocket:
return Handle.Handle.Socket;
default:
THROW_HR_MSG(E_UNEXPECTED, "Unsupported handle type: %d", Handle.Type);
}
}

std::wstring wsl::windows::common::wslutil::ConstructPipePath(std::wstring_view PipeName)
{
return c_pipePrefix + std::wstring(PipeName);
Expand Down Expand Up @@ -1311,6 +1328,51 @@ wil::unique_hlocal_string wsl::windows::common::wslutil::SidToString(_In_ PSID U
return sid;
}

WSLCHandle wsl::windows::common::wslutil::ToCOMOutputHandle(HANDLE Handle, DWORD Access)
{
wil::unique_handle duplicatedHandle{DuplicateHandle(Handle, Access)};

// N.B. COM closes the handle when returning an out parameter.
// The return value of this method should always be passed to a COM out parameter.
auto comHandle = ToCOMInputHandle(duplicatedHandle.release());

return comHandle;
}

WSLCHandle wsl::windows::common::wslutil::ToCOMInputHandle(HANDLE Handle)
{
auto type = GetFileType(Handle);
if (type == FILE_TYPE_PIPE)
{
int socketType{};
int len = sizeof(socketType);

// N.B. FILE_TYPE_PIPE can describe a pipe, a named pipe, or a socket.
// Check for a named pipe first, since getsockopt() can return success for a named pipe.

if (GetNamedPipeInfo(Handle, nullptr, nullptr, nullptr, nullptr))
{
return WSLCHandle{.Type = WSLCHandleTypePipe, .Handle = {.Pipe = Handle}};
}
else if (getsockopt(reinterpret_cast<SOCKET>(Handle), SOL_SOCKET, SO_TYPE, reinterpret_cast<char*>(&socketType), &len) == 0)
{
return WSLCHandle{.Type = WSLCHandleTypeSocket, .Handle = {.Socket = Handle}};
}
else
{
return WSLCHandle{.Type = WSLCHandleTypePipe, .Handle = {.Pipe = Handle}};
Comment thread
benhillis marked this conversation as resolved.
}
}
else if (type == FILE_TYPE_DISK)
{
return WSLCHandle{.Type = WSLCHandleTypeFile, .Handle = {.File = Handle}};
}
else
{
THROW_HR_MSG(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), "Unsupported handle type: %d", type);
}
}

winrt::Windows::Management::Deployment::PackageVolume wsl::windows::common::wslutil::GetSystemVolume()
try
{
Expand Down
53 changes: 53 additions & 0 deletions src/windows/common/wslutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,54 @@ struct COMErrorInfo
wil::unique_bstr Source;
};

static_assert(sizeof(WSLCHandle::Handle) == sizeof(HANDLE));
static_assert(sizeof(FILE_HANDLE) == sizeof(HANDLE));
static_assert(sizeof(PIPE_HANDLE) == sizeof(HANDLE));
static_assert(sizeof(SOCKET_HANDLE) == sizeof(HANDLE));

struct COMOutputHandle : public WSLCHandle
{
NON_COPYABLE(COMOutputHandle);
NON_MOVABLE(COMOutputHandle);
COMOutputHandle()
{
ZeroMemory(&Handle, sizeof(Handle));
Type = WSLCHandleTypeUnknown;
}

~COMOutputHandle()
{
Reset();
}

void Reset() noexcept
{
if (!Empty())
{
LOG_IF_WIN32_BOOL_FALSE(CloseHandle(Handle.File));
Handle.File = nullptr;
}
}

[[nodiscard]] wil::unique_handle Release() noexcept
{
wil::unique_handle handle(Handle.File);
Handle.File = nullptr;

return handle;
}

HANDLE Get() const noexcept
{
return Handle.File;
}

bool Empty() const noexcept
{
return Handle.File == nullptr || Handle.File == INVALID_HANDLE_VALUE;
}
Comment thread
OneBlue marked this conversation as resolved.
};

struct PruneResult
{
NON_COPYABLE(PruneResult);
Expand Down Expand Up @@ -170,6 +218,8 @@ std::wstring ErrorCodeToString(HRESULT Error);

ErrorStrings ErrorToString(const Error& error);

[[nodiscard]] HANDLE FromCOMInputHandle(WSLCHandle Handle);

std::filesystem::path GetBasePath();

std::optional<COMErrorInfo> GetCOMErrorInfo();
Expand Down Expand Up @@ -267,6 +317,9 @@ void SetThreadDescription(LPCWSTR Name);

wil::unique_hlocal_string SidToString(_In_ PSID Sid);

WSLCHandle ToCOMInputHandle(HANDLE Handle);
[[nodiscard]] WSLCHandle ToCOMOutputHandle(HANDLE Handle, DWORD Access);

winrt::Windows::Management::Deployment::PackageVolume GetSystemVolume();

} // namespace wsl::windows::common::wslutil
1 change: 1 addition & 0 deletions src/windows/service/exe/WSLCSessionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ void WSLCSessionManagerImpl::CreateSession(const WSLCSessionSettings* Settings,
TraceLoggingValue(Settings->DisplayName, "Name"),
TraceLoggingValue(stopWatch.ElapsedMilliseconds(), "CreationTimeMs"),
TraceLoggingValue(creationResult, "Result"),
TraceLoggingValue(tokenInfo.Elevated, "Elevated"),
TraceLoggingValue(static_cast<uint32_t>(Flags), "Flags"));

THROW_IF_FAILED_MSG(creationResult, "Failed to create session: %ls", Settings->DisplayName);
Expand Down
45 changes: 37 additions & 8 deletions src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,35 @@ typedef struct _WSLCContainerEntry
WSLCContainerState State;
} WSLCContainerEntry;

typedef [system_handle(sh_file)] HANDLE FILE_HANDLE;
typedef [system_handle(sh_pipe)] HANDLE PIPE_HANDLE;
typedef [system_handle(sh_socket)] HANDLE SOCKET_HANDLE;

typedef enum _WSLCHandleType
{
WSLCHandleTypeUnknown = 0,
Comment thread
OneBlue marked this conversation as resolved.
WSLCHandleTypeFile = 1,
WSLCHandleTypePipe = 2,
WSLCHandleTypeSocket = 3
} WSLCHandleType;

typedef struct _WSLCHandle
{
WSLCHandleType Type;

[switch_type(WSLCHandleType), switch_is(Type)]
union
{
[case(WSLCHandleTypeFile)]
FILE_HANDLE File;
Comment thread
OneBlue marked this conversation as resolved.
[case(WSLCHandleTypePipe)]
PIPE_HANDLE Pipe;
[case(WSLCHandleTypeSocket)]
SOCKET_HANDLE Socket;
[default];
} Handle;
} WSLCHandle;

typedef enum _WSLCProcessState
{
WslcProcessStateUnknown = 0,
Expand All @@ -307,7 +336,7 @@ interface IWSLCProcess : IUnknown
{
HRESULT Signal([in] int Signal);
HRESULT GetExitEvent([out, system_handle(sh_event)] HANDLE* EventHandle);
HRESULT GetStdHandle([in] ULONG Index, [out] ULONG* Handle);
HRESULT GetStdHandle([in] WSLCFD Fd, [out] WSLCHandle* Handle);
HRESULT GetFlags([out] WSLCProcessFlags* Flags);
HRESULT GetPid([out] int* Pid);
HRESULT GetState([out] WSLCProcessState* State, [out] int* Code);
Expand Down Expand Up @@ -417,16 +446,16 @@ typedef enum _WSLCDeleteFlags
]
interface IWSLCContainer : IUnknown
{
HRESULT Attach([in, unique] LPCSTR DetachKeys, [out] ULONG* StdIn, [out] ULONG* StdOut, [out] ULONG* StdErr);
HRESULT Attach([in, unique] LPCSTR DetachKeys, [out] WSLCHandle* StdIn, [out] WSLCHandle* StdOut, [out] WSLCHandle* StdErr);
HRESULT Stop([in] WSLCSignal Signal, [in] LONG TimeoutSeconds);
HRESULT Start([in] WSLCContainerStartFlags Flags, [in, unique] LPCSTR DetachKeys);
HRESULT Delete([in] WSLCDeleteFlags Flags);
HRESULT Export([in] ULONG TarHandle);
HRESULT Export([in] WSLCHandle TarHandle);
Comment thread
OneBlue marked this conversation as resolved.
HRESULT GetState([out] WSLCContainerState* State);
HRESULT GetInitProcess([out] IWSLCProcess** Process);
HRESULT Exec([in, ref] const WSLCProcessOptions* Options, [in, unique] LPCSTR DetachKeys, [out] IWSLCProcess** Process);
HRESULT Inspect([out] LPSTR* Output);
HRESULT Logs([in] WSLCLogsFlags Flags, [out] ULONG* Stdout, [out] ULONG* Stderr, [in] ULONGLONG Since, [in] ULONGLONG Until, [in] ULONGLONG Tail);
HRESULT Logs([in] WSLCLogsFlags Flags, [out] WSLCHandle* Stdout, [out] WSLCHandle* Stderr, [in] ULONGLONG Since, [in] ULONGLONG Until, [in] ULONGLONG Tail);
HRESULT GetId([out, string] WSLCContainerId Id);
HRESULT GetName([out, string] LPSTR* Name);
HRESULT GetLabels([out, size_is(, *Count)] WSLCLabelInformation** Labels, [out] ULONG* Count);
Expand Down Expand Up @@ -462,7 +491,7 @@ typedef struct _WSLCDeleteImageOptions
typedef struct _WSLCBuildImageOptions
{
LPCWSTR ContextPath;
ULONG DockerfileHandle;
WSLCHandle DockerfileHandle;
WSLCStringArray Tags;
WSLCStringArray BuildArgs; // KEY=VALUE pairs passed as --build-arg to docker.
BOOL Verbose; // Show all build progress including internal steps and all statuses.
Expand Down Expand Up @@ -529,9 +558,9 @@ interface IWSLCSession : IUnknown
// Image management.
HRESULT PullImage([in] LPCSTR Image, [in, unique] LPCSTR RegistryAuthenticationInformation, [in, unique] IProgressCallback* ProgressCallback);
HRESULT BuildImage([in] const WSLCBuildImageOptions* Options, [in, unique] IProgressCallback* ProgressCallback, [in, unique, system_handle(sh_event)] HANDLE CancelEvent);
HRESULT LoadImage([in] ULONG ImageHandle, [in, unique] IProgressCallback* ProgressCallback, [in] ULONGLONG ContentLength);
HRESULT ImportImage([in] ULONG ImageHandle, [in] LPCSTR ImageName, [in, unique] IProgressCallback* ProgressCallback, [in] ULONGLONG ContentLength);
HRESULT SaveImage([in] ULONG OutputHandle, [in] LPCSTR ImageNameOrID, [in, unique] IProgressCallback * ProgressCallback, [in, unique, system_handle(sh_event)] HANDLE CancelEvent);
HRESULT LoadImage([in] WSLCHandle ImageHandle, [in, unique] IProgressCallback* ProgressCallback, [in] ULONGLONG ContentLength);
HRESULT ImportImage([in] WSLCHandle ImageHandle, [in] LPCSTR ImageName, [in, unique] IProgressCallback* ProgressCallback, [in] ULONGLONG ContentLength);
HRESULT SaveImage([in] WSLCHandle OutputHandle, [in] LPCSTR ImageNameOrID, [in, unique] IProgressCallback * ProgressCallback, [in, unique, system_handle(sh_event)] HANDLE CancelEvent);
HRESULT ListImages([in, unique] const WSLCListImageOptions* Options, [out, size_is(, *Count)] WSLCImageInformation** Images, [out] ULONG* Count);
HRESULT DeleteImage([in] const WSLCDeleteImageOptions* Options, [out, size_is(, *Count)] WSLCDeletedImageInformation** DeletedImages, [out] ULONG* Count);
HRESULT TagImage([in] const WSLCTagImageOptions* Options);
Expand Down
38 changes: 17 additions & 21 deletions src/windows/wslc/services/ContainerService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace wsl::windows::wslc::services {
using wsl::windows::common::ClientRunningWSLCProcess;
using wsl::windows::common::wslc_schema::InspectContainer;
using wsl::windows::common::wslutil::PrintMessage;
using namespace wsl::windows::common::wslutil;
using namespace wsl::windows::wslc::models;
using namespace std::chrono_literals;

Expand Down Expand Up @@ -198,26 +199,22 @@ int ContainerService::Attach(Session& session, const std::string& id)

ClientRunningWSLCProcess runningProcess(std::move(process), processFlags);

ULONG stdinLogsHandle = 0;
ULONG stdoutLogsHandle = 0;
ULONG stderrLogsHandle = 0;
THROW_IF_FAILED(container->Attach(nullptr, &stdinLogsHandle, &stdoutLogsHandle, &stderrLogsHandle));
COMOutputHandle stdinLogs{};
COMOutputHandle stdoutLogs{};
COMOutputHandle stderrLogs{};
THROW_IF_FAILED(container->Attach(nullptr, &stdinLogs, &stdoutLogs, &stderrLogs));

wil::unique_handle stdinLogs(ULongToHandle(stdinLogsHandle));
wil::unique_handle stdoutLogs(ULongToHandle(stdoutLogsHandle));
wil::unique_handle stderrLogs(ULongToHandle(stderrLogsHandle));

if (stdoutLogs)
if (!stdoutLogs.Empty())
{
// Non-TTY process - relay separate stdout/stderr streams
WI_ASSERT(!!stderrLogs);
ConsoleService::RelayNonTtyProcess(std::move(stdinLogs), std::move(stdoutLogs), std::move(stderrLogs));
WI_ASSERT(!stderrLogs.Empty());
ConsoleService::RelayNonTtyProcess(stdinLogs.Release(), stdoutLogs.Release(), stderrLogs.Release());
}
else
{
// TTY process - relay using interactive TTY handling
WI_ASSERT(!stderrLogs);
if (!ConsoleService::RelayInteractiveTty(runningProcess, stdinLogs.get(), true))
WI_ASSERT(stderrLogs.Empty());
if (!ConsoleService::RelayInteractiveTty(runningProcess, stdinLogs.Release().get(), true))
Comment thread
OneBlue marked this conversation as resolved.
{
wsl::windows::common::wslutil::PrintMessage(L"[detached]", stderr);
return 0; // Exit early if user detached
Expand Down Expand Up @@ -386,22 +383,21 @@ void ContainerService::Logs(Session& session, const std::string& id, bool follow
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));

ULONG stdoutLogsHandle = 0;
ULONG stderrLogsHandle = 0;
COMOutputHandle stdoutHandle;
COMOutputHandle stderrHandle;
WSLCLogsFlags flags = WSLCLogsFlagsNone;
WI_SetFlagIf(flags, WSLCLogsFlagsFollow, follow);

THROW_IF_FAILED(container->Logs(flags, &stdoutLogsHandle, &stderrLogsHandle, 0, 0, 0));
wil::unique_handle stdoutLogs(ULongToHandle(stdoutLogsHandle));
wil::unique_handle stderrLogs(ULongToHandle(stderrLogsHandle));
THROW_IF_FAILED(container->Logs(flags, &stdoutHandle, &stderrHandle, 0, 0, 0));

wsl::windows::common::relay::MultiHandleWait io;
io.AddHandle(std::make_unique<wsl::windows::common::relay::RelayHandle<wsl::windows::common::relay::ReadHandle>>(
std::move(stdoutLogs), GetStdHandle(STD_OUTPUT_HANDLE)));
if (stderrLogs) // This handle is only used for non-tty processes.
stdoutHandle.Release(), GetStdHandle(STD_OUTPUT_HANDLE)));

if (!stderrHandle.Empty()) // This handle is only used for non-tty processes.
{
io.AddHandle(std::make_unique<wsl::windows::common::relay::RelayHandle<wsl::windows::common::relay::ReadHandle>>(
std::move(stderrLogs), GetStdHandle(STD_ERROR_HANDLE)));
stderrHandle.Release(), GetStdHandle(STD_ERROR_HANDLE)));
}

// TODO: Handle ctrl-c.
Expand Down
Loading