#!/afs/athena/contrib/perl5/perl5.002 -w
warn "using perl5.002 explicitly...\n";
#!/afs/athena/contrib/perl5/p -w

use strict;
use FileHandle;
use lib $ENV{'ROOT'};
use Finger;
use SelectLoop;

open(PID, "> $ENV{'ROOT'}/f-stats.pid") || die "can't open pidfile";
print PID "$$\n";
close(PID);

use vars qw( $Polling_Interval $Do_Debug );
$Polling_Interval=60;		# finger all machines every 60 seconds.

use vars qw( $SL @host @user @ltime @unadulterated );

$Finger::ConnectTimeout = 5;	# if we don't connect in 5s, give up.
# $SelectLoop::Debug=1;
# $Finger::Debug=1;

sub uniq {	# (c) center for advanced cheating studies =)
  keys %{{map(($_, ''), @_)}};
}

sub user {
  my ($i, $data) = @_;
  $data || return undef;
  ($data =~ /^No one logged on/) && return '';
  ($data =~ /^Login\s/)
    || do { warn "$host[$i]: UNKNOWN format ($data)"; return undef };
  $data =~ s/\r\n/\n/g;

  my ($format, @lines) = split(/\n+/, $data);
  my (@users) = uniq( map( (split /\s/, $_, 2)[0], @lines ) );
  warn "ignoring some ($#users) users on $host[$i]\n" if $#users;
  $users[0];
}

sub digest {
  my ($i) = @_;
  my ($who) = user(@_);
  my ($time);

  return unless defined $who;

  if (defined $user[$i]) {
    return if ($user[$i] eq $who);
    print STDERR "login change \@$host[$i]: $user[$i] -> $who\n" if $Do_Debug;
    $time = time;
    if ($user[$i] && $unadulterated[$i]) {
      printf "%d %d\n", $time, ($time - $ltime[$i]);
      print STDERR "lt: $user[$i]\@$host[$i]\n" if $Do_Debug;
    }
    $unadulterated[$i] = 1;
  } else {
    print STDERR "initial login \@$host[$i]: $who\n" if $Do_Debug;
    $time = time;
  }

  $user[$i] = $who;
  $ltime[$i] = $time;
}

######## argument list parsing

sub parse_arg {
  (/^-d$/ || /^--debug$/)    && ($Do_Debug=1, return 0);
  (/^-t=(\d+)$/ || /^--time=(\d+)$/)
                             && ($Polling_Interval=$1, return 0);
  /^-/                       && die "$0: invalid command-line flag '$_'";
  return 1;
}

@ARGV = grep(parse_arg, @ARGV);

######## main program starts here

STDOUT->autoflush(1);
STDERR->autoflush(1);
warn "Started.\n";
STDERR->flush;

@ARGV || warn "No args, reading stdin...\n";

$SL = new SelectLoop
  ('handler' => sub { digest ($_[1], $_) if ($_[0] eq 'EOF');  (1) } );

my $i;

HOST:
while (<>) {
  s/^\s+//;   s/\s+$//;
  next HOST if /^\s*\#/;
  push(@host, $_);
}
close ARGV;

while (1) {  
  my $start = time;

  $SL->timeout(0.0);	# don't block on select while querying.
  foreach $i (0..$#host) {
    my ($q) = Finger::query("\@$host[$i]");
    $SL->add( $q, $i ) if $q;
    $SL->do_select;
  }

  $SL->timeout(undef);	# might as well block now
  $SL->do_select while $SL->entries;

  my $end = time;
  my $wait = $start+$Polling_Interval - $end;
  if ($wait > 0) {
    warn "[$end] waiting: $wait s\n";
    sleep($wait);
  } else {
    warn "[$end] not waiting\n";
  }
  STDERR->flush;
}
