master
cpp 1,067 lines 26.6 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 binfmt.c
8
9 Abstract:
10
11 This file contains definitions for the NT interop binfmt interpreter.
12
13 --*/
14
15 #include <lxbusapi.h>
16 #include <sys/signalfd.h>
17 #include <pty.h>
18 #include <locale.h>
19 #include <signal.h>
20 #include "common.h"
21 #include "binfmt.h"
22 #include "wslpath.h"
23 #include <libgen.h>
24 #include "util.h"
25 #include "SocketChannel.h"
26
27 #define ACCEPT_TIMEOUT (10 * 1000)
28
29 #define LOG_STDERR(_str, ...) \
30 { \
31 fprintf(stderr, _str ": %s\n", ##__VA_ARGS__, (g_Locale ? strerror_l(errno, g_Locale) : strerror(errno))); \
32 }
33
34 int g_ConsoleFd = -1;
35 struct termios g_ConsoleInfoBackup;
36 locale_t g_Locale;
37
38 void CreateNtProcessConfigureConsole(PLX_INIT_CREATE_NT_PROCESS_COMMON Common);
39
40 std::vector<gsl::byte> CreateNtProcessMessage(LX_MESSAGE_TYPE MessageType, int Argc, char* Argv[]);
41
42 int CreateNtProcessUtilityVm(int Argc, char* Argv[]);
43
44 int CreateNtProcessWsl(int Argc, char* Argv[]);
45
46 bool HasOpenFileDescriptors(struct pollfd* PollDescriptors, int PollDescriptorSize);
47
48 void RestoreConsoleState(void);
49
50 void WindowSizeChanged(int SignalChannelFd);
51
52 int CreateNtProcess(int Argc, char* Argv[])
53
54 /*++
55
56 Routine Description:
57
58 This routine issues a create NT process request.
59
60 Arguments:
61
62 Argc - Supplies the argument count.
63
64 Argv - Supplies the command line arguments.
65
66 Return Value:
67
68 The exit code of the launched process on success, 1 on failure.
69
70 --*/
71
72 {
73 //
74 // The first argument will be the path of the binfmt interpreter, the second
75 // argument will be the full filename of the Windows binary.
76 //
77 // N.B. The binfmt interpreter is registered with the 'P' flag which preserves
78 // Argv[0] by adding it to the command line after the path of the Windows binary.
79 // https://en.wikipedia.org/wiki/Binfmt_misc
80 //
81
82 int ExitCode = 1;
83 if (Argc <= 1)
84 {
85 return ExitCode;
86 }
87
88 //
89 // Initialize a locale for localized error messages.
90 //
91 // N.B. Failure to initialize the locale is non-fatal.
92 //
93
94 g_Locale = newlocale(LC_ALL_MASK, "", NULL);
95
96 //
97 // Check if the binary is being run on WSL or in a Utility VM.
98 //
99
100 if (!UtilIsUtilityVm())
101 {
102 ExitCode = CreateNtProcessWsl(Argc, Argv);
103 }
104 else
105 {
106 ExitCode = CreateNtProcessUtilityVm(Argc, Argv);
107 }
108
109 RestoreConsoleState();
110 return ExitCode;
111 }
112
113 int CreateNtProcessUtilityVm(int Argc, char* Argv[])
114
115 /*++
116
117 Routine Description:
118
119 This routine issues a create NT process request for VM Mode.
120
121 Arguments:
122
123 Argc - Supplies the argument count.
124
125 Argv - Supplies the command line arguments.
126
127 Return Value:
128
129 The exit code of the launched process on success, 1 on failure.
130
131 --*/
132 try
133 {
134 int ExitCode = 1;
135
136 //
137 // Create the interop message.
138 //
139
140 auto Buffer = CreateNtProcessMessage(LxInitMessageCreateProcessUtilityVm, Argc, Argv);
141 if (Buffer.empty())
142 {
143 return ExitCode;
144 }
145
146 //
147 // Create a listening socket to accept connections for stdin, stdout,
148 // stderr, and the control channel.
149 //
150
151 sockaddr_vm SocketAddress;
152 wil::unique_fd Sockets[LX_INIT_CREATE_NT_PROCESS_SOCKETS];
153 wil::unique_fd ListenSocket{UtilListenVsockAnyPort(&SocketAddress, COUNT_OF(Sockets))};
154 if (!ListenSocket)
155 {
156 return ExitCode;
157 }
158
159 auto Span = gsl::make_span(Buffer);
160 auto* Message = gslhelpers::get_struct<LX_INIT_CREATE_NT_PROCESS_UTILITY_VM>(Span);
161 Message->Port = SocketAddress.svm_port;
162
163 //
164 // Establish a connection to the interop server.
165 //
166
167 wsl::shared::SocketChannel channel{UtilConnectToInteropServer(), "Interop"};
168 if (channel.Socket() < 0)
169 {
170 return ExitCode;
171 }
172
173 //
174 // Send the create process message to the interop server.
175 //
176
177 auto transaction = channel.StartTransaction();
178 transaction.Send<LX_INIT_CREATE_NT_PROCESS_UTILITY_VM>(Span);
179
180 //
181 // Accept connections from the interop server.
182 //
183
184 for (int Index = 0; Index < COUNT_OF(Sockets); Index += 1)
185 {
186 Sockets[Index] = UtilAcceptVsock(ListenSocket.get(), SocketAddress, ACCEPT_TIMEOUT);
187 if (!Sockets[Index])
188 {
189 return ExitCode;
190 }
191 }
192
193 //
194 // Close the listening socket.
195 //
196
197 ListenSocket.reset();
198
199 //
200 // Create a signalfd to detect window size changes.
201 //
202
203 sigset_t SignalMask;
204 sigemptyset(&SignalMask);
205 sigaddset(&SignalMask, SIGWINCH);
206 sigaddset(&SignalMask, SIGINT);
207 int Result = sigprocmask(SIG_BLOCK, &SignalMask, NULL);
208 if (Result < 0)
209 {
210 LOG_STDERR("sigprocmask failed %d", errno);
211 return ExitCode;
212 }
213
214 wil::unique_fd SignalFd{signalfd(-1, &SignalMask, 0)};
215 if (!SignalFd)
216 {
217 LOG_STDERR("signalfd failed %d", errno);
218 return ExitCode;
219 }
220
221 //
222 // Fill output and poll file descriptors.
223 //
224
225 int OutputFd[] = {Sockets[0].get(), 1, 2};
226 pollfd PollDescriptors[] = {
227 {0, POLLIN}, {Sockets[1].get(), POLLIN}, {Sockets[2].get(), POLLIN}, {Sockets[3].get(), POLLIN}, {SignalFd.get(), POLLIN}};
228
229 //
230 // Begin relaying from stdin to the stdin socket, and from the stdout and
231 // stderr sockets to stdout and stderr.
232 //
233
234 while (HasOpenFileDescriptors(PollDescriptors, COUNT_OF(PollDescriptors)))
235 {
236 Result = poll(PollDescriptors, COUNT_OF(PollDescriptors), -1);
237 if (Result <= 0)
238 {
239 break;
240 }
241
242 for (int Index = 0; Index < COUNT_OF(OutputFd); Index += 1)
243 {
244 if (PollDescriptors[Index].revents & (POLLIN | POLLHUP | POLLERR))
245 {
246 auto BytesRead = UtilReadBuffer(PollDescriptors[Index].fd, Buffer);
247 if (BytesRead == 0)
248 {
249 PollDescriptors[Index].fd = -1;
250 if (Index == 0)
251 {
252 if (shutdown(OutputFd[0], SHUT_WR) < 0)
253 {
254 LOG_STDERR("shutdown failed %d", errno);
255 }
256 }
257 }
258 else if (BytesRead < 0)
259 {
260 LOG_STDERR("read failed %d", errno);
261 PollDescriptors[Index].fd = -1;
262 }
263 else
264 {
265 auto BytesWritten = UtilWriteBuffer(OutputFd[Index], Buffer.data(), BytesRead);
266 if (BytesWritten < 0)
267 {
268 LOG_STDERR("write failed %d", errno);
269 }
270 }
271 }
272 }
273
274 //
275 // Read the create process response or exit status message from the
276 // control channel.
277 //
278
279 if (PollDescriptors[3].revents & POLLIN)
280 {
281 auto PollMessage = wsl::shared::socket::RecvMessage(PollDescriptors[3].fd, Buffer);
282 if (PollMessage.empty())
283 {
284 PollDescriptors[3].fd = -1;
285 continue;
286 }
287
288 auto* Header = gslhelpers::get_struct<MESSAGE_HEADER>(PollMessage);
289 if (Header->MessageType == LxInitMessageCreateProcessResponse)
290 {
291 //
292 // Verify the process launch request was successful.
293 //
294
295 auto* Response = gslhelpers::try_get_struct<LX_INIT_CREATE_PROCESS_RESPONSE>(PollMessage);
296 if (!Response)
297 {
298 LOG_STDERR("Invalid message size %zd", PollMessage.size());
299 break;
300 }
301
302 if (Response->Result != 0)
303 {
304 errno = Response->Result;
305 LOG_STDERR("%s", Argv[0]);
306 break;
307 }
308
309 //
310 // If the application was a GUI application and stdin is a console, restore
311 // the terminal mode. This allows ctrl-c and ctrl-z to function for
312 // graphical apps.
313 //
314
315 if ((Response->Flags & LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION) != 0)
316 {
317 RestoreConsoleState();
318 }
319 }
320 else if (Header->MessageType == LxInitMessageExitStatus)
321 {
322 //
323 // Set the process exit code and go through the relay loop until
324 // all data has been flushed.
325 //
326
327 auto* ExitStatus = gslhelpers::try_get_struct<LX_INIT_PROCESS_EXIT_STATUS>(PollMessage);
328 if (!ExitStatus)
329 {
330 LOG_STDERR("Invalid message size %zd", PollMessage.size());
331 break;
332 }
333
334 ExitCode = ExitStatus->ExitCode;
335 PollDescriptors[3].fd = -1;
336 }
337 else
338 {
339 LOG_STDERR("Unexpected message %d", Header->MessageType);
340 break;
341 }
342 }
343
344 //
345 // Forward window resize events via the relay pipe and handle sigint.
346 //
347
348 if (PollDescriptors[4].revents & POLLIN)
349 {
350 signalfd_siginfo SignalInfo;
351 auto BytesRead = TEMP_FAILURE_RETRY(read(PollDescriptors[4].fd, &SignalInfo, sizeof(SignalInfo)));
352 if (BytesRead != sizeof(SignalInfo))
353 {
354 LOG_STDERR("read failed %zd %d", BytesRead, errno);
355 break;
356 }
357
358 if (SignalInfo.ssi_signo == SIGWINCH)
359 {
360 WindowSizeChanged(Sockets[3].get());
361 }
362 else if (SignalInfo.ssi_signo == SIGINT)
363 {
364 if (shutdown(OutputFd[0], SHUT_WR) < 0)
365 {
366 LOG_STDERR("shutdown failed %d", errno);
367 }
368
369 break;
370 }
371 else
372 {
373 LOG_STDERR("Unexpected signal %u", SignalInfo.ssi_signo);
374 break;
375 }
376 }
377
378 //
379 // Control channel is closed. This means that the windows process has
380 // exited. Close stdin channel to unblock the relay thread and disable
381 // polling on the stdin and signalfd channels. However, there might be
382 // some unread data on the stdout and stderr channels so continue
383 // polling/reading on them until EOF is received.
384 //
385
386 if (PollDescriptors[3].fd == -1)
387 {
388 if (shutdown(OutputFd[0], SHUT_WR) < 0)
389 {
390 LOG_STDERR("shutdown failed %d", errno);
391 }
392
393 PollDescriptors[0].fd = -1;
394 PollDescriptors[4].fd = -1;
395 }
396 }
397
398 return ExitCode;
399 }
400 CATCH_RETURN_ERRNO()
401
402 int CreateNtProcessWsl(int Argc, char* Argv[])
403
404 /*++
405
406 Routine Description:
407
408 This routine issues a create NT process request for lxcore-based instances.
409
410 Arguments:
411
412 Argc - Supplies the argument count.
413
414 Argv - Supplies the command line arguments.
415
416 Return Value:
417
418 The exit code of the launched process on success, 1 on failure.
419
420 --*/
421
422 {
423 int ExitCode = 1;
424
425 //
426 // Connect to the Windows server that handles create process requests.
427 //
428
429 wil::unique_fd LxBusFd{TEMP_FAILURE_RETRY(open(LXBUS_DEVICE_NAME, O_RDWR))};
430 if (!LxBusFd)
431 {
432 return ExitCode;
433 }
434
435 LXBUS_CONNECT_SERVER_PARAMETERS ConnectParams{};
436 ConnectParams.Input.Flags = LXBUS_IPC_CONNECT_FLAG_UNNAMED_SERVER;
437 ConnectParams.Input.TimeoutMs = LXBUS_IPC_INFINITE_TIMEOUT;
438 int Result = TEMP_FAILURE_RETRY(ioctl(LxBusFd.get(), LXBUS_IOCTL_CONNECT_SERVER, &ConnectParams));
439 if (Result < 0)
440 {
441 return ExitCode;
442 }
443
444 wil::unique_fd CreateProcessFd{ConnectParams.Output.MessagePort};
445 std::vector<gsl::byte> Buffer = CreateNtProcessMessage(LxInitMessageCreateProcess, Argc, Argv);
446 if (Buffer.empty())
447 {
448 return ExitCode;
449 }
450
451 //
452 // Marshal the standard handles.
453 //
454
455 auto Span = gsl::make_span(Buffer);
456 auto* Message = gslhelpers::get_struct<LX_INIT_CREATE_NT_PROCESS>(Span);
457 for (int Index = 0; Index < LX_INIT_STD_FD_COUNT; ++Index)
458 {
459 LXBUS_IPC_MESSAGE_MARSHAL_VFS_FILE_PARAMETERS MarshalFile{};
460 MarshalFile.Input.Fd = Index;
461 Result = TEMP_FAILURE_RETRY(ioctl(CreateProcessFd.get(), LXBUS_IPC_MESSAGE_IOCTL_MARSHAL_VFS_FILE, &MarshalFile));
462 if (Result < 0)
463 {
464 return ExitCode;
465 }
466
467 Message->StdFdIds[Index] = MarshalFile.Output.VfsFileId;
468 }
469
470 //
471 // Send the create NT process message to the server.
472 //
473
474 auto Bytes = UtilWriteBuffer(CreateProcessFd.get(), Span);
475 if (Bytes != static_cast<ssize_t>(Span.size()))
476 {
477 return ExitCode;
478 }
479
480 //
481 // Close the file descriptors representing stdin, stdout, and stderr.
482 //
483
484 for (int Index = 0; Index < LX_INIT_STD_FD_COUNT; ++Index)
485 {
486 CLOSE(Index);
487 }
488
489 //
490 // Create a signalfd to detect window size changes.
491 //
492
493 sigset_t SignalMask;
494 sigemptyset(&SignalMask);
495 sigaddset(&SignalMask, SIGWINCH);
496 sigaddset(&SignalMask, SIGINT);
497 Result = sigprocmask(SIG_BLOCK, &SignalMask, NULL);
498 if (Result < 0)
499 {
500 LOG_STDERR("sigprocmask failed %d", errno);
501 return ExitCode;
502 }
503
504 wil::unique_fd SignalFd{signalfd(-1, &SignalMask, 0)};
505 if (!SignalFd)
506 {
507 LOG_STDERR("signalfd failed %d", errno);
508 return ExitCode;
509 }
510
511 //
512 // Initialize poll state.
513 //
514
515 pollfd PollDescriptors[2];
516 PollDescriptors[0].fd = CreateProcessFd.get();
517 PollDescriptors[0].events = POLLIN;
518 PollDescriptors[1].fd = SignalFd.get();
519 PollDescriptors[1].events = POLLIN;
520
521 //
522 // Begin worker loop.
523 //
524
525 wil::unique_fd SignalChannelFd{};
526 for (;;)
527 {
528 Result = poll(PollDescriptors, COUNT_OF(PollDescriptors), -1);
529 if (Result < 0)
530 {
531 LOG_STDERR("poll failed %d", errno);
532 break;
533 }
534
535 //
536 // Read the create process response or exit status message from the
537 // control channel.
538 //
539
540 if (PollDescriptors[0].revents & POLLIN)
541 {
542 union
543 {
544 MESSAGE_HEADER Header;
545 LX_INIT_PROCESS_EXIT_STATUS ExitStatus;
546 LX_INIT_CREATE_PROCESS_RESPONSE Response;
547 } Reply{};
548
549 Bytes = TEMP_FAILURE_RETRY(read(PollDescriptors[0].fd, &Reply, sizeof(Reply)));
550 if (Bytes < 0)
551 {
552 LOG_STDERR("read failed %d", errno);
553 return ExitCode;
554 }
555
556 if (Reply.Header.MessageType == LxInitMessageCreateProcessResponse)
557 {
558 //
559 // Verify the process launch request was successful.
560 //
561
562 if (Reply.Response.Result != 0)
563 {
564 errno = Reply.Response.Result;
565 LOG_STDERR("%s", Argv[0]);
566 return ExitCode;
567 }
568
569 //
570 // Unmarshal the signal channel if one was created.
571 //
572
573 if (Reply.Response.SignalPipeId != 0)
574 {
575 LXBUS_IPC_MESSAGE_UNMARSHAL_HANDLE_PARAMETERS UnmarshalHandle{};
576 UnmarshalHandle.Input.HandleId = Reply.Response.SignalPipeId;
577 Result = TEMP_FAILURE_RETRY(ioctl(CreateProcessFd.get(), LXBUS_IPC_MESSAGE_IOCTL_UNMARSHAL_HANDLE, &UnmarshalHandle));
578 if (Result < 0)
579 {
580 return ExitCode;
581 }
582
583 SignalChannelFd.reset(UnmarshalHandle.Output.FileDescriptor);
584 }
585
586 //
587 // If the application was a GUI application and stdin is a console, restore
588 // the terminal mode. This allows ctrl-c and ctrl-z to function for
589 // graphical apps.
590 //
591
592 if ((Reply.Response.Flags & LX_INIT_CREATE_PROCESS_RESULT_FLAG_GUI_APPLICATION) != 0)
593 {
594 RestoreConsoleState();
595 }
596 }
597 else if (Reply.Header.MessageType == LxInitMessageExitStatus)
598 {
599 ExitCode = Reply.ExitStatus.ExitCode;
600 UtilWriteBuffer(PollDescriptors[0].fd, &Reply, Bytes);
601 break;
602 }
603 else
604 {
605 LOG_STDERR("Unexpected message");
606 break;
607 }
608 }
609
610 //
611 // Forward window resize events via the relay pipe and handle sigint.
612 //
613
614 if (PollDescriptors[1].revents & POLLIN)
615 {
616 signalfd_siginfo SignalInfo;
617 Bytes = TEMP_FAILURE_RETRY(read(PollDescriptors[1].fd, &SignalInfo, sizeof(SignalInfo)));
618 if (Bytes != sizeof(SignalInfo))
619 {
620 LOG_STDERR("read failed %zd %d", Bytes, errno);
621 break;
622 }
623
624 if (SignalInfo.ssi_signo == SIGWINCH)
625 {
626 WindowSizeChanged(SignalChannelFd.get());
627 }
628 else if (SignalInfo.ssi_signo == SIGINT)
629 {
630 break;
631 }
632 else
633 {
634 LOG_STDERR("Unexpected signal %u", SignalInfo.ssi_signo);
635 break;
636 }
637 }
638 }
639
640 return ExitCode;
641 }
642
643 void CreateNtProcessConfigureConsole(PLX_INIT_CREATE_NT_PROCESS_COMMON Common)
644
645 /*++
646
647 Routine Description:
648
649 This routine queries stdin, stdout, and stderr and determines if a
650 Windows pseudoconsole should be created. It also performs additional logic
651 around setting and restoring the terminal mode if stdin is a console.
652
653 Arguments:
654
655 Common - Supplies a pointer to the common create process information. This
656 buffer will be modified if the console state is inconsistent.
657
658 Return Value:
659
660 None.
661
662 --*/
663
664 {
665 struct winsize WindowSize;
666
667 //
668 // Ensure that stdin, stdout, and stderr are terminals.
669 //
670
671 termios ConsoleInfo;
672 for (int Index = 0; Index < LX_INIT_STD_FD_COUNT; Index += 1)
673 {
674 if (tcgetattr(Index, &ConsoleInfo) < 0)
675 {
676 return;
677 }
678 }
679
680 //
681 // Ensure that stdin represents the foreground process group.
682 // N.B. It's possible that standard file descriptors point to tty while the process
683 // has no controlling terminal (in case its parent called setsid() without opening a new terminal for instance).
684 // See https://github.com/microsoft/WSL/issues/13173.
685 //
686
687 auto processGroup = tcgetpgrp(0);
688 if (processGroup < 0)
689 {
690 if (errno != ENOTTY)
691 {
692 LOG_STDERR("tcgetpgrp failed");
693 }
694
695 return;
696 }
697
698 if (processGroup != getpgrp())
699 {
700 return;
701 }
702
703 //
704 // Ensure stdin, stdout, and stderr represent the same terminal.
705 //
706
707 struct stat StdIn;
708 if (fstat(0, &StdIn) < 0)
709 {
710 LOG_STDERR("fstat(0) failed");
711 return;
712 }
713
714 struct stat StatBuffer;
715 for (int Index = 1; Index < LX_INIT_STD_FD_COUNT; Index += 1)
716 {
717 if (fstat(Index, &StatBuffer) < 0)
718 {
719 LOG_STDERR("fstat(%d) failed", Index);
720 return;
721 }
722
723 if (StatBuffer.st_dev != StdIn.st_dev)
724 {
725 return;
726 }
727 }
728
729 //
730 // Query the window size.
731 //
732
733 if (ioctl(0, TIOCGWINSZ, &WindowSize) < 0)
734 {
735 LOG_STDERR("ioctl(TIOCGWINSZ) failed");
736 return;
737 }
738
739 //
740 // Don't create a pseudoconsole if either the row or column size is zero.
741 //
742
743 if ((WindowSize.ws_row == 0) || (WindowSize.ws_col == 0))
744 {
745 return;
746 }
747
748 Common->Rows = WindowSize.ws_row;
749 Common->Columns = WindowSize.ws_col;
750
751 //
752 // Set the terminal to raw mode.
753 //
754
755 memcpy(&g_ConsoleInfoBackup, &ConsoleInfo, sizeof(ConsoleInfo));
756 cfmakeraw(&ConsoleInfo);
757 if (TEMP_FAILURE_RETRY(tcsetattr(0, TCSANOW, &ConsoleInfo)) < 0)
758 {
759 LOG_STDERR("tcsetattr failed");
760 return;
761 }
762
763 //
764 // Duplicate stdin to query window size changes.
765 //
766
767 g_ConsoleFd = dup(0);
768 if (g_ConsoleFd < 0)
769 {
770 LOG_STDERR("dup failed");
771 return;
772 }
773
774 Common->CreatePseudoconsole = true;
775 }
776
777 std::vector<gsl::byte> CreateNtProcessMessage(LX_MESSAGE_TYPE MessageType, int Argc, char* Argv[])
778
779 /*++
780
781 Routine Description:
782
783 This routine allocates and initializes a create NT process message.
784
785 Arguments:
786
787 MessageType - Supplies the message type.
788
789 Argc - Supplies the command line argument count.
790
791 Argv - Supplies the command line arguments.
792
793 Return Value:
794
795 The initialized message buffer.
796
797 --*/
798
799 try
800 {
801 //
802 // Calculate the size of the create process message.
803 //
804
805 size_t Size;
806 switch (MessageType)
807 {
808 case LxInitMessageCreateProcess:
809 Size = offsetof(LX_INIT_CREATE_NT_PROCESS, Common.Buffer);
810 break;
811
812 case LxInitMessageCreateProcessUtilityVm:
813 Size = offsetof(LX_INIT_CREATE_NT_PROCESS_UTILITY_VM, Common.Buffer);
814 break;
815
816 default:
817 return {};
818 }
819
820 //
821 // Translate the Linux filename into a Windows path.
822 //
823
824 auto Filename = WslPathTranslate(Argv[0], (TRANSLATE_FLAG_ABSOLUTE | TRANSLATE_FLAG_RESOLVE_SYMLINKS), TRANSLATE_MODE_WINDOWS);
825 if (Filename.empty())
826 {
827 return {};
828 }
829
830 if (UtilSizeTAdd(Filename.length(), Size, &Size) == false)
831 {
832 return {};
833 }
834
835 if (UtilSizeTAdd(1, Size, &Size) == false)
836 {
837 return {};
838 }
839
840 //
841 // Attempt to translate the current working directory, if translation fails
842 // use an empty current working directory.
843 //
844
845 auto Cwd = std::filesystem::current_path().string();
846 auto CurrentWorkingDirectory = WslPathTranslate(Cwd.data(), TRANSLATE_FLAG_ABSOLUTE, TRANSLATE_MODE_WINDOWS);
847 if (UtilSizeTAdd(CurrentWorkingDirectory.length(), Size, &Size) == false)
848 {
849 return {};
850 }
851
852 if (UtilSizeTAdd(1, Size, &Size) == false)
853 {
854 return {};
855 }
856
857 //
858 // Initialize the environment.
859 //
860
861 auto Environment = UtilParseWslEnv(nullptr);
862 if (UtilSizeTAdd(Environment.size(), Size, &Size) == false)
863 {
864 return {};
865 }
866
867 if (UtilSizeTAdd(1, Size, &Size) == false)
868 {
869 return {};
870 }
871
872 //
873 // If Argv[0] and Argv[1] match, use the basename for Argv[1].
874 // This is useful for Windows binaries that inspect the first argument.
875 //
876 // N.B. Arg[1] cannot be passed as-is because some windows binaries (like cmd.exe)
877 // do not handle the Linux-style path.
878 //
879
880 if ((Argc > 1) && (strcmp(Argv[0], Argv[1])) == 0)
881 {
882 Argv[1] = basename(Argv[1]);
883 }
884
885 //
886 // Calculate the size of the command line.
887 //
888
889 for (int Index = 1; Index < Argc; Index += 1)
890 {
891 if (UtilSizeTAdd(strlen(Argv[Index]), Size, &Size) == false)
892 {
893 return {};
894 }
895
896 if (UtilSizeTAdd(1, Size, &Size) == false)
897 {
898 return {};
899 }
900 }
901
902 if (Size > ULONG_MAX)
903 {
904 return {};
905 }
906
907 //
908 // Initialize the message.
909 //
910
911 std::vector<gsl::byte> Buffer(Size);
912 auto Message = gsl::make_span(Buffer);
913 auto* Header = gslhelpers::get_struct<MESSAGE_HEADER>(Message);
914 Header->MessageType = MessageType;
915 Header->MessageSize = static_cast<unsigned>(Size);
916 Message = Message.subspan(
917 (MessageType == LxInitMessageCreateProcess) ? offsetof(LX_INIT_CREATE_NT_PROCESS, Common)
918 : offsetof(LX_INIT_CREATE_NT_PROCESS_UTILITY_VM, Common));
919
920 auto* Common = gslhelpers::get_struct<LX_INIT_CREATE_NT_PROCESS_COMMON>(Message);
921 size_t Offset = offsetof(LX_INIT_CREATE_NT_PROCESS_COMMON, Buffer);
922
923 //
924 // Copy filename, cwd, environment into the message buffer.
925 //
926
927 Common->FilenameOffset = wsl::shared::string::CopyToSpan(Filename, Message, Offset);
928 Common->CurrentWorkingDirectoryOffset = wsl::shared::string::CopyToSpan(CurrentWorkingDirectory, Message, Offset);
929 Common->EnvironmentOffset = wsl::shared::string::CopyToSpan(std::string_view{Environment.data(), Environment.size()}, Message, Offset);
930
931 //
932 // Copy the command line arguments.
933 //
934
935 Common->CommandLineOffset = gsl::narrow_cast<unsigned int>(Offset);
936 Common->CommandLineCount = gsl::narrow_cast<unsigned short>(Argc - 1);
937 for (int Index = 1; Index < Argc; Index += 1)
938 {
939 wsl::shared::string::CopyToSpan(Argv[Index], Message, Offset);
940 }
941
942 //
943 // Initialize the console state.
944 //
945
946 CreateNtProcessConfigureConsole(Common);
947
948 //
949 // Return the message to the caller.
950 //
951
952 return Buffer;
953 }
954 catch (...)
955 {
956 LOG_CAUGHT_EXCEPTION();
957 return {};
958 }
959
960 bool HasOpenFileDescriptors(struct pollfd* PollDescriptors, int PollDescriptorSize)
961 /*++
962
963 Routine Description:
964
965 This routine checks if the given array of PollDescriptors has any
966 PollDescriptor that is still open. In other words, it checks if there
967 is at least one descriptor that has a fd >= 0.
968
969 Arguments:
970
971 PollDescriptors - The array of poll descriptors.
972 PollDescriptorSize - The count of number of elements in the
973 PollDescriptors array.
974
975 Return Value:
976
977 True if there is at least one open poll descriptor, False otherwise.
978
979 --*/
980 {
981 for (int i = 0; i < PollDescriptorSize; i++)
982 {
983 if (PollDescriptors[i].fd >= 0)
984 {
985 return true;
986 }
987 }
988
989 return false;
990 }
991
992 void RestoreConsoleState(void)
993
994 /*++
995
996 Routine Description:
997
998 This routine restores the original console state.
999
1000 Arguments:
1001
1002 None.
1003
1004 Return Value:
1005
1006 None.
1007
1008 --*/
1009
1010 {
1011 if (g_ConsoleFd != -1)
1012 {
1013 tcsetattr(g_ConsoleFd, TCSANOW, &g_ConsoleInfoBackup);
1014 CLOSE(g_ConsoleFd);
1015 g_ConsoleFd = -1;
1016 }
1017 }
1018
1019 void WindowSizeChanged(int SignalChannelFd)
1020
1021 /*++
1022
1023 Routine Description:
1024
1025 This routine is the signal handler interop window size changes.
1026
1027 Arguments:
1028
1029 SignalChannelFd - Supplies a file descriptor to write the window size
1030 message.
1031
1032 Return Value:
1033
1034 None.
1035
1036 --*/
1037
1038 {
1039 if ((SignalChannelFd == -1) || (g_ConsoleFd == -1))
1040 {
1041 return;
1042 }
1043
1044 //
1045 // Query the new window size and send the updated size via the signal
1046 // channel.
1047 //
1048
1049 winsize WindowSize;
1050 int Result = ioctl(g_ConsoleFd, TIOCGWINSZ, &WindowSize);
1051 if (Result < 0)
1052 {
1053 LOG_STDERR("ioctl(TIOCGWINSZ) failed");
1054 return;
1055 }
1056
1057 LX_INIT_WINDOW_SIZE_CHANGED ResizeMessage{};
1058 ResizeMessage.Header.MessageType = LxInitMessageWindowSizeChanged;
1059 ResizeMessage.Header.MessageSize = sizeof(ResizeMessage);
1060 ResizeMessage.Columns = WindowSize.ws_col;
1061 ResizeMessage.Rows = WindowSize.ws_row;
1062 Result = UtilWriteBuffer(SignalChannelFd, gslhelpers::struct_as_bytes(ResizeMessage));
1063 if (Result < 0)
1064 {
1065 LOG_STDERR("sending resize message failed");
1066 }
1067 }