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
16 changes: 8 additions & 8 deletions src/windows/wslc/commands/RegistryCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,29 @@ using namespace wsl::shared;

namespace {

auto MaskInput()
auto MaskInput(wsl::windows::wslc::Reporter& reporter)
{
HANDLE input = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode = 0;

if ((input != INVALID_HANDLE_VALUE) && GetConsoleMode(input, &mode))
{
THROW_IF_WIN32_BOOL_FALSE(SetConsoleMode(input, mode & ~ENABLE_ECHO_INPUT));
return wil::scope_exit(std::function<void()>([input, mode] {
return wil::scope_exit(std::function<void()>([input, mode, &reporter] {
SetConsoleMode(input, mode);
std::wcerr << L'\n';
reporter.Info(L"\n");
}));
Comment on lines +36 to 39
}

return wil::scope_exit(std::function<void()>([] {}));
}

std::wstring Prompt(const std::wstring& label, bool maskInput)
std::wstring Prompt(wsl::windows::wslc::Reporter& reporter, const std::wstring& label, bool maskInput)
{
// Write without a trailing newline so the cursor stays inline (matching Docker's behavior).
std::wcerr << label;
reporter.Info(L"{}", label);

auto restoreConsole = maskInput ? MaskInput() : wil::scope_exit(std::function<void()>([] {}));
auto restoreConsole = maskInput ? MaskInput(reporter) : wil::scope_exit(std::function<void()>([] {}));

std::wstring value;
std::getline(std::wcin, value);
Expand Down Expand Up @@ -127,7 +127,7 @@ void RegistryLoginCommand::ExecuteInternal(CLIExecutionContext& context) const
// Prompt for username if not provided.
if (!context.Args.Contains(ArgType::Username))
{
context.Args.Add(ArgType::Username, Prompt(Localization::WSLCCLI_LoginUsernamePrompt(), false));
context.Args.Add(ArgType::Username, Prompt(context.Reporter, Localization::WSLCCLI_LoginUsernamePrompt(), false));
}

// Resolve password: --password, --password-stdin, or interactive prompt.
Expand All @@ -146,7 +146,7 @@ void RegistryLoginCommand::ExecuteInternal(CLIExecutionContext& context) const
}
else
{
context.Args.Add(ArgType::Password, Prompt(Localization::WSLCCLI_LoginPasswordPrompt(), true));
context.Args.Add(ArgType::Password, Prompt(context.Reporter, Localization::WSLCCLI_LoginPasswordPrompt(), true));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/windows/wslc/commands/RootCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void RootCommand::ExecuteInternal(CLIExecutionContext& context) const
{
if (context.Args.Contains(ArgType::Version))
{
VersionCommand::PrintVersion();
VersionCommand::PrintVersion(context.Reporter);
return;
}

Expand Down
2 changes: 1 addition & 1 deletion src/windows/wslc/commands/SettingsCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ std::wstring SettingsResetCommand::LongDescription() const
void SettingsResetCommand::ExecuteInternal(CLIExecutionContext& context) const
{
settings::User().Reset();
PrintMessage(Localization::WSLCCLI_SettingsResetConfirm());
context.Reporter.Output(L"{}\n", Localization::WSLCCLI_SettingsResetConfirm());
}

} // namespace wsl::windows::wslc
8 changes: 4 additions & 4 deletions src/windows/wslc/commands/VersionCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Module Name:

--*/
#include "VersionCommand.h"
#include "CLIExecutionContext.h"

using namespace wsl::shared;
using namespace wsl::windows::wslc::execution;
Expand All @@ -27,14 +28,13 @@ std::wstring VersionCommand::LongDescription() const
return Localization::WSLCCLI_VersionLongDesc();
}

void VersionCommand::PrintVersion()
void VersionCommand::PrintVersion(Reporter& reporter)
{
wsl::windows::common::wslutil::PrintMessage(std::format(L"{} {}", s_ExecutableName, WSL_PACKAGE_VERSION));
reporter.Output(L"{} {}\n", s_ExecutableName, WSL_PACKAGE_VERSION);
}

void VersionCommand::ExecuteInternal(CLIExecutionContext& context) const
{
UNREFERENCED_PARAMETER(context);
PrintVersion();
PrintVersion(context.Reporter);
}
} // namespace wsl::windows::wslc
3 changes: 2 additions & 1 deletion src/windows/wslc/commands/VersionCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ Module Name:
#include "Command.h"

namespace wsl::windows::wslc {
struct Reporter;
struct VersionCommand final : public Command
{
constexpr static std::wstring_view CommandName = L"version";
VersionCommand(const std::wstring& parent) : Command(CommandName, parent)
{
}
static void PrintVersion();
static void PrintVersion(Reporter& reporter);
std::wstring ShortDescription() const override;
std::wstring LongDescription() const override;

Expand Down
15 changes: 7 additions & 8 deletions src/windows/wslc/services/BuildImageCallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ CATCH_LOG()

void BuildImageCallback::WriteTerminal(std::wstring_view content) const
{
DWORD written;
LOG_IF_WIN32_BOOL_FALSE(WriteConsoleW(m_console, content.data(), static_cast<DWORD>(content.size()), &written, nullptr));
// Route the scrolling build display through the Reporter's Info channel (stderr) so it
// respects the global output state. Each call is one atomic write.
m_reporter.Write(Reporter::Level::Info, L"{}", content);
}
Comment on lines 47 to 52

bool BuildImageCallback::IsCancelled() const
Expand Down Expand Up @@ -103,7 +104,7 @@ try
// Skip pull progress updates when output is redirected, show only major steps
if (!isPullProgress)
{
wprintf(L"%hs", status);
m_reporter.Write(Reporter::Level::Info, L"{}", MultiByteToWide(status));
}
return S_OK;
}
Expand Down Expand Up @@ -184,9 +185,7 @@ CATCH_RETURN();

void BuildImageCallback::Redraw()
{
CONSOLE_SCREEN_BUFFER_INFO info{};
THROW_IF_WIN32_BOOL_FALSE(GetConsoleScreenBufferInfo(m_console, &info));
const int consoleWidth = std::max(0, static_cast<int>(info.srWindow.Right) - info.srWindow.Left);
const auto consoleWidth = m_reporter.GetConsoleWidth(Reporter::Level::Info);

const bool showPending = !m_pendingLine.empty();
const int pullCount = static_cast<int>(m_pullLines.size());
Expand Down Expand Up @@ -220,9 +219,9 @@ void BuildImageCallback::Redraw()

auto appendLine = [&](const std::string& line) {
auto wline = MultiByteToWide(line);
if (wline.size() > static_cast<size_t>(consoleWidth))
if (consoleWidth.has_value() && wline.size() > static_cast<size_t>(*consoleWidth))
{
wline.resize(static_cast<size_t>(consoleWidth));
wline.resize(static_cast<size_t>(*consoleWidth));
}
m_frameBuffer += std::move(wline);
m_frameBuffer += Erase::LineForward;
Expand Down
12 changes: 6 additions & 6 deletions src/windows/wslc/services/BuildImageCallback.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Module Name:
--*/
#pragma once
#include "Reporter.h"
#include "SessionService.h"
#include "VTSupport.h"
#include <deque>
Expand All @@ -23,7 +24,7 @@ class DECLSPEC_UUID("3EDD5DBF-CA6C-4CF7-923A-AD94B6A732E5") BuildImageCallback
{
public:
// The cancel event handle must remain valid for the lifetime of this callback.
BuildImageCallback(HANDLE cancelEvent, bool verbose) : m_verbose(verbose), m_cancelEvent(cancelEvent)
BuildImageCallback(Reporter& reporter, HANDLE cancelEvent, bool verbose) : m_reporter(reporter), m_verbose(verbose), m_cancelEvent(cancelEvent)
{
}
~BuildImageCallback();
Expand All @@ -37,16 +38,15 @@ class DECLSPEC_UUID("3EDD5DBF-CA6C-4CF7-923A-AD94B6A732E5") BuildImageCallback
void CollapseWindow();
void Redraw();
void RedrawIfNeeded();
// Use WriteConsoleW directly rather than wprintf: wprintf is noticeably slower for
// the per-redraw scrolling display and produces visible flicker.
// Routes terminal writes through the Reporter's Info channel so each frame is emitted
// as a single atomic write and respects the global output state.
void WriteTerminal(std::wstring_view content) const;
bool IsCancelled() const;

Reporter& m_reporter;
const bool m_verbose;
const HANDLE m_cancelEvent;
HANDLE m_console = GetStdHandle(STD_OUTPUT_HANDLE);
bool m_isConsole = wsl::windows::common::wslutil::IsConsoleHandle(m_console);
wsl::windows::common::vt::EnableVirtualTerminal m_vtMode{m_console};
bool m_isConsole = m_reporter.IsVTEnabled(Reporter::Level::Info);
std::deque<std::string> m_lines;
// Each entry already contains the trailing newline so the bytes match what's replayed.
// TODO: Track logs per step so the destructor can replay only the failing step's
Expand Down
5 changes: 3 additions & 2 deletions src/windows/wslc/services/ConsoleService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,14 @@ void ConsoleService::RelayNonTtyProcess(wil::unique_handle&& Stdin, wil::unique_
io.Run({});
}

int ConsoleService::AttachToCurrentConsole(wsl::windows::common::ConsoleState& console, wsl::windows::common::ClientRunningWSLCProcess&& process, bool triggerRefresh)
int ConsoleService::AttachToCurrentConsole(
Reporter& reporter, wsl::windows::common::ConsoleState& console, wsl::windows::common::ClientRunningWSLCProcess&& process, bool triggerRefresh)
{
if (WI_IsFlagSet(process.Flags(), WSLCProcessFlagsTty))
{
if (!RelayInteractiveTty(console, process, process.GetStdHandle(WSLCFDTty).get(), triggerRefresh))
{
windows::common::wslutil::PrintMessage(L"[detached]", stderr);
reporter.Info(L"[detached]\n");
return 0;
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/windows/wslc/services/ConsoleService.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ Module Name:
#include <wslc.h>
#include <WSLCContainerLauncher.h>
#include <ConsoleState.h>
#include "Reporter.h"

namespace wsl::windows::wslc::services {
class ConsoleService
{
public:
static int AttachToCurrentConsole(
wsl::windows::common::ConsoleState& console, wsl::windows::common::ClientRunningWSLCProcess&& process, bool triggerRefresh = false);
Reporter& reporter,
wsl::windows::common::ConsoleState& console,
wsl::windows::common::ClientRunningWSLCProcess&& process,
bool triggerRefresh = false);
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);
Expand Down
31 changes: 15 additions & 16 deletions src/windows/wslc/services/ContainerService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ Module Name:
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::shared;
using namespace wsl::windows::wslc::models;
Expand All @@ -43,7 +42,7 @@ static void SetContainerArguments(WSLCProcessOptions& options, std::vector<const
}

static wsl::windows::common::RunningWSLCContainer CreateInternal(
Session& session, const std::string& image, const ContainerOptions& options, IWarningCallback* warningCallback = nullptr)
Reporter& reporter, Session& session, const std::string& image, const ContainerOptions& options, IWarningCallback* warningCallback = nullptr)
{
auto processFlags = WSLCProcessFlagsNone;
WI_SetFlagIf(processFlags, WSLCProcessFlagsStdin, options.Interactive);
Expand Down Expand Up @@ -219,8 +218,8 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal(
{
{
// Attempt to pull the image if not found
ImageProgressCallback callback;
PrintMessage(Localization::WSLCCLI_ImageNotFoundPulling(wsl::shared::string::MultiByteToWide(image)), stderr);
ImageProgressCallback callback(reporter);
reporter.Info(L"{}\n", Localization::WSLCCLI_ImageNotFoundPulling(wsl::shared::string::MultiByteToWide(image)));
ImageService imageService;
imageService.Pull(session, image, &callback);
}
Expand Down Expand Up @@ -294,7 +293,7 @@ std::wstring ContainerService::FormatRelativeTime(ULONGLONG timestamp)
return pluralize(elapsed / SecondsPerYear, L"year", L"years");
}

int ContainerService::Attach(Session& session, const std::string& id)
int ContainerService::Attach(Reporter& reporter, Session& session, const std::string& id)
{
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
Expand Down Expand Up @@ -325,7 +324,7 @@ int ContainerService::Attach(Session& session, const std::string& id)
wsl::windows::common::ConsoleState console;
if (!ConsoleService::RelayInteractiveTty(console, runningProcess, stdinLogs.Release().get(), true))
{
wsl::windows::common::wslutil::PrintMessage(L"[detached]", stderr);
reporter.Info(L"[detached]\n");
return 0; // Exit early if user detached
}
}
Expand Down Expand Up @@ -395,7 +394,7 @@ std::wstring ContainerService::FormatPorts(WSLCContainerState state, const std::
return result;
}

int ContainerService::Run(Session& session, const std::string& image, ContainerOptions runOptions)
int ContainerService::Run(Reporter& reporter, Session& session, const std::string& image, ContainerOptions runOptions)
{
// Reserve the CID file (fails if it already exists) before creating the container so a
// container isn't created when the caller-requested path can't be written. The file is
Expand All @@ -404,7 +403,7 @@ int ContainerService::Run(Session& session, const std::string& image, ContainerO
auto warningCallback = Microsoft::WRL::Make<WarningCallback>();

// Create the container
auto runningContainer = CreateInternal(session, image, runOptions, warningCallback.Get());
auto runningContainer = CreateInternal(reporter, session, image, runOptions, warningCallback.Get());
auto& container = runningContainer.Get();

WSLCContainerId containerId{};
Expand Down Expand Up @@ -435,18 +434,18 @@ int ContainerService::Run(Session& session, const std::string& image, ContainerO
// Handle attach if requested
if (attach)
{
return ConsoleService::AttachToCurrentConsole(console, runningContainer.GetInitProcess());
return ConsoleService::AttachToCurrentConsole(reporter, console, runningContainer.GetInitProcess());
}

PrintMessage(L"%hs", stdout, containerId);
reporter.Output(L"{}\n", wsl::shared::string::MultiByteToWide(containerId));
return 0;
}

CreateContainerResult ContainerService::Create(Session& session, const std::string& image, ContainerOptions runOptions)
CreateContainerResult ContainerService::Create(Reporter& reporter, Session& session, const std::string& image, ContainerOptions runOptions)
{
CidFile cidFile(runOptions.CidFile);
auto warningCallback = Microsoft::WRL::Make<WarningCallback>();
auto runningContainer = CreateInternal(session, image, runOptions, warningCallback.Get());
auto runningContainer = CreateInternal(reporter, session, image, runOptions, warningCallback.Get());
runningContainer.SetDeleteOnClose(false);
auto& container = runningContainer.Get();
WSLCContainerId id{};
Expand All @@ -455,7 +454,7 @@ CreateContainerResult ContainerService::Create(Session& session, const std::stri
return {.Id = id};
}

int ContainerService::Start(Session& session, const std::string& id, bool attach)
int ContainerService::Start(Reporter& reporter, Session& session, const std::string& id, bool attach)
{
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
Expand All @@ -482,7 +481,7 @@ int ContainerService::Start(Session& session, const std::string& id, bool attach
THROW_IF_FAILED(process->GetFlags(&processFlags));
ClientRunningWSLCProcess runningProcess(std::move(process), processFlags);

return ConsoleService::AttachToCurrentConsole(console, std::move(runningProcess), true);
return ConsoleService::AttachToCurrentConsole(reporter, console, std::move(runningProcess), true);
}

void ContainerService::Stop(Session& session, const std::string& id, StopContainerOptions options)
Expand Down Expand Up @@ -553,7 +552,7 @@ std::vector<ContainerInformation> ContainerService::List(
return result;
}

int ContainerService::Exec(Session& session, const std::string& id, ContainerOptions options)
int ContainerService::Exec(Reporter& reporter, Session& session, const std::string& id, ContainerOptions options)
{
wil::com_ptr<IWSLCContainer> container;
THROW_IF_FAILED(session.Get()->OpenContainer(id.c_str(), &container));
Expand Down Expand Up @@ -581,7 +580,7 @@ int ContainerService::Exec(Session& session, const std::string& id, ContainerOpt
processLauncher.SetWorkingDirectory(std::move(options.WorkingDirectory));
}

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

InspectContainer ContainerService::Inspect(Session& session, const std::string& id)
Expand Down
11 changes: 6 additions & 5 deletions src/windows/wslc/services/ContainerService.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Module Name:
#pragma once
#include "SessionModel.h"
#include "ContainerModel.h"
#include "Reporter.h"
#include <docker_schema.h>
#include <wslc_schema.h>

Expand All @@ -23,17 +24,17 @@ struct ContainerService
static std::wstring ContainerStateToString(WSLCContainerState state, ULONGLONG stateChangedAt = 0);
static std::wstring FormatRelativeTime(ULONGLONG timestamp);
static std::wstring FormatPorts(WSLCContainerState state, const std::vector<models::PortInformation>& ports);
static int Attach(models::Session& session, const std::string& id);
static int Run(models::Session& session, const std::string& image, models::ContainerOptions options);
static models::CreateContainerResult Create(models::Session& session, const std::string& image, models::ContainerOptions options);
static int Start(models::Session& session, const std::string& id, bool attach = false);
static int Attach(Reporter& reporter, models::Session& session, const std::string& id);
static int Run(Reporter& reporter, models::Session& session, const std::string& image, models::ContainerOptions options);
static models::CreateContainerResult Create(Reporter& reporter, models::Session& session, const std::string& image, models::ContainerOptions options);
static int Start(Reporter& reporter, models::Session& session, const std::string& id, bool attach = false);
static void Stop(models::Session& session, const std::string& id, models::StopContainerOptions options);
static void Kill(models::Session& session, const std::string& id, WSLCSignal signal = WSLCSignalSIGKILL);
static void Delete(models::Session& session, const std::string& id, bool force);
static std::vector<models::ContainerInformation> List(
models::Session& session, bool all = false, int limit = -1, const std::vector<std::pair<std::string, std::string>>& filters = {});

static int Exec(models::Session& session, const std::string& id, models::ContainerOptions options);
static int Exec(Reporter& reporter, models::Session& session, const std::string& id, models::ContainerOptions options);
static void Export(models::Session& session, const std::string& id, const std::wstring& outputPath);
static void Export(models::Session& session, const std::string& id, HANDLE outputHandle);
static wsl::windows::common::wslc_schema::InspectContainer Inspect(models::Session& session, const std::string& id);
Expand Down
Loading
Loading