Sample code for 30+ languages & platforms
Pascal (Lazarus/Delphi)

SSH Parallel Remote Commands on Multiple Servers

See more SSH Examples

Demonstrates running the same command on several servers simultaneously. It is simply a matter of using one Ssh object per server, starting the command on each with QuickCmdSend, and then polling each connection with QuickCmdCheck until all have finished.

Background: This is the fan-out pattern behind configuration-management and monitoring tools: query or update a whole fleet in roughly the time the slowest single host takes, rather than the sum of all of them. Each server needs its own object because each has its own connection and authentication state. Note that results come back in whatever order the servers finish, which is why the example tracks completion per connection. Production code would keep the objects in arrays instead of repeating the logic per server.

Chilkat Pascal (Lazarus/Delphi) Downloads

Pascal (Lazarus/Delphi)
program ChilkatDemo;

// Demonstrates using the Chilkat Pascal wrapper via the C bridge DLL.
// Builds as a console application under Lazarus (FPC) or Delphi.

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}
{$APPTYPE CONSOLE}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  SysUtils,
  CkDllLoader,
  Chilkat.Ssh;

// ---------------------------------------------------------------------------

procedure RunDemo;
var
  success: Boolean;
  ssh1: TSsh;
  ssh2: TSsh;
  ssh3: TSsh;
  port: Integer;
  password: string;
  cmd: string;
  channel1: Integer;
  channel2: Integer;
  channel3: Integer;
  pollTimeoutMs: Integer;
  numFinished: Integer;
  finished1: Boolean;
  finished2: Boolean;
  finished3: Boolean;
  ch1: Integer;
  out1: string;
  ch2: Integer;
  out2: string;
  ch3: Integer;
  out3: string;

begin
  success := False;

  //  This example requires the Chilkat API to have been previously unlocked.
  //  See Global Unlock Sample for sample code.

  //  Demonstrates running the same command on several servers simultaneously.  It is simply a
  //  matter of using one Ssh object per server.

  ssh1 := TSsh.Create;
  ssh2 := TSsh.Create;
  ssh3 := TSsh.Create;

  port := 22;

  //  Normally you would not hard-code the password in source.  You should instead obtain it
  //  from an interactive prompt, environment variable, or a secrets vault.
  password := 'mySshPassword';

  success := ssh1.Connect('ssh-server1.example.com',port);
  if (success = False) then
    begin
      WriteLn(ssh1.LastErrorText);
      Exit;
    end;
  success := ssh1.AuthenticatePw('mySshLogin',password);
  if (success = False) then
    begin
      WriteLn(ssh1.LastErrorText);
      Exit;
    end;

  success := ssh2.Connect('ssh-server2.example.com',port);
  if (success = False) then
    begin
      WriteLn(ssh2.LastErrorText);
      Exit;
    end;
  success := ssh2.AuthenticatePw('mySshLogin',password);
  if (success = False) then
    begin
      WriteLn(ssh2.LastErrorText);
      Exit;
    end;

  success := ssh3.Connect('ssh-server3.example.com',port);
  if (success = False) then
    begin
      WriteLn(ssh3.LastErrorText);
      Exit;
    end;
  success := ssh3.AuthenticatePw('mySshLogin',password);
  if (success = False) then
    begin
      WriteLn(ssh3.LastErrorText);
      Exit;
    end;

  //  This command sleeps for 5 seconds and then prints the system date/time, making the parallel
  //  execution easy to observe.
  cmd := 'sleep 5; date';

  //  Start the command on each server.  QuickCmdSend returns immediately.
  channel1 := ssh1.QuickCmdSend(cmd);
  if (channel1 < 0) then
    begin
      WriteLn(ssh1.LastErrorText);
      Exit;
    end;

  channel2 := ssh2.QuickCmdSend(cmd);
  if (channel2 < 0) then
    begin
      WriteLn(ssh2.LastErrorText);
      Exit;
    end;

  channel3 := ssh3.QuickCmdSend(cmd);
  if (channel3 < 0) then
    begin
      WriteLn(ssh3.LastErrorText);
      Exit;
    end;

  //  The command is now running on all three servers at once.  Poll each connection until every
  //  command has finished.  (Production code would keep the objects in arrays rather than
  //  repeating the logic.)
  pollTimeoutMs := 50;
  numFinished := 0;
  finished1 := False;
  finished2 := False;
  finished3 := False;

  while numFinished < 3 do
    begin
      if (not finished1) then
        begin
          ch1 := ssh1.QuickCmdCheck(pollTimeoutMs);
          if (ch1 = -2) then
            begin
              WriteLn(ssh1.LastErrorText);
              Exit;
            end;
          if (ch1 >= 0) then
            begin
              WriteLn('---- ssh1 channel ' + ch1 + ' finished ----');
              out1 := ssh1.GetReceivedText(ch1,'utf-8');
              if (ssh1.LastMethodSuccess = False) then
                begin
                  WriteLn(ssh1.LastErrorText);
                  Exit;
                end;
              WriteLn(out1);
              finished1 := True;
              numFinished := numFinished + 1;
            end;
        end;

      if (not finished2) then
        begin
          ch2 := ssh2.QuickCmdCheck(pollTimeoutMs);
          if (ch2 = -2) then
            begin
              WriteLn(ssh2.LastErrorText);
              Exit;
            end;
          if (ch2 >= 0) then
            begin
              WriteLn('---- ssh2 channel ' + ch2 + ' finished ----');
              out2 := ssh2.GetReceivedText(ch2,'utf-8');
              if (ssh2.LastMethodSuccess = False) then
                begin
                  WriteLn(ssh2.LastErrorText);
                  Exit;
                end;
              WriteLn(out2);
              finished2 := True;
              numFinished := numFinished + 1;
            end;
        end;

      if (not finished3) then
        begin
          ch3 := ssh3.QuickCmdCheck(pollTimeoutMs);
          if (ch3 = -2) then
            begin
              WriteLn(ssh3.LastErrorText);
              Exit;
            end;
          if (ch3 >= 0) then
            begin
              WriteLn('---- ssh3 channel ' + ch3 + ' finished ----');
              out3 := ssh3.GetReceivedText(ch3,'utf-8');
              if (ssh3.LastMethodSuccess = False) then
                begin
                  WriteLn(ssh3.LastErrorText);
                  Exit;
                end;
              WriteLn(out3);
              finished3 := True;
              numFinished := numFinished + 1;
            end;
        end;
    end;

  ssh1.Disconnect();
  ssh2.Disconnect();
  ssh3.Disconnect();


  ssh1.Free;
  ssh2.Free;
  ssh3.Free;

end;

// ---------------------------------------------------------------------------

begin

  try
    RunDemo;
  except
    on E: Exception do
      WriteLn('Unhandled exception: ', E.ClassName, ': ', E.Message);
  end;

  WriteLn;
  {$IFDEF MSWINDOWS}
  WriteLn('Press Enter to exit...');
  ReadLn;
  {$ENDIF}
end.