Sample code for 30+ languages & platforms
Visual FoxPro

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 Visual FoxPro Downloads

Visual FoxPro
LOCAL lnSuccess
LOCAL loSsh1
LOCAL loSsh2
LOCAL loSsh3
LOCAL lnPort
LOCAL lcPassword
LOCAL lcCmd
LOCAL lnChannel1
LOCAL lnChannel2
LOCAL lnChannel3
LOCAL lnPollTimeoutMs
LOCAL lnNumFinished
LOCAL lnFinished1
LOCAL lnFinished2
LOCAL lnFinished3
LOCAL lnCh1
LOCAL lcOut1
LOCAL lnCh2
LOCAL lcOut2
LOCAL lnCh3
LOCAL lcOut3

lnSuccess = 0

*  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.

loSsh1 = CreateObject('Chilkat.Ssh')
loSsh2 = CreateObject('Chilkat.Ssh')
loSsh3 = CreateObject('Chilkat.Ssh')

lnPort = 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.
lcPassword = "mySshPassword"

lnSuccess = loSsh1.Connect("ssh-server1.example.com",lnPort)
IF (lnSuccess = 0) THEN
    ? loSsh1.LastErrorText
    RELEASE loSsh1
    RELEASE loSsh2
    RELEASE loSsh3
    CANCEL
ENDIF

lnSuccess = loSsh1.AuthenticatePw("mySshLogin",lcPassword)
IF (lnSuccess = 0) THEN
    ? loSsh1.LastErrorText
    RELEASE loSsh1
    RELEASE loSsh2
    RELEASE loSsh3
    CANCEL
ENDIF

lnSuccess = loSsh2.Connect("ssh-server2.example.com",lnPort)
IF (lnSuccess = 0) THEN
    ? loSsh2.LastErrorText
    RELEASE loSsh1
    RELEASE loSsh2
    RELEASE loSsh3
    CANCEL
ENDIF

lnSuccess = loSsh2.AuthenticatePw("mySshLogin",lcPassword)
IF (lnSuccess = 0) THEN
    ? loSsh2.LastErrorText
    RELEASE loSsh1
    RELEASE loSsh2
    RELEASE loSsh3
    CANCEL
ENDIF

lnSuccess = loSsh3.Connect("ssh-server3.example.com",lnPort)
IF (lnSuccess = 0) THEN
    ? loSsh3.LastErrorText
    RELEASE loSsh1
    RELEASE loSsh2
    RELEASE loSsh3
    CANCEL
ENDIF

lnSuccess = loSsh3.AuthenticatePw("mySshLogin",lcPassword)
IF (lnSuccess = 0) THEN
    ? loSsh3.LastErrorText
    RELEASE loSsh1
    RELEASE loSsh2
    RELEASE loSsh3
    CANCEL
ENDIF

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

*  Start the command on each server.  QuickCmdSend returns immediately.
lnChannel1 = loSsh1.QuickCmdSend(lcCmd)
IF (lnChannel1 < 0) THEN
    ? loSsh1.LastErrorText
    RELEASE loSsh1
    RELEASE loSsh2
    RELEASE loSsh3
    CANCEL
ENDIF

lnChannel2 = loSsh2.QuickCmdSend(lcCmd)
IF (lnChannel2 < 0) THEN
    ? loSsh2.LastErrorText
    RELEASE loSsh1
    RELEASE loSsh2
    RELEASE loSsh3
    CANCEL
ENDIF

lnChannel3 = loSsh3.QuickCmdSend(lcCmd)
IF (lnChannel3 < 0) THEN
    ? loSsh3.LastErrorText
    RELEASE loSsh1
    RELEASE loSsh2
    RELEASE loSsh3
    CANCEL
ENDIF

*  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.)
lnPollTimeoutMs = 50
lnNumFinished = 0
lnFinished1 = 0
lnFinished2 = 0
lnFinished3 = 0

DO WHILE lnNumFinished < 3
    IF (NOT lnFinished1) THEN
        lnCh1 = loSsh1.QuickCmdCheck(lnPollTimeoutMs)
        IF (lnCh1 = -2) THEN
            ? loSsh1.LastErrorText
            RELEASE loSsh1
            RELEASE loSsh2
            RELEASE loSsh3
            CANCEL
        ENDIF

        IF (lnCh1 >= 0) THEN
            ? "---- ssh1 channel " + STR(lnCh1) + " finished ----"
            lcOut1 = loSsh1.GetReceivedText(lnCh1,"utf-8")
            IF (loSsh1.LastMethodSuccess = 0) THEN
                ? loSsh1.LastErrorText
                RELEASE loSsh1
                RELEASE loSsh2
                RELEASE loSsh3
                CANCEL
            ENDIF

            ? lcOut1
            lnFinished1 = 1
            lnNumFinished = lnNumFinished + 1
        ENDIF

    ENDIF

    IF (NOT lnFinished2) THEN
        lnCh2 = loSsh2.QuickCmdCheck(lnPollTimeoutMs)
        IF (lnCh2 = -2) THEN
            ? loSsh2.LastErrorText
            RELEASE loSsh1
            RELEASE loSsh2
            RELEASE loSsh3
            CANCEL
        ENDIF

        IF (lnCh2 >= 0) THEN
            ? "---- ssh2 channel " + STR(lnCh2) + " finished ----"
            lcOut2 = loSsh2.GetReceivedText(lnCh2,"utf-8")
            IF (loSsh2.LastMethodSuccess = 0) THEN
                ? loSsh2.LastErrorText
                RELEASE loSsh1
                RELEASE loSsh2
                RELEASE loSsh3
                CANCEL
            ENDIF

            ? lcOut2
            lnFinished2 = 1
            lnNumFinished = lnNumFinished + 1
        ENDIF

    ENDIF

    IF (NOT lnFinished3) THEN
        lnCh3 = loSsh3.QuickCmdCheck(lnPollTimeoutMs)
        IF (lnCh3 = -2) THEN
            ? loSsh3.LastErrorText
            RELEASE loSsh1
            RELEASE loSsh2
            RELEASE loSsh3
            CANCEL
        ENDIF

        IF (lnCh3 >= 0) THEN
            ? "---- ssh3 channel " + STR(lnCh3) + " finished ----"
            lcOut3 = loSsh3.GetReceivedText(lnCh3,"utf-8")
            IF (loSsh3.LastMethodSuccess = 0) THEN
                ? loSsh3.LastErrorText
                RELEASE loSsh1
                RELEASE loSsh2
                RELEASE loSsh3
                CANCEL
            ENDIF

            ? lcOut3
            lnFinished3 = 1
            lnNumFinished = lnNumFinished + 1
        ENDIF

    ENDIF

ENDDO

loSsh1.Disconnect()
loSsh2.Disconnect()
loSsh3.Disconnect()

RELEASE loSsh1
RELEASE loSsh2
RELEASE loSsh3