#!/usr/bin/perl
#
# Simple configuration checker.  This reports errors and reports on the 
# current InterMezzo configuration on the local host.
#

use IO::File;
use Getopt::Long;
use File::stat;

my $version= "0.001";
my $configdir= undef;
my $idfile= undef;
my $v_hash= undef;
my $s_hash= undef;
my @s_fsets;
my @r_fsets;

my $errors= 0;
my $warnings= 0;

my ($mysysid, $prestodev, $mysysip);

main();


my $prog_name;
sub print_usage {
    print("Usage : $prog_name [--configdir=my_config_dir] " .
      "[--idfile=my_idfile]\n");
}

sub main {
    my ($idfile_r,$fsetdb_r,$serverdb_r);
    my ($fd1);

    $prog_name= $0;
    if ( !GetOptions("configdir=s" => \$configdir,
             "idfile=s" => \$idfile) ) {
    print_usage();
    exit(1);
    }

    print(">>\n>> InterMezzo config checker version $version.\n");
    print(">>\n");
    print(">> NOTE : currently does not check for all possible errors.\n");
    print(">>\n");

    if ( !defined( $configdir ) ) {
    $configdir= "/etc/intermezzo";
    print(">>  --configdir=my_confdir not set, using \"$configdir\"\n");
    }
    if ( !defined( $idfile ) ) {
    $idfile= "sysid";
    print(">>  --idfile=my_idfile not set, using \"$idfile\"\n");
    }

    if ( $idfile ) {
    if ( ! -f $idfile ) { $idfile= $configdir . "/" . $idfile; }
    }
    else { 
    $idfile= $configdir . "/sysid";
    }

    $idfile_r= $idfile;
    $fsetdb_r=  $configdir . "/fsetdb";
    $serverdb_r= $configdir . "/serverdb";

    print(">>\n");
    print(">> Reading \"$idfile_r\"\n");
    $fd1= new IO::File;
    $buffer= undef;
    $fd1->open( $idfile_r ) or die "Could not open $idfile_r";
    while ( <$fd1> ) { $buffer.= $_; }
    $fd1->close();
    ($mysysid, $prestodev, $mysysip)= split ' ', $buffer;

    print(">> Reading \"$fsetdb_r\"\n");
    $buffer= undef;
    $fd1->open( $fsetdb_r ) or die "Could not open $fsetdb_r";
    while ( <$fd1> ) { $buffer.= $_; }
    $fd1->close();
    $v_hash= eval( $buffer );
    die "Error in evaluating $fsetdb_r:\n$@\n" if $@;

    print(">> Reading \"$serverdb_r\"\n");
    $buffer= undef;
    $fd1->open( $serverdb_r ) or die "Could not open $serverdb_r";
    while ( <$fd1> ) { $buffer.= $_; }
    $fd1->close();
    $s_hash = eval( $buffer );
    die "Error in evaluating $serverdb_r:\n$@\n" if $@;

    validate_hash_tables();
    validate_presto_dev();
    validate_conf_modules();
    validate_proc_filesystems();
    
    print(">>\n");
    print(">> Found the following configuration :\n");
    print(">>   My SysID       :\t$mysysid\n");
    if ( @s_fsets ) {
    print(">>   Server for     :\t@s_fsets\n");
    }
    if ( @r_fsets ) {
    print(">>   Replicator for :\t@r_fsets\n");
    }
    if ( !@s_fsets && !@r_fsets ) {
    print(">>   Not serving or replicating any filesets.\n");
    }
    print(">>\n");
    if ( $errors || $warnings ) {
    printf(">> Found %d error(s) and %d warning(s).\n",
           $errors, $warnings );
    printf(">> See InterMezzo-HOWTO file for example config files.\n");
    }
    else {
    print(">> Did not find any errors - but check printout above.\n");
    }
    print(">>\n");
};


sub validate_hash_tables {
    validate_fsetdb();
    validate_serverdb();
}


sub validate_fsetdb {
    my ($fset,$count);
    my @fsets;

    foreach $fset (keys %{$v_hash}) {
    push( @fsets, $fset );
    }

    # Check that each fileset has a server.
    foreach $fset (@fsets) {
    if (!defined( $v_hash->{$fset}->{servername} )) {
        print("ERROR fsetdb : fileset $fset has no server.\n");
        $errors++;
    }
    }

    # Check that each fileset has at least one client.
    foreach $fset (@fsets) {
    $count= 0;
    foreach my $rep (@{$v_hash->{$fset}->{replicators}}) {
        $count++;
    }
    if ($count == 0) {
        print("ERROR fsetdb : fileset $fset has no replicators.\n");
        $errors++;
    }
    }

    # Check that server is not also client.
    foreach $fset (@fsets) {
    $serv= $v_hash->{$fset}->{servername};
    if ( $serv ) {
               foreach my $rep (@{$v_hash->{$fset}->{replicators}}) {
        if ( $serv eq $client ) {
            print("ERROR fsetdb : $serv is both a server and " .
              "replicator for fileset $fset.\n");
            $errors++;
        }
        }
    }
    }

    # Check that any client only appears once in the replicator list.
    foreach $fset (@fsets) {
    foreach my $rep (@{$v_hash->{$fset}->{replicators}}) {
        $count= 0;
        foreach my $rep2 (@{$v_hash->{$fset}->{replicators}}) {
        if ( $rep eq $rep2 ) {
            $count++;
        }
        }
        if ( $count > 1 ) {
        print("ERROR fsetdb: $rep appears twice in replicator list " .
              "for fileset $fset.\n");
        $errors++;
        }
    }
    }

    # Determine which filesets we are a server for.
    foreach $fset (@fsets) {
    if ( $mysysid eq $v_hash->{$fset}->{servername} ) {
        push(@s_fsets, $fset);
    }
    }

    # Determine which filesets we are a replicator for.
    foreach $fset (@fsets) {
    foreach my $rep (@{$v_hash->{$fset}->{replicators}}) {
        if ( $rep eq $mysysid ) {
        push(@r_fsets, $fset);
        }
    }    
    }
}

sub validate_serverdb {

    # If we are the server make sure that the IP address seems correct.
    if ( $s_hash->{$mysysid} ) {
    if ( ! defined $s_hash->{$mysysid}->{ipaddr} ) {
        print("ERROR serverdb : no ipaddr for server $mysysid.\n");
        $errors++;
    }
    else {
        if ( !( $s_hash->{$mysysid}->{ipaddr} eq $mysysip ) ) {
        print("WARNING serverdb : the IP addresses for $mysysid are " .
              "different in the serverdb and fsetdb files.\n");
        $warnings++;
        }
        if ( defined  $s_hash->{$mysysid}->{bindaddr} ) {
        if ( !( $s_hash->{$mysysid}->{bindaddr} eq 
            $s_hash->{$mysysid}->{ipaddr} ) ) {
            print("WARNING serverdb : the ipaddr and binaddr " .
              "addresses differ.\n");
            $warnings++;
        }
        }
    }
#    printf("INFO serverdb : the server is using port %d.\n",
#           $s_hash->{$mysysid}->{port} );
    }

    # Now make sure that all the servers that are mentioned in the fsetdb
    # file are also mentioned in the serverdb file.
    foreach $fset (@fsets) {
    $serv= $v_hash->{$fset}->{server};
    if ( ! defined $s_hash->{$serv} ) {
        print("ERROR serverdb : server $serv is mentioned in fsetdb, " .
          "but has no serverdb entry.\n");
        $errors++;
    }
    }
}


sub validate_presto_dev {
    # Check for a presto device.
    if ( ! -c $prestodev ) {
    print("ERROR : $prestodev is not a valid character device.\n");
    print("      : probable error in idfile ($configdir/$idfile).\n");
    $errors++;
    }
    else {
    # Check the presto device has the correct major/minor numbers.
    my $st= stat($prestodev);
    if ( ! $st ) {
        print("ERROR : could not stat $prestodev\n");
        $errors++;
    }
    else {
        my $dminor= 0xff & $st->rdev;
        my $dmajor= (0xff00 & $st->rdev)>>8;
        my $my_minor= $prestodev;
        $my_minor=~ s/^.*(.)$/$1/;

        if ($dmajor != 185) {
        print("ERROR : $prestodev has major $dmajor, " .
              "should be 185.\n");
        $errors++;
        }
        if ($dminor != $my_minor) {
        print("WARNING : $prestodev has minor $dminor, " .
              "is this correct?\n");
        $warnings++;
        }
    }
    }
}


# do I need to check for both lines????
sub validate_conf_modules {
    my $fd= new IO::File();
    my ($flag, $flag1, $flag2) = (0, 0, 0);
    if ( ! $fd->open("/etc/modules.conf") &&
     ! $fd->open("/etc/conf.modules")) {
    print("WARNING : you have no \`/etc/modules.conf' or \`/etc/conf.modules'");
    $warnings++;
    $flag++;
    }
    else {
    my @line;
    while ( <$fd> ) {
        if (/InterMezzo/) {
           @line= split;
           if ( !(($line[0]=~ /^alias$/) &&
              ($line[1]=~ /^InterMezzo$/) &&
              ($line[2]=~ /^presto$/)) ) {
           print("WARNING : the InterMezzo line in " .
             "/etc/conf.modules may not be correct.\n");
           $warnings++;
           $flag++;
           }
           $flag1++;
        }
        if (/char-major-185/) {
        @line= split;
        if ( !(($line[0]=~ /^alias$/) &&
               ($line[1]=~ /^char-major-185$/) &&
               ($line[2]=~ /^presto$/)) ) {
            print("WARNING : the char-major-185 line in " .
              "/etc/conf.modules may not be correct.\n");
            $warnings++;
        }
        $flag2++;
        }
    }
    $fd->close();
    }
    if ( !$flag1 || !$flag2 ) {
    if ( !$flag1 ) {
        print("WARNING : no InterMezzo line in /etc/conf.modules\n");
        $warnings++;
    }
    if ( !$flag2 ) {
        print("WARNING : no char-major-185 line in /etc/conf.modules\n");
        $warnings++;
    }
    $flag++;
    }
    if ( $flag ) {
    print(" -- The warnings above will prevent the " .
          "presto module from autoloading.\n");
    }

}


sub validate_proc_filesystems {
    my $fh= new IO::File;
    my $flag= 0;
    $fh->open("/proc/filesystems");
    while (<$fh>) {
    if (/InterMezzo/) { $flag++; }
    }
    $fh->close();
    if ( !$flag ) {
    print("WARNING : did not find \"InterMezzo\" in /proc/filesystems\n" .
          " -- this means that the presto kernel module " .
          "is not currently loaded.\n");
    $warnings++;
    }
}
