8

I wrote a small expect script to connect to multiple SSH Servers. However if I interact with the terminal after initializing the connection, the terminal window behaves very odd.

For example, if I start the script in a non-fullsize terminal, and resize the window to make it larger after the Interact, it looks like that:

Weird Terminal Preview

Now, I had this issue in my first expect script as well. But I was able to resolve it by adding the following code:

trap {
    set XZ [stty rows   ]
    set YZ [stty columns]
    stty rows $XZ columns $YZ < $spawn_out(slave,name)
} WINCH

And this worked perfectly fine! I was able to resize the terminal without a problem.

However, I added this piece of code to my new script, where there are multiple interact's in different proc's (functions). And on window resize, I get the following error:

can't read "spawn_out(slave,name)": no such variable

while executing

"stty rows $XZ columns $YZ < $spawn_out(slave,name)"

I have no idea on how to resolve this. Here is my code:

#!/usr/bin/expect -f

set SERVER "0"
set CHOICE "0"

set SERVER_1_PKEY [exec cat /home/me/p1]
set SERVER_2_PKEY [exec cat /home/me/p2]

set SERVER_1_HOST "server1.com"
set SERVER_2_HOST "server2.com"

set SERVER_1_USER "server1user"
set SERVER_2_USER "server2user"

set SERVER_1_PORT "22"
set SERVER_2_PORT "22"

trap {
    set XZ [stty rows   ]
    set YZ [stty columns]
    stty rows $XZ columns $YZ < $spawn_out(slave,name)
} WINCH

proc s1 {SERVER_1_PKEY SERVER_1_HOST SERVER_1_USER SERVER_1_PORT} {
    send_user "\033c"
    spawn ssh ${SERVER_1_USER}@${SERVER_1_HOST} -p ${SERVER_1_PORT}
    expect "assword:"
    send "${SERVER_1_PKEY}\r"
    interact
}

proc s2 {} {
    send_user "\033c"
    spawn ssh ${SERVER_2_USER}@${SERVER_2_HOST} -p ${SERVER_2_PORT}
    expect "assword:"
    send "${SERVER_2_PKEY}\r"
    interact
}

set arg [lindex $argv 0]
switch $arg {
    ""  { set CHOICE "0" }
    "1" { set CHOICE "1" }
    "2" { set CHOICE "2" }
}

if {$CHOICE eq "0"} {
    puts -nonewline " Input \[1,2\]: "
    flush stdout
    gets stdin SERVER
    if {$SERVER eq "1"} { s1 $SERVER_1_PKEY $SERVER_1_HOST $SERVER_1_USER $SERVER_1_PORT }
    if {$SERVER eq "2"} { s2 $SERVER_2_PKEY $SERVER_2_HOST $SERVER_2_USER $SERVER_2_PORT }
}

if {$CHOICE eq "1"} { s1 $SERVER_1_PKEY $SERVER_1_HOST $SERVER_1_USER $SERVER_1_PORT }
if {$CHOICE eq "2"} { s2 $SERVER_2_PKEY $SERVER_2_HOST $SERVER_2_USER $SERVER_2_PORT }

Can anyone help me resolve this issue or tell me what I'm missing?

Community
  • 1
  • 1
NullDev
  • 6,739
  • 4
  • 30
  • 54
  • Where does spawn_out(slave,name) get set? – Brad Lanam Aug 02 '17 at 13:06
  • @BradLanam It doesn't need to get set: `spawn returns the UNIX process id. If no process is spawned, 0 is returned. The variable spawn_out(slave,name) is set to the name of the pty slave device.` Rel: https://www.tcl.tk/man/expect5.31/expect.1.html – NullDev Aug 02 '17 at 13:39
  • 3
    Just wanted to thank you for that `trap` code for propagating resizes to the inner pty. I wanted to alter the output of bash using a pipe, but needed expect to trick it into interactive mode. Making `COLUMNS`/`LINES` work was the last thing I needed to be comfortable :) – Warbo Jul 09 '18 at 10:31
  • 1
    I came across this looking for how to get resize to work properly with expect. After using strace to track SIGWINCH, checking signal process masks, etc finally you provided the answer in your question! – Alexandros Nov 12 '18 at 18:39

1 Answers1

4

When you call spawn inside a procedure the array variable spawn_out(slave,name) has the scope of that procedure only. Usually, you can just make this into a global scope by declaring it as such inside each procedure:

proc s1 {...} {
  global spawn_out
  ...
  spawn ...
}
send_user $spawn_out(slave,name)
meuh
  • 11,500
  • 2
  • 29
  • 45
  • Thank you! All I had to do was adding your suggested `global spawn_out` to every function. That resolved the issue :) – NullDev Aug 03 '17 at 07:02