#!../expect -- # 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 # Version: 2.11 exp_version -exit 5.0 # 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 noproc 0 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] { set catflags "-u" } else { set catflags "" } # 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. #set catflags "" #set catflags "-u" # Some flags must be passed onto the remote kibitz process. They are stored # in "kibitz_flags". Currently, they include -tty and -silent. set kibitz_flags "" while {[llength $argv]>0} { set flag [lindex $argv 0] switch -- $flag \ "-noproc" { set noproc 1 set argv [lrange $argv 1 end] } "-catu" { set catflags "-u" set argv [lrange $argv 1 end] } "-tty" { set tty [lindex $argv 1] set argv [lrange $argv 2 end] set kibitz_flags "$kibitz_flags -tty $tty" } "-noescape" { set allow_escape 0 set argv [lrange $argv 1 end] } "-escape" { set escape_char [lindex $argv 1] set escape_printable $escape_char set argv [lrange $argv 2 end] } "-silent" { set verbose 0 set argv [lrange $argv 1 end] set kibitz_flags "$kibitz_flags -silent" } "-proxy" { set proxy [lindex $argv 1] set argv [lrange $argv 2 end] } default { break } } if {([llength $argv]<1) && ($noproc==0)} { send_user "usage: kibitz \[args] user \[program ...]\n" send_user " or: kibitz \[args] user@host \[program ...]\n" exit } log_user 0 set timeout -1 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 set user_number 3 # need to check that it exists first! set user [lindex $argv 1] } else { 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 #exec rm -f $user_number #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 # valid at . delimiters # if !do_if0, skip the whole thing - this is here just to make caller simpler proc is_prefix {do_if0 x xjunk} { if 0!=$do_if0 {return 0} 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} } return 0 } # get domainname. Unfortunately, on some systems, domainname(1) # returns NIS domainname which is not the internet domainname. proc domainname {} { # open pops stack upon failure set rc [catch {open /etc/resolv.conf r} file] if {$rc==0} { while {-1!=[gets $file buf]} { if 1==[scan $buf "domain %s" name] { close $file return $name } } close $file } # fall back to using domainname if {0==[catch {exec domainname} name]} {return $name} error "could not figure out domainname" } if $user_number==1 { if $noproc==0 { if [llength $argv]>1 { set pid [eval spawn [lrange $argv 1 end]] } else { # if running as CGI, shell may not be set! set shell /bin/sh catch {set shell $env(SHELL)} set pid [spawn $shell] } set shell $spawn_id } # is user2 remote? regexp (\[^@\]*)@*(.*) $user ignore tmp host set user $tmp 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]} { set user2_islocal 0 } } if !$user2_islocal { if $verbose {send_user "connecting to $host\n"} if ![info exists proxy] { proc whoami {} { global env 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!" } set proxy [whoami] } spawn rlogin $host -l $proxy -8 set userin $spawn_id set userout $spawn_id catch {set prompt $env(EXPECT_PROMPT)} set timeout 120 expect { assword: { stty -echo send_user "password (for $proxy) on $host: " set old_timeout $timeout; set timeout -1 expect_user -re "(.*)\n" send_user "\n" set timeout $old_timeout send "$expect_out(1,string)\r" # bother resetting echo? exp_continue } incorrect* { send_user "invalid password or account\n" exit } "TERM = *) " { send "\r" exp_continue } timeout { send_user "connection to $host timed out\n" exit } eof { send_user "connection to host failed: $expect_out(buffer)" exit } -re $prompt } 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" expect { -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" exit } timeout { send_user "unable to start kibitz on $host: " set expect_out(buffer) "timed out" set timeout 0; expect -re .+ send_user $expect_out(buffer) exit } } expect { -re ".*\n" { # pass back diagnostics # should really strip out extra cr send_user $expect_out(buffer) exp_continue } KABORT exit default exit KDATA } } } if $user_number==2 { 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 } proc prompt1 {} { return "kibitz[info level].[history nextid]> " } set esc_match {} if {$allow_escape} { set esc_match { $escape_char { 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" interpreter send_user "returning to kibitz\n" } } } proc prompt1 {} { return "kibitz[info level].[history nextid]> " } set timeout -1 # kibitzer executes following code if $user_number==2 { # for readability, swap variables set tmp $userinfile set userinfile $useroutfile set useroutfile $tmp if ![file readable $userinfile] { send_user "Eh? No one is asking you to kibitz.\n" exit -1 } spawn -open [open "|cat $catflags < $userinfile" "r"] set userin $spawn_id spawn -open [open $useroutfile w] set userout $spawn_id # open will hang until other user's cat starts stty -echo raw 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 \ -output $userout \ -input $userin exit } # only user_numbers 1 and 3 execute remaining code proc abort {} { global user_number # 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} exit } if $local_io { proc mkfifo {f} { 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?" abort } proc rmfifos {} { global userinfile useroutfile catch {exec rm -f $userinfile $useroutfile} } trap {rmfifos; exit} {SIGINT SIGQUIT SIGTERM} # create 2 fifos to communicate with other user mkfifo $userinfile mkfifo $useroutfile # 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! set rc [catch { system echo "Can we talk? Run: \"kibitz -$pid\"" | \ /bin/write $user $tty } ] if $rc {rmfifos;abort} spawn -open [open $useroutfile w] set userout $spawn_id # open will hang until other user's cat starts spawn -open [open "|cat $catflags < $userinfile" "r"] set userin $spawn_id catch {exec rm $userinfile} } stty -echo raw if $user_number==3 { send_user "KDATA" ;# this tells user_number 1 to send data interact { -output $userout -input $userin eof { wait -i $userin return -tcl } -output $user_spawn_id } } else { if $allow_escape {send_user "Escape sequence is $escape_printable\r\n"} if $noproc { interact { -output $userout -input $userin eof {wait -i $userin; return} -output $user_spawn_id } } else { eval interact $esc_match { -output $shell \ -input $userin eof { wait -i $userin close -i $shell return } -output $shell \ -input $shell -output "$user_spawn_id $userout" } wait -i $shell } } if $local_io rmfifos