From 31007a7e9c013e19e52855358f535e0693a8c64c Mon Sep 17 00:00:00 2001 From: Robert Kajic Date: Fri, 6 Dec 2024 01:14:57 -0800 Subject: [PATCH] Add support for piping into wsl-sudo.py Make the following work: echo "foo" | ./wsl-sudo.py cat Without these changes, cat would hang indefinitely on the pipe. Fix the issue by: 1. Prevent the server process from inheriting the client's stdin, by explicitly setting its stdin to subprocess.DEVNULL. The primary reason for this is to prevent the server process from messing up the client's stdin pipe. It's unclear if there is an issue with wsl, but letting the server inherit the pipe seems to consume it, leaving nothing for the client to read. Besides, the server receives all of its input from the client-server socket, so it doesn't even need the client's stdin. 2. Send an empty string CMD_STDIN command from the client to the server when a empty chunk is read from stdin. The client already uses 'empty string' to detect a closed pipe, and now, by also transmitting an empty string to the server, the server can do the same. 3. Receive the empty string CMD_STDIN in the server's transfer loop, closing the server's child process stdin in response. --- wsl-sudo.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/wsl-sudo.py b/wsl-sudo.py index 8eaae20..063d265 100755 --- a/wsl-sudo.py +++ b/wsl-sudo.py @@ -130,7 +130,8 @@ def child_process(self, argv, cwd, winsize, envdict): def main_process(self): self.transfer_loop() for fd in set(self.child_fds): - os.close(fd) + if fd is not None: + os.close(fd) print('pty closed, getting return value') (success, exit_status) = os.waitpid(self.child_pid, 0) @@ -152,7 +153,12 @@ def transfer_loop(self): if fd == sock_fd: cmd, data = self.channel.recv_command() if cmd == CMD_STDIN: - os.write(self.child_fds[0], data) + if not data: + # Close the child's stdin to send EOF + os.close(self.child_fds[0]) + self.child_fds[0] = None + else: + os.write(self.child_fds[0], data) elif cmd == CMD_WINSZ: fcntl.ioctl(self.child_fds[1], termios.TIOCSWINSZ, data) os.kill(self.child_pid, signal.SIGWINCH) @@ -220,14 +226,20 @@ def main(self, command, visibility, **kwargs): window_style = ['Hidden', 'Minimized', 'Normal'][visibility] try: - subprocess.check_call( - ["powershell.exe", "Start-Process", "-Verb", "runas", - "-WindowStyle", window_style, - "-FilePath", "wsl", "-ArgumentList", - '"{}"'.format(subprocess.list2cmdline([ - sys.executable, os.path.abspath(__file__), - '--elevated', 'visible' if visibility else 'hidden', - str(port), pwf.name]))]) + subprocess.check_call([ + "powershell.exe", "Start-Process", + "-Verb", "runas", + "-WindowStyle", window_style, + "-FilePath", "wsl", + "-ArgumentList", '"{}"'.format(subprocess.list2cmdline([ + sys.executable, + os.path.abspath(__file__), + '--elevated', + 'visible' if visibility else 'hidden', + str(port), + pwf.name, + ])) + ], stdin=subprocess.DEVNULL) except subprocess.CalledProcessError as e: print("wudo: failed to start elevated process") return @@ -264,6 +276,7 @@ def handle_sigwinch(n, f): self.channel.send_command(CMD_STDIN, chunk) else: # stdin is a pipe and is closed + self.channel.send_command(CMD_STDIN, b'') fdset.remove(0) else: self.recv_command()