How to write stdin / read from stdout of a remote SSH process? (Renci.SshNet)

I want to connect to an SSH server, which, when connected, runs a user command that expects XML to be entered (UTF-8 encoding, a few lines with a special line at the end of each XML document marking its end) and returns XML ( UTF-8 encoding, several lines, with the same special line at the end, which means the end of the document).

There is currently a mechanism for using plink.exe (from the fame of PuTTy) (which means: the server side works as indicated), but I'm in the process of eliminating the need for an external process.

I am reading the Renci.SshNet library, but I find the documentation inadequate and cannot find examples of what I need.

My attempt:

using System; using Renci.SshNet; namespace ServerConnect { public class ServerConnection { private const string EOC= "*-* COMMAND DELIMITER *-*"; private SshClient scon; private System.IO.Stream stdin, stdout, stderr; private System.IO.StreamWriter stdin_writer; private System.IO.StreamReader stdout_reader; private Shell shell; public ServerConnection (string keyFileName, string keyFilePassphrase) { ConnectionInfo ci= new ConnectionInfo("my.server.local", 22, "datamgr", new PrivateKeyAuthenticationMethod("datamgr", new PrivateKeyFile(keyFileName, keyFilePassphrase))); this.scon= new SshClient(ci); } public ServerConnection (string keyFilePassphrase): this("client_id_rsa", keyFilePassphrase) {} public void Connect() { this.scon.Connect(); this.stdin= new System.IO.MemoryStream(); this.stdin_writer= new System.IO.StreamWriter(stdin); this.stdout= new System.IO.MemoryStream(); this.stdout_reader= new System.IO.StreamReader(stdout); this.stderr= new System.IO.MemoryStream(); this.shell= this.scon.CreateShell(this.stdin, this.stdout, this.stderr); this.shell.Start(); } public void Disconnect() { this.scon.Disconnect(); } public string Command(string text) { Console.WriteLine("sending command"); this.stdin_writer.WriteLine(text); Console.WriteLine("sending EOC"); this.stdin_writer.WriteLine(EOC); this.stdin_writer.Flush(); for (;;) { string line; line= this.stdout_reader.ReadLine(); if (line == null) { Console.WriteLine("got null"); break; } Console.WriteLine("READ"); Console.WriteLine(line); if (line.StartsWith(EOC)) break; } return ""; // yes, this should return the built output } } } 

Given this as an assembly, I am at a stage where I am trying to connect to the server (tests pass through booish to facilitate):

 >>> import ServerConnect >>> a= ServerConnection("passphrase") ServerConnect.ServerConnection >>> a.Connect() >>> a.Command("<i command=\"ping\"/>") sending command sending EOC got null '' 

I see (on auth.log server) that the connection is established after a.Connect() called. However, it seems that everything I write to stdin_writer is not sent to the server.

How can I execute this mechanism? Send data to a server, then send a separator, and then read the data until I read the separator? The cycle is repeated many times during the connection.

Please note that I am not an experienced C # programmer, so it’s possible that I don’t have a basic understanding of the C # stream model.

PS I saw this answer, but it seems that the CreateShellStream call blocks may have a PseudoTty distribution (this process does not need pseudo -ttys, only input / output).

PS2 I downloaded and compiled the latest version of Renci.SshNet from codeplex.com, and now I am in this place (excerpt from the Connect method):

 this.scon.Connect(); this.stream= this.scon.CreateShellStream("dumb", 80, 24, 800, 600, 1024); this.stdin_writer= new System.IO.StreamWriter(this.stream); this.stdout_reader= new System.IO.StreamReader(this.stream); this.shell= this.scon.CreateShell(this.stream, this.stream, this.stream); this.shell.Start(); 

However, any entries in stdin_writer (followed by flushes) do not seem to go through.

+3
c # ssh
source share
2 answers

Sorry, after entering this message I noticed that I am a necroposis ...

You can use the solution here, which I used when I wanted to do something similar to you:

Renci ssh.net - Create a console screen from a form when connected

You can use one of the System.IO objects and manage the IO of the SSH session using the ShellStream object.

ShellStream.Expect will read the output and look for a specific string. ShellStream.WriteLine will send input to the shell.

  string reply = string.Empty; ShellStream ss = scon.CreateShellStream("dumb", 80, 24, 800, 600, 1024); reply = ss.Expect("Press enter to continue"); //The prompt I get after login ParseOutput(reply); ss.WriteLine("./myscript.sh") reply = ss.Expect("$"); //My prompt, when the script has finished. ParseOutput(reply); 

ParseOutput is a regular program that I wrote to split the response line into each character of the line and add each line to the list in the form.

+1
source share

This is not possible with Renci.SshNet (for now).

The first answer does not address the original question about pipeline data in the standard input of a remote command. A typical example would be the flow of the contents of a compressed file to a decompressor, which will be executed on the target node.

Only recently (September 11, 2016) was this improvement proposed for the SSH.NET project on GitHub. You can see it here:

Support for writing to stdin when executing a remote command

+1
source share

All Articles