Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable ConPTY by default in terminals on Windows #44468

Merged
merged 11 commits into from
Jul 29, 2024

Conversation

gzdunek
Copy link
Contributor

@gzdunek gzdunek commented Jul 19, 2024

Closes #38087

ConPTY is an official Windows API for bridging discrepancies between Unix-style terminals and Windows consoles.
It's better at rendering colors and handling resizing (although this one is still not ideal) than winpty.

Implementation

We detect Windows version in ptyService.ts and based on that decide if ConPTY should be enabled or not. Then we pass that information to two places: node-pty and xterm.js.

There is a chance that ConPTY may not work well in some setups. Because of that it can be disabled using terminal.windowsBackend in the app config.

Changelog: Teleport Connect now uses ConPTY for better terminal resizing and accurate color rendering on Windows, with an option to disable it in the app config

Copy link

🤖 Vercel preview here: https://docs-8s5hko9h5-goteleport.vercel.app/docs/ver/preview

Copy link

🤖 Vercel preview here: https://docs-flcylr3w1-goteleport.vercel.app/docs/ver/preview

@@ -76,8 +82,7 @@ export class PtyProcess extends EventEmitter implements IPtyProcess {
// https://unix.stackexchange.com/questions/123858
cwd: this.options.cwd || getDefaultCwd(this.options.env),
env: this.options.env,
// Turn off ConPTY due to an uncaught exception being thrown when a PTY is closed.
useConpty: false,
useConpty: this.options.useConpty,
Copy link
Member

@ravicious ravicious Jul 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, every time I open a new shell, there's a new conhost.exe under Task Manager -> Details. If I explicitly close a tab, it goes away. But when I just close Connect, that conhost.exe never goes away.

task manager

Screen.Recording.2024-07-24.at.14.43.17.mov

Copy link
Member

@ravicious ravicious Jul 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That isn't the case with winpty, where a new conhost.exe is created, but it goes away when closing Connect. conhost.exe spawned by winpty doesn't have those extra arguments.

Copy link
Contributor Author

@gzdunek gzdunek Jul 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to reproduce it, but with no luck.
In the recording below, conhost.exe processes are killed both when closing tabs one by one and when closing the entire app:

conhostmov.mov

I also don't see these extra arguments.
EDIT: I had that column disabled in the task manager, nvm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just tried your build. conhost.exe goes away when starting a local terminal. But when connecting through SSH to a node, it stays open after closing Connect.

I've tested this only with SSH nodes. Because of the bug with the css prop, I cannot access additional action menu and because the shortcut to open a new terminal tab doesn't work inside the VM, I couldn't open a local terminal. ;f

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can confirm, it happens for SSH connections.
I'm trying to to dispose the pty processes when the shared process exits, but it's quite hard to debug since I can't get any logs from there when the main process kills it :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd look if there are any extra node-pty options there to take care of the issue or if someone else had the same problem. It's interesting that this happens with ConPTY but not winpty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any node-pty options that would take care of that. When it comes to similar issues, I found some (microsoft/node-pty#333, Eugeny/tabby#6318).
The most useful seems this PR from vscode microsoft/vscode#118437.

However, it seems to me that simply killing all pty process on exit would work. I tested this by setting a 30 seconds timeout after the shared process launched; after that, I didn't see any remaining conhost.exe processes.
The problem is that process.on('exit') doesn't seem to work on Windows and our cleanup code is not invoked at all :/ I think we need to send a graceful kill message to the shared process in a different way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'm making progress:

  1. To gracefully kill the shared process we can send a message through the Node.js IPC:
      terminateWithTimeout(this.sharedProcess, 5_000, process =>
        process.send('gracefullyKill')
      )

and then listen for it on the Node.js side:

  process.on('message', message => {
    if (message === 'gracefullyKill') {
      server.forceShutdown();
      ptyHostService.dispose();
      process.exit(0);
    }
  });

This works fine.
2. It turned out that simply killing all processes and then exiting doesn't work (as I said before). Adding a timeout before process.exit(0) helps, I'm looking now what timeouts vscode uses.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have it 🥵
To make it work I needed two things:

  1. Gracefully killing the shared process on Windows (done with process.send).
  2. Waiting for the pty processes to exit before closing the app. Killing the pty process takes some time (from my testing, the time between calling ptyProcess.kill and ptyProcess.onExit was around 400 ms). If I'm correct, in vsocde they set timeouts to achieve waiting for processes to exit, but I thought that the better option is to simply wait for onExit to be called (I took an inspiration from terminateWithTimeout).

Tested on Windows with ConPTY enabled/disabled and on macOS. I didn't see any unterminated processes.

Screen.Recording.2024-07-25.at.17.03.15.mov

Copy link
Member

@ravicious ravicious left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes because of the processes left over.

@gzdunek gzdunek requested a review from ravicious July 25, 2024 15:19
Copy link

🤖 Vercel preview here: https://docs-ebg5kb4jz-goteleport.vercel.app/docs/ver/preview

Copy link
Member

@ravicious ravicious left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. 👍

I'm not sure how I feel about the graceful kill message through IPC, especially since we already have "infrastructure" in place to gracefully terminate tsh on Windows. OTOH, the way we kill tsh on Windows is a huge workaround, so perhaps it's best to stick to IPC which is guaranteed to work.

@@ -276,3 +276,5 @@ export enum MainProcessIpc {
export enum WindowsManagerIpc {
SignalUserInterfaceReadiness = 'windows-manager-signal-user-interface-readiness',
}

export const GRACEFUL_KILL_MESSAGE = 'GRACEFUL_KILL';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about TERMINATE_MESSAGE instead? SIGKILL just ends a process with no hesitation, the graceful version of it has a name and it's SIGTERM. ;)

Also, please add a JSDoc explaining what the message signals.

@gzdunek
Copy link
Contributor Author

gzdunek commented Jul 26, 2024

I'm not sure how I feel about the graceful kill message through IPC, especially since we already have "infrastructure" in place to gracefully terminate tsh on Windows. OTOH, the way we kill tsh on Windows is a huge workaround, so perhaps it's best to stick to IPC which is guaranteed to work.

Yeah, I thought about it, but simplicity of process.send won :) (+ it's cross-platform)

Copy link

🤖 Vercel preview here: https://docs-7kiakc9sx-goteleport.vercel.app/docs/ver/preview

Copy link

🤖 Vercel preview here: https://docs-lf1zcxzj1-goteleport.vercel.app/docs/ver/preview

@gzdunek gzdunek added this pull request to the merge queue Jul 29, 2024
Merged via the queue into master with commit 981aed6 Jul 29, 2024
42 checks passed
@gzdunek gzdunek deleted the gzdunek/use-conpty-by-default branch July 29, 2024 09:34
@public-teleport-github-review-bot

@gzdunek See the table below for backport results.

Branch Result
branch/v14 Failed
branch/v15 Failed
branch/v16 Create PR

gzdunek added a commit that referenced this pull request Jul 29, 2024
* Add `terminal.windowsUseConpty` config option

* Calculate `windowsPty` options

* Pass `useConpty` to node-pty

* Pass `windowsPty` to xterm

* Fix test

* Replace `terminal.windowsUseConpty` with `terminal.windowsBackend`, pass boolean all the way through

* Wait for pty processes to exit before closing the app

* Simplify `Array.from`

* `GRACEFUL_KILL_MESSAGE` -> `TERMINATE_MESSAGE`

* Adjust callsites to async `dispose`

* Add `winpty` to ignored words in `cspell.json`

(cherry picked from commit 981aed6)
@gzdunek
Copy link
Contributor Author

gzdunek commented Jul 29, 2024

I wanted to do a backport to v14, but there were too many conflicts.

github-merge-queue bot pushed a commit that referenced this pull request Jul 30, 2024
* Add `terminal.windowsUseConpty` config option

* Calculate `windowsPty` options

* Pass `useConpty` to node-pty

* Pass `windowsPty` to xterm

* Fix test

* Replace `terminal.windowsUseConpty` with `terminal.windowsBackend`, pass boolean all the way through

* Wait for pty processes to exit before closing the app

* Simplify `Array.from`

* `GRACEFUL_KILL_MESSAGE` -> `TERMINATE_MESSAGE`

* Adjust callsites to async `dispose`

* Add `winpty` to ignored words in `cspell.json`

(cherry picked from commit 981aed6)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve resizing and colors in terminals on Windows by enabling ConPTY in Xterm.js
3 participants