Repository navigation
Linux platform SSH process creation for remoting - #3901
Conversation
…rs in the error stream
|
Paul Higinbotham (@PaulHigin) Please add reviewers for your PR and assign it to an appropriate maintainer. |
|
I leave a comment in the original Issue |
|
Ilya (@iSazonov) Please put comments here rather than providing a link. The SSH -t option to create a pseudo tty prevents using subsystems which is needed to host PowerShell for SSH remoting. So it won't work here as far as I can tell. If there was some way to create and use the child SSH process such that it is not part of the process group (so that it doesn't get Ctrl+C signals) but still can use a tty for user prompt, then that would be great. But I don't currently see a way to do this. However, I am definitely open to any ideas as this would be preferable to using SSH_ASKPASS! |
|
Paul Higinbotham (@PaulHigin) I don't read in depth but it is interesting https://unix.stackexchange.com/questions/266866/how-to-prevent-ctrlc-to-break-ssh-connection |
|
Oh, I think about a remote session all the time, but the problem is on the local side, isn't it? If so there is a simple test:
If this works well we can make the same internally in PowerShell - disable ISIG by means of TERMIOS(3) at startup time. |
|
This looks interesting, but I don't understand what the fix would be. I don't want to disable the ISIG for the PowerShell /dev/tty but instead want to keep the child SSH process from getting it. I only want PowerShell to handle it. It is not clear to me that disabling ISIG via termios can be done per process. But if so that would certainly solve our problem! BTW I did look at your previous link as a way to turn off ISIG processing on the server side, but I wasn't able to get it to work. "set -m" didn't seem to do anything. |
Fork inherits its. I have no more thoughts and PG can make an conclusion. |
|
Nevertheless my position remains "why do it when it is not necessary?". Its funny but I just watched some presentations about performance yesterday. A big theme from the presenter was that best practices and accepted patterns were many times poor choices performance wise. One of the top things the presenter looked for when investigating performance problems was unnecessary work such as memory allocations, thread creation, uses of locks, and other system resource usage. So after that the idea of creating an unneeded process when it is just as easy to create a single process seems undesirable. |
|
Very interesting discussion here and I believe calling I changed to code in |
|
|
||
| // Open pipes for any requests to redirect stdin/stdout/stderr | ||
| if ((redirectStdin && pipe(stdinFds) != 0) || (redirectStdout && pipe(stdoutFds) != 0) || | ||
| (redirectStderr && pipe(stderrFds) != 0)) |
There was a problem hiding this comment.
The code in this function was changed a bit to address https://github.com/dotnet/corefx/issues/13447, you can see the latest code at pal_process.cpp. The change was basically to make all pipe file descriptors close-on-exec. If not doing this here, then it's possible that the ssh process will continue to block on waiting for input even if the redirected standard input has been closed in powershell.
There was a problem hiding this comment.
I don't think this is applicable in this situation. The pipes are only used for PSRP messages for a single remoting session. PowerShell only closes the pipe at the end of the session at which time it also terminates the SSH process.
There was a problem hiding this comment.
Without close-on-exec, the pipe file descriptors will be leaked to all subsequent processes created by powershell. The symptom I described above is one result this would cause. Although it's not applicable to the SSH remoting scenario, my worry is that there may be other symptoms resulted by the leak that would cause a problem we don't know yet which might be hard to diagnose.
There was a problem hiding this comment.
Ok, the leak definitely sounds bad. I'll add the close-on-exec. So that I understand more clearly, can you provide an example where the Fd is leaked even when SSH process is terminated?
There was a problem hiding this comment.
Take the write end of the redirected stdin pipe as an instance (similar to the redirected stdout/stderr pipes), a FD pointing to the write end of that pipe will be created in powershell process after creating the SSH process. Assuming powershell then creates some other processes before SSH process terminates (I'm thinking of the scenario where I create a SSH PSSession but no an interactive remote session, so it's possible to run native commands locally while the SSH process is still alive), then that FD will be inherited by all those processes, so they are all potentially able to write to that pipe or affect that pipe in possible ways.
In this case, the SSH process is the only reader of that pipe, so when SSH terminates, "the only reader dies, and then the writer gets notified about this by getting a SIGPIPE or at least an EPIPE error (depending on how signals are defined)." (quoted from here)
I'm not saying it will definitely cause a problem, but just that if the leaked FD cause trouble to us, it might be very hard to figure out the real cause.
There was a problem hiding this comment.
Thanks, this definitely helps and could be an issue for the fan-out scenario. I'll have this change in the PR update.
|
Paul Higinbotham (@PaulHigin) Dongbo Wang (@daxian-dbw) I want to get clarify about |
|
|
||
| #if UNIX | ||
| private static readonly UTF8Encoding s_utf8NoBom = | ||
| new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); |
There was a problem hiding this comment.
It seems we can use this everywhere. So maybe put this in "right place" as internal?
There was a problem hiding this comment.
I am not sure what you mean, I only see this being used here in my branch.
There was a problem hiding this comment.
I think Ilya (@iSazonov) means that s_utf8NoBom should be put at some common place so that a single instance can be used in all places in our code base.
There was a problem hiding this comment.
Oh I see. Yes this does seem like something that would be useful. I'll move it to PsUtils.cs
| /// <returns>The opened stream.</returns> | ||
| private static FileStream OpenStream(int fd, FileAccess access) | ||
| { | ||
| Debug.Assert(fd >= 0); |
There was a problem hiding this comment.
Should we add an usefull message?
There was a problem hiding this comment.
Sure, but I do see the point of DotNet CoreFx developers who originated this code. The assert condition is pretty explanatory...
| if (context != null) | ||
| { | ||
| var cmdInfo = context.CommandDiscovery.LookupCommandInfo("ssh.exe", CommandOrigin.Internal) as ApplicationInfo; | ||
| var cmdInfo = context.CommandDiscovery.LookupCommandInfo("ssh", CommandOrigin.Internal) as ApplicationInfo; |
There was a problem hiding this comment.
Why we remove ".exe" for Windows? We can catch .bat or .com file.
There was a problem hiding this comment.
I was hoping to simply the code a bit between platforms. But I see your point and will fix.
|
Ilya (@iSazonov) I see this in the man page of
This makes me feel safer to use |
| // Close the child's copy of the parent end of any open pipes | ||
| CloseIfOpen(stdinFds[WRITE_END_OF_PIPE]); | ||
| CloseIfOpen(stdoutFds[READ_END_OF_PIPE]); | ||
| CloseIfOpen(stderrFds[READ_END_OF_PIPE]); |
There was a problem hiding this comment.
There are some additional changes for the close-on-exec -- calls to CloseIfOpen are removed from the if (processId == 0) block, see the CoreFx code here.
I may be easier to see the differences in the PR change: dotnet/corefx@6d204a6
There was a problem hiding this comment.
Fixed
|
Paul Higinbotham (@PaulHigin) Could you please update the PR description? A great discussion happened in this PR that led to a better solution. So the original description is out-dated and we'd better capture the solution of using |
|
LGTM. Sorry for the long discussion - my last code for Unix was exactly 20 years ago. I'm glad I can remember just something. 😄 |
| // process creation code. | ||
| // | ||
|
|
||
| #if UNIX |
There was a problem hiding this comment.
This whole region of code should be moved to the #if UNIX region of SSHConnectionInfo.StartSSHProcessImpl. The CorePSPlatform class is designed for generic operations that use compile-time definitions in order to reduce the number of #if's throughout the code. These functions are specific to SSH remoting and are not used anywhere else in the code.
An example of a function that would work well here is SSHConnectionInfo.GetCurrentUserName(). It doesn't necessarily need to be moved right now, but it fits the pattern.
There was a problem hiding this comment.
Done
| internal static extern bool IsSameFileSystemItem([MarshalAs(UnmanagedType.LPStr)]string filePathOne, | ||
| [MarshalAs(UnmanagedType.LPStr)]string filePathTwo); | ||
|
|
||
| [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] |
There was a problem hiding this comment.
This should move too
There was a problem hiding this comment.
Done
|
Ilya (@iSazonov) Thank you for your help on this. |
|
Ilya (@iSazonov) can you formally approve if you're good with the changes? Thanks! |
|
Steve Lee (@SteveL-MSFT) Approved. |
|
Mike Richmond (@mirichmo) Is there any reason why this cannot be merged? |
| using Microsoft.Win32; | ||
| using Microsoft.Win32.SafeHandles; | ||
| using System.IO; | ||
| using System.Diagnostics; |
There was a problem hiding this comment.
Please clean this up
|
Mike Richmond (@mirichmo) Thanks! |
* Fix SSH process creation on Linux platforms * Fixed typos * Added UNIX managed code StartProcess * Fixed compile errors * Changed process create to return pid * Now resolve full SSH command file path for all platforms * Removed data reader error handling because it conflicts with SSH errors in the error stream * Clean up work * Fix for file line endings * More clean up * Change to Linux platform SSH process creation to create process in new session * Added third party license text for DotNet Core * Removed process creation in new session and added suppress SIGINT * Removed unneeded code for creating SSH process * Fixed Unix compile errors * Changes for code review * Response to more code review comments * Removed unneeded using statements
Update:
The technique is to now use sigaction to create an SSH client process that ignores SIGINT signals. It remains in the PowerShell process group and has access to the terminal, which means it can prompt on the command line as before. So SSH_ASKPASS dependency is not needed.
This change addresses Issue #2321. The original idea for this fix was to follow what I did in Windows and create the SSH process in its own process group. That way a Ctrl+C typed signal in PowerShell (the parent process) wouldn't be handled by the child SSH process (causing it to end), and instead would allow PowerShell to handle it through its protocol (PSRP).
But this ended up disabling the /dev/tty terminal for the SSH process so it could not prompt the user for a password, if that was how SSH was set up.
It turns out that SSH will detect if there is no tty available and fall back to using SSH_ASKPASS program if available. This will happen if the SSH child process is created in its own session. So this change does that, it creates the SSH process (used for SSH remoting) in its own session.
The downside to this is that now the SSH_ASKPASS program needs to be installed if we want to support any SSH prompting. We will need to make sure it gets installed with Enable-SSHRemoting (a separate issue/work item).
Creating a Linux process in its own session does not seem like a common need and so I felt it was not necessary to add this functionality to DotNet CoreFx. Instead I pulled some of the UNIX process creation code over to PowerShell and added the ability to create in a separate session. So this change has a fair amount of native and platform interop code.
I will update the license document to indicate that PowerShell is using this DotNet CoreFx code under the MIT license.