0 votes
by (170 points)
edited

When executing a basic "df" command I am getting "Response reading timed out". Here's my code...

 // create client, connect and log in 
 Ssh client = new Ssh();

 client.Connect("***.***.***.***");
 client.Login("********", "********");

 // run the 'df' command to retrieve disk usage info 
 string diskFree = client.RunCommand("df");
 // display the output 
 Console.WriteLine("Disk usage info:");
 Console.WriteLine(diskFree);

Here's the verbose log...

 2012-08-14 09:25:12.908 DEBUG Ssh(1)[1] SSH: Authentication successful.
 2012-08-14 09:25:12.918 VERBOSE Ssh(1)[1] SSH: Sending packet SSH_MSG_CHANNEL_OPEN (24 bytes).
  0000 |5A-00-00-00-07-73-65-73 73-69-6F-6E-00-00-00-00| Z....session....
  0010 |00-02-00-00-00-00-40-00                        | ......@.
 2012-08-14 09:25:12.918 VERBOSE Ssh(1)[1] SSH: Received packet SSH_MSG_CHANNEL_OPEN_CONFIRMATION (17 bytes).
  0000 |5B-00-00-00-00-00-00-00 00-00-02-00-00-00-00-06| [...............
  0010 |20                                             |  
 2012-08-14 09:25:12.918 DEBUG Ssh(1)[1] SSH: Executing command 'df'.
 2012-08-14 09:25:12.918 VERBOSE Ssh(1)[1] SSH: Sending packet SSH_MSG_CHANNEL_REQUEST (20 bytes).
  0000 |62-00-00-00-00-00-00-00 04-65-78-65-63-01-00-00| b........exec...
  0010 |00-02-64-66                                    | ..df
 2012-08-14 09:25:12.918 VERBOSE Ssh(1)[1] SSH: Received packet SSH_MSG_CHANNEL_SUCCESS (5 bytes).
  0000 |63-00-00-00-00                                 | c....

It seems to work ok running from Puty...

 [1]z001gtwy02# df
 File(s) at A:/
 PRIMARY       <dir>     5-31-2012   17:29
 CONFIG        <dir>     5-31-2012   17:31
 ETC           <dir>     5-31-2012   17:31
 AUDLOG        <dir>     6-15-2012   15:20
 SECONDAR      <dir>     6-27-2012   19:28
    0 file(s)                  0 bytes
    5 subdirectory(s)
                         5820416 bytes free
 [2]z001gtwy02#

Any thoughts?

Thanks -Tom

4 Answers

0 votes
by (58.9k points)
edited
 
Best answer

Hello Tom,

we are not sure whether it will work for any Prompt in the world but here is an idea how to do it. I have tested with two different servers and it worked.

The idea is to send "echo something" command then wait until timeout is passed (the expecting pattern has to be set to something which will occur nowhere in response of the server until the Prompt is detected) and then parse the Prompt. Look at the GetCommandPromptExperimentally method.

    Ssh ssh = new Ssh();
    ssh.Connect(hostName, hostPort);
    ssh.Login(userName, password);
    VirtualShell shell = new VirtualShell(ssh.StartVirtualTerminal());
    GetCommandPromptExperimentally(shell);

    // send command
    shell.SendCommand("df"); // in your case 'df'

    // read response
    string response = shell.ReadAll();
    Console.WriteLine("Output of the 'df':");
    Console.WriteLine(response);

    // send command
    shell.SendCommand("ls -la"); // in your case 'df'

    // read response
    string response2 = shell.ReadAll();
    Console.WriteLine("Output of the 'ls -la':");
    Console.WriteLine(response2);

private void GetCommandPromptExperimentally(VirtualShell shell)
{
    string commandArgs = "detect-prompt";

    // send something to the server
    // it will help us to determine prompt
    shell.SendCommand("echo " + commandArgs);

    // a string with low probability of occuring in the terminal outputs
    string dummy = string.Format("-{0}{0}{0}-", DateTime.Now);

    // wait for something which probably doesn't appear in output 
    // it means: process all incoming data until timeout expires
    string received = shell.Expect(Regex.Escape(dummy));
    if (received != null)
        throw new InvalidOperationException("Cannot determine Prompt!");

    // check whether received data contains command arguments
    received = shell.ReceivedData;
    int pos = received.LastIndexOf(commandArgs, StringComparison.Ordinal);
    if (pos < 0)
        throw new InvalidOperationException("Cannot determine Prompt!");

    // get the prompt
    string commandPrompt = received.Substring(pos + commandArgs.Length);

    // remove new line from prompt beginning
    commandPrompt = commandPrompt.TrimStart('\r', '\n');

    // escape regex special characters
    commandPrompt = Regex.Escape(commandPrompt);

    // add regex end of line
    commandPrompt += "$";

    // set command prompt to detected prompt
    shell.Prompt = commandPrompt;
}
by (170 points)
edited

Thanks Tomas,

This wasn't working for us, only because we are connecting to a router that does not have the full linux operating system available. But I suspect if we were connecting to a regular server this would work. We decided to take a step back and try another direction. But I greatly appreciate the help you have given us.

Thanks again! -Tom

by (58.9k points)
edited

You are welcome! Just for my info, can you please explain what was not working connecting to the router? The GetCommandPromptExperimentally method?

0 votes
by (58.9k points)
edited

It looks like the server did not respond within the default Timeout (which is 60000, i.e. 60 seconds).

Could you please try to increase the Timeout property to e.g. 180 seconds before executing the 'df' command like this:

    // create client, connect and log in  
    Ssh client = new Ssh();
    client.Timeout = 180000; //set timeout to 180 seconds

    client.Connect("***.***.***.***");
    client.Login("********", "********");
    ...

and let us know whether it has solved the problem?

by (170 points)
edited

Thanks Tomas

I tried your suggestion and am now getting an "exceeded login time"...

SSH: Rebex.Net.SshException: Connection has been closed by the remote connection end; protocol error. Disconnecting, excceded max graceful logintime of 60 secs

When I issue the df command from PuTTY I receive a reply instantly. I can't imagine I would need to increase the timeout value. Thoughts?

by (58.9k points)
edited

Sorry, increasing the Timeout really did not have any effect. See the answer below for a solution.

0 votes
by (58.9k points)
edited

Hello,

The RunCommand() method is not using terminal to run the command. Please try to use this alternative which actually uses terminal to connect and execute the command which is what Putty does.

To script a query please download our experimental VirtualShell class. You can use it as follows:

        string hostName = "server";
        int hostPort = 22;
        string userName = "userName";
        string password = "password";
        string commandPrompt = userName + "[@].*[#$] ?$"; // command prompt in format "username@server:path$ ", if the prompt of your server is different, adjust it

        // initialize Ssh shell
        Ssh ssh = new Ssh();
        ssh.Connect(hostName, hostPort);
        ssh.Login(userName, password);
        VirtualShell shell = new VirtualShell(ssh.StartVirtualTerminal());

        // set command prompt
        shell.Prompt = commandPrompt;

        // read welcome message
        string welcome = shell.ReadAll();
        Console.WriteLine("Welcome message:");
        Console.WriteLine(welcome);

        // send command
        shell.SendCommand("df");

        // read response
        string response = shell.ReadAll();
        Console.WriteLine("Output of the 'df':");
        Console.WriteLine(response);

Please give it a try and let us know whether it helped.

by (170 points)
edited

Thanks Tomas

This appears to solve the problem, but I have an issue with setting the "Prompt" value. Do you know how to handle the situation where you don't know in advance what the prompt will be? My application will be talking with hundreds of servers and each one could use a different prompt.

Thanks -Tom

0 votes
by (15.2k points)
edited

Hi, we have just release new Scripting API in 2014-R2 release. You can read more of its features on Scripting features page.

...