The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/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, <Lee@Eakin.org>
# 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>;
            $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 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);
   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 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);
   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;
}