SSH.NET doesn't process my shell input commands - c#

I'm using SSH.NET to connect to my Raspberry Pi from a Console Application in C#.
I want to send text from my very own stream, writing to it through a StreamWriter.
The problem is that it does nothing. It's like the WriteLine("ls") doesn't produce any effect.
This is the code:
using System;
using System.IO;
using Renci.SshNet;
namespace SSHTest
{
class Program
{
static void Main(string[] args)
{
var ssh = new SshClient("raspberrypi", 22, "pi", "raspberry");
ssh.Connect();
var input = new MemoryStream();
var streamWriter = new StreamWriter(input) { AutoFlush = true };
var stdout = Console.OpenStandardOutput();
var shell = ssh.CreateShell(input, stdout, new MemoryStream());
shell.Start();
streamWriter.WriteLine("ls");
while (true)
{
}
}
}
}
What's the problem?

MemoryStream is not a good class for implementing an input stream.
When you write to MemoryStream, as with most stream implementations, its pointer is moved at the end of the written data.
So when SSH.NET channel tries to read data, it has nothing to read.
You can move the pointer back:
streamWriter.WriteLine("ls");
input.Position = 0;
But the right approach is to use PipeStream from SSH.NET, which has separate read and write pointers (just as a *nix pipe):
var input = new PipeStream();
Another option is to use SshClient.CreateShellStream (ShellStream class), which is designed for task like this. It gives you one Stream interface, that you can both write and read.
See also Is it possible to execute multiple SSH commands from a single login session with SSH.NET?
Though SshClient.CreateShell (SSH "shell" channel) is not the right method for automating command execution. Use "exec" channel. For simple cases, use SshClient.RunCommand. If you want to read a command output continuously, use SshClient.CreateCommand to retrieve the command output stream:
var command = ssh.CreateCommand("ls");
var asyncExecute = command.BeginExecute();
command.OutputStream.CopyTo(Console.OpenStandardOutput());
command.EndExecute(asyncExecute);

Related

.NET Core: Read /dev/ file

Connecting my RFID reader to my Linux machine it get installed automatically. I can see its output using hexdump /dev/hidraw0.
I want to read that input using C# .NET Core. I works fine using Pinvoke. Here the opening part (details over here):
[DllImport("libc")]
public static extern int open(string pathname, OpenFlags flags);
int fd = open("/dev/hidraw0", OpenFlags.O_RDONLY);
Is it possible to do the opening and reading using .NET Core methods? /dev/hidraw0 is just a (device) file, right? Wouldn't it be possible to use FileStream or BinaryReader? The problem I am facing: I only find methods which read available data, but I need a blocking read method which wait until data is available and then return it. Or maybe there is a DataAvailable event or alike?
Basically my question boils down to: Am a stuck with libc's read() and open() methods or is there a .NET Core way for reading /dev/ files?
I think this could help you: Linux & Dotnet – Read from a device file
public void ReadDeviceStream(CancellationToken stoppingToken)
{
// Use the device file
var targetFile = new FileInfo("/dev/inputs/event1");
// Open a stream
using (FileStream fs = targetFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
stoppingToken.Register(() => fs?.Close());
int blockId = 1;
// A big buffer, for simplicity purpose and to receive the entire touch report. We should use
// the proper buffer size based on the event size. Note that we could also
// use the binary reader
var buffer = new byte[1024];
// Read until the token gets cancelled
while (!stoppingToken.IsCancellationRequested && fs.Read(buffer) > 0)
{
ShowBinaryContent(blockId, buffer);
blockId++;
}
}
}
public void ShowBinaryContent(int blockId, byte[] buffer)
{
Console.WriteLine($"Block #{blockId}");
Console.WriteLine(BitConverter.ToString(bytes)); // Hex format: AB-1D...
Console.WriteLine(string.Empty);
}

How do I get a Named Pipe to block in C#?

I'm writing a C# Windows Service (server) that will need to receive messages from C++ GUI applications (clients) using a Named Pipe very infrequently; it could be days to weeks before a message is received. In my testing I noticed that instead of blocking for data to come in, it just continually checks and prints newlines. It does receive the test message but I had to use the debugger in Visual Studio to verify.
Is there a way to get the C# portion to block until there is actually data to be received?
--C# Code:
var client = new NamedPipeServerStream("PipeTest");
client.WaitForConnection();
StreamReader reader = new StreamReader(client);
while (true)
{
Console.WriteLine(reader.ReadLine());
}
--C++ Code:
DWORD written;
INT error_code;
CHAR buffer[1024];
LPCWSTR pipe_name = L"\\\\.\\pipe\\PipeTest";
LRESULT result;
WaitNamedPipe(pipe_name, NMPWAIT_WAIT_FOREVER);
HANDLE hpipe = CreateFile(pipe_name,
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
error_code = GetLastError();
sprintf_s(buffer,"Sending Test Message");
WriteFile(hpipe,buffer,strlen(buffer),&written,NULL);
result = GetLastError();
fprintf(stdout,"Pipe: %d Write: %d Written: %d",error_code,result,written);
DisconnectNamedPipe(hpipe);
CloseHandle(hpipe);
std::cin.get();
[ANSWER]
--C# Code(server) with corrected names:
var server = new NamedPipeServerStream("PipeTest");
while (true)
{
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
Console.WriteLine(reader.ReadLine());
server.Disconnect();
}
For this code I'll only need to capture one line worth of data, #The Shooter was correct but you'll need to add server.Disconnect() in order to read again. I found this from the C documentation and the same principle works in C# apparently.
The server process must call DisconnectNamedPipe to disconnect a pipe handle from its previous client before the handle can be connected to another client by using the ConnectNamedPipe function.
The client end is disconnecting the pipe, so the server sees the end of the input stream immediately after the test message is received.
The documentation for StreamReader.ReadLine() says:
The next line from the input stream, or null if the end of the input stream is reached.
So ReadLine is returning null; what does Console.WriteLine(String) do in that case?
If value is null, only the line terminator is written to the standard output stream.
QED. The behaviour you're seeing is exactly as expected.
You need to check the value returned from ReadLine and deal with null appropriately. In this case you presumably want to wait for another connection.
Instead of:
var client = new NamedPipeServerStream("PipeTest");
client.WaitForConnection();
StreamReader reader = new StreamReader(client);
while (true)
{
Console.WriteLine(reader.ReadLine());
}
Try this:
var client = new NamedPipeServerStream("PipeTest");
while (true)
{
client.WaitForConnection();
StreamReader reader = new StreamReader(client);
Console.WriteLine(reader.ReadLine());
}

Standard input/output in C# process

My application App is using other my application SubApp.
When App needs SubApp it is creating process with SubApp, putting data to SubApp stdin and reading from SubApp stdout.
The problem is that SubApp is using some library which sometimes writes to stdout.
Fragment of SubApp code:
OutsideLibrary.DoSomeInitialization(); // <-- this sometimes writes to stdout
Stream input = Console.OpenStandardInput();
Stream output = Console.OpenStandardOutput();
data = (dataFormat)formatter.Deserialize(input);
//do some job
formatter.Serialize(output, result);
Is there any way to prevent code I don't have from writing to stdout?
Assuming you want to disable third party component output and you have control over the rest of SubApp code you can do following trick: Redirect standard output to null at application bootstrap. When you need to write something to stdout temporary set standard output back to normal, write and set to null again.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace stdio
{
class Program
{
static void Main(string[] args)
{
Console.SetOut(System.IO.TextWriter.Null);
Console.WriteLine("This will go to > null");
WriteOutput("This is written to standard output");
Console.WriteLine("This will also go to > null");
Console.ReadKey();
}
static void WriteOutput(String someString)
{
Console.SetOut(Console.Out);
Stream output = Console.OpenStandardOutput();
StreamWriter sw = new StreamWriter(output);
sw.Write(someString);
sw.Flush();
sw.Close();
output.Close();
Console.SetOut(System.IO.TextWriter.Null);
}
}
}
I've tried this:
StreamWriter sw = new StreamWriter(#"c:\nul");
Console.SetOut(sw);
Console.WriteLine("hello!");
But it throws an exception in new StreamWriter().
The following might work (call it before your external module becomes active):
StreamWriter sw = new StreamWriter(Stream.Null);
Console.SetOut(sw);
A workaround would be to open a real text file and delete it.

Named Pipe Server & Client - No Message

I am trying to learn how to do Named Pipes. So I created a Server and Client in LinqPad.
Here is my Server:
var p = new NamedPipeServerStream("test3", PipeDirection.Out);
p.WaitForConnection();
Console.WriteLine("Connected!");
new StreamWriter(p).WriteLine("Hello!");
p.Flush();
p.WaitForPipeDrain();
p.Close();
Here is my Client:
var p = new NamedPipeClientStream(".", "test3", PipeDirection.In);
p.Connect();
var s = new StreamReader(p).ReadLine();
Console.Write("Message: " + s);
p.Close();
I run the server, and then the client, and I see "Connected!" appear on the server so it is connecting properly. However, the Client always displays Message: with nothing after it, so the data isn't actually travelling from server to client to be displayed. I have already tried swapping pipe directions and having the client send data to the server with the same result.
Why isn't the data being printed out in the screen in this example? What am I missing?
Thanks!
Like L.B said, you must flush the StreamWriter. But employing the using pattern will prevent such mistakes:
using (var p = new NamedPipeServerStream("test3", PipeDirection.Out))
{
p.WaitForConnection();
Console.WriteLine("Connected!");
using (var writer = new StreamWriter(p))
{
writer.WriteLine("Hello!");
writer.Flush();
}
p.WaitForPipeDrain();
p.Close();
}
In the above code, even if Flush() and Close() were omitted, everything would work as intended (since these operations are also performed when an object is disposed). Also, if any exceptions are thrown, everything will still be cleaned up properly.
Change your server code as follows:
StreamWriter wr = new StreamWriter(p);
wr.WriteLine("Hello!\n");
wr.Flush();
your string doesn't get flushed in StreamWriter

Capturing binary output from Process.StandardOutput

In C# (.NET 4.0 running under Mono 2.8 on SuSE) I would like to run an external batch command and capture its ouput in binary form. The external tool I use is called 'samtools' (samtools.sourceforge.net) and among other things it can return records from an indexed binary file format called BAM.
I use Process.Start to run the external command, and I know that I can capture its output by redirecting Process.StandardOutput. The problem is, that's a text stream with an encoding, so it doesn't give me access to the raw bytes of the output. The almost-working solution I found is to access the underlying stream.
Here's my code:
Process cmdProcess = new Process();
ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
cmdStartInfo.FileName = "samtools";
cmdStartInfo.RedirectStandardError = true;
cmdStartInfo.RedirectStandardOutput = true;
cmdStartInfo.RedirectStandardInput = false;
cmdStartInfo.UseShellExecute = false;
cmdStartInfo.CreateNoWindow = true;
cmdStartInfo.Arguments = "view -u " + BamFileName + " " + chromosome + ":" + start + "-" + end;
cmdProcess.EnableRaisingEvents = true;
cmdProcess.StartInfo = cmdStartInfo;
cmdProcess.Start();
// Prepare to read each alignment (binary)
var br = new BinaryReader(cmdProcess.StandardOutput.BaseStream);
while (!cmdProcess.StandardOutput.EndOfStream)
{
// Consume the initial, undocumented BAM data
br.ReadBytes(23);
// ... more parsing follows
But when I run this, the first 23bytes that I read are not the first 23 bytes in the ouput, but rather somewhere several hundred or thousand bytes downstream. I assume that StreamReader does some buffering and so the underlying stream is already advanced say 4K into the output. The underlying stream does not support seeking back to the start.
And I'm stuck here. Does anyone have a working solution for running an external command and capturing its stdout in binary form? The ouput may be very large so I would like to stream it.
Any help appreciated.
By the way, my current workaround is to have samtools return the records in text format, then parse those, but this is pretty slow and I'm hoping to speed things up by using the binary format directly.
Using StandardOutput.BaseStream is the correct approach, but you must not use any other property or method of cmdProcess.StandardOutput. For example, accessing cmdProcess.StandardOutput.EndOfStream will cause the StreamReader for StandardOutput to read part of the stream, removing the data you want to access.
Instead, simply read and parse the data from br (assuming you know how to parse the data, and won't read past the end of stream, or are willing to catch an EndOfStreamException). Alternatively, if you don't know how big the data is, use Stream.CopyTo to copy the entire standard output stream to a new file or memory stream.
Since you explicitly specified running on Suse linux and mono, you can work around the problem by using native unix calls to create the redirection and read from the stream. Such as:
using System;
using System.Diagnostics;
using System.IO;
using Mono.Unix;
class Test
{
public static void Main()
{
int reading, writing;
Mono.Unix.Native.Syscall.pipe(out reading, out writing);
int stdout = Mono.Unix.Native.Syscall.dup(1);
Mono.Unix.Native.Syscall.dup2(writing, 1);
Mono.Unix.Native.Syscall.close(writing);
Process cmdProcess = new Process();
ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
cmdStartInfo.FileName = "cat";
cmdStartInfo.CreateNoWindow = true;
cmdStartInfo.Arguments = "test.exe";
cmdProcess.StartInfo = cmdStartInfo;
cmdProcess.Start();
Mono.Unix.Native.Syscall.dup2(stdout, 1);
Mono.Unix.Native.Syscall.close(stdout);
Stream s = new UnixStream(reading);
byte[] buf = new byte[1024];
int bytes = 0;
int current;
while((current = s.Read(buf, 0, buf.Length)) > 0)
{
bytes += current;
}
Mono.Unix.Native.Syscall.close(reading);
Console.WriteLine("{0} bytes read", bytes);
}
}
Under unix, file descriptors are inherited by child processes unless marked otherwise (close on exec). So, to redirect stdout of a child, all you need to do is change the file descriptor #1 in the parent process before calling exec. Unix also provides a handy thing called a pipe which is a unidirectional communication channel, with two file descriptors representing the two endpoints. For duplicating file descriptors, you can use dup or dup2 both of which create an equivalent copy of a descriptor, but dup returns a new descriptor allocated by the system and dup2 places the copy in a specific target (closing it if necessary). What the above code does, then:
Creates a pipe with endpoints reading and writing
Saves a copy of the current stdout descriptor
Assigns the pipe's write endpoint to stdout and closes the original
Starts the child process so it inherits stdout connected to the write endpoint of the pipe
Restores the saved stdout
Reads from the reading endpoint of the pipe by wrapping it in a UnixStream
Note, in native code, a process is usually started by a fork+exec pair, so the file descriptors can be modified in the child process itself, but before the new program is loaded. This managed version is not thread-safe as it has to temporarily modify the stdout of the parent process.
Since the code starts the child process without managed redirection, the .NET runtime does not change any descriptors or create any streams. So, the only reader of the child's output will be the user code, which uses a UnixStream to work around the StreamReader's encoding issue,
I checked out what's happening with reflector. It seems to me that StreamReader doesn't read until you call read on it. But it's created with a buffer size of 0x1000, so maybe it does. But luckily, until you actually read from it, you can safely get the buffered data out of it: it has a private field byte[] byteBuffer, and two integer fields, byteLen and bytePos, the first means how many bytes are in the buffer, the second means how many have you consumed, should be zero. So first read this buffer with reflection, then create the BinaryReader.
Maybe you can try like this:
public class ThirdExe
{
private static TongueSvr _instance = null;
private Diagnostics.Process _process = null;
private Stream _messageStream;
private byte[] _recvBuff = new byte[65536];
private int _recvBuffLen;
private Queue<TonguePb.Msg> _msgQueue = new Queue<TonguePb.Msg>();
void StartProcess()
{
try
{
_process = new Diagnostics.Process();
_process.EnableRaisingEvents = false;
_process.StartInfo.FileName = "d:/code/boot/tongueerl_d.exe"; // Your exe
_process.StartInfo.UseShellExecute = false;
_process.StartInfo.CreateNoWindow = true;
_process.StartInfo.RedirectStandardOutput = true;
_process.StartInfo.RedirectStandardInput = true;
_process.StartInfo.RedirectStandardError = true;
_process.ErrorDataReceived += new Diagnostics.DataReceivedEventHandler(ErrorReceived);
_process.Exited += new EventHandler(OnProcessExit);
_process.Start();
_messageStream = _process.StandardInput.BaseStream;
_process.BeginErrorReadLine();
AsyncRead();
}
catch (Exception e)
{
Debug.LogError("Unable to launch app: " + e.Message);
}
private void AsyncRead()
{
_process.StandardOutput.BaseStream.BeginRead(_recvBuff, 0, _recvBuff.Length
, new AsyncCallback(DataReceived), null);
}
void DataReceived(IAsyncResult asyncResult)
{
int nread = _process.StandardOutput.BaseStream.EndRead(asyncResult);
if (nread == 0)
{
Debug.Log("process read finished"); // process exit
return;
}
_recvBuffLen += nread;
Debug.LogFormat("recv data size.{0} remain.{1}", nread, _recvBuffLen);
ParseMsg();
AsyncRead();
}
void ParseMsg()
{
if (_recvBuffLen < 4)
{
return;
}
int len = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(_recvBuff, 0));
if (len > _recvBuffLen - 4)
{
Debug.LogFormat("current call can't parse the NetMsg for data incomplete");
return;
}
TonguePb.Msg msg = TonguePb.Msg.Parser.ParseFrom(_recvBuff, 4, len);
Debug.LogFormat("recv msg count.{1}:\n {0} ", msg.ToString(), _msgQueue.Count + 1);
_recvBuffLen -= len + 4;
_msgQueue.Enqueue(msg);
}
The key is _process.StandardOutput.BaseStream.BeginRead(_recvBuff, 0, _recvBuff.Length, new AsyncCallback(DataReceived), null); and the very very important is that convert to asynchronous reads event like Process.OutputDataReceived.

Categories