master
cpp 616 lines 23.4 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 interop.cpp
8
9 Abstract:
10
11 This file contains interop function definitions.
12
13 --*/
14
15 #include "precomp.h"
16 #include "interop.hpp"
17 #include "HandleIO.h"
18 #include "helpers.hpp"
19 #include "socket.hpp"
20 #include "hvsocket.hpp"
21 #include "relay.hpp"
22 #include "LxssServerPort.h"
23 #include "LxssMessagePort.h"
24 #include <gsl/algorithm>
25 #include <gslhelpers.h>
26 #include "WslTelemetry.h"
27
28 namespace {
29
30 std::wstring BuildEnvironment(gsl::span<gsl::byte> EnvironmentData);
31
32 std::string FormatCommandLine(gsl::span<gsl::byte> CommandLineData, USHORT CommandLineCount);
33
34 struct CreateProcessResult;
35 DWORD ProcessInteropMessages(_In_ HANDLE CommunicationChannel, _Inout_ CreateProcessResult* Result);
36
37 struct CreateProcessParsed
38 {
39 CreateProcessParsed(_In_ const gsl::span<gsl::byte>& Common)
40 {
41 // Validate the message size and get spans to the various buffers. Note
42 // that the spans will be larger than the actual data since the message
43 // does not specify the size of each data element; the length is encoded
44 // via NULL termination.
45
46 const auto* Params = gslhelpers::try_get_struct<LX_INIT_CREATE_NT_PROCESS_COMMON>(Common);
47 THROW_HR_IF(E_INVALIDARG, !Params || (Common.size() < (Params->CommandLineOffset)));
48
49 // Parse the application name, command line, and current working directory
50 // and convert to unicode.
51
52 ApplicationName = wsl::shared::string::MultiByteToWide(wsl::shared::string::FromSpan(Common, Params->FilenameOffset));
53 const auto FormattedCommandLine = FormatCommandLine(Common.subspan(Params->CommandLineOffset), Params->CommandLineCount);
54 CommandLineBuffer = wsl::shared::string::MultiByteToWide(FormattedCommandLine);
55 const auto* Cwd = wsl::shared::string::FromSpan(Common, Params->CurrentWorkingDirectoryOffset);
56 if (strlen(Cwd) > 0)
57 {
58 CwdBuffer = wsl::shared::string::MultiByteToWide(Cwd);
59 }
60
61 // Construct an environment if one was provided.
62
63 if (Params->EnvironmentOffset > 0)
64 {
65 THROW_HR_IF(E_INVALIDARG, Common.size() < Params->EnvironmentOffset);
66
67 EnvironmentBuffer = BuildEnvironment(Common.subspan(Params->EnvironmentOffset));
68 }
69
70 Rows = Params->Rows;
71 Columns = Params->Columns;
72 CreatePseudoconsole = Params->CreatePseudoconsole;
73 }
74
75 LPWSTR CommandLine()
76 {
77 return CommandLineBuffer.data();
78 }
79
80 LPCWSTR Cwd() const
81 {
82 return CwdBuffer.empty() ? nullptr : CwdBuffer.c_str();
83 }
84
85 LPVOID Environment()
86 {
87 return EnvironmentBuffer.empty() ? nullptr : EnvironmentBuffer.data();
88 }
89
90 std::wstring ApplicationName{};
91 std::wstring CommandLineBuffer{};
92 std::wstring EnvironmentBuffer{};
93 std::wstring CwdBuffer{};
94 DWORD Rows{};
95 DWORD Columns{};
96 bool CreatePseudoconsole{};
97 };
98
99 struct CreateProcessResult
100 {
101 wil::unique_handle Process{};
102 int Status{};
103 unsigned int Flags{};
104 wsl::windows::common::helpers::unique_pseudo_console PseudoConsole{};
105 };
106
107 struct CreateProcessVmModeContext
108 {
109 GUID VmId{};
110 std::vector<gsl::byte> Buffer{};
111 };
112
113 std::wstring BuildEnvironment(gsl::span<gsl::byte> EnvironmentData)
114 {
115 std::map<std::wstring, std::wstring> Environment;
116
117 // Construct a map of the current environment strings.
118 const wsl::windows::common::helpers::unique_environment_strings EnvironmentStrings(GetEnvironmentStrings());
119 PCZZWSTR CurrentEnvironment = EnvironmentStrings.get();
120 while (*CurrentEnvironment)
121 {
122 const PCWSTR Divider = wcschr(CurrentEnvironment, '=');
123 THROW_HR_IF_NULL(E_UNEXPECTED, Divider);
124 std::wstring Key(CurrentEnvironment, Divider);
125 Environment[Key] = Divider + 1;
126 CurrentEnvironment += wcslen(CurrentEnvironment) + 1;
127 }
128
129 // Update the map with the Linux environment data.
130 for (;;)
131 {
132 std::string_view Variable = wsl::shared::string::FromSpan(EnvironmentData);
133 if (Variable.empty())
134 {
135 break;
136 }
137
138 const size_t Divider = Variable.find('=');
139 THROW_HR_IF(E_UNEXPECTED, Divider == Variable.npos);
140 std::wstring Key = wsl::shared::string::MultiByteToWide(std::string{Variable.substr(0, Divider)});
141 auto Value = Variable.substr(Divider + 1);
142 if (Value.empty())
143 {
144 Environment.erase(Key);
145 }
146 else
147 {
148 Environment[Key] = wsl::shared::string::MultiByteToWide(std::string{Value});
149 }
150
151 EnvironmentData = EnvironmentData.subspan(Variable.size() + 1);
152 }
153
154 // Construct a new environment block.
155 std::wstring Block;
156 for (const auto& Variable : Environment)
157 {
158 Block += Variable.first;
159 Block += L'=';
160 Block += Variable.second;
161 Block += L'\0';
162 }
163
164 return Block;
165 }
166
167 HRESULT GetProcessImageSubSystem(_In_ HANDLE Process, _Out_ ULONG* ImageSubsystem)
168 {
169 *ImageSubsystem = IMAGE_SUBSYSTEM_UNKNOWN;
170
171 PROCESS_BASIC_INFORMATION ProcessBasicInfo{};
172 RETURN_IF_NTSTATUS_FAILED(NtQueryInformationProcess(Process, ProcessBasicInformation, &ProcessBasicInfo, sizeof(ProcessBasicInfo), NULL));
173
174 // Terminal uses a similar method to read the PEB.
175 // See: https://github.com/microsoft/terminal/blob/ec434e3fba2a6ef254123e31f5257c25b04f2547/src/tools/ConsoleBench/conhost.cpp#L159-L164
176 PEB* dummyPeb = nullptr;
177 const auto offset = ((char*)&dummyPeb->Reserved9[24]) - (char*)dummyPeb;
178
179 SIZE_T BytesRead = 0;
180 RETURN_IF_WIN32_BOOL_FALSE(ReadProcessMemory(
181 Process, ((char*)ProcessBasicInfo.PebBaseAddress) + offset, ImageSubsystem, sizeof(*ImageSubsystem), &BytesRead));
182
183 if (BytesRead < sizeof(*ImageSubsystem))
184 {
185 *ImageSubsystem = IMAGE_SUBSYSTEM_UNKNOWN;
186 return E_UNEXPECTED;
187 }
188
189 return S_OK;
190 }
191
192 CreateProcessResult CreateProcess(_In_ CreateProcessParsed* Parsed, _In_ HANDLE StdIn, _In_ HANDLE StdOut, _In_ HANDLE StdErr)
193 {
194 wsl::windows::common::helpers::SetHandleInheritable(StdIn);
195 wsl::windows::common::helpers::SetHandleInheritable(StdOut);
196 wsl::windows::common::helpers::SetHandleInheritable(StdErr);
197
198 // N.B. Passing StartupFlags = 0 so that the cursor feedback is set to its default behavior.
199 // See: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa
200 wsl::windows::common::SubProcess process(Parsed->ApplicationName.c_str(), Parsed->CommandLine(), CREATE_UNICODE_ENVIRONMENT, 0);
201
202 CreateProcessResult Result{};
203 if (Parsed->CreatePseudoconsole)
204 {
205 const COORD Size{static_cast<SHORT>(Parsed->Columns), static_cast<SHORT>(Parsed->Rows)};
206 THROW_IF_FAILED(CreatePseudoConsole(Size, StdIn, StdOut, PSEUDOCONSOLE_INHERIT_CURSOR, &Result.PseudoConsole));
207
208 process.SetPseudoConsole(Result.PseudoConsole.get());
209 }
210 else
211 {
212 // In the case where this is a console process, don't create a new console window.
213 // This is useful for wslg.exe, when a console program is created through interop,
214 // we don't want to create a new console window.
215 // N.B. CREATE_NO_WINDOW only applies to console executables, so GUI applications
216 // are not affected by this flag.
217 process.SetFlags(CREATE_NO_WINDOW);
218 process.SetStdHandles(StdIn, StdOut, StdErr);
219 }
220
221 // Set the breakaway override flag to ensure that processes created via interop are not packaged.
222 process.SetDesktopAppPolicy(PROCESS_CREATION_DESKTOP_APP_BREAKAWAY_OVERRIDE);
223 process.SetEnvironment(Parsed->Environment());
224 process.SetWorkingDirectory(Parsed->Cwd());
225
226 try
227 {
228 Result.Process = process.Start();
229 // Check if the process that was launched is a graphical application.
230 // Non-graphical applications should be terminated when the file
231 // descriptor that represents the process is closed.
232 ULONG ImageSubsystem = IMAGE_SUBSYSTEM_UNKNOWN;
233 LOG_IF_FAILED(GetProcessImageSubSystem(Result.Process.get(), &ImageSubsystem));
234 const bool IsGuiApp = (ImageSubsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI);
235 WI_SetFlagIf(Result.Flags, LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION, IsGuiApp);
236 }
237 catch (...)
238 {
239 const DWORD LastError = wil::ResultFromCaughtException();
240 switch (LastError)
241 {
242 case ERROR_FILE_NOT_FOUND:
243 Result.Status = -LX_ENOENT;
244 break;
245
246 case ERROR_ELEVATION_REQUIRED:
247 Result.Status = -LX_EACCES;
248 break;
249
250 default:
251 Result.Status = -LX_EINVAL;
252 LOG_IF_WIN32_ERROR_MSG(LastError, "CreateProcessW");
253 break;
254 }
255 }
256
257 return Result;
258 }
259
260 void CreateProcessVmMode(_In_ const GUID& VmId, _In_ const gsl::span<gsl::byte>& Buffer)
261 {
262 // Create a worker thread to service the interop request.
263 //
264 // N.B. The worker thread takes ownership of the arguments.
265 auto Arguments = std::make_unique<CreateProcessVmModeContext>();
266 Arguments->VmId = VmId;
267 Arguments->Buffer.resize(Buffer.size());
268 gsl::copy(Buffer, gsl::make_span(Arguments->Buffer));
269 std::thread([Arguments = std::move(Arguments)]() {
270 try
271 {
272 wsl::windows::common::wslutil::SetThreadDescription(L"Interop");
273 auto Message = gsl::make_span(Arguments->Buffer);
274 auto* Params = gslhelpers::try_get_struct<LX_INIT_CREATE_NT_PROCESS_UTILITY_VM>(Message);
275 THROW_HR_IF(E_INVALIDARG, !Params || (Params->Header.MessageType != LxInitMessageCreateProcessUtilityVm));
276
277 // Parse the message.
278 CreateProcessParsed Parsed(Message.subspan(offsetof(LX_INIT_CREATE_NT_PROCESS_UTILITY_VM, Common)));
279
280 // Establish connections on the specified port.
281 static_assert(LX_INIT_CREATE_NT_PROCESS_SOCKETS == 4);
282
283 wil::unique_socket Sockets[LX_INIT_CREATE_NT_PROCESS_SOCKETS];
284 for (ULONG Index = 0; Index < RTL_NUMBER_OF(Sockets); Index += 1)
285 {
286 Sockets[Index] = wsl::windows::common::hvsocket::Connect(Arguments->VmId, Params->Port);
287 }
288
289 // Clean up relay threads.
290 //
291 // N.B. This must be declared before the stdin / stdout / stderr handles so they go out of scope first.
292 std::vector<std::thread> Relays;
293 auto CancelIo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&Relays] {
294 for (auto& Relay : Relays)
295 {
296 if (Relay.joinable())
297 {
298 Relay.join();
299 }
300 }
301 });
302
303 // If a pseudoconsole is not being used, create hvsocket / pipe relays.
304 wil::unique_handle StdIn{reinterpret_cast<HANDLE>(Sockets[0].release())};
305 wil::unique_handle StdOut{reinterpret_cast<HANDLE>(Sockets[1].release())};
306 wil::unique_handle StdErr{reinterpret_cast<HANDLE>(Sockets[2].release())};
307 if (Parsed.CreatePseudoconsole == FALSE)
308 {
309 auto Pipe = wsl::windows::common::wslutil::OpenAnonymousPipe(0, false, true);
310 Relays.push_back(wsl::windows::common::relay::CreateThread(std::move(StdIn), wil::unique_handle{Pipe.second.release()}));
311 StdIn.reset(Pipe.first.release());
312
313 Pipe = wsl::windows::common::wslutil::OpenAnonymousPipe(0, true, false);
314 Relays.push_back(wsl::windows::common::relay::CreateThread(wil::unique_handle{Pipe.first.release()}, std::move(StdOut)));
315 StdOut.reset(Pipe.second.release());
316
317 Pipe = wsl::windows::common::wslutil::OpenAnonymousPipe(0, true, false);
318 Relays.push_back(wsl::windows::common::relay::CreateThread(wil::unique_handle{Pipe.first.release()}, std::move(StdErr)));
319 StdErr.reset(Pipe.second.release());
320 }
321
322 // Launch the process and write the status via the control channel.
323 auto Result = CreateProcess(&Parsed, StdIn.get(), StdOut.get(), StdErr.get());
324 LX_INIT_CREATE_PROCESS_RESPONSE Response{};
325 Response.Header.MessageType = LxInitMessageCreateProcessResponse;
326 Response.Header.MessageSize = sizeof(Response);
327 Response.Flags = Result.Flags;
328 Response.Result = Result.Status;
329 wsl::windows::common::socket::Send(Sockets[3].get(), gslhelpers::struct_as_bytes(Response));
330 if (Result.Status == 0)
331 {
332 // Process messages from the binfmt interpreter and wait for the process to exit.
333 LX_INIT_PROCESS_EXIT_STATUS ExitStatus{};
334 ExitStatus.Header.MessageType = LxInitMessageExitStatus;
335 ExitStatus.Header.MessageSize = sizeof(ExitStatus);
336 ExitStatus.ExitCode = ProcessInteropMessages(reinterpret_cast<HANDLE>(Sockets[3].get()), &Result);
337
338 // Write the exit status to the binfmt interpreter.
339 wsl::windows::common::socket::Send(Sockets[3].get(), gslhelpers::struct_as_bytes(ExitStatus));
340 }
341 }
342 CATCH_LOG()
343 }).detach();
344 }
345
346 std::string FormatCommandLine(gsl::span<gsl::byte> CommandLineData, USHORT CommandLineCount)
347 {
348 // Concatenate all of the command line arguments into a single string for
349 // the CreateProcess api.
350 //
351 // N.B. Any empty arguments or arguments that contain whitespace must be
352 // encapsulated in quotes. Quotes must also be escaped according to
353 // standard command-line parsing rules:
354 // https://msdn.microsoft.com/en-us/library/17w5ykft.aspx.
355 //
356 // This logic is largely taken from AppendQuotedForWindows in
357 // hcsdiag.cpp. In the future it would make sense to merge this
358 // functionality.
359 std::string CommandLine;
360 for (USHORT Index = 0; Index < CommandLineCount; Index += 1)
361 {
362 std::string_view Buffer = wsl::shared::string::FromSpan(CommandLineData);
363 if (!Buffer.empty() && Buffer.find_first_of(" \t\r\n\"") == Buffer.npos)
364 {
365 CommandLine.append(Buffer);
366 }
367 else
368 {
369 CommandLine += '"';
370 size_t BackslashCount = 0;
371 for (const char Ch : Buffer)
372 {
373 switch (Ch)
374 {
375 case '"':
376 CommandLine.append(((BackslashCount * 2) + 1), '\\');
377 BackslashCount = 0;
378 CommandLine += '"';
379 break;
380
381 case '\\':
382 BackslashCount += 1;
383 break;
384
385 default:
386 CommandLine.append(BackslashCount, '\\');
387 BackslashCount = 0;
388 CommandLine += Ch;
389 break;
390 }
391 }
392
393 CommandLine.append(BackslashCount * 2, '\\');
394 CommandLine += '"';
395 }
396
397 // Add a space between command line arguments.
398 if (Index < CommandLineCount - 1)
399 {
400 CommandLine += ' ';
401 }
402
403 CommandLineData = CommandLineData.subspan(Buffer.size() + 1);
404 }
405
406 return CommandLine;
407 }
408
409 DWORD
410 ProcessInteropMessages(_In_ HANDLE MessageHandle, _Inout_ CreateProcessResult* Result)
411 {
412 namespace io = wsl::windows::common::io;
413
414 DWORD exitCode = 1;
415 std::vector<char> pending;
416
417 static_assert(sizeof(LX_INIT_WINDOW_SIZE_CHANGED) % alignof(LX_INIT_WINDOW_SIZE_CHANGED) == 0);
418
419 auto processExit = [&] {
420 THROW_IF_WIN32_BOOL_FALSE(GetExitCodeProcess(Result->Process.get(), &exitCode));
421
422 // Close the pseudoconsole, this causes all pending data to be flushed.
423 Result->PseudoConsole.reset();
424 };
425
426 io::MultiHandleWait wait;
427 wait.AddHandle(
428 std::make_unique<io::ReadHandle>(
429 io::HandleWrapper{MessageHandle},
430 [&](const gsl::span<char>& input) {
431 if (input.empty())
432 {
433 const DWORD waitStatus = WaitForSingleObject(Result->Process.get(), 0);
434 if (waitStatus == WAIT_OBJECT_0)
435 {
436 processExit();
437 }
438 else
439 {
440 THROW_HR_IF(E_UNEXPECTED, waitStatus != WAIT_TIMEOUT);
441 if (WI_IsFlagClear(Result->Flags, LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION))
442 {
443 THROW_IF_WIN32_BOOL_FALSE(TerminateProcess(Result->Process.get(), 1));
444 }
445 }
446
447 return;
448 }
449
450 std::vector<char> stitchedInput;
451 auto remaining = input;
452 if (!pending.empty())
453 {
454 stitchedInput.reserve(pending.size() + input.size());
455 stitchedInput.insert(stitchedInput.end(), pending.begin(), pending.end());
456 stitchedInput.insert(stitchedInput.end(), input.begin(), input.end());
457 pending.clear();
458 remaining = gsl::make_span(stitchedInput);
459 }
460
461 while (remaining.size() >= sizeof(LX_INIT_WINDOW_SIZE_CHANGED))
462 {
463 const auto* message = gslhelpers::get_struct<const LX_INIT_WINDOW_SIZE_CHANGED>(remaining);
464 THROW_HR_IF(
465 E_UNEXPECTED,
466 (message->Header.MessageType != LxInitMessageWindowSizeChanged) || (message->Header.MessageSize != sizeof(*message)));
467
468 const COORD size{static_cast<SHORT>(message->Columns), static_cast<SHORT>(message->Rows)};
469 THROW_IF_FAILED(ResizePseudoConsole(Result->PseudoConsole.get(), size));
470 remaining = remaining.subspan(sizeof(*message));
471 }
472
473 pending.assign(remaining.begin(), remaining.end());
474 }),
475 io::MultiHandleWait::CancelOnCompleted);
476
477 wait.AddHandle(std::make_unique<io::EventHandle>(io::HandleWrapper{Result->Process.get()}, processExit), io::MultiHandleWait::CancelOnCompleted);
478
479 wait.Run(std::nullopt);
480 return exitCode;
481 }
482
483 } // namespace
484
485 void wsl::windows::common::interop::WorkerThread(_In_ wil::unique_handle&& ServerPortHandle)
486 {
487 // This thread waits for connections and processes create process messages.
488 //
489 // N.B. This thread lives until the main thread of the process exits.
490 //
491 // TODO_LX: Wait for connection blocks in the driver until the server port
492 // is closed. The wait for connection ioctl should be moved to
493 // async so this thread can wait on the server port and a second
494 // event indicating that the thread should exit.
495
496 LxssServerPort ServerPort(std::move(ServerPortHandle));
497 for (;;)
498 {
499 try
500 {
501 // Wait for a client to connect, break out of the loop on disconnect.
502 std::unique_ptr<LxssMessagePort> MessagePort;
503 if (!NT_SUCCESS(ServerPort.WaitForConnectionNoThrow(&MessagePort)))
504 {
505 break;
506 }
507
508 std::thread([MessagePort = std::move(MessagePort)]() mutable {
509 try
510 {
511 // Read the create process request from the client.
512 auto CreateProcessMessage = MessagePort->Receive();
513 const auto Message = gsl::make_span(CreateProcessMessage);
514 const auto* Params = gslhelpers::try_get_struct<LX_INIT_CREATE_NT_PROCESS>(Message);
515 THROW_HR_IF(E_INVALIDARG, !Params || (Params->Header.MessageType != LxInitMessageCreateProcess));
516
517 // Parse the message.
518 CreateProcessParsed Parsed(Message.subspan(offsetof(LX_INIT_CREATE_NT_PROCESS, Common)));
519
520 // Unmarshal the handles to be used as stdin / stdout / stederr and mark
521 // them as inheritable.
522 static_assert(LX_INIT_STD_FD_COUNT == 3);
523
524 wil::unique_handle StdHandles[LX_INIT_STD_FD_COUNT];
525 for (ULONG Index = 0; Index < LX_INIT_STD_FD_COUNT; Index += 1)
526 {
527 StdHandles[Index] = MessagePort->UnmarshalVfsFile(Params->StdFdIds[Index]);
528 }
529
530 // Create the signal pipe to handle resize requests.
531 auto SignalPipe = wsl::windows::common::wslutil::OpenAnonymousPipe(0, true, true);
532
533 // Launch the process.
534 auto Result = CreateProcess(&Parsed, StdHandles[0].get(), StdHandles[1].get(), StdHandles[2].get());
535
536 // Send a response back to the init daemon.
537 LX_INIT_CREATE_PROCESS_RESPONSE Response{};
538 Response.Header.MessageType = LxInitMessageCreateProcessResponse;
539 Response.Header.MessageSize = sizeof(Response);
540 Response.Flags = Result.Flags;
541 Response.Result = Result.Status;
542 if (Result.Status == 0)
543 {
544 // Marshal the write end of the signal pipe.
545 const LXBUS_IPC_MESSAGE_MARSHAL_HANDLE_DATA HandleData{HandleToUlong(SignalPipe.second.get()), LxBusIpcMarshalHandleTypeOutput};
546 Response.SignalPipeId = MessagePort->MarshalHandle(&HandleData);
547 SignalPipe.second.reset();
548
549 // Write the response to the binfmt interpreter.
550 MessagePort->Send(&Response, sizeof(Response));
551
552 // Process messages from the binfmt interpreter and wait for the
553 // process to exit.
554 LX_INIT_PROCESS_EXIT_STATUS ExitStatus{};
555 ExitStatus.Header.MessageType = LxInitMessageExitStatus;
556 ExitStatus.Header.MessageSize = sizeof(ExitStatus);
557 ExitStatus.ExitCode = ProcessInteropMessages(SignalPipe.first.get(), &Result);
558
559 // Write the exit status to the binfmt interpreter.
560 MessagePort->Send(&ExitStatus, sizeof(ExitStatus));
561 }
562 else
563 {
564 MessagePort->Send(&Response, sizeof(Response));
565 }
566 }
567 CATCH_LOG()
568 }).detach();
569 }
570 CATCH_LOG()
571 }
572 }
573
574 DWORD
575 wsl::windows::common::interop::VmModeWorkerThread(_In_ wsl::shared::SocketChannel& Channel, _In_ const GUID& VmId, _In_ bool IgnoreExit)
576 {
577 std::vector<gsl::byte> Buffer;
578
579 for (;;)
580 {
581 auto [Message, Span] = Channel.ReceiveMessageOrClosed<MESSAGE_HEADER>();
582 if (Message == nullptr)
583 {
584 break;
585 }
586
587 switch (Message->MessageType)
588 {
589 case LxInitMessageExitStatus:
590 {
591 const auto* ExitStatusMessage = gslhelpers::try_get_struct<LX_INIT_PROCESS_EXIT_STATUS>(Span);
592 THROW_HR_IF(E_INVALIDARG, !ExitStatusMessage);
593
594 Channel.SendMessage<LX_INIT_PROCESS_EXIT_STATUS>(Span);
595
596 if (!IgnoreExit)
597 {
598 return ExitStatusMessage->ExitCode;
599 }
600
601 break;
602 }
603
604 case LxInitMessageCreateProcessUtilityVm:
605 THROW_HR_IF(E_INVALIDARG, (Span.size() < sizeof(LX_INIT_CREATE_PROCESS_UTILITY_VM)));
606
607 CreateProcessVmMode(VmId, Span);
608 break;
609
610 default:
611 THROW_HR_MSG(E_UNEXPECTED, "Unexpected message %d", Message->MessageType);
612 }
613 }
614
615 return 1;
616 }