-
Notifications
You must be signed in to change notification settings - Fork 1.5k
WSLA: Add initial wsladiag tool with basic --list command and placeholder ListSessions #13725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7273a41
71d3514
06fd79b
de15229
7ec00b4
dc7781e
e02bd5d
637a121
494da27
bcb57dd
b09a666
be46f23
f3b5903
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
|
|
||
|
|
||
| set(SOURCES | ||
| main.cpp | ||
| ) | ||
|
|
||
| add_executable(wsladiag ${SOURCES}) | ||
|
|
||
| target_link_libraries(wsladiag | ||
| ${COMMON_LINK_LIBRARIES} | ||
| common | ||
| ) | ||
|
|
||
| target_precompile_headers(wsladiag REUSE_FROM common) | ||
|
|
||
| set_target_properties(wsladiag PROPERTIES FOLDER windows) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| /*++ | ||
|
|
||
| Copyright (c) Microsoft. All rights reserved. | ||
|
|
||
| Module Name: | ||
|
|
||
| main.cpp | ||
|
|
||
| Abstract: | ||
|
|
||
| Entry point for the wsladiag tool, performs WSL runtime initialization and parses --list/--help. | ||
|
|
||
| --*/ | ||
|
|
||
| #include "precomp.h" | ||
| #include "CommandLine.h" | ||
| #include "wslutil.h" | ||
| #include "wslaservice.h" | ||
| #include "WslSecurity.h" | ||
|
|
||
| using namespace wsl::shared; | ||
| namespace wslutil = wsl::windows::common::wslutil; | ||
|
|
||
| int wsladiag_main(std::wstring_view commandLine) | ||
| { | ||
| wslutil::ConfigureCrt(); | ||
| wslutil::InitializeWil(); | ||
|
|
||
| WslTraceLoggingInitialize(WslaTelemetryProvider, !wsl::shared::OfficialBuild); | ||
| auto cleanupTelemetry = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { WslTraceLoggingUninitialize(); }); | ||
|
|
||
| wslutil::SetCrtEncoding(_O_U8TEXT); | ||
|
|
||
| auto coInit = wil::CoInitializeEx(COINIT_MULTITHREADED); | ||
| wslutil::CoInitializeSecurity(); | ||
|
|
||
| WSADATA data{}; | ||
| THROW_IF_WIN32_ERROR(WSAStartup(MAKEWORD(2, 2), &data)); | ||
| auto wsaCleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, []() { WSACleanup(); }); | ||
|
|
||
| // Command-line parsing using ArgumentParser. | ||
| ArgumentParser parser(std::wstring{commandLine}, L"wsladiag"); | ||
|
|
||
| bool help = false; | ||
| bool list = false; | ||
|
|
||
| parser.AddArgument(list, L"--list"); | ||
| parser.AddArgument(help, L"--help", L'h'); // short option is a single wide char | ||
| parser.Parse(); | ||
|
|
||
| auto printUsage = []() { | ||
| wslutil::PrintMessage( | ||
| L"wsladiag - WSLA diagnostics tool\n" | ||
| L"Usage:\n" | ||
| L" wsladiag --list List WSLA sessions\n" | ||
| L" wsladiag --help Show this help", | ||
| stderr); | ||
| }; | ||
|
|
||
| // If '--help' was requested, print usage and exit. | ||
| if (help) | ||
| { | ||
| printUsage(); | ||
| return 0; | ||
| } | ||
|
|
||
| if (!list) | ||
| { | ||
| // No recognized command → show usage | ||
| printUsage(); | ||
| return 0; | ||
| } | ||
|
|
||
| // --list: Call WSLA service COM interface to retrieve and display sessions. | ||
|
|
||
| try | ||
| { | ||
| wil::com_ptr<IWSLAUserSession> userSession; | ||
| THROW_IF_FAILED(CoCreateInstance(__uuidof(WSLAUserSession), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&userSession))); | ||
|
|
||
| wsl::windows::common::security::ConfigureForCOMImpersonation(userSession.get()); | ||
|
|
||
| wil::unique_cotaskmem_array_ptr<WSLA_SESSION_INFORMATION> sessions; | ||
|
|
||
| THROW_IF_FAILED(userSession->ListSessions(&sessions, sessions.size_address<ULONG>())); | ||
|
|
||
| if (sessions.size() == 0) | ||
| { | ||
| wslutil::PrintMessage(L"No WSLA sessions found.\n", stdout); | ||
| } | ||
| else | ||
| { | ||
| wslutil::PrintMessage(std::format(L"Found {} WSLA session{}:\n", sessions.size(), sessions.size() > 1 ? L"s" : L""), stdout); | ||
|
|
||
| wslutil::PrintMessage(L"ID\tCreator PID\tDisplay Name\n", stdout); | ||
| wslutil::PrintMessage(L"--\t-----------\t------------\n", stdout); | ||
|
|
||
| for (const auto& session : sessions) | ||
| { | ||
| const auto * displayName = session.DisplayName; | ||
| if (displayName[0] == L'\0') | ||
| { | ||
| displayName = L"<unnamed>"; | ||
| } | ||
|
|
||
| wslutil::PrintMessage( | ||
| std::format(L"{}\t{}\t\t{}\n", session.SessionId, session.CreatorPid, displayName), stdout); | ||
| } | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
| catch (...) | ||
| { | ||
| const auto hr = wil::ResultFromCaughtException(); | ||
| const std::wstring hrMessage = wslutil::ErrorCodeToString(hr); | ||
|
|
||
| if (!hrMessage.empty()) | ||
| { | ||
| wslutil::PrintMessage(std::format(L"Error listing WSLA sessions: 0x{:08x} - {}\n", static_cast<unsigned int>(hr), hrMessage), stderr); | ||
| } | ||
| else | ||
| { | ||
| wslutil::PrintMessage(std::format(L"Error listing WSLA sessions: 0x{:08x}\n", static_cast<unsigned int>(hr)), stderr); | ||
| } | ||
|
|
||
| return 1; | ||
| } | ||
| } | ||
|
|
||
| int wmain(int /*argc*/, wchar_t** /*argv*/) | ||
| { | ||
| try | ||
| { | ||
| // Use raw Unicode command line so ArgumentParser gets original input. | ||
| return wsladiag_main(GetCommandLineW()); | ||
| } | ||
| CATCH_RETURN(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,7 +20,10 @@ Module Name: | |
|
|
||
| using wsl::windows::service::wsla::WSLASession; | ||
|
|
||
| WSLASession::WSLASession(const WSLA_SESSION_SETTINGS& Settings, WSLAUserSessionImpl& userSessionImpl, const VIRTUAL_MACHINE_SETTINGS& VmSettings) : | ||
| WSLASession::WSLASession(ULONG id, const WSLA_SESSION_SETTINGS& Settings, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: move Settings arg to new line for consistency |
||
| WSLAUserSessionImpl& userSessionImpl, | ||
| const VIRTUAL_MACHINE_SETTINGS& VmSettings) : | ||
| m_id(id), | ||
| m_sessionSettings(Settings), | ||
| m_userSession(&userSessionImpl), | ||
| m_virtualMachine(wil::MakeOrThrow<WSLAVirtualMachine>(VmSettings, userSessionImpl.GetUserSid(), &userSessionImpl)), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,19 +50,51 @@ PSID WSLAUserSessionImpl::GetUserSid() const | |
| HRESULT wsl::windows::service::wsla::WSLAUserSessionImpl::CreateSession( | ||
| const WSLA_SESSION_SETTINGS* Settings, const VIRTUAL_MACHINE_SETTINGS* VmSettings, IWSLASession** WslaSession) | ||
| { | ||
| auto session = wil::MakeOrThrow<WSLASession>(*Settings, *this, *VmSettings); | ||
|
|
||
| std::lock_guard lock(m_wslaSessionsLock); | ||
| auto it = m_sessions.emplace(session.Get()); | ||
| ULONG id = m_nextSessionId++; | ||
| auto session = wil::MakeOrThrow<WSLASession>(id, *Settings, *this, *VmSettings); | ||
|
|
||
| { | ||
| std::lock_guard lock(m_wslaSessionsLock); | ||
| auto it = m_sessions.emplace(session.Get()); | ||
| m_wslaSessions.emplace_back(session); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't. I'd recommend removing |
||
| // Client now owns the session. | ||
| // TODO: Add a flag for the client to specify that the session should outlive its process. | ||
|
|
||
| } | ||
|
|
||
| THROW_IF_FAILED(session.CopyTo(__uuidof(IWSLASession), (void**)WslaSession)); | ||
|
|
||
| return S_OK; | ||
| } | ||
|
|
||
| HRESULT wsl::windows::service::wsla::WSLAUserSessionImpl::ListSessions(_Out_ WSLA_SESSION_INFORMATION** Sessions, _Out_ ULONG* SessionsCount) | ||
| { | ||
| const auto count = m_wslaSessions.size(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: this variable isn't needed. We can directly use |
||
| auto output = wil::make_unique_cotaskmem<WSLA_SESSION_INFORMATION[]>(m_wslaSessions.size()); | ||
| std::lock_guard lock(m_wslaSessionsLock); | ||
| for (size_t i = 0; i < m_wslaSessions.size(); ++i) | ||
| { | ||
| output[i].SessionId = m_wslaSessions[i]->GetId(); | ||
| output[i].CreatorPid = 0; // placeholder until we populate this later | ||
| PWSTR tempName = nullptr; | ||
|
|
||
| RETURN_IF_FAILED(m_wslaSessions[i]->GetDisplayName(&tempName)); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To simplify things a bit, we could have |
||
|
|
||
| if (tempName) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| { | ||
| wcscpy_s(output[i].DisplayName, tempName); | ||
| CoTaskMemFree(tempName); | ||
| } | ||
| else | ||
| { | ||
| output[i].DisplayName[0] = L'\0'; | ||
| } | ||
|
|
||
| } | ||
| *Sessions = output.release(); | ||
| *SessionsCount = static_cast<ULONG>(count); | ||
| return S_OK; | ||
| } | ||
|
|
||
| wsl::windows::service::wsla::WSLAUserSession::WSLAUserSession(std::weak_ptr<WSLAUserSessionImpl>&& Session) : | ||
| m_session(std::move(Session)) | ||
| { | ||
|
|
@@ -89,9 +121,21 @@ try | |
| CATCH_RETURN(); | ||
|
|
||
| HRESULT wsl::windows::service::wsla::WSLAUserSession::ListSessions(WSLA_SESSION_INFORMATION** Sessions, ULONG* SessionsCount) | ||
| try | ||
| { | ||
| return E_NOTIMPL; | ||
| if (!Sessions || !SessionsCount) | ||
| { | ||
| return E_INVALIDARG; | ||
| } | ||
|
|
||
| auto session = m_session.lock(); | ||
| RETURN_HR_IF(RPC_E_DISCONNECTED, !session); | ||
|
|
||
| RETURN_IF_FAILED(session->ListSessions(Sessions, SessionsCount)); | ||
| return S_OK; | ||
| } | ||
| CATCH_RETURN(); | ||
|
|
||
| HRESULT wsl::windows::service::wsla::WSLAUserSession::OpenSession(ULONG Id, IWSLASession** Session) | ||
| { | ||
| return E_NOTIMPL; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,8 @@ Module Name: | |
| #pragma once | ||
| #include "WSLAVirtualMachine.h" | ||
| #include "WSLASession.h" | ||
| #include <atomic> | ||
| #include <vector> | ||
|
|
||
| namespace wsl::windows::service::wsla { | ||
|
|
||
|
|
@@ -30,14 +32,17 @@ class WSLAUserSessionImpl | |
| PSID GetUserSid() const; | ||
|
|
||
| HRESULT CreateSession(const WSLA_SESSION_SETTINGS* Settings, const VIRTUAL_MACHINE_SETTINGS* VmSettings, IWSLASession** WslaSession); | ||
|
|
||
| HRESULT ListSessions(_Out_ WSLA_SESSION_INFORMATION** Sessions, _Out_ ULONG* SessionsCount); | ||
| void OnVmTerminated(WSLAVirtualMachine* machine); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is OnVmTerminated added back here?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's probably a merge issue. Let's remove this. |
||
| void OnSessionTerminated(WSLASession* Session); | ||
|
|
||
| private: | ||
| wil::unique_tokeninfo_ptr<TOKEN_USER> m_tokenInfo; | ||
|
|
||
| std::atomic<ULONG> m_nextSessionId{1}; | ||
| std::recursive_mutex m_wslaSessionsLock; | ||
| std::recursive_mutex m_lock; | ||
| // Track active sessions for diagnostics / ListSessions. | ||
| std::vector<Microsoft::WRL::ComPtr<WSLASession>> m_wslaSessions; | ||
|
|
||
| // TODO-WSLA: Consider using a weak_ptr to easily destroy when the last client reference is released. | ||
| std::unordered_set<WSLASession*> m_sessions; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably OK as the first implementation, but long term (maybe in a followup PR), we'll want to compute what's the longest string that we'll display, so we can have the right number of columns displayed here so everything is alligned regardless of the DisplayName's sizes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's how do it for wsl --list for reference: https://github.com/microsoft/WSL/blob/ba90ee11fa83a1cb7d908d64cb50197afa6a8aeb/src/windows/common/WslClient.cpp#L730C8-L738C12
(We could probably do a more modern version of that)