master
cpp 212 lines 7.49 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 SessionService.cpp
8
9 Abstract:
10
11 This file contains the SessionService implementation
12
13 --*/
14
15 #include "precomp.h"
16 #include "SessionService.h"
17 #include "ConsoleService.h"
18 #include "WarningCallback.h"
19 #include <wslc.h>
20 #include <WSLCProcessLauncher.h>
21
22 namespace wsl::windows::wslc::services {
23 using namespace wsl::shared;
24 using namespace wsl::windows::wslc::models;
25 namespace wslutil = wsl::windows::common::wslutil;
26
27 static wil::com_ptr<IWSLCSessionManager> CreateSessionManager()
28 {
29 wil::com_ptr<IWSLCSessionManager> manager;
30 THROW_IF_FAILED(CoCreateInstance(__uuidof(WSLCSessionManager), nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&manager)));
31 wsl::windows::common::security::ConfigureForCOMImpersonation(manager.get());
32 return manager;
33 }
34
35 Session SessionService::OpenSessionByName(const wil::com_ptr<IWSLCSessionManager>& manager, LPCWSTR displayName)
36 {
37 wil::com_ptr<IWSLCSession> session;
38 THROW_IF_FAILED(manager->OpenSessionByName(displayName, &session));
39
40 wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
41 return Session(std::move(session));
42 }
43
44 Session SessionService::OpenSession(const std::wstring& sessionName)
45 {
46 return OpenSessionByName(CreateSessionManager(), sessionName.c_str());
47 }
48
49 Session SessionService::OpenDefaultSession()
50 {
51 // Null DisplayName = default session, resolved from caller's token by the server.
52 return OpenSessionByName(CreateSessionManager(), nullptr);
53 }
54
55 Session SessionService::OpenOrCreateDefaultSession(Terminal& terminal)
56 {
57 WarningCallback warningCallback(terminal);
58 auto manager = CreateSessionManager();
59
60 // Null Settings = default session with server-determined name and settings. The warning callback
61 // is consumed during CreateSession (session initialization); it is not retained afterwards.
62 wil::com_ptr<IWSLCSession> session;
63 THROW_IF_FAILED(manager->CreateSession(nullptr, WSLCSessionFlagsNone, &warningCallback, &session));
64 wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
65
66 return Session(std::move(session));
67 }
68
69 int SessionService::Attach(Terminal& terminal, const Session& session)
70 {
71 // Configure console for interactive usage.
72 wsl::windows::common::ConsoleState console{};
73 console.SetInteractiveMode();
74 const auto windowSize = console.GetWindowSize();
75
76 const std::string shell = "/bin/sh";
77
78 // Launch with terminal fds (PTY).
79 wsl::windows::common::WSLCProcessLauncher launcher{shell, {shell, "--login"}, {"TERM=xterm-256color"}, WSLCProcessFlagsTty | WSLCProcessFlagsStdin};
80 launcher.SetTtySize(windowSize.Y, windowSize.X);
81 auto process = launcher.Launch(*session.Get());
82 auto tty = process.GetStdHandle(WSLCFDTty);
83 auto updateTerminalSize = [&]() {
84 const auto windowSize = console.GetWindowSize();
85 LOG_IF_FAILED(process.Get().ResizeTty(windowSize.Y, windowSize.X));
86 };
87
88 // Start input relay thread to forward console input to TTY
89 // Runs in parallel with output relay (main thread)
90 auto exitEvent = wil::unique_event(wil::EventOptions::ManualReset);
91 std::thread inputThread([&] {
92 try
93 {
94 wsl::windows::common::relay::StandardInputRelay(
95 GetStdHandle(STD_INPUT_HANDLE), tty.Get(), updateTerminalSize, exitEvent.get());
96 }
97 catch (...)
98 {
99 exitEvent.SetEvent();
100 }
101 });
102
103 auto joinInput = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] {
104 exitEvent.SetEvent();
105 if (inputThread.joinable())
106 {
107 inputThread.join();
108 }
109 });
110
111 // Relay tty output -> console (blocks until output ends).
112 wsl::windows::common::relay::InterruptableRelay(tty.Get(), GetStdHandle(STD_OUTPUT_HANDLE), exitEvent.get());
113
114 process.GetExitEvent().wait();
115
116 auto exitCode = process.GetExitCode();
117
118 terminal.Output(L"{}\n", wsl::shared::Localization::MessageWslcShellExited(string::MultiByteToWide(shell), static_cast<int>(exitCode)));
119
120 return static_cast<int>(exitCode);
121 }
122
123 int SessionService::Enter(Terminal& terminal, const std::wstring& storagePath, const std::wstring& displayName)
124 {
125 THROW_HR_IF(E_INVALIDARG, storagePath.empty());
126 THROW_HR_IF(E_INVALIDARG, displayName.empty());
127
128 WarningCallback warningCallback(terminal);
129 auto sessionManager = CreateSessionManager();
130
131 wil::com_ptr<IWSLCSession> session;
132 THROW_IF_FAILED(sessionManager->EnterSession(displayName.c_str(), storagePath.c_str(), &warningCallback, &session));
133 wsl::windows::common::security::ConfigureForCOMImpersonation(session.get());
134 terminal.Info(L"{}\n", Localization::MessageWslcCreatedSession(displayName));
135
136 const std::string shell = "/bin/sh";
137 wsl::windows::common::WSLCProcessLauncher launcher{shell, {shell, "--login"}, {"TERM=xterm-256color"}, WSLCProcessFlagsTty | WSLCProcessFlagsStdin};
138
139 wsl::windows::common::ConsoleState console;
140 const auto windowSize = console.GetWindowSize();
141 launcher.SetTtySize(windowSize.Y, windowSize.X);
142
143 return ConsoleService::AttachToCurrentConsole(terminal, console, launcher.Launch(*session.get()));
144 }
145
146 WSLCVersion SessionService::ManagerVersion()
147 {
148 WSLCVersion version{};
149 THROW_IF_FAILED(CreateSessionManager()->GetVersion(&version));
150
151 return version;
152 }
153
154 std::vector<SessionInformation> SessionService::List()
155 {
156 std::vector<SessionInformation> result;
157 auto sessionManager = CreateSessionManager();
158
159 wil::unique_cotaskmem_array_ptr<WSLCSessionListEntry> sessions;
160 THROW_IF_FAILED(sessionManager->ListSessions(&sessions, sessions.size_address<ULONG>()));
161 for (size_t i = 0; i < sessions.size(); ++i)
162 {
163 const auto& current = sessions[i];
164 SessionInformation info{};
165 info.CreatorPid = current.CreatorPid;
166 info.SessionId = current.SessionId;
167 info.DisplayName = current.DisplayName;
168 result.emplace_back(info);
169 }
170
171 return result;
172 }
173
174 int SessionService::Run(Terminal& terminal, const Session& session, const std::vector<std::string>& arguments)
175 {
176 WI_ASSERT(!arguments.empty());
177
178 // Pass a default $PATH environment for convenience.
179 const std::vector<std::string> environment{"PATH=/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin"};
180 wsl::windows::common::WSLCProcessLauncher launcher{arguments.front(), arguments, environment, WSLCProcessFlagsStdin};
181
182 auto [result, process, error] = launcher.LaunchNoThrow(*session.Get());
183 THROW_HR_WITH_USER_ERROR_IF(result, Localization::MessageWslcFailedToLaunchCommand(arguments.front(), error), FAILED(result) && error != 0);
184
185 THROW_IF_FAILED(result);
186
187 wsl::windows::common::ConsoleState console{};
188 return ConsoleService::AttachToCurrentConsole(terminal, console, std::move(process.value()));
189 }
190
191 int SessionService::TerminateSession(Terminal& terminal, const Session& session)
192 {
193 HRESULT hr = session.Get()->Terminate();
194 if (FAILED(hr))
195 {
196 auto errorString = wsl::windows::common::wslutil::ErrorCodeToString(hr);
197
198 wil::unique_cotaskmem_string displayName;
199 if (SUCCEEDED(session.Get()->GetDisplayName(&displayName)) && displayName)
200 {
201 terminal.Error(L"{}\n", Localization::MessageErrorCode(Localization::MessageWslcTerminateSessionFailed(displayName.get()), errorString));
202 }
203 else
204 {
205 terminal.Error(L"{}\n", Localization::MessageErrorCode(Localization::MessageWslcTerminateDefaultSessionFailed(), errorString));
206 }
207 return 1;
208 }
209
210 return 0;
211 }
212 } // namespace wsl::windows::wslc::services