master
cpp 730 lines 25.1 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 svccomm.cpp
8
9 Abstract:
10
11 This file contains function definitions for the SvcComm helper class.
12
13 --*/
14
15 #include "precomp.h"
16 #include "svccomm.hpp"
17 #include "registry.hpp"
18 #include "relay.hpp"
19
20 #pragma hdrstop
21
22 //
23 // Macros to test exit status (defined in sys\wait.h).
24 //
25
26 #define LXSS_WEXITSTATUS(_status) ((_status) >> 8)
27 #define LXSS_WSTATUS(_status) ((_status) & 0x7F)
28 #define LXSS_WIFEXITED(_status) (LXSS_WSTATUS((_status)) == 0)
29
30 #define IS_VALID_HANDLE(_handle) ((_handle != NULL) && (_handle != INVALID_HANDLE_VALUE))
31
32 using wsl::windows::common::ClientExecutionContext;
33 namespace {
34
35 BOOL GetNextCharacter(_In_ INPUT_RECORD* InputRecord, _Out_ PWCHAR NextCharacter);
36 BOOL IsActionableKey(_In_ PKEY_EVENT_RECORD KeyEvent);
37 void SpawnWslHost(_In_ HANDLE ServerPort, _In_ const GUID& DistroId, _In_opt_ LPCGUID VmId);
38
39 struct CreateProcessArguments
40 {
41 CreateProcessArguments(LPCWSTR Filename, int Argc, LPCWSTR Argv[], ULONG LaunchFlags, LPCWSTR WorkingDirectory)
42 {
43 // Populate the current working directory.
44 //
45 // N.B. Failure to get the current working directory is non-fatal.
46 if (ARGUMENT_PRESENT(WorkingDirectory))
47 {
48 // If a current working directory was provided, it must be a Linux-style path.
49 WI_ASSERT(*WorkingDirectory == L'/' || *WorkingDirectory == L'~');
50
51 CurrentWorkingDirectory = WorkingDirectory;
52 }
53 else
54 {
55 LOG_IF_FAILED(wil::GetCurrentDirectoryW(CurrentWorkingDirectory));
56 }
57
58 // Populate the command line and file name.
59 //
60 // N.B. The CommandLineStrings vector contains weak references to the
61 // strings in the CommandLine vector.
62 if (Argc > 0)
63 {
64 CommandLine.reserve(Argc);
65 std::transform(Argv, Argv + Argc, std::back_inserter(CommandLine), [](LPCWSTR Arg) {
66 return wsl::shared::string::WideToMultiByte(Arg);
67 });
68
69 CommandLineStrings.reserve(CommandLine.size());
70 std::transform(CommandLine.cbegin(), CommandLine.cend(), std::back_inserter(CommandLineStrings), [](const std::string& string) {
71 return string.c_str();
72 });
73 }
74
75 if (ARGUMENT_PRESENT(Filename))
76 {
77 FilenameString = wsl::shared::string::WideToMultiByte(Filename);
78 }
79
80 // Query the current NT %PATH% environment variable.
81 //
82 // N.B. Failure to query the path is non-fatal.
83 LOG_IF_FAILED(wil::ExpandEnvironmentStringsW(L"%PATH%", NtPath));
84
85 if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_TRANSLATE_ENVIRONMENT))
86 {
87 NtEnvironment.reset(GetEnvironmentStringsW());
88
89 // Calculate the size of the environment block.
90 for (PCWSTR Variable = NtEnvironment.get(); Variable[0] != '\0';)
91 {
92 const size_t Length = wcslen(Variable) + 1;
93 NtEnvironmentLength += Length;
94 Variable += Length;
95 }
96
97 NtEnvironmentLength += 1;
98 }
99 }
100
101 std::vector<std::string> CommandLine{};
102 std::vector<LPCSTR> CommandLineStrings{};
103 std::wstring CurrentWorkingDirectory{};
104 std::string FilenameString{};
105 wsl::windows::common::helpers::unique_environment_strings NtEnvironment;
106 size_t NtEnvironmentLength{};
107 std::wstring NtPath{};
108 };
109
110 void InitializeInterop(_In_ HANDLE ServerPort, _In_ const GUID& DistroId)
111 {
112 //
113 // Create a thread to handle interop requests.
114 //
115
116 wil::unique_handle WorkerThreadSeverPort{wsl::windows::common::wslutil::DuplicateHandle(ServerPort)};
117 std::thread([WorkerThreadSeverPort = std::move(WorkerThreadSeverPort)]() mutable {
118 wsl::windows::common::wslutil::SetThreadDescription(L"Interop");
119 wsl::windows::common::interop::WorkerThread(std::move(WorkerThreadSeverPort));
120 }).detach();
121
122 //
123 // Spawn wslhost to handle interop requests from processes that have
124 // been backgrounded and their console window has been closed.
125 //
126
127 SpawnWslHost(ServerPort, DistroId, nullptr);
128 }
129
130 void SpawnWslHost(_In_ HANDLE ServerPort, _In_ const GUID& DistroId, _In_opt_ LPCGUID VmId)
131 {
132 wsl::windows::common::helpers::SetHandleInheritable(ServerPort);
133 const auto RegistrationComplete = wil::unique_event(wil::EventOptions::None);
134 const wil::unique_handle ParentProcess{wsl::windows::common::wslutil::DuplicateHandle(GetCurrentProcess(), std::nullopt, TRUE)};
135 THROW_LAST_ERROR_IF(!ParentProcess);
136
137 const wil::unique_handle Process{wsl::windows::common::helpers::LaunchInteropServer(
138 &DistroId, ServerPort, RegistrationComplete.get(), ParentProcess.get(), VmId)};
139
140 // Wait for either the child to exit, or the registration complete event to be set.
141 const HANDLE WaitHandles[] = {Process.get(), RegistrationComplete.get()};
142 const DWORD WaitStatus = WaitForMultipleObjects(RTL_NUMBER_OF(WaitHandles), WaitHandles, FALSE, INFINITE);
143 LOG_HR_IF_MSG(E_FAIL, (WaitStatus == WAIT_OBJECT_0), "wslhost failed to register");
144 }
145 } // namespace
146
147 //
148 // Exported function definitions.
149 //
150
151 wsl::windows::common::SvcComm::SvcComm()
152 {
153 // Ensure that the OS has support for running lifted WSL. This interface is always present on Windows 11 and later.
154 //
155 // Prior to Windows 11 there are two cases where the IWslSupport interface may not be present:
156 // 1. The machine has not installed the DCR that contains support for lifted WSL.
157 // 2. The WSL optional component which contains the interface is not installed.
158 if (!wsl::windows::common::helpers::IsWindows11OrAbove() && !wsl::windows::common::helpers::IsWslSupportInterfacePresent())
159 {
160 THROW_HR(wsl::windows::common::helpers::IsWslOptionalComponentPresent() ? WSL_E_OS_NOT_SUPPORTED : WSL_E_WSL_OPTIONAL_COMPONENT_REQUIRED);
161 }
162
163 auto retry_pred = []() {
164 const auto errorCode = wil::ResultFromCaughtException();
165
166 return errorCode == HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) || errorCode == REGDB_E_CLASSNOTREG;
167 };
168
169 wsl::shared::retry::RetryWithTimeout<void>(
170 [this]() { m_userSession = wil::CoCreateInstance<LxssUserSession, ILxssUserSession>(CLSCTX_LOCAL_SERVER); },
171 std::chrono::seconds(1),
172 std::chrono::minutes(1),
173 retry_pred);
174
175 // Query client security interface.
176 auto clientSecurity = m_userSession.query<IClientSecurity>();
177
178 // Get the current proxy blanket settings.
179 DWORD authnSvc, authzSvc, authnLvl, capabilities;
180 THROW_IF_FAILED(clientSecurity->QueryBlanket(m_userSession.get(), &authnSvc, &authzSvc, NULL, &authnLvl, NULL, NULL, &capabilities));
181
182 // Make sure that dynamic cloaking is used.
183 WI_ClearFlag(capabilities, EOAC_STATIC_CLOAKING);
184 WI_SetFlag(capabilities, EOAC_DYNAMIC_CLOAKING);
185 THROW_IF_FAILED(clientSecurity->SetBlanket(
186 m_userSession.get(), authnSvc, authzSvc, NULL, authnLvl, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, capabilities));
187 }
188
189 wsl::windows::common::SvcComm::~SvcComm()
190 {
191 }
192
193 void wsl::windows::common::SvcComm::ConfigureDistribution(_In_opt_ LPCGUID DistroGuid, _In_ ULONG DefaultUid, _In_ ULONG Flags) const
194 {
195 ClientExecutionContext context;
196 THROW_IF_FAILED(m_userSession->ConfigureDistribution(DistroGuid, DefaultUid, Flags, context.OutError()));
197 }
198
199 void wsl::windows::common::SvcComm::CreateInstance(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags)
200 {
201 ClientExecutionContext context;
202 THROW_IF_FAILED(CreateInstanceNoThrow(DistroGuid, Flags, context.OutError()));
203 }
204
205 HRESULT
206 wsl::windows::common::SvcComm::CreateInstanceNoThrow(_In_opt_ LPCGUID DistroGuid, _In_ ULONG Flags, _Out_ LXSS_ERROR_INFO* Error) const
207 {
208 return m_userSession->CreateInstance(DistroGuid, Flags, Error);
209 }
210
211 std::vector<LXSS_ENUMERATE_INFO> wsl::windows::common::SvcComm::EnumerateDistributions() const
212 {
213 ExecutionContext enumerateDistroContext(Context::EnumerateDistros);
214 ClientExecutionContext context;
215
216 wil::unique_cotaskmem_array_ptr<LXSS_ENUMERATE_INFO> Distributions;
217 THROW_IF_FAILED(m_userSession->EnumerateDistributions(Distributions.size_address<ULONG>(), &Distributions, context.OutError()));
218
219 std::vector<LXSS_ENUMERATE_INFO> DistributionList;
220 for (size_t Index = 0; Index < Distributions.size(); Index += 1)
221 {
222 DistributionList.push_back(Distributions[Index]);
223 }
224
225 return DistributionList;
226 }
227
228 HRESULT
229 wsl::windows::common::SvcComm::ExportDistribution(_In_opt_ LPCGUID DistroGuid, _In_ HANDLE FileHandle, _In_ ULONG Flags) const
230 {
231 ClientExecutionContext context;
232
233 // Create a pipe for reading errors from bsdtar.
234 wil::unique_handle stdErrRead;
235 wil::unique_handle stdErrWrite;
236 THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&stdErrRead, &stdErrWrite, nullptr, 0));
237
238 relay::ScopedRelay stdErrRelay(
239 std::move(stdErrRead), GetStdHandle(STD_ERROR_HANDLE), LX_RELAY_BUFFER_SIZE, [&stdErrWrite]() { stdErrWrite.reset(); });
240
241 HRESULT result = E_FAIL;
242 if (GetFileType(FileHandle) != FILE_TYPE_PIPE)
243 {
244 result = m_userSession->ExportDistribution(DistroGuid, FileHandle, stdErrWrite.get(), Flags, context.OutError());
245 }
246 else
247 {
248 result = m_userSession->ExportDistributionPipe(DistroGuid, FileHandle, stdErrWrite.get(), Flags, context.OutError());
249 }
250
251 stdErrWrite.reset();
252 stdErrRelay.Sync();
253
254 RETURN_HR(result);
255 }
256
257 void wsl::windows::common::SvcComm::GetDistributionConfiguration(
258 _In_opt_ LPCGUID DistroGuid,
259 _Out_ LPWSTR* Name,
260 _Out_ ULONG* Version,
261 _Out_ ULONG* DefaultUid,
262 _Out_ ULONG* DefaultEnvironmentCount,
263 _Out_ LPSTR** DefaultEnvironment,
264 _Out_ ULONG* Flags) const
265 {
266 ClientExecutionContext context;
267
268 THROW_IF_FAILED(m_userSession->GetDistributionConfiguration(
269 DistroGuid, Name, Version, DefaultUid, DefaultEnvironmentCount, DefaultEnvironment, Flags, context.OutError()));
270 }
271
272 DWORD
273 wsl::windows::common::SvcComm::LaunchProcess(
274 _In_opt_ LPCGUID DistroGuid,
275 _In_opt_ LPCWSTR Filename,
276 _In_ int Argc,
277 _In_reads_(Argc) LPCWSTR Argv[],
278 _In_ ULONG LaunchFlags,
279 _In_opt_ PCWSTR Username,
280 _In_opt_ PCWSTR CurrentWorkingDirectory,
281 _In_ DWORD Timeout) const
282 {
283 ClientExecutionContext context;
284
285 //
286 // Parse the input arguments.
287 //
288
289 DWORD ExitCode = 1;
290 CreateProcessArguments Parsed(Filename, Argc, Argv, LaunchFlags, CurrentWorkingDirectory);
291
292 //
293 // Create the process.
294 //
295
296 ConsoleState Io;
297 Io.SetInteractiveMode();
298 COORD WindowSize = Io.GetWindowSize();
299 ULONG Flags = LXSS_CREATE_INSTANCE_FLAGS_ALLOW_FS_UPGRADE;
300 if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_USE_SYSTEM_DISTRO))
301 {
302 WI_SetFlag(Flags, LXSS_CREATE_INSTANCE_FLAGS_USE_SYSTEM_DISTRO);
303 }
304
305 if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_SHELL_LOGIN))
306 {
307 WI_SetFlag(Flags, LXSS_CREATE_INSTANCE_FLAGS_SHELL_LOGIN);
308 }
309
310 // This method is also used by Terminal.
311 // See: https://github.com/microsoft/terminal/blob/ec434e3fba2a6ef254123e31f5257c25b04f2547/src/tools/ConsoleBench/conhost.cpp#L159-L164
312 HANDLE console = NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->Reserved2[0];
313
314 LXSS_STD_HANDLES StdHandles{};
315 const HANDLE InputHandle = GetStdHandle(STD_INPUT_HANDLE);
316 const bool IsConsoleInput = wsl::windows::common::wslutil::IsConsoleHandle(InputHandle);
317 StdHandles.StdIn.HandleType = IsConsoleInput ? LxssHandleConsole : LxssHandleInput;
318 StdHandles.StdIn.Handle = IsConsoleInput ? LXSS_HANDLE_USE_CONSOLE : HandleToUlong(InputHandle);
319 const HANDLE OutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
320 const bool IsConsoleOutput = wsl::windows::common::wslutil::IsConsoleHandle(OutputHandle);
321 StdHandles.StdOut.HandleType = IsConsoleOutput ? LxssHandleConsole : LxssHandleOutput;
322 StdHandles.StdOut.Handle = IsConsoleOutput ? LXSS_HANDLE_USE_CONSOLE : HandleToUlong(OutputHandle);
323 const HANDLE ErrorHandle = GetStdHandle(STD_ERROR_HANDLE);
324 const bool IsConsoleError = wsl::windows::common::wslutil::IsConsoleHandle(ErrorHandle);
325 StdHandles.StdErr.HandleType = IsConsoleError ? LxssHandleConsole : LxssHandleOutput;
326 StdHandles.StdErr.Handle = IsConsoleError ? LXSS_HANDLE_USE_CONSOLE : HandleToUlong(ErrorHandle);
327
328 GUID DistributionId;
329 GUID InstanceId;
330 wil::unique_handle ProcessHandle;
331 wil::unique_handle ServerPortHandle;
332 wil::unique_handle StdInSocket;
333 wil::unique_handle StdOutSocket;
334 wil::unique_handle StdErrSocket;
335 wil::unique_handle ControlSocket;
336 wil::unique_handle InteropSocket;
337
338 if (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == FILE_TYPE_CHAR)
339 {
340 context.EnableInteractiveWarnings();
341 }
342
343 THROW_IF_FAILED(m_userSession->CreateLxProcess(
344 DistroGuid,
345 Parsed.FilenameString.empty() ? nullptr : Parsed.FilenameString.c_str(),
346 Argc,
347 Parsed.CommandLineStrings.data(),
348 Parsed.CurrentWorkingDirectory.empty() ? nullptr : Parsed.CurrentWorkingDirectory.c_str(),
349 Parsed.NtPath.empty() ? nullptr : Parsed.NtPath.c_str(),
350 Parsed.NtEnvironment.get(),
351 static_cast<ULONG>(Parsed.NtEnvironmentLength),
352 Username,
353 WindowSize.X,
354 WindowSize.Y,
355 HandleToUlong(console),
356 &StdHandles,
357 Flags,
358 &DistributionId,
359 &InstanceId,
360 &ProcessHandle,
361 &ServerPortHandle,
362 &StdInSocket,
363 &StdOutSocket,
364 &StdErrSocket,
365 &ControlSocket,
366 &InteropSocket,
367 context.OutError()));
368
369 context.FlushWarnings();
370
371 WI_ASSERT((!ARGUMENT_PRESENT(DistroGuid)) || (IsEqualGUID(*DistroGuid, DistributionId)));
372
373 //
374 // If a process handle was returned, this is a WSL process. Otherwise, the
375 // process is running in a utility VM.
376 //
377
378 if (ProcessHandle)
379 {
380 //
381 // Mark the process handle as uninheritable.
382 //
383
384 helpers::SetHandleInheritable(ProcessHandle.get(), false);
385
386 //
387 // If the caller requested interop and a server port was created, start
388 // the interop worker thread and background wslhost process.
389 //
390
391 if ((WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_ENABLE_INTEROP)) && (ServerPortHandle))
392 {
393 try
394 {
395 InitializeInterop(ServerPortHandle.get(), DistributionId);
396 }
397 CATCH_LOG()
398 }
399
400 ServerPortHandle.reset();
401
402 //
403 // Wait for the launched process to exit and return the process exit
404 // code.
405 //
406
407 LXBUS_IPC_LX_PROCESS_WAIT_FOR_TERMINATION_PARAMETERS Parameters{};
408 Parameters.Input.TimeoutMs = Timeout;
409 THROW_IF_NTSTATUS_FAILED(LxBusClientWaitForLxProcess(ProcessHandle.get(), &Parameters));
410
411 if (LXSS_WIFEXITED(Parameters.Output.ExitStatus))
412 {
413 Parameters.Output.ExitStatus = LXSS_WEXITSTATUS(Parameters.Output.ExitStatus);
414 }
415
416 ExitCode = Parameters.Output.ExitStatus;
417 }
418 else
419 {
420 //
421 // Create stdin, stdout and stderr worker threads.
422 //
423
424 std::thread StdOutWorker;
425 std::thread StdErrWorker;
426 auto ExitEvent = wil::unique_event(wil::EventOptions::ManualReset);
427 auto outWorkerExit = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&StdOutWorker, &StdErrWorker, &ExitEvent] {
428 ExitEvent.SetEvent();
429 if (StdOutWorker.joinable())
430 {
431 StdOutWorker.join();
432 }
433
434 if (StdErrWorker.joinable())
435 {
436 StdErrWorker.join();
437 }
438 });
439
440 // This channel needs to be a shared_ptr because closing it will cause the linux relay to exit so we should keep it open
441 // even after the stdin thread exits, but we can't keep give a simple reference to that thread because the main thread
442 // might return from this method before the stdin relay thread does.
443
444 auto ControlChannel = std::make_shared<wsl::shared::SocketChannel>(
445 wil::unique_socket{reinterpret_cast<SOCKET>(ControlSocket.release())}, "Control");
446
447 auto StdIn = GetStdHandle(STD_INPUT_HANDLE);
448 if (IS_VALID_HANDLE(StdIn))
449 {
450 std::thread([StdIn, StdInSocket = std::move(StdInSocket), ControlChannel = ControlChannel, ExitHandle = ExitEvent.get(), Io = &Io]() mutable {
451 auto updateTerminal = [&]() {
452 //
453 // Query the window size and send an update message via the
454 // control channel.
455 //
456 if (ControlChannel)
457 {
458 auto WindowSize = Io->GetWindowSize();
459
460 LX_INIT_WINDOW_SIZE_CHANGED WindowSizeMessage{};
461 WindowSizeMessage.Header.MessageType = LxInitMessageWindowSizeChanged;
462 WindowSizeMessage.Header.MessageSize = sizeof(WindowSizeMessage);
463 WindowSizeMessage.Columns = WindowSize.X;
464 WindowSizeMessage.Rows = WindowSize.Y;
465
466 try
467 {
468 ControlChannel->SendMessage(WindowSizeMessage);
469 }
470 CATCH_LOG();
471 }
472 };
473
474 wsl::windows::common::relay::StandardInputRelay(StdIn, StdInSocket.get(), updateTerminal, ExitHandle);
475 }).detach();
476 }
477
478 auto StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
479 StdOutWorker = relay::CreateThread(std::move(StdOutSocket), IS_VALID_HANDLE(StdOut) ? StdOut : nullptr);
480 auto StdErr = GetStdHandle(STD_ERROR_HANDLE);
481 StdErrWorker = relay::CreateThread(std::move(StdErrSocket), IS_VALID_HANDLE(StdErr) ? StdErr : nullptr);
482
483 //
484 // Spawn wslhost to handle interop requests from processes that have
485 // been backgrounded and their console window has been closed.
486 //
487
488 if (WI_IsFlagSet(LaunchFlags, LXSS_LAUNCH_FLAG_ENABLE_INTEROP))
489 {
490 try
491 {
492 SpawnWslHost(InteropSocket.get(), DistributionId, &InstanceId);
493 }
494 CATCH_LOG()
495 }
496
497 //
498 // Begin reading messages from the utility vm.
499 //
500
501 wsl::shared::SocketChannel InteropChannel{
502 wil::unique_socket{reinterpret_cast<SOCKET>(InteropSocket.release())}, "Interop"};
503 ExitCode = interop::VmModeWorkerThread(InteropChannel, InstanceId);
504 }
505
506 return ExitCode;
507 }
508
509 GUID wsl::windows::common::SvcComm::GetDefaultDistribution() const
510 {
511 ClientExecutionContext context;
512 GUID DistroId;
513 THROW_IF_FAILED(m_userSession->GetDefaultDistribution(context.OutError(), &DistroId));
514
515 return DistroId;
516 }
517
518 ULONG
519 wsl::windows::common::SvcComm::GetDistributionFlags(_In_opt_ LPCGUID DistroGuid) const
520 {
521 ClientExecutionContext context;
522
523 wil::unique_cotaskmem_string Name;
524 ULONG Version;
525 ULONG Uid;
526 wil::unique_cotaskmem_array_ptr<wil::unique_cotaskmem_ansistring> Environment;
527 ULONG Flags;
528 THROW_IF_FAILED(m_userSession->GetDistributionConfiguration(
529 DistroGuid, &Name, &Version, &Uid, Environment.size_address<ULONG>(), &Environment, &Flags, context.OutError()));
530
531 return Flags;
532 }
533
534 GUID wsl::windows::common::SvcComm::GetDistributionId(_In_ LPCWSTR Name, _In_ ULONG Flags) const
535 {
536 ClientExecutionContext context;
537
538 GUID DistroId;
539 THROW_IF_FAILED(m_userSession->GetDistributionId(Name, Flags, context.OutError(), &DistroId));
540
541 return DistroId;
542 }
543
544 GUID wsl::windows::common::SvcComm::ImportDistributionInplace(_In_ LPCWSTR Name, _In_ LPCWSTR VhdPath) const
545 {
546 ClientExecutionContext context;
547
548 GUID DistroGuid;
549 THROW_IF_FAILED(m_userSession->ImportDistributionInplace(Name, VhdPath, context.OutError(), &DistroGuid));
550
551 return DistroGuid;
552 }
553
554 void wsl::windows::common::SvcComm::MoveDistribution(_In_ const GUID& DistroGuid, _In_ LPCWSTR Location) const
555 {
556 ClientExecutionContext context;
557
558 THROW_IF_FAILED(m_userSession->MoveDistribution(&DistroGuid, Location, context.OutError()));
559 }
560
561 std::pair<GUID, wil::unique_cotaskmem_string> wsl::windows::common::SvcComm::RegisterDistribution(
562 _In_ LPCWSTR Name,
563 _In_ ULONG Version,
564 _In_ HANDLE FileHandle,
565 _In_ LPCWSTR TargetDirectory,
566 _In_ ULONG Flags,
567 _In_ std::optional<uint64_t> VhdSize,
568 _In_opt_ LPCWSTR PackageFamilyName) const
569 {
570 ClientExecutionContext context;
571
572 // Create a pipe for reading errors from bsdtar.
573 wil::unique_handle stdErrRead;
574 wil::unique_handle stdErrWrite;
575 THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&stdErrRead, &stdErrWrite, nullptr, 0));
576
577 relay::ScopedRelay stdErrRelay(
578 std::move(stdErrRead), GetStdHandle(STD_ERROR_HANDLE), LX_RELAY_BUFFER_SIZE, [&stdErrWrite]() { stdErrWrite.reset(); });
579
580 GUID DistroGuid{};
581 HRESULT Result = E_FAIL;
582 wil::unique_cotaskmem_string installedName;
583 if (GetFileType(FileHandle) != FILE_TYPE_PIPE)
584 {
585 Result = m_userSession->RegisterDistribution(
586 Name,
587 Version,
588 FileHandle,
589 stdErrWrite.get(),
590 TargetDirectory,
591 Flags,
592 VhdSize.value_or(0),
593 PackageFamilyName,
594 &installedName,
595 context.OutError(),
596 &DistroGuid);
597 }
598 else
599 {
600 Result = m_userSession->RegisterDistributionPipe(
601 Name,
602 Version,
603 FileHandle,
604 stdErrWrite.get(),
605 TargetDirectory,
606 Flags,
607 VhdSize.value_or(0),
608 PackageFamilyName,
609 &installedName,
610 context.OutError(),
611 &DistroGuid);
612 }
613
614 stdErrWrite.reset();
615 stdErrRelay.Sync();
616
617 THROW_IF_FAILED(Result);
618
619 return std::make_pair(DistroGuid, std::move(installedName));
620 }
621
622 void wsl::windows::common::SvcComm::SetDefaultDistribution(_In_ LPCGUID DistroGuid) const
623 {
624 ClientExecutionContext context;
625 THROW_IF_FAILED(m_userSession->SetDefaultDistribution(DistroGuid, context.OutError()));
626 }
627
628 HRESULT
629 wsl::windows::common::SvcComm::SetSparse(_In_ LPCGUID DistroGuid, _In_ BOOL Sparse, _In_ BOOL AllowUnsafe) const
630 {
631 ClientExecutionContext context;
632
633 RETURN_HR(m_userSession->SetSparse(DistroGuid, Sparse, AllowUnsafe, context.OutError()));
634 }
635
636 HRESULT
637 wsl::windows::common::SvcComm::ResizeDistribution(_In_ LPCGUID DistroGuid, _In_ ULONG64 NewSize) const
638 {
639 ClientExecutionContext context;
640
641 wil::unique_handle outputRead;
642 wil::unique_handle outputWrite;
643 THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&outputRead, &outputWrite, nullptr, 0));
644
645 relay::ScopedRelay outputRelay(
646 std::move(outputRead), GetStdHandle(STD_ERROR_HANDLE), LX_RELAY_BUFFER_SIZE, [&outputWrite]() { outputWrite.reset(); });
647
648 const auto result = m_userSession->ResizeDistribution(DistroGuid, outputWrite.get(), NewSize, context.OutError());
649
650 outputWrite.reset();
651 outputRelay.Sync();
652
653 RETURN_HR(result);
654 }
655
656 HRESULT
657 wsl::windows::common::SvcComm::CompactDistribution(_In_ LPCGUID DistroGuid) const
658 {
659 ClientExecutionContext context;
660 RETURN_HR(m_userSession->CompactDistribution(DistroGuid, context.OutError()));
661 }
662
663 HRESULT
664 wsl::windows::common::SvcComm::SetVersion(_In_ LPCGUID DistroGuid, _In_ ULONG Version) const
665 {
666 ClientExecutionContext context;
667
668 // Create a pipe for reading errors from bsdtar.
669 wil::unique_handle stdErrRead;
670 wil::unique_handle stdErrWrite;
671 THROW_IF_WIN32_BOOL_FALSE(CreatePipe(&stdErrRead, &stdErrWrite, nullptr, 0));
672
673 relay::ScopedRelay stdErrRelay(
674 std::move(stdErrRead), GetStdHandle(STD_ERROR_HANDLE), LX_RELAY_BUFFER_SIZE, [&stdErrWrite]() { stdErrWrite.reset(); });
675
676 RETURN_HR(m_userSession->SetVersion(DistroGuid, Version, stdErrWrite.get(), context.OutError()));
677 }
678
679 HRESULT
680 wsl::windows::common::SvcComm::AttachDisk(_In_ LPCWSTR Disk, _In_ ULONG Flags) const
681 {
682 ClientExecutionContext context;
683
684 RETURN_HR(m_userSession->AttachDisk(Disk, Flags, context.OutError()));
685 }
686
687 std::pair<int, int> wsl::windows::common::SvcComm::DetachDisk(_In_opt_ LPCWSTR Disk) const
688 {
689 ClientExecutionContext context;
690
691 int Result = -1;
692 int Step = 0;
693 THROW_IF_FAILED(m_userSession->DetachDisk(Disk, &Result, &Step, context.OutError()));
694
695 return std::make_pair(Result, Step);
696 }
697
698 wsl::windows::common::SvcComm::MountResult wsl::windows::common::SvcComm::MountDisk(
699 _In_ LPCWSTR Disk, _In_ ULONG Flags, _In_ ULONG PartitionIndex, _In_opt_ LPCWSTR Name, _In_opt_ LPCWSTR Type, _In_opt_ LPCWSTR Options) const
700 {
701 ClientExecutionContext context;
702
703 MountResult Result;
704 THROW_IF_FAILED(m_userSession->MountDisk(
705 Disk, Flags, PartitionIndex, Name, Type, Options, &Result.Result, &Result.Step, &Result.MountName, context.OutError()));
706
707 return Result;
708 }
709
710 void wsl::windows::common::SvcComm::Shutdown(_In_ bool Force) const
711 {
712 THROW_IF_FAILED(m_userSession->Shutdown(Force));
713 }
714
715 void wsl::windows::common::SvcComm::TerminateInstance(_In_opt_ LPCGUID DistroGuid) const
716 {
717 ClientExecutionContext context;
718
719 //
720 // If there is an instance running, terminate it.
721 //
722
723 THROW_IF_FAILED(m_userSession->TerminateDistribution(DistroGuid, context.OutError()));
724 }
725
726 void wsl::windows::common::SvcComm::UnregisterDistribution(_In_ LPCGUID DistroGuid) const
727 {
728 ClientExecutionContext context;
729 THROW_IF_FAILED(m_userSession->UnregisterDistribution(DistroGuid, context.OutError()));
730 }