#!/usr/local/bin/perl # Original author: Author: Don Libes, NIST (tcl/expect) # Date written: December 5, 1991 # Date last editted: October 19, 1994 # Version: 2.11 # # Ported to Perl Expect module by: Lee Eakin, # Date first ported: January 28, 2001 # require 5.004; my ($pgm)=$0=~m|([^/]*)$|; $DEBUG=0; use strict; use Expect; use Getopt::Long; use vars qw($opt_noproc $opt_catu $opt_tty $opt_noescape $opt_escape $opt_silent $opt_proxy $opt_r); $|=1; my $prompt= $ENV{EXPECT_PROMPT} || '(?:%|#|\$)\s'; Getopt::Long::Configure('auto_abbrev','pass_through'); &GetOptions(qw(noproc catu tty=s noescape escape=s silent proxy=s r)) or &usage; &usage unless @ARGV or $opt_noproc; $opt_escape="\035" unless $opt_escape or $opt_noescape; my @flags; push @flags,'-tty',$opt_tty if $opt_tty; push @flags,'-silent' if $opt_silent; $Expect::Log_Stdout=0; my $stdin=Expect->exp_init(\*STDIN); my $stdout=Expect->exp_init(\*STDOUT); my $pid; my $user=shift; my $usernum; if ($opt_r) { print "KRUN"; $usernum=3; } else { $usernum=$user=~/^-\d+/ ? 2 : 1 } # User who originated kibitz session has $usernum == 1 on local machine. # User who is responding to kibitz has $usernum == 2. # User who originated kibitz session has $usernum == 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". my $sh; my $rhost; my $remsh; if ($usernum == 1) { unless ($opt_noproc) { if (@ARGV) { $sh=Expect->spawn("@ARGV") or die "$pgm: \"@ARGV\" spawn failed"; } else { $sh=Expect->spawn($ENV{SHELL} || '/bin/sh') or die "$pgm: shell spawn failed"; } } if ($user=~/([^@]+)@(.+)/) { $user=$1; $rhost=$2; } if ($rhost) { print "connecting to $rhost\n" unless $opt_silent; $opt_proxy||=$ENV{USER} || $ENV{LOGNAME} || `whoami` || `logname`; my $rcmd="rlogin $rhost -l $opt_proxy -8"; $remsh=Expect->spawn($rcmd); while (1) { $remsh->expect(60, -re => 'word:\s*$', -re => '\s+incorrect.*', -re => $prompt) or die "$pgm: connection to $rhost timed out\n"; if ($remsh->exp_match_number == 1) { print "password (for $opt_proxy) on $rhost: "; $stdin->exp_stty('-echo'); my $pswd=; $stdin->exp_stty('echo'); print "\n"; chomp $pswd; print $remsh "$pswd\r"; } elsif ($remsh->exp_match_number == 2) { die "$pgm: invalid password or account\n"; } elsif ($remsh->exp_match_number == 3) { last; } } print "starting $pgm on $rhost\n" unless $opt_silent; print $remsh "$pgm @flags -r $user;kill -9 $$\r"; $remsh->expect(120, -re => "$pgm @flags -r $user.*KRUN", -re => "$pgm @flags -r $user.*$pgm"."[^\r\n]*\r") or die "$pgm: unable to run $pgm on $rhost: timed out\n"; if ($remsh->exp_match_number == 2) { die "$pgm: unable to run $pgm on $rhost\n". "try rlogin by hand followed by \"$pgm $user\"\n"; } while (1) { $remsh->expect(120, -re => ".*\n",'KABORT','KDATA'); print $remsh->exp_match if $remsh->exp_match_number == 1; exit if $remsh->exp_match_number == 2; last if $remsh->exp_match_number == 3; } } } elsif ($usernum == 2) { ($pid)=$user=~/^-(\d+)/; } my $localio=(($usernum == 3) or not $rhost); my $inf; my $outf; my $exin; my $exout; if ($localio) { $pid||=$$; if ($usernum == 2) { $inf="/tmp/exp1.$pid"; $outf="/tmp/exp0.$pid"; } else { $inf="/tmp/exp0.$pid"; $outf="/tmp/exp1.$pid"; } } else { $exin=$remsh; $exout=$remsh; } if ($usernum == 2) { die "$pgm: Huh? No one is asking you to $pgm.\n" unless -r $inf; open IN,$inf or die "$pgm: read pipe open failed: $!\n"; select((select(IN),$|=1)[0]); open OUT,">$outf" or die "$pgm: write pipe open failed: $!\n"; select((select(OUT),$|=1)[0]); $stdin->exp_stty('-echo raw'); $exin=Expect->exp_init(\*IN); $exout=Expect->exp_init(\*OUT); if ($opt_escape) { &vprint("Escape sequence is $opt_escape"); print "\r\n"; $stdin->set_seq($opt_escape,\&local_esc); } unlink $inf; $exin->set_group($stdout); $stdin->set_group($exout); Expect::interconnect($exin,$stdin); exit; } if ($localio) { $SIG{'INT'}=$SIG{'QUIT'}=$SIG{'TERM'}=eval "sub {unlink '$inf','$outf';exit}"; my $fifocmd; foreach (qw(/usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /bin /sbin)) { $fifocmd="$_/mkfifo %s",last if -x "$_/mkfifo"; } unless ($fifocmd) { foreach (qw(/usr/bin /usr/sbin /usr/local/bin /usr/local/sbin /bin /sbin /usr/etc /etc)) { $fifocmd="$_/mknod %s p",last if -x "$_/mknod"; } } die "$pgm: could not determine how to make a fifo - where is mknod?\n" unless $fifocmd; system(sprintf $fifocmd,$inf) == 0 or die "$pgm: could not make fifo \"$inf\": $?\n"; system(sprintf $fifocmd,$outf) == 0 or unlink($inf), die "$pgm: could not make fifo \"$outf\": $?\n"; chmod 0666,$inf,$outf; print "asking $user to type: $pgm -$pid\n" unless $opt_silent; open WQ,"|/usr/bin/write $user $opt_tty" or unlink($inf,$outf), die "$pgm: write command failed: $!\n"; print WQ "Can we talk? Run: $pgm -$pid\n"; close WQ; open OUT,">$outf" or die "$pgm: write pipe open failed: $!\n"; select((select(OUT),$|=1)[0]); open IN,$inf or die "$pgm: read pipe open failed: $!\n"; select((select(IN),$|=1)[0]); $stdin->exp_stty('-echo raw'); $exin=Expect->exp_init(\*IN); $exout=Expect->exp_init(\*OUT); unlink $inf; } if ($usernum==3) { print "KDATA"; $exin->set_group($stdout); $stdin->set_group($exout); Expect::interconnect($exin,$stdin); } else { if ($opt_escape) { &vprint("Escape sequence is $opt_escape"); print "\r\n"; $stdin->set_seq($opt_escape,\&local_esc); } if ($opt_noproc) { $exin->set_group($stdout); $stdin->set_group($exout); Expect::interconnect($exin,$stdin); } else { $exin->set_group($sh); $stdin->set_group($sh); $sh->set_group($stdout,$exout); Expect::interconnect($exin,$stdin,$sh); } $sh->hard_close; } unlink $inf,$outf; exit; sub local_esc { print "\r\n$pgm:\r\n E) Exit program\r\n any other key to return to program\r\n"; my $ans=getc; print "\r\n"; exit if $ans eq 'E'; print "returning to $pgm\r\n"; } sub vprint { my @keys=@_; foreach (@keys) { s/([\200-\277])/'M-'.chr(ord($1)^128)/eg; s/([\000-\011\013-\037\177])/"^".chr(ord($1)^ord('@'))/eg; } print @keys; } sub usage { print STDERR "Usage: $pgm [options] user [program ...]\n"; print STDERR " or: $pgm [options] user\@host [program ...]\n"; exit 1; }