Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 29 additions & 14 deletions src/windows/common/ConsoleState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,33 @@ namespace wsl::windows::common {

ConsoleState::ConsoleState()
{
// Ensure console state is restored if the constructor throws.
auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { RestoreConsoleState(); });

m_InputHandle.reset(
CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));

if (!m_InputHandle)
{
LOG_LAST_ERROR_MSG("CreateFileW(CONIN$) failed");
}

m_OutputHandle.reset(
CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));

if (!m_OutputHandle)
{
LOG_LAST_ERROR_MSG("CreateFileW(CONOUT$) failed");
}
}

void ConsoleState::SetInteractiveMode()
{
if (m_interactiveModeConfigured)
{
return;
}

// Ensure console state is restored if this method throws.
auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() { RestoreConsoleState(); });

if (m_InputHandle)
{
m_SavedInputCodePage = GetConsoleCP();
Expand All @@ -85,13 +106,6 @@ ConsoleState::ConsoleState()
ChangeConsoleMode(m_InputHandle.get(), NewMode);
m_SavedInputMode = mode;
}
else
{
LOG_LAST_ERROR_MSG("CreateFileW(CONIN$) failed");
}

m_OutputHandle.reset(
CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));

if (m_OutputHandle)
{
Expand All @@ -107,11 +121,8 @@ ConsoleState::ConsoleState()
ChangeConsoleMode(m_OutputHandle.get(), NewMode);
m_SavedOutputMode = mode;
}
else
{
LOG_LAST_ERROR_MSG("CreateFileW(CONOUT$) failed");
}

m_interactiveModeConfigured = true;
cleanup.release();
}

Expand All @@ -127,11 +138,13 @@ void ConsoleState::RestoreConsoleState()
if (m_SavedInputCodePage.has_value())
{
LOG_IF_WIN32_BOOL_FALSE(SetConsoleCP(m_SavedInputCodePage.value()));
m_SavedInputCodePage.reset();
}

if (m_SavedInputMode.has_value())
{
TrySetConsoleMode(m_InputHandle.get(), m_SavedInputMode.value());
m_SavedInputMode.reset();
}
}

Expand All @@ -140,11 +153,13 @@ void ConsoleState::RestoreConsoleState()
if (m_SavedOutputCodePage.has_value())
{
LOG_IF_WIN32_BOOL_FALSE(SetConsoleOutputCP(m_SavedOutputCodePage.value()));
m_SavedOutputCodePage.reset();
}

if (m_SavedOutputMode.has_value())
{
TrySetConsoleMode(m_OutputHandle.get(), m_SavedOutputMode.value());
m_SavedOutputMode.reset();
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/windows/common/ConsoleState.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ class ConsoleState
ConsoleState& operator=(ConsoleState&&) = delete;

COORD GetWindowSize() const;
void SetInteractiveMode();

private:
void RestoreConsoleState();

wil::unique_hfile m_InputHandle;
wil::unique_hfile m_OutputHandle;
bool m_interactiveModeConfigured{false};
std::optional<DWORD> m_SavedInputMode{};
std::optional<UINT> m_SavedInputCodePage{};
std::optional<DWORD> m_SavedOutputMode{};
Expand Down
6 changes: 5 additions & 1 deletion src/windows/common/WSLCContainerLauncher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,11 @@ std::pair<HRESULT, std::optional<RunningWSLCContainer>> WSLCContainerLauncher::L
return std::make_pair(result, std::optional<RunningWSLCContainer>{});
}

result = container.value().Get().Start(Flags, nullptr, WarningCallback);
WSLCProcessStartOptions startOptions{};
startOptions.TtyRows = m_rows;
startOptions.TtyColumns = m_columns;

result = container.value().Get().Start(Flags, &startOptions, WarningCallback);

return std::make_pair(result, std::move(container));
}
Expand Down
1 change: 1 addition & 0 deletions src/windows/common/WSLCContainerLauncher.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class WSLCContainerLauncher : private WSLCProcessLauncher
void AddUlimit(const std::string& Name, std::int64_t Soft, std::int64_t Hard);

using WSLCProcessLauncher::FormatResult;
using WSLCProcessLauncher::SetTtySize;
using WSLCProcessLauncher::SetUser;
using WSLCProcessLauncher::SetWorkingDirectory;

Expand Down
11 changes: 7 additions & 4 deletions src/windows/common/WSLCProcessLauncher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ std::tuple<WSLCProcessOptions, std::vector<const char*>, std::vector<const char*
WSLCProcessOptions options{};
options.CommandLine = {.Values = commandLine.data(), .Count = static_cast<DWORD>(commandLine.size())};
options.Environment = {.Values = environment.data(), .Count = static_cast<DWORD>(environment.size())};
options.TtyColumns = m_columns;
options.TtyRows = m_rows;
options.Flags = m_flags;

if (!m_workingDirectory.empty())
Expand Down Expand Up @@ -182,7 +180,7 @@ std::tuple<HRESULT, std::optional<ClientRunningWSLCProcess>, int> WSLCProcessLau

wil::com_ptr<IWSLCProcess> process;
int error = -1;
auto result = Session.CreateRootNamespaceProcess(m_executable.c_str(), &options, &process, &error);
auto result = Session.CreateRootNamespaceProcess(m_executable.c_str(), &options, m_rows, m_columns, &process, &error);
if (FAILED(result))
{
return std::make_tuple(result, std::optional<ClientRunningWSLCProcess>(), error);
Expand All @@ -198,7 +196,12 @@ std::tuple<HRESULT, std::optional<ClientRunningWSLCProcess>> WSLCProcessLauncher
auto [options, commandLine, env] = CreateProcessOptions();

wil::com_ptr<IWSLCProcess> process;
auto result = Container.Exec(&options, m_detachKeys.has_value() ? m_detachKeys->c_str() : nullptr, &process);
WSLCProcessStartOptions startOptions{};
startOptions.TtyRows = m_rows;
startOptions.TtyColumns = m_columns;
startOptions.DetachKeys = m_detachKeys.has_value() ? m_detachKeys->c_str() : nullptr;

auto result = Container.Exec(&options, &startOptions, &process);
if (FAILED(result))
{
return std::make_pair(result, std::optional<ClientRunningWSLCProcess>());
Expand Down
6 changes: 3 additions & 3 deletions src/windows/common/WSLCProcessLauncher.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ class WSLCProcessLauncher
std::optional<std::string> m_detachKeys;
std::vector<std::string> m_arguments;
std::vector<std::string> m_environment;
DWORD m_rows = 0;
DWORD m_columns = 0;
DWORD m_rows = 24;
DWORD m_columns = 80;
};

} // namespace wsl::windows::common
} // namespace wsl::windows::common
1 change: 1 addition & 0 deletions src/windows/common/WslClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1501,6 +1501,7 @@ int RunDebugShell()

// Create a thread to relay stdin to the pipe.
wsl::windows::common::ConsoleState console;
console.SetInteractiveMode();
auto exitEvent = wil::unique_event(wil::EventOptions::ManualReset);
std::thread inputThread([&]() {
wsl::windows::common::relay::StandardInputRelay(GetStdHandle(STD_INPUT_HANDLE), pipe.get(), []() {}, exitEvent.get());
Expand Down
1 change: 1 addition & 0 deletions src/windows/common/svccomm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ wsl::windows::common::SvcComm::LaunchProcess(
//

ConsoleState Io;
Io.SetInteractiveMode();
COORD WindowSize = Io.GetWindowSize();
ULONG Flags = LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE;
if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_USE_SYSTEM_DISTRO))
Expand Down
2 changes: 1 addition & 1 deletion src/windows/service/exe/PluginManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ try

wil::com_ptr<IWSLCProcess> process;
int errnoValue = 0;
auto result = session->CreateRootNamespaceProcess(Executable, &options, &process, &errnoValue);
auto result = session->CreateRootNamespaceProcess(Executable, &options, 0, 0, &process, &errnoValue);

if (Errno != nullptr)
{
Expand Down
13 changes: 9 additions & 4 deletions src/windows/service/inc/wslc.idl
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,14 @@ typedef struct _WSLCProcessOptions
WSLCStringArray CommandLine;
WSLCStringArray Environment;
WSLCProcessFlags Flags;
} WSLCProcessOptions;

typedef struct _WSLCProcessStartOptions
{
ULONG TtyRows; // Only needed when tty fd's are passed.
ULONG TtyColumns;
} WSLCProcessOptions;
[unique, string] LPCSTR DetachKeys;
} WSLCProcessStartOptions;

typedef struct _WSLCNamedVolume
{
Expand Down Expand Up @@ -566,12 +571,12 @@ interface IWSLCContainer : IUnknown
{
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, [in, unique] IWarningCallback* WarningCallback);
HRESULT Start([in] WSLCContainerStartFlags Flags, [in, unique] const WSLCProcessStartOptions* StartOptions, [in, unique] IWarningCallback* WarningCallback);
HRESULT Delete([in] WSLCDeleteFlags Flags);
HRESULT Export([in] WSLCHandle TarHandle);
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 Exec([in, ref] const WSLCProcessOptions* Options, [in, unique] const WSLCProcessStartOptions* StartOptions, [out] IWSLCProcess** Process);
HRESULT Inspect([out] LPSTR* Output);
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);
Expand Down Expand Up @@ -752,7 +757,7 @@ interface IWSLCSession : IUnknown
HRESULT PruneContainers([in, unique, size_is(FiltersCount)] const WSLCFilter* Filters, [in] ULONG FiltersCount, [out] WSLCPruneContainersResults* Result);

// Create a process at the VM level. This is meant for debugging.
HRESULT CreateRootNamespaceProcess([in, ref] LPCSTR Executable, [in, ref] const WSLCProcessOptions* Options, [out] IWSLCProcess** Process, [out] int* Errno);
HRESULT CreateRootNamespaceProcess([in, ref] LPCSTR Executable, [in, ref] const WSLCProcessOptions* Options, [in] ULONG TtyRows, [in] ULONG TtyColumns, [out] IWSLCProcess** Process, [out] int* Errno);

Comment thread
OneBlue marked this conversation as resolved.
// TODO: an OpenProcess() method can be added later if needed.

Expand Down
10 changes: 5 additions & 5 deletions src/windows/wslc/services/ConsoleService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ using wsl::windows::common::ClientRunningWSLCProcess;
using wsl::windows::common::io::ReadHandle;
using wsl::windows::common::io::RelayHandle;

bool ConsoleService::RelayInteractiveTty(ClientRunningWSLCProcess& Process, HANDLE Tty, bool triggerRefresh)
bool ConsoleService::RelayInteractiveTty(wsl::windows::common::ConsoleState& console, ClientRunningWSLCProcess& Process, HANDLE Tty, bool triggerRefresh)
{
// Configure console for interactive usage.
wsl::windows::common::ConsoleState console;
// Configure the console for interactive usage.
console.SetInteractiveMode();

if (triggerRefresh)
{
Expand Down Expand Up @@ -108,11 +108,11 @@ void ConsoleService::RelayNonTtyProcess(wil::unique_handle&& Stdin, wil::unique_
io.Run({});
}

int ConsoleService::AttachToCurrentConsole(wsl::windows::common::ClientRunningWSLCProcess&& process)
int ConsoleService::AttachToCurrentConsole(wsl::windows::common::ConsoleState& console, wsl::windows::common::ClientRunningWSLCProcess&& process)
{
if (WI_IsFlagSet(process.Flags(), WSLCProcessFlagsTty))
{
if (!RelayInteractiveTty(process, process.GetStdHandle(WSLCFDTty).get()))
if (!RelayInteractiveTty(console, process, process.GetStdHandle(WSLCFDTty).get()))
{
wsl::windows::common::wslutil::PrintMessage(L"[detached]", stderr);
return 0;
Expand Down
6 changes: 4 additions & 2 deletions src/windows/wslc/services/ConsoleService.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ Module Name:

#include <wslc.h>
#include <WSLCContainerLauncher.h>
#include <ConsoleState.h>

namespace wsl::windows::wslc::services {
class ConsoleService
{
public:
static int AttachToCurrentConsole(wsl::windows::common::ClientRunningWSLCProcess&& process);
static bool RelayInteractiveTty(wsl::windows::common::ClientRunningWSLCProcess& process, HANDLE tty, bool triggerRefresh = false);
static int AttachToCurrentConsole(wsl::windows::common::ConsoleState& console, wsl::windows::common::ClientRunningWSLCProcess&& process);
static bool RelayInteractiveTty(
wsl::windows::common::ConsoleState& console, wsl::windows::common::ClientRunningWSLCProcess& process, HANDLE tty, bool triggerRefresh = false);
static void RelayNonTtyProcess(wil::unique_handle&& Stdin, wil::unique_handle&& Stdout, wil::unique_handle&& Stderr);
};
} // namespace wsl::windows::wslc::services
53 changes: 44 additions & 9 deletions src/windows/wslc/services/ContainerService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Module Name:
#include "WarningCallback.h"
#include <wslutil.h>
#include <WSLCProcessLauncher.h>
#include <ConsoleState.h>
#include <CommandLine.h>
#include <filesystem>
#include <unordered_map>
Expand Down Expand Up @@ -270,7 +271,8 @@ int ContainerService::Attach(Session& session, const std::string& id)
{
// TTY process - relay using interactive TTY handling
WI_ASSERT(stderrLogs.Empty());
if (!ConsoleService::RelayInteractiveTty(runningProcess, stdinLogs.Release().get(), true))
wsl::windows::common::ConsoleState console;
if (!ConsoleService::RelayInteractiveTty(console, runningProcess, stdinLogs.Release().get(), true))
{
wsl::windows::common::wslutil::PrintMessage(L"[detached]", stderr);
return 0; // Exit early if user detached
Expand Down Expand Up @@ -360,17 +362,32 @@ int ContainerService::Run(Session& session, const std::string& image, ContainerO
// Start the created container
WSLCContainerStartFlags startFlags{};
WI_SetFlagIf(startFlags, WSLCContainerStartFlagsAttach, !runOptions.Detach);
THROW_IF_FAILED(container.Start(startFlags, nullptr, warningCallback.Get())); // TODO: Error message, detach keys

const bool attach = WI_IsFlagSet(startFlags, WSLCContainerStartFlagsAttach);

std::optional<wsl::windows::common::ConsoleState> console;
WSLCProcessStartOptions startOptions{};
if (attach)
{
console.emplace();
if (runOptions.TTY)
{
const auto size = console->GetWindowSize();
startOptions.TtyRows = size.Y;
startOptions.TtyColumns = size.X;
}
}

THROW_IF_FAILED(container.Start(startFlags, &startOptions, warningCallback.Get())); // TODO: detach keys
Comment thread
OneBlue marked this conversation as resolved.

// Disable auto-delete only after successful start
runningContainer.SetDeleteOnClose(false);
cidFile.Commit(containerId);

// Handle attach if requested
if (WI_IsFlagSet(startFlags, WSLCContainerStartFlagsAttach))
if (attach)
{
ConsoleService consoleService;
return consoleService.AttachToCurrentConsole(runningContainer.GetInitProcess());
return ConsoleService::AttachToCurrentConsole(*console, runningContainer.GetInitProcess());
}

PrintMessage(L"%hs", stdout, containerId);
Expand All @@ -396,7 +413,18 @@ int ContainerService::Start(Session& session, const std::string& id, bool attach
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
WSLCContainerStartFlags flags = attach ? WSLCContainerStartFlagsAttach : WSLCContainerStartFlagsNone;
auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
THROW_IF_FAILED_EXCEPT(container->Start(flags, nullptr, warningCallback.Get()), WSLC_E_CONTAINER_IS_RUNNING);

std::optional<wsl::windows::common::ConsoleState> console;
WSLCProcessStartOptions startOptions{};
if (attach)
{
console.emplace();
const auto size = console->GetWindowSize();
startOptions.TtyRows = size.Y;
startOptions.TtyColumns = size.X;
}

THROW_IF_FAILED_EXCEPT(container->Start(flags, &startOptions, warningCallback.Get()), WSLC_E_CONTAINER_IS_RUNNING);
Comment thread
OneBlue marked this conversation as resolved.

if (!attach)
{
Expand All @@ -410,8 +438,7 @@ int ContainerService::Start(Session& session, const std::string& id, bool attach
THROW_IF_FAILED(process->GetFlags(&processFlags));
ClientRunningWSLCProcess runningProcess(std::move(process), processFlags);

ConsoleService consoleService;
return consoleService.AttachToCurrentConsole(std::move(runningProcess));
return ConsoleService::AttachToCurrentConsole(*console, std::move(runningProcess));
}

void ContainerService::Stop(Session& session, const std::string& id, StopContainerOptions options)
Expand Down Expand Up @@ -492,6 +519,14 @@ int ContainerService::Exec(Session& session, const std::string& id, ContainerOpt
WI_SetFlagIf(execFlags, WSLCProcessFlagsTty, options.TTY);

auto processLauncher = wsl::windows::common::WSLCProcessLauncher({}, options.Arguments, options.EnvironmentVariables, execFlags);

wsl::windows::common::ConsoleState console;
if (options.TTY)
{
const auto size = console.GetWindowSize();
processLauncher.SetTtySize(size.Y, size.X);
}

if (options.User.has_value())
{
auto user = options.User.value();
Expand All @@ -502,7 +537,7 @@ int ContainerService::Exec(Session& session, const std::string& id, ContainerOpt
processLauncher.SetWorkingDirectory(std::move(options.WorkingDirectory));
}

return ConsoleService::AttachToCurrentConsole(processLauncher.Launch(*container));
return ConsoleService::AttachToCurrentConsole(console, processLauncher.Launch(*container));
}

InspectContainer ContainerService::Inspect(Session& session, const std::string& id)
Expand Down
Loading
Loading