How to keep a session going with .net ssh sshclient - c#

SshClient client;
var sethost = client.RunCommand("export DOCKER_HOST=:2375");
var infohost = client.RunCommand("docker info");
var ps = client.RunCommand("docker ps");
I am using the above commands with ssh client for .Net but it does not keep the state from the first command that does an export in the next two commands.
If I ssh in manually and do the 3 commands it works as I expects, but above c# code seem to forget about the export in the followup commands.

I assume you are using the SSH.NET library. In the following solution I used the stable version (2013.4.7) which is also available as NuGet package.
There are two possible solutions to get this to work. The first one is to combine the commands you want to run in one single RunCommand. A RunCommand starts a new shell each time its invoked and with that it forgets any environment settings you have executed. By chaining the commands with the && or || operator you achieve that all commands can be executed in the same Shell.
static void SolutionOne(ConnectionInfo connection)
{
using (SshClient client = new SshClient(connection))
{
client.ErrorOccurred += (e, s) =>
{
Debug.WriteLine(s.Exception);
};
client.Connect();
// each RunCommand starts its own/new Shell...
var sethost = client.RunCommand("export DOCKER_HOST=:2375");
// ... nothing is kept...
var infohost = client.RunCommand("export -p");
if (!infohost.Result.Contains("DOCKER_HOST"))
{
Console.WriteLine("sorry, a new shell was started for this command");
}
// ... across run commands
var ps = client.RunCommand("echo has value: $DOCKER");
Console.WriteLine(ps.Result);
Console.WriteLine("");
Console.WriteLine("Chain the linux commands...");
// chain all commands with &&
var concatAll = client.RunCommand("export DOCKER_HOST=:2375 && export -p | grep DO && echo has value: $DOCKER_HOST");
// you should see DOCKER_HOST on the last line!
Console.WriteLine("> see has value: 2375 at the end?");
Console.WriteLine(concatAll.Result);
}
}
The second solution is to obtain a ShellStream from the SshClient. That gives you the opportunity to read and write bytes very much similar like you would do a file.
After we have a connection and stream established we Read everything there is to read. Once the stream is empty we send our first command by Writing to the stream. Then we wait a bit for the stream to get data to read and then proceed in reading from the stream again until the stream is empty. That is when we can start the next command, rinse and repeat until all commands are done.
static void SolutionTwo(ConnectionInfo connection)
{
using (SshClient client = new SshClient(connection))
{
client.ErrorOccurred += (e, s) =>
{
Debug.WriteLine(s.Exception);
};
client.Connect();
using (var stream = client.CreateShellStream("dumb", 512, 96, 800, 600, 8191))
{
stream.ErrorOccurred += (e, s) =>
{
Debug.WriteLine(s.Exception);
};
// read welcome message
Console.WriteLine(ReadFromStream(stream));
// first command and read its output
WriteToStream(stream, "uname\n");
Console.WriteLine(ReadFromStream(stream));
// second and output
WriteToStream(stream, "df\n");
Console.WriteLine(ReadFromStream(stream));
// third and output
WriteToStream(stream, "ps aux\n");
Console.WriteLine(ReadFromStream(stream));
}
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(" >>>>>we are all done");
Console.ForegroundColor = ConsoleColor.White;
}
}
static void WriteToStream(ShellStream stream, string command)
{
stream.Write(command);
}
static StringBuilder ReadFromStream(ShellStream stream)
{
var result = new StringBuilder();
// there seems to be a timing/concurrency issue
// which I only could fix with using this
// useless Sleep calls
Thread.Sleep(500);
// wait for the Stream to have data
while (stream.Length == 0)
{
// yes, I know ...
Thread.Sleep(500);
}
// let's read!
string line;
while ((line = stream.ReadLine()) != null)
{
result.AppendLine(line);
}
return result;
}
As you noticed in ReadFromStream method there is some dubious hacking. I had a huge struggle to read reliable from the stream. In my initial attempts I used the DataReceived event but no matter how I tried it either did work for a few lines or even chars or it didn't work at all. In the end I fell back to a nasty Thread.Sleep call that worked reliable all the time. Maybe it is a bug in the library or something in my local setup but I gave up.

Related

Make application close when .exe is executed again

I'm currently working on a simple converter tool and was wondering if it's possible to make the application close if I run the .exe again. Some kind of "if two instances run -> close both".
I need this function because I run the application via a shortcut-button inside a third party program. So I would like if my converter app closes once I press this shortcut-button again.
I know it sounds counter intuitive running the exe again to close, but i have to have my app work the same way as the integrated tools in the third party program, and this involves opening and closing tools by pressing their respective toggle-buttons. I can't add a plug-in running inside the third party program, but i CAN add a shortcut button next to the integrated tools. It's a work around, but it will at least act like a toggle button.
You could do something like this:
Process currentProcess = Process.GetCurrentProcess();
bool suocide = false;
foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
{
if (process.MainModule.FileName == currentProcess.MainModule.FileName && process.Id != currentProcess.Id)
{
process.CloseMainWindow();
process.Close();
//process.Kill(); or you can do kill instead
suocide = true;
}
}
if (suocide)
currentProcess.Kill(); // you probably don't care about new process as it is just for closing purpose but if you do then do a proper application exit
You can put it inside your window constructor.
Step 1 Identify a 2nd instance:
I'd recommend the MUTEX answer in this question:
How can I prevent launching my app multiple times?
Step 2 Get that first instance closed
Although the MUTEX answer identifies a second instance, it gives no way to find it and tell it to close.
Solution: Listen with a named pipe in the app (first instance the ClosEE):
//using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public static class SomeClass
{
public static void SomeMethod()
{
Threading.Thread t = new Threading.Thread(() =>
{
try {
while (true) {
dynamic server = new NamedPipeServerStream("Closer", PipeDirection.InOut, -1);
server.WaitForConnection();
if (!server.IsConnected)
return;
dynamic reader = new IO.StreamReader(server);
dynamic casetxt = reader.ReadToEnd();
server.Close();
RootForm.Invoke(() =>
{
if (casetxt == "End") {
System.Environment.Exit(0);
}
});
}
} catch (Exception ex) {
// try/catch required in all child threads as error silently ends app.
// log it...
}
});
t.IsBackground = true;
t.Name = "EnderListener";
t.Start();
}
}
//=======================================================
//Service provided by Telerik (www.telerik.com)
Then when you detect a second instance via the Mutex, send this message from the 2nd instance the "Closer":
dynamic serverloopcount = 1;
dynamic iteration = 1;
dynamic GotServerCount = false;
do {
NamedPipeClientStream client = new NamedPipeClientStream("Closer");
client.Connect();
if (!GotServerCount) {
GotServerCount = true;
serverloopcount = client.NumberOfServerInstances;
}
dynamic reader = new IO.StreamReader(client);
dynamic writer = new IO.StreamWriter(client);
writer.WriteLine("End");
writer.Flush();
writer.Close();
client.Close();
iteration += 1;
} while (iteration <= serverloopcount);
Good luck.

Redirected output of a process stucks

I'm starting a 3rd party command line application from my C# program using this part of code.
// define Process Start Info and give filename of the 3rd Party app
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(FileName);
myProcessStartInfo.CreateNoWindow = true;
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardInput = true;
myProcessStartInfo.RedirectStandardOutput = true;
theProcess.StartInfo = myProcessStartInfo;
// instantiate observer class that contains a function
// (ProcessStandardOutput) that is run in separate thread
observer = new StreamObserver();
// create the thread
outputObserverThread = new Thread(observer.ProcessStandardOutput);
// start the 3rd party console application
theProcess.Start();
// set the input stream (to send commands to the console app)
commandStream = theProcess.StandardInput;
// connect the output stream with the observer class
observer.SetOutputStream(theProcess.StandardOutput);
// start the observer thread
outputObserverThread.Start();
// send any command to the console app
SendCommand("init");
This is actually nothing special and has been taken 70% from C# documentation example.
It works so far. I can send commands using the SendCommand() to the console application and I'm getting the response back.
But at one point the output stream gets stuck. Meaning I do not get any text back and even the end of the previous textblock is missing.
Sending commands that result usually in just one line of reply will not be passed to the stream.
A command that usually produces a hugh reply (e.g. "help") will "flush" the stream and I'm getting text through the stream (including the missing data)
This is the (stripped down) implementation of the StreamObserver which processes the output stream:
public class StreamObserver
{
// Volatile is used as hint to the compiler that this data
// member will be accessed by multiple threads.
private volatile bool _shouldStop;
private volatile StreamReader outputStream;
// This method will be called when the thread is started.
public void ProcessStandardOutput()
{
while (!_shouldStop)
{
string line = outputStream.ReadLine();
Console.WriteLine(line);
}
}
public void SetOutputStream(StreamReader stream)
{
outputStream = stream;
}
}
Nothing magic here either....
Any idea what could cause the stream to get stuck and recover when hugh data is present?
I have just counted the returned text. It looks like a block has to contain ~4k data before it is send to the output stream.
Does that ring any bell???
Btw, I'm running on Windows 7 64bit and Use Visual Studio 2013 prof.

Unable to read from Serial Port using C# Mono (RaspberryPi)

I'm attempting to write a C# library which looks at all available USB serial ports on a Raspberry Pi so that I can enumerate, identify and communicate with a set of Arduinos connected to the Pi via a USB hub.
I am able to make this work on my windows machine (several Arduinos connected to my desktop computer) and have even been able to make it work on my Pi however, I am struggling to understand how to generalize the fix.
If I attempt to run the program by itself on the Pi, I am able to open the serial port and send data however, I cannot receive anything from the Arduinos: I get timeout exceptions. I understand that Mono's implementation of SerialPort is limited and I must use SerialPort.ReadByte() instead of Readline() and the data received events (my solution is based on code from HowToSystemIOPorts). My Serial port enumeration is using a method outlined in another stack exchange response here.
My timeout is currently set to 4 seconds, which is several orders of magnitude longer than I expect to receive the message.
After a lot of googling, I came across mention of using minicom to initialize the serial port here, which to my surprise allowed me to receive data from the Arduino. The biggest drawback is that I need to initialize the port using minicom and leave the process opening each time I boot the Pi. I also can't seem to figure out how to make this work with multiple Arduinos.
Here is what I have tried so far:
Updated the Pi firmware and software to their latest versions
Attempted to use both an Arduino MEGA 2560 R3 and Arduino UNO
Changed the owner of the tty* ports (ttyACM0 and ttyUSB0 in this case) to both my user and group
Successfully configured the port via minicom, left the process running and start the program and read/wrote data. A manual process which only seems to work for one Arduino at a time
Successfully run the program in Windows without fault
Verified the Arduinos are recognized by the Pi running "dmesg | grep tty"
Here is what I hope to solve:
Automatic setup/initialization of the Arduino serial ports. Whether through a shell script run before the main program or within Mono code so that the code below can run as intended.
Here is my connection code:
public bool StartArduinoComms()
{
string[] ports = GetPortNames();
foreach (string port in ports)
{
mLogger.LogMessage(ProsthesisCore.Utility.Logger.LoggerChannels.Arduino, string.Format("Found serial port {0}", port));
}
bool foundCorrectArduino = false;
var idPacket = new ArduinoMessageBase();
idPacket.ID = ArduinoMessageValues.kIdentifyValue;
string jsonOutput = Newtonsoft.Json.JsonConvert.SerializeObject(idPacket);
foreach (string port in ports)
{
SerialPort serialPort = new SerialPort(port, kArduinoCommsBaudRate);
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
//Only check unopened ports
if (!serialPort.IsOpen)
{
serialPort.Open();
//Disable telemtry just incase
var toggle = new { ID = ArduinoMessageValues.kTelemetryEnableValue, EN = false };
string disableTelem = Newtonsoft.Json.JsonConvert.SerializeObject(toggle);
serialPort.Write(disableTelem);
//Discard any built up data
serialPort.DiscardInBuffer();
serialPort.Write(jsonOutput);
serialPort.ReadTimeout = kIDTimeoutMilliseconds;
string response = string.Empty;
for (int i = 0; i < kNumRetries; ++i)
{
try
{
//This is guaranteed to timeout if not configured through minicom
response = ReadLine(serialPort);
break;
}
//Catch case where the serial port is unavailable. MOve to next port
catch (TimeoutException)
{
continue;
}
}
if (!string.IsNullOrEmpty(response))
{
//Perform response validation
}
else
{
//Got no response
}
if (!foundCorrectArduino)
{
serialPort.Close();
}
}
}
return foundCorrectArduino;
}
/// <summary>
/// From https://stackoverflow.com/questions/434494/serial-port-rs232-in-mono-for-multiple-platforms
/// </summary>
/// <returns></returns>
private static string[] GetPortNames()
{
int p = (int)Environment.OSVersion.Platform;
List<string> serial_ports = new List<string>();
// Are we on Unix?
if (p == 4 || p == 128 || p == 6)
{
string[] ttys = System.IO.Directory.GetFiles("/dev/", "tty*");
foreach (string dev in ttys)
{
//Arduino MEGAs show up as ttyACM due to their different USB<->RS232 chips
if (dev.StartsWith("/dev/ttyS") || dev.StartsWith("/dev/ttyUSB") || dev.StartsWith("/dev/ttyACM"))
{
serial_ports.Add(dev);
}
}
}
else
{
serial_ports.AddRange(SerialPort.GetPortNames());
}
return serial_ports.ToArray();
}
Have a look at stty command. It will let you set/read teminal settings
http://linux.about.com/od/lna_guide/a/gdelna38t01.htm will give a rundown on it's use.
It would be easier to call out to than minicom, and the settings stay on the device.
I have done something like the same as you before.
I had to read and write data through USB Serial adapter, and didnt use minicom.
It may not be god code but i found that inorder to read the data I could create a new thread and have that check for data, my code include a lot of stuff but basicly i did this:
System.Threading.Thread newThread;
newThread = new System.Threading.Thread(this.check_get_data);
and the check_get_data method
public void check_get_data ()
{
byte tmpByte = 0;
while (m_objSerialPort.BytesToRead != 0) {
tmpByte = (byte)m_objSerialPort.ReadByte ();
DoSomethingWithByte(tmpByte);
Thread.Sleep(20);
}
}
this is currently running with two usbserials. dont know if it helps but hope you find your solution

Why does StandardOutput.Read() never return? (deadlock?)

Using C#, I want to automate a third-party Windows command-line program. Usually, it is an interactive console, you send commands, it may prompt for details, send back a result and display a prompt to ask for more commands. Typically:
c:\>console_access.exe
Prompt> version
2.03g.2321
Prompt>
I used .NET classes Process and ProcessStartInfo along with redirections of stdin/stdout/stderr.
public ConsoleAccess()
{
if (!File.Exists(consoleAccessPath)) throw new FileNotFoundException(consoleAccessPath + " not found");
myProcess = new Process();
ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(consoleAccessPath, ""); // even "2>&1" as argument does not work; my code still hangs
myProcessStartInfo.CreateNoWindow = true;
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardOutput = true;
myProcessStartInfo.RedirectStandardError = true;
myProcessStartInfo.RedirectStandardInput = true;
//myProcessStartInfo.ErrorDialog = true; // I tried, to no avail.
myProcess.StartInfo = myProcessStartInfo;
outputQueue = new ConcurrentQueue<string>(); // thread-safe queue
errorQueue = new ConcurrentQueue<string>();
myProcess.Start();
myStandardOutput = myProcess.StandardOutput;
myStandardError = myProcess.StandardError;
myStandardInput = myProcess.StandardInput;
stdOutPumper = new Thread(new ThreadStart(PumpStdOutLoop));
stdOutPumper.Start();
stdErrPumper = new Thread(new ThreadStart(PumpStdErrLoop));
stdErrPumper.Start();
string empty = getResponse(); // check for prompt
string version = getVersion(); // one simple command
}
// [...]
private void PumpStdErrLoop()
{
while (true)
{
string message = myStandardError.ReadLine();
errorQueue.Enqueue(message);
}
}
private void PumpStdOutLoop()
{
while (true)
{
bool done = false;
string buffer = "";
//int blocksize = 1024;
string prompt = "Prompt> ";
while (!done)
{
//char[] intermediaire = new char[blocksize];
//int res = myStandardOutput.Read(intermediaire, 0, blocksize);
//buffer += new string(intermediaire).Substring(0, res);
byte b = (byte)myStandardOutput.Read(); // I go byte per byte, just in case the char[] above is the source of the problem. To no avail.
buffer += (char)b;
done = buffer.EndsWith(prompt);
}
buffer = buffer.Substring(0, buffer.Length - prompt.Length);
outputQueue.Enqueue(buffer);
}
}
Since this program returns "Prompt> " (important : without "\n" at the end) when it's waiting for commands, I can't use myProcess.BeginOutputReadLine();
However, I have to use threads because I must listen stdout AND stderr at the same time.
This is why I used threads and thread-safe queues for a class producer/consumer pattern.
"You can use asynchronous read operations to avoid these dependencies and their deadlock potential. Alternately, you can avoid the deadlock condition by creating two threads and reading the output of each stream on a separate thread." source: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput%28v=vs.100%29.aspx
With this design, all sequences like
* cmd -> result with no err (something on stdout, nothing on stderr)
* cmd -> error (something on stderr, nothing on stdout)
works as expected. no problem.
cmd -> result with warning (something on both stderr and stdout)
should work (I'm trying to reproduce this scenario)
however, for one command in particular -- a command that prompts for a password during its execution -- does not work:
main thread principal loops forever on
if (errorQueue.Count == 0 && outputQueue.Count == 0) { System.Threading.Thread.Sleep(500); }
thread pumping stdout waits forever on
byte b = (byte)myStandardOutput.Read();
thread pumping stdout waits a line forever on
string message = myStandardError.ReadLine();
What I don't get is why byte b = (byte)myStandardOutput.Read(); does not pump the message "password:". Nothing happens. I never get the first 'p'.
I feel I hit a deadlock scenario, but I do not understand why.
What's wrong?
(I don't think it is very relevant but I tried the above on .NET 4.0 with MS Visual Studio 2010 on Windows 7 32-bit.)
This is a very common failure mode for these kind of interactive console mode programs. The C runtime library automatically switches the stderr and stdout streams to buffered mode when it detects that output is being redirected. Important to improve throughput. So output goes into that buffer instead of getting directly written to the console. Getting your program to see the output requires the buffer to be flushed.
There are three scenarios where the buffer gets flushed. A flush occurs when the buffer is full, typically around 2 kilobytes. Or when the program writes a line terminator (\n). Or when the program explicitly calls fflush(). The first two scenarios do not occur, not enough output and the program isn't using \n. Which points at the problem, the original programmer forgot to call fflush(). Forgetting this is very common, the programmer simply never intended the program to be used other than in an interactive way.
Nothing can do about it, you'll need to ask the owner or author of the program to add fflush(). Maybe you can limp along by just assuming that the prompt is being written.

Capturing standard out from tail -f "follow"

I am trying to capture the output from tail in follow mode, where it outputs the text as it detects changes in the file length - particularly useful for following log files as lines are added. For some reason, my call to StandardOutput.Read() is blocking until tail.exe exits completely.
Relevant code sample:
var p = new Process() {
StartInfo = new ProcessStartInfo("tail.exe") {
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = "-f c:\\test.log"
}
};
p.Start();
// the following thread blocks until the process exits
Task.Factory.StartNew(() => p.StandardOutput.Read());
// main thread wait until child process exits
p.WaitForExit();
I have also tried using the support for the OutputDataReceived event handler which exhibits the same blocking behavior:
p.OutputDataReceived += (proc, data) => {
if (data != null && data.Data != null) {
Console.WriteLine(data.Data);
}
};
p.BeginOutputReadLine();
I do have a little bit more code around the call to StandardOutput.Read(), but this simplifies the example and still exhibits the undesirable blocking behavior. Is there something else I can do to allow my code to react to the availability of data in the StandardOutput stream prior to the child application exiting?
Is this just perhaps a quirk of how tail.exe runs? I am using version 2.0 compiled as part of the UnxUtils package.
Update: this does appear to be at least partially related to quirks in tail.exe. I grabbed the binary from the GnuWin32 project as part of the CoreUtils package and the version bumped up to 5.3.0. If I use the -f option to follow without retries, I get the dreaded "bad file descriptor" issue on STDERR (easy to ignore) and the process terminates immediately. If I use the -F option to include retries it seems to work properly after the bad file descriptor message has come by and it attempts to open the file a second time.
Is there perhaps a more recent win32 build from the coreutils git repository I could try?
I know it is not exatly what you are asking but as James says in the comments, you could do the equivalent functionality directly in c# to save you having to launch another process.
One way you can do it is like this:
using System;
using System.IO;
using System.Text;
using System.Threading;
public class FollowingTail : IDisposable
{
private readonly Stream _fileStream;
private readonly Timer _timer;
public FollowingTail(FileInfo file,
Encoding encoding,
Action<string> fileChanged)
{
_fileStream = new FileStream(file.FullName,
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite);
_timer = new Timer(o => CheckForUpdate(encoding, fileChanged),
null,
0,
500);
}
private void CheckForUpdate(Encoding encoding,
Action<string> fileChanged)
{
// Read the tail of the file off
var tail = new StringBuilder();
int read;
var b = new byte[1024];
while ((read = _fileStream.Read(b, 0, b.Length)) > 0)
{
tail.Append(encoding.GetString(b, 0, read));
}
// If we have anything notify the fileChanged callback
// If we do not, make sure we are at the end
if (tail.Length > 0)
{
fileChanged(tail.ToString());
}
else
{
_fileStream.Seek(0, SeekOrigin.End);
}
}
// Not the best implementation if IDisposable but you get the idea
// See http://msdn.microsoft.com/en-us/library/ms244737(v=vs.80).aspx
// for how to do it properly
public void Dispose()
{
_timer.Dispose();
_fileStream.Dispose();
}
}
Then to call for example:
new FollowingTail(new FileInfo(#"C:\test.log"),
Encoding.ASCII,
s =>
{
// Do something with the new stuff here, e.g. print it
Console.Write(s);
});

Categories

Resources