#!/usr/local/bin/perl
#	There are three new things in this example. First is the concept
#of closing the process, second is the concept of using regular
#expressions in match patterns, and third is the concept of grabbing
#stuff that is in the process's buffer. This last part is like when you
#telnet to a host, do ls, and want to know the name of the files returned:
#
#
#somehost$ ls
#thing
#thing.gz
#core
#Mail
#mail
#somehost$ 
#
#	When you use the expect() function it is possible to grab the
#data before the next patter looked for. in this case if you were
#looking for "somehost$ " you could get the names of the files returned
#in the ls. In the example below the output of the 'id' command is grabbed
#for use by the program.
#
#	What this example tries to do is log in to foreign hosts and
#grab the user id on each host.
#

use Expect;

# Optional debugging, explained later.
# $Expect::Debug=1
# $Expect::Exp_Internal=1;
# $Expect::Log_Stdout=0; # On by default.

die "Usage: $0 host1 host2... hostn\n" unless defined $ARGV[0];
@hosts = @ARGV;


# Ssh would be better, rlogin is used here because it is more familiar.
foreach $host (@hosts) {
  $rlogin=Expect->spawn("rlogin $host");

  $rlogin->expect(30,"ssword: ") || die "Never got password prompt on $host, ".$rlogin->exp_error()."\n";

# We got the prompt, so send a password.
  print $rlogin "H4yB3vis\r";

# Now we look for a shell prompt. Notice the use of the regular expression.
# To use a regexp make the previous argument '-re'.
# expect will match on the hostname followed by a %, >, or $ followed by
# whitespace. Notice that the regular expression part is in single quotes.
# This is so that perl won't interpret control characters such as 
# whitespace (\s) for us until it gets in to the pattern matching engine.
# my prompt is "darkwing$ ". Yours is probably different.
# The below expect() call will return 1 if it matches the closed by foreign
# host string or 2 if it matches the regular expression.
# undef is returned if nothing matches.
  $match=$rlogin->expect(30,"closed by foreign host","-re",$host.'[%>$]\s');

  die "Dumped by server\n" if $match == 1;
  die "Never got shell prompt on $host, ".$rlogin->exp_error()."\n" unless $match;

# Ok, so we have a shell prompt. let's give it the 'id' command.
  print $rlogin "id\r";

# Now, we know that because ther terminal will echo "id\r" back to us
# we should grab it so it won't muck with our final output.
# Also, on most terminals if you send a carriage return it will respond
# with a carriage return-newline combination. So we do:
  $rlogin->expect(30,"id\r\n");
# which clears id out of the buffer. Note that I used \r\n in double
# quotes instead of single quotes because I want perl to look for
# a carriage return instead of the literal \r\n. If I had instead done
# $rlogin->expect(30,'-re','id\r\n'); it would work because it gets
# tossed in to a regular expression where \r and \n get evaluated.

# Now when we use expect() to find the next prompt the output of the last
# command will be in the buffer that holds output between expect() calls.
# this output should be 'before' we match.
# I'm going to define what prompt we're looking for ahead of time to
# make the call to expect more readable.
  $prompt = $host.'[%>$]\s';

  $rlogin->expect(30,'-re',$prompt);

# Yet another way to find out if we had a successful match is to test for
# an error.
  die "Never got prompt after sending 'id' command\n" if $rlogin->exp_error();

# And the output of id:
  print "id on $host: $rlogin->exp_before()";

# Now, we do a hard close here. We are positively done with rlogin and we
# know that it won't be ending by itself. This would be unlike, say, 
# a command like "tail /etc/passwd" which will exit as soon as it is finished
# sending us its output. For that sort of a command soft_close() would 
# suffice.
# It is important that you close rlogin so that processes don't 
# pile up eating your system resources during further loops through
# hosts.
 $rlogin->hard_close();

# next host
}



