I was asked last week if there was a way to interactively communicate with a remote Linux client. The only way that I knew of was to create a temporary file with the commands and then use Plink (a part of PuTTY) to execute the file of commands on the remote machine.
This works well when you want to execute a list of commands and are not interested in the response from each command. If you want the response from each command, then you will need to call this method for each command. This is not very efficient as each time this method runs, it will log in to a different session before executing the command.
This method will also not work if you need to run custom applications on the remote client where the session needs to stay in memory.
I did a bit of searching and didn’t come across any LabVIEW solutions that suited my needs. I did find a really good C# library that looked like it would work. It is still actively supported and the latest version is only a few months old. (All good so far)
As the library is massive, my plan of action was to use the library to write my own C# dll that would do what I wanted it to do. All that I needed my library to do was connect to a remote client, execute commands and return the response and then disconnect when finished.
C# class
I decided to put everything into a class so that I can add to it in the future. There are still a few features that I want to add but will leave them for another time.
connect()
You need to create the following objects that will be used by the various parts of the application.
- SshClient – used to connect and disconnect from the client
- StreamReader – this will be used to read what is returned from the client
- StreamWriter – use this object to write to the remote client
public void connect()
{
sshClient = new SshClient(remote, user, password);
sshClient.Connect(); //connect to the client
stream = sshClient.CreateShellStream("dumb", 80, 24, 800, 600, 1024);
reader = new StreamReader(stream);
writer = new StreamWriter(stream);
writer.AutoFlush = true;
}
Once these references are created, they are stored in public properties.
executeCommand()
The execute command methods writes a string to the StreamWriter object.
public void executeCommand(string command)
{
writer.WriteLine(command);
writer.AutoFlush = true;
}
Once the string has been written, the buffer is flushed.
disconnect()
The disconnect() method closes the session and disconnects from the client.
public void disconnect()
{
sshClient.Disconnect();
}
The class only contains these three methods. There is one more step that needs to be taken and that is to read the StreamReader object for responses from the client. I have left this out of the class and leave it up to the application to do the reading.
readReader()
private void readReader()
{
try
{
int i;
for (i = 0; i < 3; i++)
{
Thread.Sleep(500);
if (ssh.stream.DataAvailable)
{
tbOutput.AppendText(ssh.reader.ReadToEnd());
tbOutput.ScrollToCaret();
Thread.Sleep(50);
}
tbDebug.AppendText(i.ToString());
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
In the readReader() method, I monitor the DataAvailable flag. I have had to play around with the number of iterations of the for loop as different tasks take different amounts of time. Some examples that I came across only call this method once, but I found that some data is then missed. Setting the iteration too high causes unnecessary delays and setting it too low does not allow all the data to be read. There are other methods that can be used, but I still need to give those a try.
Implementation in LabVIEW
UPDATE: I have uploaded a video of a LabVIEW application being used to to communicate with LinuxLite running in a virtual machine.
My main aim was to get a LabVIEW application working so once I had my class written, I moved into LabVIEW mode and connected everything up.
Using the .NET palette, I was able to call the constructor, read/write properties and execute methods on the class.
I wrote a simple state machine application to test the class. All the references are stored in a typedef cluster which is shifted around each state. On the front panel there are buttons that generate events to call each case.
Constructor and connect to client
Execute commands on the remote client
Disconnect from the client
Read the StreamReader object
With this application, I am able to connect to a remote client, execute commands as I would in PuTTY and then disconnect when I am done.
There are a few things that I still need to sort out. The main one is that terminal characters are being returned. These are not seen in PuTTY as PuTTY removes them from the string that is shown. The other is that the prompt is being written twice. From what I have read this is caused by the client echoing what it receives. I still need to look at this too.
In future versions, I also want to add SFTP functionality. Being able to upload and download a file will be quite a nice feature. I also want to add different login options. Being able to login using private keys will also be useful.
As with all my posts, you can download the LabVIEW code. The download link contains a slightly modified library from the above post. If you have any feedback, please feel free to contact me on Google+ or send me an email. The LabVIEW application have been tested on the default Raspbian image running on a Raspberry Pi B.
Download LabVIEW LabSSH & LabSFTP LibraryGreg