#!/usr/local/bin/perl
#boot-check
# only prints a list of the boot devices
# and checks if they are configured correctly
# if given an argument that contains a q -- the run in "quiet mode"
#    quiet mode" only prints output if there is an error -- useful as a cronjob

# read_format
#   builds @devices from output of format command
# read_vfstab
#   builds/updates %used with data from vfstab file	-- $used{cXtXdXsX} = "/"
# run_metadb
#   builds/updates %used with data from metadb command	-- $used{cXtXdXsX} = "metadb"
# run_metastat
#   builds/updates %used with data from metastat command - $used{cXtXdXsX} = "submirror..."
# find_devices
#   builds %mappings from contents of /dev/rdsk 	-- $mappings{cXtXdXsX} = "/sbus..."
# find_entry
#   builds %aliases based on output of prtconf command 	-- $aliases{pri-boot} = "/sbus...".\t."cXtXdXsX"
# find_boot_devices
#   compares entries in %aliases with %used 

$debug = 0;
$verbose = 0;
if ($ARGV[0] =~ m#q#i)
{
    $quietmode = 1; 
}

$user = (getpwuid($<))[0];
#print "USER: $user\n";

my (@devices,%mappings,%used);
if ($#ARGV != -1)
{
    $bootflag = 1;
}

#
## find the installed drives
#
$format = "/tmp/format.output";
read_format($format);

foreach $dev (@devices)
{
    $slice = $dev."s0";
    $used{$slice} = "";
}

read_vfstab();
run_metadb();
run_metastat();

#find relationship between /dev/rdsk/c0t0d0s0  and  ../../devices/pci@1f,4000/scsi@3/sd@0,0:a,raw
find_devices();

$prtconf = "/tmp/prtconf.output";
if ((-f $prtconf) && ($user ne "root"))
{
    if (! $quietmode) { print "using existing $prtconf\n"; }
}
else
{
    system("/usr/sbin/prtconf -vp > $prtconf");
}

foreach $dev (sort keys %mappings)
{
    $flag = 0;
    # aliases only apply to silce 0 (zero)
    if ($dev =~ m#s0#)
    {
        find_entry($mappings{$dev});
        if ($flag == 0)
        {
            if ($verbose)
            {
                printf "%18s\t%-65s\t%s\n"," ",$mappings{$dev},$dev;
            }
        }
    }
}

find_boot_devices();

if (! $quietmode)
{
    foreach $dev (sort keys %used)
    {
        print "$dev\t$mappings{$dev}\t$used{$dev}\n";
    }
}
 

################################################################################
sub find_devices
################################################################################
# builds %mappings from contents of /dev/rdsk
{
#    print "find_devices\n";

    $dir = "/dev/rdsk";
    
    #
    ## read a directory $dir ( ignoring . and .. )
    #
    opendir(DIR,"$dir") || die "opendir $dir failed";
    @files = grep(!/^\.\.?$/,readdir(DIR));
#    @files = grep(m#s0#,readdir(DIR));
    closedir(DIR);
    
    foreach $file (@files)
    {
        $file = $dir."/".$file;
        $link = readlink($file);
        if ($verbose)
        {
            print " $file maps to $link\n";
        }
        ($front,$string) = split(m#devices#,$link);
        if ($string =~ m#(.*?)(,[0-9]+):#)
        {
            $string2 = $1.$2;
#        ($string2,$j)    = split(m#(,[0-9]:)#,$string);
#        $string2 = $string2.$1;
        }
# for 3000's we need to leave "sd" as "sd"
# /dev/rdsk/c0t0d0s0 -> ../../devices/sbus@3,0/SUNW,fas@3,8800000/sd@0,0:a,raw
#
# for 3500's we need to change "sdisk" to "ssd"
# /dev/rdsk/c0t0d0s0 -> ../../devices/sbus@2,0/SUNW,socal@d,10000/sf@0,0/ssd@w21000020379cebf9,0:a,raw
#        if (($string2 =~ m#^/sbus#) || ($string2 =~ m#socal#))
#        {
#            $string2 =~ s#sdisk#ssd#;
#        }

# for 450's we need to change "sd" to "disk"
# /dev/rdsk/c0t0d0s0 -> ../../devices/pci@1f,4000/scsi@3/sd@0,0:a,raw
        if ($string2 =~ m#^/pci#)
        {
            $string2 =~ s#sd#disk#;
        }

# for Ultra 10's we need to change "dad" to "disk"
# /dev/rdsk/c0t0d0s0 -> ../../devices/pci@1f,0/pci@1,1/ide@3/dad@0,0:a,raw
        if ($string2 =~ m#^/pci#)
        {
            $string2 =~ s#dad#disk#;
        }

        if ($debug)
        {
            print " look for $string2 in prtconf output\n";
        }
        $dev = $file;
        $dev =~ s#/dev/rdsk/##;
#        $mappings{$file} = $string2;
        if (! defined($mappings{$dev}))
        {
            $mappings{$dev} = $string2;
        }
    }
}


################################################################################
sub find_entry
################################################################################
# builds %aliases based on output of prtconf command
{
    if ($verbose) { print "find_entry\n"; }
    ($string2) = @_;

    if ($string2 eq "")
    {
#        print "disk not found\n";
#        exit();
    }

    if ($string2 =~ m#fibre-channel#)
    {
#        print " non-local fibre-channel device $file\n";
    }
    else
    {
        open(IN,"$prtconf");
        {
            while ($line = <IN>)
            {
                chomp($line);
                if ($line =~ m#$string2#)
                {
                    if ($debug)
                    {
                        print "$line\n";
                    }
                    $line =~ s#^\s+##;
                    ($alias,$device) = split(m#:#,$line);
                    $device =~ s#^\s+##;
                    $device =~ s#'##g;
                    $short_dev = $dev;
                    $short_dev =~ s#/dev/rdsk/##;
                    if ($verbose)
                    {
                        printf "%18s\t%-65s\t%s\n",$alias,$device,$short_dev;
                    }
                    $flag = 1;
                    $aliases{$alias} = $device."\t".$short_dev;
                }
                else
                {
#if ($line =~ m#socal#)
#{
#    $line =~ s#^\s+##;
#    ($alias,$device) = split(m#:#,$line);
#    $device =~ s#^\s+##;
#    $device =~ s#'##g;
##    printf "%15s\t%s\t%s\n",$alias,$device,$dev;
#printf "%15s\t%s\n%15s\t%s\n",$alias, $device, "=>", $string2;
#}
                }
            }
        }
        close(IN);
    }
}


################################################################################
sub find_boot_devices
################################################################################
# compares entries in %aliases with %used 
{
#    print "find_boot_devices\n";
    $eeprom = "/tmp/eeprom.output";
    if ((-f $eeprom) && ($user ne "root"))
    {
        if (! $quietmode) { print "using existing $eeprom\n"; }
    }
    else
    {
        system("/usr/sbin/eeprom > $eeprom");
    }

    open(IN,"$eeprom");
    while ($line = <IN>)
    {
        chomp($line);
        if ($line =~ m#boot-device#)
        {
            if (! $quietmode) { print "$line\n"; }
            if (($line =~ m#pri#) && ($line =~ m#mir#) && ($line =~ m#bck#))
            {
            }
            else
            {
                print "ERROR: boot-device does not include 'pri-bootdisk' 'mir-bootdisk' and 'bck-bootdisk'\n";
            }
            ($name,$vals) = split(m#=#,$line);
            @names = split(m#\s+#,$vals);
            foreach $alias (@names)
            {
                if (defined($aliases{$alias}))
                {
                    $string = "$alias\t$aliases{$alias}\t";

                    ($machine,$human) = split(m#\t#,$aliases{$alias});
                    $human =~ s#/dev/rdsk/##;
                    $err = "";
                    if (defined($used{$human}))
                    {
                        if (($alias =~ m#pri#) && (!($used{$human} =~ m#\(/\)#)))
                        {
                            $err = "\tERROR: not mounted as root";
                        }
                        elsif (($alias =~ m#mir#) && (!($used{$human} =~ m#\(/\)#)))
                        {
                            $err = "\tERROR: not mounted as root";
                        }
                        elsif (($alias =~ m#bck#) && (!($used{$human} =~ m#bck#i)))
                        {
                            $err = "\tERROR: not mounted as bck_root";
                        }

                        $string = $string."$used{$human}$err\n";
                    }
                    else
                    {
                        $string = $string."ERROR: device not mounted\n";
                    }

                    if ($quietmode)
                    {
                        if ($string =~ m#ERROR#)
                        {
                            print "$string";
                        }
                    }
                    else
                    {
                        print "$string";
                    }
                }
                else
                {
                    print "$alias\tERROR: alias '$alias' not defined\n";
                }
            }
        }
    }
    close(IN);
}




################################################################################
sub run_get_dev_info2
################################################################################
{
#    print "run_get_dev_info2\n";
    my (%cf,%data);

    read_vfstab();
    run_metadb();
    run_metastat();
}


################################################################################
sub run_format
################################################################################
{
    ($formatfile) = @_;

    if (! defined($ENV{SUDO_USER}))
    {
        print "USAGE: sudo $0\n";
        exit(1);
    }
    else
    {
        $formatfile = "/tmp/format.output";
        $input = "/tmp/format.input";
        open(OUT,">$input");
        print OUT "0\nq\n";
        close(OUT);
        @res = `/usr/sbin/format < $input > $formatfile`;
    }
}


################################################################################
sub read_format
################################################################################
# builds @devices from output of format command
{
     my ($formatfile) = @_;
#
## get disk information from format
#
    if ($debug)
    {
        print "data from format\n";
    }
    if (( ! -f $formatfile) || ($user eq "root"))
    {
        run_format($formatfile);
    }
    
    open(IN,"$formatfile");
    while ($line = <IN>)
    {
        chomp($line);
        if ($line =~ m#^\s+(\d+)\.\s+(.*?)\s+<(.*?)>#)
        {
            if ($debug)
            {
                print "$1 - $2\n";
            }
            push(@devices,$2);
        }
    }
    close(IN);
    if ($debug)
    {
        print "\n";
    }
}


################################################################################
sub run_metadb
################################################################################
# builds/updates %used with data from metadb command
{
#    print "run_metadb\n";
#
## get meta-device information from metadb
#
    if (-f "/usr/opt/SUNWmd/sbin/metadb")
    {
        $execdir = "/usr/opt/SUNWmd/sbin";
    }
    elsif (-f "/usr/sbin/metadb")
    {
        $execdir = "/usr/sbin";
    }
    if ($debug)
    {
        print "data from metadb\n";
    }

    $cmd = $execdir."/metadb";
    open(METADB,"$cmd |");
    while ($line = <METADB>)
    {
        chomp($line);
        ($flags,$first,$count,$unk,$unk2,$file) = split(m#\t#,$line);
        if ($file =~ m#/#)
        {
             @parts = split(m#/#,$file);
             $devname = pop(@parts);
             $used{$devname} = "metadb";
        }
    }
    close(METADB);
}


################################################################################
sub run_metastat
################################################################################
# builds/updates %used with data from metastat command
{
#    print "run_metastat\n";
#
## get meta-device information from metastat
#
    if (-f "/usr/opt/SUNWmd/sbin/metastat")
    {
        $execdir = "/usr/opt/SUNWmd/sbin";
    }
    if (-f "/usr/sbin/metastat")
    {
        $execdir = "/usr/sbin";
    }
#    /usr/opt/SUNWmd/sbin/metastat
#    /usr/sbin/metastat
    if ($debug)
    {
        print "data from metastat\n";
    }
    
    $metafile = "/tmp/metastat.output";
    if ((-f $metafile) && ($user ne "root"))
    {
        if (! $quietmode) { print "using existing $metafile\n"; }
    }
    else
    {
        @res = system($execdir."/metastat > $metafile");
    }
    
#    ### TESTING ################################
#    $metafile = "/tmp/metastat.output.website1";
    
    
    $size = (-s $metafile);
    open(IN,"$metafile");
    read(IN,$buf,$size);
    close(IN);
    
    # join the multi-line outup for each meta device
    $buf =~ s#\s+Invoke:#\tInvoke:#g;
    $buf =~ s#\s+State:#\tState:#g;
    $buf =~ s#\s+Size:#\tSize:#g;
    $buf =~ s#\s+Submirror#\tSubmirror#g;
    $buf =~ s#\s+Stripe#\tStripe#g;
    $buf =~ s#\s+Device\s+Start\s+Block\s+Dbase\s+State\s+Hot\s+Spare\s+# #g;
    while ($buf =~ m#\n\t(c\d+t\d+d\d+s\d)#)
    {
        $buf =~ s#\n\t(c\d+t\d+d\d+s\d)# $1#;
    }
    
    @data = split(m#[\n\r]+#,$buf);
    
#    @devs = grep(m#c\d+t\d+d\d+s\d#,$buf);
    
    foreach $line (@data)
    {
        chomp($line);
#                print "$line\n";
    
        $line =~ s#^\s+##;
        @words = split(m#\s+#,$line);
#    print "$#words\t$line\n";
        if (($line =~ m#^d#) && ($words[1] =~ m#Mirror#))
        {
#            print "mirror - $line\n";
            $volume =  $words[0];
            $voltype = $words[1];
            @parts = split(m#\t#,$line);
            foreach $pair (@parts)
            {
                ($name,$val) = split(m#:#,$pair,2);
                if ($name =~ m#Submirror#)
                {
#                    print " Submirror: $val\n"; 
                }
                elsif ($name =~ m#State#)
                {
#                    print " State: $val\n"; 
                }
                elsif ($name =~ m#Size#)
                {
#                    print " Size: $val\n"; 
                }
                elsif ($name =~ m#Stripe#)
                {
#                    print " $name: $val\n"; 
                }
            }
        }
        if (($line =~ m#^d#) && ($words[1] =~ m#Submirror#))
        {
#            print "submirror - $line\n";
            $volume =  $words[0];
            $volume =~ s#:$##;
            $voltype = $words[1];
            $parent =  $words[3];
            @parts = split(m#\t#,$line);
            foreach $pair (@parts)
            {
                if ($pair =~ m#(.*?):(.*?)$#)
                {
                    $name = $1;
                    $val  = $2;
#                    ($name,$val) = split(m#:#,$pair,2);
                    if ($name =~ m#Submirror#)
                    {
#                        print " Submirror: $val\n";
                    }
                    elsif ($name =~ m#State#)
                    {
#                        print " State: $val\n";
                    }
                    elsif ($name =~ m#Size#)
                    {
#                        print " Size: $val\n";
                    }
                    elsif ($name =~ m#Stripe#)
                    {
#                        print " $pair] [$name:] [$val\n"; 
                        if ($val =~ m#\)#)
                        {
                            ($front,$back) = split(m#\)#,$val);
#                            print "[$front] [$back]\n";
                            $val = $back;
                        }
                        $val =~ s#\s+c#XYzzYc#g;
                        @slices = split(m#XYzzY#,$val);
                        foreach $slice (@slices)
                        {
                            ($dev,$start,$dbase,$state,$spare) = split(m#\s+#,$slice);
                            if ($dev ne "")
                            {
                                if (($debug) or ($state =~ m#Maintenance#))
                                {
                                    print " Dev: $dev\t$state\t$spare\tsubmirror $volume of $parent\n";
                                }
                                $string = "submirror $volume of $parent ($used{$parent})\t$state\t$spare";
                                $used{$dev} = $string;
                            }
                        }
    
                    }
                }
                if ($pair =~ m#^c#)
                {
                    ($dev,$start,$dbase,$state,$spare) = split(m#\s+#,$pair);
                    print " dev: $dev] [$start][$dbase]\n";
                }
            }
        }
    }
    if ($debug)
    {
        print "\n";
    }
}

#exit();


################################################################################
sub read_vfstab
################################################################################
# builds/updates %used with data from vfstab file
{
#    print "read_vfstab\n";
#
## get mount point info from vfstab
#
    if ($debug)
    {
        print "data from vfstab\n";
    }
    $vfstab = "/etc/vfstab";
    open(IN,"$vfstab");
    read(IN,$buf,$size);
    
    while ($line = <IN>)
    {
        chomp($line);
        if ($line =~ m/^#/)
        {
            #ignore comments
        }
        else
        {
#            if ($line =~ m#/dev/dsk#)
            {
                ($vdevice,$vrawdevice,$vmountpoint,$vfsck,$vpass,$vboot,$vopt) = split(m#\s+#,$line);
                @dparts = split(m#/#,$vdevice);
                $dev = pop(@dparts);
                if ($vfsck =~ m#swap#)
                {
                    $vmountpoint = $vfsck;
                }
                if (($vfsck ne "proc") && ($vfsck ne "nfs"))
                {
                    $used{$dev} = $vmountpoint;
                    if ($debug)
                    {
                        print "$dev -> $vmountpoint\n";
                    }
                }
            }
        }
    }
    close(IN);
    if ($debug)
    {
        print "\n";
    }
}


################################################################################
sub parse_cf
################################################################################
# not called?
{
    print "parse_cf\n";
    my ($devname);
    open(CF,"/etc/lvm/md.cf");
    while ($line = <CF>)
    {
        if ($line =~ m#\s+-m\s+#)
        {
            chomp($line);
            ($mir,$j,$rest) = split(m#\s+#,$line,3);
#            print "[$mir][$j][$rest]\n";

        }
        elsif ($line =~ m#(.*?)\s+\d+\s+\d+\s+(.*?)$#)
        {
            $submir = $1;
            $devname = $2;
#            print "[$submir] [$devname]\n";
            if (($dir eq "") && (defined($used{$devname})))
            {
                $dir = $used{$devname};
            }

            $string = sprintf "%12s\t\tmounted as\tsubmirror $submir of $mir ($used{$mir})\t[Maintenance?]",$foo;
            $cf{$devname} = $string;
#            print "$devname => $string\n";
        }
    }
    close(CF);
}


################################################################################
sub add_commas
################################################################################
{
    local($num) = @_;
    $wholenumber = (split(m#\.#,$num))[0];

    while ($wholenumber =~ s#(.*\d)(\d\d\d)#$1,$2#)
    {
#        print "$wholenumber\n";
    }
    return($wholenumber);
}


