exec expect
-- "$0" ${1+"$@"}
# allow another user to share a shell (or other program) with you
# See kibitz(1) man page for complete info.
# Author: Don Libes, NIST
# Date written: December 5, 1991
# Date last editted: October 19, 1994
# if environment variable "EXPECT_PROMPT" exists, it is taken as a regular
# expression which matches the end of your login prompt (but does not other-
# wise occur while logging in).
set prompt
"(%|#|\\$) $" ;# default prompt
set tty
"" ;# default if no -tty flag
set allow_escape
1 ;# allow escapes if true
set escape_char
\035 ;# control-right-bracket
set escape_printable
"^\]"
set verbose
1 ;# if true, describe what kibitz is doing
set kibitz
"kibitz" ;# where kibitz lives if some unusual place.
;# this must end in "kibitz", but can have
;# things in front (like directory names).
#set proxy "kibitz" ;# uncomment and set if you want kibitz to use
;# some other account on remote systems
# The following code attempts to intuit whether cat buffers by default.
# The -u flag is required on HPUX (8 and 9) and IBM AIX (3.2) systems.
if {[file exists
$exp_exec_library/cat-buffers
]} {
# If this fails, you can also force it by commenting in one of the following.
# Or, you can use the -catu flag to the script.
# Some flags must be passed onto the remote kibitz process. They are stored
# in "kibitz_flags". Currently, they include -tty and -silent.
while {[llength
$argv]>0} {
set flag
[lindex
$argv 0]
set argv
[lrange
$argv 1 end
]
set argv
[lrange
$argv 1 end
]
set argv
[lrange
$argv 2 end
]
set kibitz_flags
"$kibitz_flags -tty $tty"
set argv
[lrange
$argv 1 end
]
set escape_char
[lindex
$argv 1]
set escape_printable
$escape_char
set argv
[lrange
$argv 2 end
]
set argv
[lrange
$argv 1 end
]
set kibitz_flags
"$kibitz_flags -silent"
set proxy
[lindex
$argv 1]
set argv
[lrange
$argv 2 end
]
if {([llength
$argv]<1) && ($noproc==0)} {
send_user
"usage: kibitz \[args] user \[program ...]\n"
send_user
" or: kibitz \[args] user@host \[program ...]\n"
set user
[lindex
$argv 0]
if {[string match
-r $user]} {
send_user
"KRUN" ;# this tells user_number 1 that we're running
;# and to prepare for possible error messages
# need to check that it exists first!
set user
[lindex
$argv 1]
set user_number
[expr 1+(0==[string first
- $user])]
# at this point, user_number and user are correctly determined
# User who originated kibitz session has user_number == 1 on local machine.
# User who is responding to kibitz has user_number == 2.
# User who originated kibitz session has user_number == 3 on remote machine.
# user 1 invokes kibitz as "kibitz user[@host]"
# user 2 invokes kibitz as "kibitz -####" (some pid).
# user 3 invokes kibitz as "kibitz -r user".
# uncomment for debugging: leaves each user's session in a file: 1, 2 or 3
#exp_internal -f $user_number 0
set user2_islocal
1 ;# assume local at first
# later move inside following if $user_number == 1
# return true if x is a prefix of xjunk, given that prefixes are only
# if !do_if0, skip the whole thing - this is here just to make caller simpler
proc is_prefix
{do_if0 x xjunk
} {
set split [split $xjunk .
]
for {set i
[expr [llength
$split]-1]} {$i>=0} {incr i
-1} {
if {[string match
$x [join [lrange
$split 0 $i] .
]]} {return 1}
# get domainname. Unfortunately, on some systems, domainname(1)
# returns NIS domainname which is not the internet domainname.
# open pops stack upon failure
set rc
[catch
{open
/etc
/resolv.conf r
} file]
while {-1!=[gets
$file buf
]} {
if 1==[scan
$buf "domain %s" name
] {
# fall back to using domainname
if {0==[catch
{exec domainname
} name
]} {return $name}
error
"could not figure out domainname"
set pid
[eval spawn
[lrange
$argv 1 end
]]
# if running as CGI, shell may not be set!
catch
{set shell
$env(SHELL
)}
regexp
(\
[^@\
]*)@
*(.
*) $user ignore tmp
host
if ![string match
$host ""] {
set h_rc
[catch
{exec hostname
} hostname
]
set d_rc
[catch domainname domainname
]
if {![is_prefix
$h_rc $host $hostname]
&& ![is_prefix
$d_rc $host $hostname.
$domainname]} {
if $verbose {send_user
"connecting to $host\n"}
if ![info exists proxy
] {
if {[info exists env
(USER
)]} {return $env(USER
)}
if {[info exists env
(LOGNAME
)]} {return $env(LOGNAME
)}
if {![catch
{exec whoami
} user
]} {return $user}
if {![catch
{exec logname
} user
]} {return $user}
# error "can't figure out who you are!"
spawn
rlogin $host -l $proxy -8
catch
{set prompt
$env(EXPECT_PROMPT
)}
send_user
"password (for $proxy) on $host: "
set old_timeout
$timeout; set timeout
-1
send
"$expect_out(1,string)\r"
send_user
"invalid password or account\n"
send_user
"connection to $host timed out\n"
send_user
"connection to host failed: $expect_out(buffer)"
if {$verbose} {send_user
"starting kibitz on $host\n"}
# the kill protects user1 from receiving user3's
# prompt if user2 exits via expect's exit.
send
"$kibitz $kibitz_flags -r $user;kill -9 $$\r"
-re "kibitz $kibitz_flags -r $user.*KRUN" {}
-re "kibitz $kibitz_flags -r $user.*(kibitz\[^\r\]*)\r" {
send_user
"unable to start kibitz on $host: \"$expect_out(1,string)\"\n"
send_user
"try rlogin by hand followed by \"kibitz $user\"\n"
send_user
"unable to start kibitz on $host: "
set expect_out
(buffer
) "timed out"
set timeout
0; expect
-re .
+
send_user
$expect_out(buffer
)
# should really strip out extra cr
send_user
$expect_out(buffer
)
set pid
[string trimleft
$user -]
set local_io
[expr ($user_number==3)||
$user2_islocal]
if {$local_io||
($user_number==2)} {
if {0==[info exists pid
]} {set pid
[pid
]}
set userinfile
/tmp
/exp0.
$pid
set useroutfile
/tmp
/exp1.
$pid
return "kibitz[info level].[history nextid]> "
send_user
"\nto exit kibitz, enter: exit\n"
send_user
"to suspend kibitz, press appropriate job control sequence\n"
send_user
"to return to kibitzing, enter: return\n"
send_user
"returning to kibitz\n"
return "kibitz[info level].[history nextid]> "
# kibitzer executes following code
# for readability, swap variables
set userinfile
$useroutfile
if ![file readable
$userinfile] {
send_user
"Eh? No one is asking you to kibitz.\n"
spawn
-open [open
"|cat $catflags < $userinfile" "r"]
spawn
-open [open
$useroutfile w
]
# open will hang until other user's cat starts
if {$allow_escape} {send_user
"Escape sequence is $escape_printable\r\n"}
# While user is reading message, try to delete other fifo
catch
{exec rm -f $userinfile}
eval interact
$esc_match \
# only user_numbers 1 and 3 execute remaining code
# KABORT tells user_number 1 that user_number 3 has run into problems
# and is exiting, and diagnostics have been returned already
if {$
::user_number
==3} {send_user KABORT
}
if 0==[catch
{exec mkfifo $f}] return ;# POSIX
if 0==[catch
{exec mknod
$f p
}] return
# some systems put mknod in wierd places
if 0==[catch
{exec /usr
/etc
/mknod
$f p
}] return ;# Sun
if 0==[catch
{exec /etc
/mknod
$f p
}] return ;# AIX, Cray
puts
"Couldn't figure out how to make a fifo - where is mknod?"
global userinfile useroutfile
catch
{exec rm -f $userinfile $useroutfile}
trap {rmfifos
; exit} {SIGINT SIGQUIT SIGTERM
}
# create 2 fifos to communicate with other user
# make sure other user can access despite umask
exec chmod 666 $userinfile $useroutfile
if {$verbose} {send_user
"asking $user to type: kibitz -$pid\n"}
# can't use exec since write insists on being run from a tty!
system
echo "Can we talk? Run: \"kibitz -$pid\"" | \
spawn
-open [open
$useroutfile w
]
# open will hang until other user's cat starts
spawn
-open [open
"|cat $catflags < $userinfile" "r"]
catch
{exec rm $userinfile}
send_user
"KDATA" ;# this tells user_number 1 to send data
if {$allow_escape} {send_user
"Escape sequence is $escape_printable\r\n"}
-input $userin eof
{wait -i $userin; return}
eval interact
$esc_match {
} -output "$user_spawn_id $userout"