'bash + expect, running in background

I'm using expect to establish a persistent ssh connection

set stb_ip [lindex $argv 0]

spawn -noecho ssh -o ControlMaster=auto -o ControlPath=/tmp/ssh-master-%r@%h:%p -o ConnectTimeout=1 -O exit root@$stb_ip
spawn -noecho ssh -fN -o ControlMaster=yes -o ControlPath=/tmp/ssh-master-%r@%h:%p -o ControlPersist=360 -o ConnectTimeout=1 root@$stb_ip
expect {
-re ".*password:" {send "\r"; interact}
}

Unfortunately I can't manage to put this into background, I triend expect_background, fork+disconect but no luck. Even triend running this from another script with

excpect -f script.ex param1 param2 & 

but with no luck. Any help ?



Solution 1:[1]

Heres a proc you can use to login and then interact. I have not tried it with all the ssh Options but I don't see any reason it would not work. Since I use the 8.6 command "try" this is for 8.6 tcl only but you can modify the try to use catch for earlier versions pretty easily.

#!/bin/sh
 # the next line restarts using wish \
 exec /opt/usr8.6b.5/bin/tclsh8.6  "$0" ${1+"$@"}

if { [ catch {package require Expect } err ] != 0 } {
    puts stderr "Unable to find package Expect ... adjust your auto_path!";
}
proc login { user password cmdline } {
    set pid [spawn  -noecho {*}$cmdline ] 
    set bad 0;
    set done 0;
    exp_internal 0; # set to one for extensive debug
    log_user 0;     # set to one to watch action
    set timeout 10 
    set passwdcount 0
    set errMsg {} 
    # regexp to match prompt after successfull login you may need to change
    set intialpromptregexp {^.*[\$\#>]}
    expect {
        -i $spawn_id
        -re $intialpromptregexp {
        send_user $expect_out(0,string);
        set done 1 
        }
        -re {.*assword:} {
        if { $passwdcount >= 1 } {
            lappend errMsg "Invalid username or password for user $user"
            set bad 1
        } else {
            exp_send -i $spawn_id  "$password\r"
            incr passwdcount
            exp_continue;
        }
        }
        -re {.*Host key verification failed.} {
        lappend errMsg "Host key verification failed."
        set bad 1
        } 
        -re {.*onnection refused} {
        lappend errMsg "Connection Refused"
        set bad 1
        } 
        -re {.*onnection closed by remote host} {
        lappend errMsg "Connection Refused"
        set bad 1
        } 
        -re {.*Could not resolve hostname (.*): Name or service not known}  {
        lappend errMsg "Host  invalid: Could not resolve hostname in $cmdline : Name or service not known"
        set bad 1
        } 
        -re {\(yes/no\)\?} {
        exp_send -i $spawn_id  "yes\r"
        exp_continue;
        }
        timeout { 
        lappend errMsg "timeout \[[expr { [clock seconds] - $start } ]\]" 
        set bad 1
        }
        fullbuffer {
        lappend errMsg " buffer is full"
        exp_continue;
        }
        eof {
        puts "Eof detected "
        set bad 1
        set done 1 ;
        }
    }
    if { $bad } {
        throw CONNECTION_ERROR [join $errMsg \n ] 
    } 
    return $spawn_id
}
# get login information in somehow in this case from command line 
set user [lindex $argv 0]
set passwd [lindex $argv 1]
set host [lindex $argv 2 ]  
try {
    set spawn_id [login $user $passwd "ssh -X $user@$host" ]
} trap CONNECTION_ERROR a {
    puts "CONNECTION ERROR: $a"
    exit 1
} 
interact 
set exitstatus [ exp_wait -i $spawn_id ];
catch { exp_close -i $spawn_id };
# more clean up here if you want 

Solution 2:[2]

Assuming your script works in the "foreground"...

nohup expect -f script.ex param1 param2 & 

Solution 3:[3]

Here's a script I made a long time ago. It does what you want but doesn't use Expect (which I loathe). I don't use it any more, I can't guarantee that it even still works but it should get you going.

#!/bin/sh
#
# Persistent ssh: Automatically create persistent ssh connections using OpenSSH 4.0

[ -z "$USER" ] && USER=`whoami`
MASTERSOCKDIR="/tmp/pssh-$USER"
MASTERSOCK="$MASTERSOCKDIR/%r-%h-%p"

# Check if master is running
output=`ssh -o ControlPath="$MASTERSOCK" -O check "$@" 2>&1`
if [ $? -ne 0 ]; then
        case "$output" in
        Control*)
                # Master not running, SSH supports master

                # Figure out socket filename
                socket=`echo "$output" | sed -n -e 's/[^(]*(\([^)]*\)).*/\1/p' -e '1q'`

                # Clean old socket if valid filename
                case "$socket" in
                "$MASTERSOCKDIR"/*) rm -f "$socket" >/dev/null 2>&1 ;;
                esac

                # Start persistent master connection
                if [ ! -d "$MASTERSOCKDIR" ]; then
                        mkdir "$MASTERSOCKDIR"
                        chmod 700 "$MASTERSOCKDIR"
                fi
                ssh -o ControlPath="$MASTERSOCK" -MNf "$@"
                if [ $? -ne 0 ]; then
                        echo "$0: Can't create master SSH connection, falling back to regular SSH" >&2
                fi
                ;;
        *)
                # SSH doesn't support master or bad command line parameters
                ERRCODE=$?
                echo "$output" >&2
                echo "$0: SSH doesn't support persistent connections or bad parameters" >&2
                exit $ERRCODE
                ;;
        esac
fi
exec ssh -o ControlPath="$MASTERSOCK" -o ControlMaster=no "$@"

Solution 4:[4]

To execute an expect script in the background use expect eof at the end of your expect script. In case you have defined interact remove it from your script.

Changed script of OP

set stb_ip [lindex $argv 0]

spawn -noecho ssh -o ControlMaster=auto -o ControlPath=/tmp/ssh-master-%r@%h:%p -o ConnectTimeout=1 -O exit root@$stb_ip
spawn -noecho ssh -fN -o ControlMaster=yes -o ControlPath=/tmp/ssh-master-%r@%h:%p -o ControlPersist=360 -o ConnectTimeout=1 root@$stb_ip
expect {
-re ".*password:" {send "\r"; interact}
}
expect eof

An other example [1].

#!/usr/bin/expect -f

set host "host"
set password "password"

spawn ssh $host

expect {
"(yes/no)?"     {
        send -- "yes\r"
        exp_continue
        }
"*password:*" {
        send -- "$password\r"
        }
}

##Removing this:
#interact 

##And adding this:
expect eof
exit

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Cjolly
Solution 2 Mike Pennington
Solution 3 w00t
Solution 4