Skip to content

Commit c60e003

Browse files
committed
Refine localized error handling
1 parent 57a6654 commit c60e003

File tree

3 files changed

+71
-120
lines changed

3 files changed

+71
-120
lines changed

src/windows/common/ExecutionContext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ enum Context : ULONGLONG
6666
UpdatePackage = 0x10000000000,
6767
QueryLatestGitHubRelease = 0x20000000000,
6868
VerifyChecksum = 0x40000000000,
69+
WslaDiag = 0x80000000000,
6970
};
7071

7172
DEFINE_ENUM_FLAG_OPERATORS(Context)

src/windows/common/wslutil.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ static const std::map<Context, LPCWSTR> g_contextStrings{
191191
X(HNS),
192192
X(ReadDistroConfig),
193193
X(MoveDistro),
194-
X(VerifyChecksum)};
194+
X(VerifyChecksum),
195+
X(WslaDiag)};
195196

196197
#undef X
197198

src/windows/wsladiag/wsladiag.cpp

Lines changed: 68 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Module Name:
88
99
Abstract:
1010
11-
Entry point for the wsladiag tool, performs WSL runtime initialization and parses list/shell/help.
11+
Entry point for the wsladiag tool. Provides diagnostic commands for WSLA sessions.
1212
1313
--*/
1414

@@ -24,32 +24,25 @@ Module Name:
2424

2525
using namespace wsl::shared;
2626
namespace wslutil = wsl::windows::common::wslutil;
27+
using wsl::windows::common::Context;
28+
using wsl::windows::common::ExecutionContext;
2729
using wsl::windows::common::WSLAProcessLauncher;
2830

29-
// Adding a helper to factor error handling between all the arguments.
31+
// Report an operation failure with localized context and HRESULT details.
3032
static int ReportError(const std::wstring& context, HRESULT hr)
3133
{
32-
const std::wstring hrMessage = wslutil::GetErrorString(hr);
33-
34-
if (!hrMessage.empty())
35-
{
36-
wslutil::PrintMessage(std::format(L"{}: 0x{:08x} - {}", context, static_cast<uint32_t>(hr), hrMessage), stderr);
37-
}
38-
else
39-
{
40-
wslutil::PrintMessage(std::format(L"{}: 0x{:08x}", context, static_cast<uint32_t>(hr)), stderr);
41-
}
42-
34+
auto errorString = wsl::windows::common::wslutil::ErrorCodeToString(hr);
35+
wslutil::PrintMessage(context, stderr);
4336
return 1;
4437
}
4538

46-
// Handler for `wsladiag shell <SessionName> [--verbose]` command - launches TTY-backed interactive shell.
39+
// Handler for `wsladiag shell <SessionName>` command.
4740
static int RunShellCommand(std::wstring_view commandLine)
4841
{
4942
std::wstring sessionName;
5043
bool verbose = false;
5144

52-
ArgumentParser parser(std::wstring{commandLine}, L"wsladiag", 2, false); // Skip "wsladiag.exe shell" to parse shell-specific args
45+
ArgumentParser parser(std::wstring{commandLine}, L"wsladiag", 2, false);
5346
parser.AddPositionalArgument(sessionName, 0);
5447
parser.AddArgument(verbose, L"--verbose", L'v');
5548

@@ -61,15 +54,6 @@ static int RunShellCommand(std::wstring_view commandLine)
6154
E_INVALIDARG, wsl::shared::Localization::MessageMissingArgument(L"<SessionName>", L"wsladiag shell"));
6255
}
6356

64-
const auto log = [&](std::wstring_view msg) {
65-
if (verbose)
66-
{
67-
wslutil::PrintMessage(std::wstring(msg), stdout);
68-
}
69-
};
70-
71-
log(std::format(L"[diag] shell='{}'", sessionName));
72-
7357
wil::com_ptr<IWSLAUserSession> userSession;
7458
THROW_IF_FAILED(CoCreateInstance(__uuidof(WSLAUserSession), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&userSession)));
7559
wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get());
@@ -87,8 +71,6 @@ static int RunShellCommand(std::wstring_view commandLine)
8771
return ReportError(Localization::MessageWslaOpenSessionFailed(sessionName.c_str()), hr);
8872
}
8973

90-
log(L"[diag] OpenSessionByName succeeded");
91-
9274
// Console size for TTY.
9375
CONSOLE_SCREEN_BUFFER_INFO info{};
9476
THROW_LAST_ERROR_IF(!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info));
@@ -105,12 +87,9 @@ static int RunShellCommand(std::wstring_view commandLine)
10587
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 1, .Type = WSLAFdTypeTerminalOutput, .Path = nullptr});
10688
launcher.AddFd(WSLA_PROCESS_FD{.Fd = 2, .Type = WSLAFdTypeTerminalControl, .Path = nullptr});
10789

108-
log(std::format(L"[diag] tty rows={} cols={}", rows, cols));
10990
launcher.SetTtySize(rows, cols);
11091

111-
log(L"[diag] launching shell process...");
11292
auto process = launcher.Launch(*session);
113-
log(L"[diag] shell launched (TTY)");
11493

11594
auto ttyIn = process.GetStdHandle(0);
11695
auto ttyOut = process.GetStdHandle(1);
@@ -129,13 +108,12 @@ static int RunShellCommand(std::wstring_view commandLine)
129108
// Save/restore console state.
130109
DWORD originalInMode{};
131110
DWORD originalOutMode{};
111+
const UINT originalOutCP = GetConsoleOutputCP();
112+
const UINT originalInCP = GetConsoleCP();
132113

133114
THROW_LAST_ERROR_IF(!GetConsoleMode(consoleIn, &originalInMode));
134115
THROW_LAST_ERROR_IF(!GetConsoleMode(consoleOut, &originalOutMode));
135116

136-
const UINT originalOutCP = GetConsoleOutputCP();
137-
const UINT originalInCP = GetConsoleCP();
138-
139117
auto restoreConsole = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
140118
LOG_IF_WIN32_BOOL_FALSE(SetConsoleMode(consoleIn, originalInMode));
141119
LOG_IF_WIN32_BOOL_FALSE(SetConsoleMode(consoleOut, originalOutMode));
@@ -158,19 +136,13 @@ static int RunShellCommand(std::wstring_view commandLine)
158136

159137
auto exitEvent = wil::unique_event(wil::EventOptions::ManualReset);
160138

161-
auto ttyControl = process.GetStdHandle(2); // TerminalControl
162-
wsl::shared::SocketChannel controlChannel{wil::unique_socket{(SOCKET)ttyControl.release()}, "TerminalControl", exitEvent.get()};
163-
164139
auto updateTerminalSize = [&]() {
165140
CONSOLE_SCREEN_BUFFER_INFOEX infoEx{};
166141
infoEx.cbSize = sizeof(infoEx);
167142
THROW_IF_WIN32_BOOL_FALSE(GetConsoleScreenBufferInfoEx(consoleOut, &infoEx));
168143

169-
WSLA_TERMINAL_CHANGED message{};
170-
message.Columns = infoEx.srWindow.Right - infoEx.srWindow.Left + 1;
171-
message.Rows = infoEx.srWindow.Bottom - infoEx.srWindow.Top + 1;
172-
173-
controlChannel.SendMessage(message);
144+
LOG_IF_FAILED(process.Get().ResizeTty(
145+
infoEx.srWindow.Bottom - infoEx.srWindow.Top + 1, infoEx.srWindow.Right - infoEx.srWindow.Left + 1));
174146
};
175147

176148
// Start input relay thread to forward console input to TTY
@@ -186,7 +158,7 @@ static int RunShellCommand(std::wstring_view commandLine)
186158
}
187159
});
188160

189-
auto joinInput = wil::scope_exit([&] {
161+
auto joinInput = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
190162
exitEvent.SetEvent();
191163
if (inputThread.joinable())
192164
{
@@ -199,22 +171,30 @@ static int RunShellCommand(std::wstring_view commandLine)
199171

200172
process.GetExitEvent().wait();
201173

202-
auto [exitCode, signalled] = process.GetExitState();
174+
auto exitCode = process.GetExitCode();
203175

204176
std::wstring shellWide(shell.begin(), shell.end());
205-
wslutil::PrintMessage(Localization::MessageWslaShellExited(shellWide.c_str(), exitCode), stdout);
177+
wslutil::PrintMessage(wsl::shared::Localization::MessageWslaShellExited(shellWide.c_str(), static_cast<int>(exitCode)), stdout);
206178

207-
return 0;
179+
return static_cast<int>(exitCode);
208180
}
209181

182+
// Handler for `wsladiag list` command.
210183
static int RunListCommand(std::wstring_view commandLine)
211184
{
212185
bool verbose = false;
213186

214-
ArgumentParser parser(std::wstring{commandLine}, L"wsladiag", 2, false); // Skip "wsladiag.exe list" to parse list-specific args
187+
ArgumentParser parser(std::wstring{commandLine}, L"wsladiag", 2, false);
215188
parser.AddArgument(verbose, L"--verbose", L'v');
216189

217-
parser.Parse();
190+
try
191+
{
192+
parser.Parse();
193+
}
194+
catch (...)
195+
{
196+
THROW_HR_WITH_USER_ERROR(E_INVALIDARG, wsl::shared::Localization::MessageWsladiagUsage());
197+
}
218198

219199
wil::com_ptr<IWSLAUserSession> userSession;
220200
THROW_IF_FAILED(CoCreateInstance(__uuidof(WSLAUserSession), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&userSession)));
@@ -235,7 +215,8 @@ static int RunListCommand(std::wstring_view commandLine)
235215
}
236216

237217
wslutil::PrintMessage(Localization::MessageWslaSessionsFound(sessions.size(), sessions.size() == 1 ? L"" : L"s"), stdout);
238-
// Compute column widths from headers + data.
218+
219+
// Use localized headers
239220
const auto idHeader = Localization::MessageWslaHeaderId();
240221
const auto pidHeader = Localization::MessageWslaHeaderCreatorPid();
241222
const auto nameHeader = Localization::MessageWslaHeaderDisplayName();
@@ -279,7 +260,6 @@ static int RunListCommand(std::wstring_view commandLine)
279260
for (const auto& s : sessions)
280261
{
281262
const wchar_t* displayName = s.DisplayName ? s.DisplayName : L"";
282-
283263
wprintf(
284264
L"%-*lu %-*lu %-*ls\n",
285265
static_cast<int>(idWidth),
@@ -293,14 +273,15 @@ static int RunListCommand(std::wstring_view commandLine)
293273
return 0;
294274
}
295275

276+
// Print localized usage message to stderr.
296277
static void PrintUsage()
297278
{
298279
wslutil::PrintMessage(Localization::MessageWsladiagUsage(), stderr);
299280
}
300281

301282
int wsladiag_main(std::wstring_view commandLine)
302283
{
303-
// Basic initialization that was previously in this function.
284+
// Initialize runtime and COM.
304285
wslutil::ConfigureCrt();
305286
wslutil::InitializeWil();
306287

@@ -316,107 +297,75 @@ int wsladiag_main(std::wstring_view commandLine)
316297
THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &data));
317298
auto wsaCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { WSACleanup(); });
318299

319-
// Enable contextualized error collection and create an ExecutionContext so
320-
// THROW_HR_WITH_USER_ERROR will save a user-visible message.
321-
wsl::windows::common::EnableContextualizedErrors(false);
300+
// Parse the top-level verb (list, shell, --help).
301+
ArgumentParser parser(std::wstring{commandLine}, L"wsladiag", 1, true);
302+
303+
bool help = false;
304+
std::wstring verb;
305+
306+
parser.AddPositionalArgument(verb, 0);
307+
parser.AddArgument(help, L"--help", L'h');
322308

323-
FILE* warningsFile = nullptr;
324-
const char* disableWarnings = getenv("WSL_DISABLE_WARNINGS");
325-
if (disableWarnings == nullptr || strcmp(disableWarnings, "1") != 0)
309+
parser.Parse();
310+
311+
if (help || verb.empty())
326312
{
327-
warningsFile = stderr;
313+
PrintUsage();
314+
return 0;
328315
}
329316

330-
std::optional<wsl::windows::common::ExecutionContext> context;
331-
context.emplace(wsl::windows::common::Context::Wsl, warningsFile);
332-
333-
int exitCode = 0;
334-
HRESULT result = S_OK;
317+
if (verb == L"list")
318+
{
319+
return RunListCommand(commandLine);
320+
}
335321

336-
try
322+
if (verb == L"shell")
337323
{
338-
ArgumentParser parser(std::wstring{commandLine}, L"wsladiag", 1, true);
324+
return RunShellCommand(commandLine);
325+
}
339326

340-
bool help = false;
341-
std::wstring verb;
327+
// Unknown verb - show usage and fail.
328+
wslutil::PrintMessage(Localization::MessageWslaUnknownCommand(verb.c_str()), stderr);
329+
PrintUsage();
330+
return 1;
331+
}
342332

343-
parser.AddPositionalArgument(verb, 0);
344-
parser.AddArgument(help, L"--help", L'h');
333+
int wmain(int, wchar_t**)
334+
{
335+
wsl::windows::common::EnableContextualizedErrors(false);
345336

346-
parser.Parse(); // Let exceptions propagate to this try/catch
337+
ExecutionContext context{Context::WslaDiag};
338+
int exitCode = 1;
339+
HRESULT result = S_OK;
347340

348-
if (help || verb.empty())
349-
{
350-
PrintUsage();
351-
exitCode = 0;
352-
}
353-
else if (verb == L"list")
354-
{
355-
exitCode = RunListCommand(commandLine);
356-
}
357-
else if (verb == L"shell")
358-
{
359-
exitCode = RunShellCommand(commandLine);
360-
}
361-
else
362-
{
363-
wslutil::PrintMessage(Localization::MessageWslaUnknownCommand(verb.c_str()), stderr);
364-
PrintUsage();
365-
exitCode = 1;
366-
}
341+
try
342+
{
343+
exitCode = wsladiag_main(GetCommandLineW());
367344
}
368345
catch (...)
369346
{
370-
// Capture the exception HRESULT and fall through to printing contextualized error.
371347
result = wil::ResultFromCaughtException();
372-
// Default nonzero exit code on failure.
373-
exitCode = 1;
374348
}
375349

376-
// If there was a failure, attempt to print a contextualized error message collected
377-
// by the ExecutionContext. Otherwise fall back to the HRESULT -> string path.
378350
if (FAILED(result))
379351
{
380352
try
381353
{
382-
std::wstring errorString{};
383-
if (context.has_value() && context->ReportedError().has_value())
354+
if (auto reported = context.ReportedError())
384355
{
385-
auto strings = wsl::windows::common::wslutil::ErrorToString(context->ReportedError().value());
386-
387-
// For most errors, show both message and code
388-
errorString = wsl::shared::Localization::MessageErrorCode(strings.Message, strings.Code);
389-
390-
// Log telemetry for user-visible errors (matches other tools).
391-
WSL_LOG("UserVisibleError", TraceLoggingValue(strings.Code.c_str(), "ErrorCode"));
356+
auto strings = wsl::windows::common::wslutil::ErrorToString(*reported);
357+
wslutil::PrintMessage(wsl::shared::Localization::MessageErrorCode(strings.Message, strings.Code), stderr);
392358
}
393359
else
394360
{
395-
// Fallback: show basic HRESULT string.
396-
errorString = wslutil::ErrorCodeToString(result);
361+
wslutil::PrintMessage(wslutil::GetErrorString(result), stderr);
397362
}
398-
399-
wslutil::PrintMessage(errorString, stderr);
400363
}
401364
catch (...)
402365
{
403366
LOG_CAUGHT_EXCEPTION();
404367
}
405-
406368
}
407369

408370
return exitCode;
409-
}
410-
411-
int wmain(int, wchar_t**)
412-
{
413-
try
414-
{
415-
return wsladiag_main(GetCommandLineW());
416-
}
417-
catch (...)
418-
{
419-
const auto hr = wil::ResultFromCaughtException();
420-
return ReportError(L"wsladiag failed", hr);
421-
}
422371
}

0 commit comments

Comments
 (0)