#! /usr/local/bin/perl -- # This program is designed to discover under and overallocated disk # space. To discover what disks are available, it uses the program # format, and the files [v]fstab and m[n]tab. It gathers mount point # information from [v]fstab and m[n]tab, and determines disk geometries # using the program dkinfo/prtvtoc. It also finds out if it's the cache # for an optical filesystem, or part of a metadevice. # Some assumptions made are: # - an unmentioned "b" partition on the same disk as the root # partition is used for swap space. # - the "c" partition should be the entire disk #MB = #SECTORS / 2048 #SECTORS = #cyls * #SEC_PER_CYL #SEC_PER_CYL = #sec_per_track * #heads_per_cyl # Warning: This currently does not print warning if partition is more # than one of: # mounted locally # part of a metadevice # an optical disk cache # it should. (daw, 5/11/95) sub parseLine { # parameters: line, cyl, sec, hd local($xxx) = @_; local($i); @NAME = split(' ',$xxx); for ($i=0;$i<=$#NAME;$i++) { # print " ",$i," ",$NAME[$i],"\n"; pname: { if ($NAME[$i] eq "cyl") { $_[1] = $NAME[$i+1]; # print "cyl ",$_[1],"\n";; last pname; } if ($NAME[$i] eq "sec") { $_[2] = $NAME[$i+1]; # print "sec ",$_[2],"\n";; last pname; } if ($NAME[$i] eq "hd") { $_[3] = $NAME[$i+1]; # print "hd ",$_[3],"\n";; last pname; } } } } sub tryFormat { open(FORMAT, "$FORMAT < /dev/null 2>&1 |") || die "can't run '$FORMAT': $!\n"; while () { @LINE = split; # $len = length($LINE[1]); # if (substr($LINE[1],$len-1) eq ":") { if (index($_,"<") > 0) { if ($OSTYPE eq "SunOS") { $len = length($LINE[1]); $devices[$ndev++] = substr($LINE[1],0,$len-1); } elsif ($OSTYPE eq "Solaris") { # print; # if ($LINE[0] eq "") { # perl4 # $devices[$ndev++] = $LINE[2]; #print "debug1a: .$LINE[0]. $devices[$ndev-1]\n"; # } # else { # perl5 (difference in split?) # $devices[$ndev++] = $LINE[1]; #print "debug1b: .$LINE[0]. $devices[$ndev-1]\n"; # } if ($LINE[0] eq "") { shift(LINE); # perl5 does not give empty first arg } $devices[$ndev++] = $LINE[1]; #print "debug1b: .$LINE[0]. $devices[$ndev-1]\n"; } $il = index($_,"<"); $ir = index($_,">"); # print $il," ",$ir,"\n"; # print substr($_,$il+1,$ir-$il-1),"\n"; $names[$ndev-1] = substr($_,index($_,"<")); $names[$ndev-1] = substr($_,$il+1,$ir-$il-1); # print "$names[$ndev-1]\n"; # print $LINE[$#LINE]; # print $names[$ndev-1]; $cyl[$ndev-1] = ""; $sec[$ndev-1] = ""; $hd[$ndev-1] = ""; do parseLine($names[$ndev-1],$cyl[$ndev-1],$sec[$ndev-1],$hd[$ndev-1]); # print "cyl = $cyl[$ndev-1];sec = $sec[$ndev-1];hd = $hd[$ndev-1]\n"; } } close(FORMAT); } sub tryRfutil { open(RFUTIL, "/usr/bin/echo s q | /sbin/rfutil $_[0] 2>&1 |") || die "can't run '/sbin/rfutil': $!\n"; while () { if (substr($_,0,1) eq "<") { # print($_); $line = substr($_,1,length($_)-3); } } close(RFUTIL); print $line,"\n"; $cyl[$cur] = ""; $sec[$cur] = ""; $hd[$cur] = ""; do parseLine($line,$cyl[$cur],$sec[$cur],$hd[$cur]); #print $cur," ",$cyl[$cur]," ",$sec[$cur]," ",$hd[$cur],"\n"; } sub checkFstab { open(FSTAB,$FSTAB); while () { @LINE = split; if ((index($LINE[0],"/dev/") == 0) && (($LINE[2] eq "4.2") || $LINE[2] eq "swap")) { # SunOS $len = length($LINE[0]); $dev = substr($LINE[0],5,$len-6); $idx = $dev.substr($LINE[0],$len-1); $used{$idx} = 1; if ($LINE[2] eq "4.2") { $fsmpt{$idx} = $LINE[1]; } else { $fsmpt{$idx} = "swap"; } if ($LINE[1] eq "/") { $dswap = $dev.$B; } for ($i=0;$i<$ndev;) { if ($devices[$i++] eq $dev) { last } if ($i == $ndev) { $devices[$ndev++] = $dev; last; } } } if ((index($LINE[0],"/dev/") == 0) && (index($LINE[0],"/dsk/") == 4) && (($LINE[3] eq "ufs") || $LINE[3] eq "swap")) { # Solaris $len = length($LINE[0]); $dev = substr($LINE[0],9,$len-11); # $idx = $dev.substr($LINE[0],$len-1); $idx = substr($LINE[0],9); $used{$idx} = 1; if ($LINE[3] eq "ufs") { $fsmpt{$idx} = $LINE[2]; } else { $fsmpt{$idx} = "swap"; } if ($LINE[2] eq "/") { $dswap = $dev."s1"; } for ($i=0;$i<$ndev;) { if ($devices[$i++] eq $dev) { last } if ($i == $ndev) { $devices[$ndev++] = $dev; print "debug2: $devices[$ndev-1]\n"; last; } } } if ((index($LINE[0],"/dev/") == 0) && (index($LINE[0],"/md/") == 4) && (($LINE[3] eq "ufs") || $LINE[3] eq "swap")) { # Solaris # print; $idx = substr($LINE[0],12); # print "idx = $idx\n"; $used{$idx} = 1; $fsmpt{$idx} = $LINE[2]; } } close(FSTAB); } sub lookInMtab { open(MTAB,$MTAB); while () { @LINE = split; if ((index($LINE[0],":") == -1) && $LINE[2] eq "4.2") { # SunOS $len = length($LINE[0]); $dev = substr($LINE[0],5,$len-6); $idx = $dev.substr($LINE[0],$len-1); $used{$idx} = 1; $mtmpt{$idx} = $LINE[1]; for ($i=0;$i<$ndev;) { if ($devices[$i++] eq $dev) { last } if ($i == $ndev) { $devices[$ndev++] = $dev; last; } } } if ($LINE[2] eq "mcfs") { # SunOS Optical $mtmpt{$LINE[0]} = $LINE[1]; # print "mtmpt{$LINE[0]} = $mtmpt{$LINE[0]}\n"; } if ((index($LINE[0],":") == -1) && (index($LINE[0],"/dsk/") == 4) && $LINE[2] eq "ufs") { # Solaris $len = length($LINE[0]); $dev = substr($LINE[0],9,$len-11); # $idx = $dev.substr($LINE[0],$len-1); $idx = substr($LINE[0],9); $used{$idx} = 1; $mtmpt{$idx} = $LINE[1]; for ($i=0;$i<$ndev;) { if ($devices[$i++] eq $dev) { last } if ($i == $ndev) { $devices[$ndev++] = $dev; print "debug3: $devices[$ndev-1]\n"; last; } } } if ((index($LINE[0],"/dev/") == 0) && (index($LINE[0],"/md/") == 4) && (($LINE[2] eq "ufs") || $LINE[3] eq "swap")) { # Solaris # print; $idx = substr($LINE[0],12); # print "idx = $idx\n"; $used{$idx} = 1; $mtmpt{$idx} = $LINE[1]; # print "mtmpt{$idx} = $mtmpt{$idx}\n" } } close(MTAB); } sub markSwap { if ($cyls{$dswap} && ! $fsmpt{$dswap} && ! $mtmpt{$dswap} ) { $used{$dswap} = 1; } } sub getPartitioning { if ($OSTYPE eq "SunOS") { for ($cur=0;$cur<$ndev;$cur++) { # print "$devices[$cur]\n"; open(DKINFO, "/etc/dkinfo $devices[$cur] 2>&1 |") || die "can't run '/etc/dkinfo': $!\n"; while () { @LINE = split; if ((substr($LINE[0],1) eq ":") && ($LINE[1] ne "No")) { $curp = substr($LINE[0],0,1); $idx = $devices[$cur].$curp; $cyls{$idx} = substr($LINE[3],1); $unacc{$idx} = $cyls{$idx}; if ($LINE[4] ne "cyls)") { # can't handle disks with partial cylinder allocations die "dkinfo output for $devices[$cur]: $_\n"; } } if ($LINE[1] eq "cylinders") { $size{$devices[$cur]} = $LINE[0]; } if (($LINE[1] eq "starting") && ($curp ne "")) { $start{$idx} = $LINE[3]; $end{$idx} = $start{$idx} + $cyls{$idx}; } } close(DKINFO); } } elsif ($OSTYPE eq "Solaris") { for ($cur=0;$cur<$ndev;$cur++) { # print "$devices[$cur]\n"; open(PRTVTOC, "/etc/prtvtoc /dev/rdsk/$devices[$cur]s2 2>&1 |") || die "can't run '/etc/prtvtoc': $!\n"; while () { # print; @LINE = split; if ($LINE[0] eq "") { shift(LINE); # perl5 does not give empty first arg } if ($LINE[2] eq "accessible") { # print; $size{$devices[$cur]} = $LINE[1]; # print "size = $size{$devices[$cur]}\n"; } if ($LINE[2] eq "sectors/cylinder") { # print; $spc = $LINE[1]; # print "spc = $spc\n"; } if ($LINE[1] eq "Partition") { # print } if ($LINE[0] eq "prtvtoc:") { print STDERR # print } if (($LINE[0] ne "*") && ($LINE[0] ne "prtvtoc:")) { # print; $curp = "s".$LINE[0]; # print "curp = $curp\n"; $idx = $devices[$cur].$curp; # print "idx = $idx\n"; $cyls{$idx} = $LINE[4] / $spc; # print "cyls = $cyls{$idx}\n"; $unacc{$idx} = $cyls{$idx}; if ($LINE[4] != ($cyls{$idx} * $spc)) { #print "$LINE[4] != ($cyls{$idx} * $spc)\n"; die "$idx not whole number of cylinders\n"; } $start{$idx} = $LINE[3] / $spc; $end{$idx} = $start{$idx} + $cyls{$idx}; # print "start = $start{$idx}; end = $end{$idx}\n"; } } close(PRTVTOC); } } } sub checkOs { open(UNAME, "/bin/uname -s -r 2>&1 |") || die "can't run '/bin/uname': $!\n"; $OS = ; chop($OS); if (index($OS,"SunOS 4") == 0) { $OSTYPE = "SunOS" } elsif(index($OS,"SunOS 5") == 0) { $OSTYPE = "Solaris"} else { $OSTYPE = "unknown" } close(UNAME); } sub checkOptical { open(VLLSSET, "/usr/local/etc/vllsset 2>&1 |") || die "can't run '/usr/local/etc/vllsset': $!\n"; while () { @LINE = split; if ($#LINE > 0) { # print "$LINE[1]\n"; $oset[$nset++]=$LINE[1]; } } close(VLLSSET); for ($set=0;$set<$nset;$set++) { # print "$oset[$set]\n"; open(VLSETEDIT, "/usr/local/etc/vlsetedit -l $oset[$set] 2>&1 |") || die "can't run '/usr/local/etc/vlsetedit': $!\n"; while () { @LINE = split; # print; if ($LINE[0] eq "cfsmount") { # print "$LINE[3]\n"; $idx = substr($LINE[3],5); # print "$idx\n"; $used{$idx} = 1; $cmpt{$idx} = $oset[$set]; # print "cmpt{$idx} = $cmpt{$idx}\n"; } } close(VLSETEDIT); } } sub checkMeta { open(METASTAT, "/usr/opt/SUNWmd/sbin/metastat -p 2>&1 |") || die "can't run '/usr/opt/SUNWmd/sbin/metastat': $!\n"; while () { # print; @LINE = split; if ($remainingstripes > 0) { # print " line is continuation of a two stripe metadevice\n"; for ($i=0;$i<$LINE[0];$i++) { # print " $LINE[$i+1]\n"; $idx = $LINE[$i+1]; # print " idx = $idx\n"; $md{$idx} = $omd; # print " metadevice for $idx is $md{$idx}\n"; } if ($LINE[$#LINE-1] eq "-h") { if ($hs{$LINE[$#LINE]}) { $hs{$LINE[$#LINE]} .= ", " . $omd; } else { $hs{$LINE[$#LINE]} = $omd; } # print " $LINE[$#LINE] is hot spare for $hs{$LINE[$#LINE]}\n"; } $remainingstripes = 0; } elsif ($LINE[1] eq "1") { # print " line is a one stripe metadevice\n"; for ($i=0;$i<$LINE[2];$i++) { # print " $LINE[$i+3]\n"; # $idx = substr($LINE[$i+3],9); $idx = $LINE[$i+3]; # print " idx = $idx\n"; $md{$idx} = $LINE[0]; # print " metadevice for $idx is $md{$idx}\n"; } if ($LINE[$#LINE-1] eq "-h") { if ($hs{$LINE[$#LINE]}) { $hs{$LINE[$#LINE]} .= ", " . $LINE[0]; } else { $hs{$LINE[$#LINE]} = $LINE[0]; } # print " $LINE[$#LINE] is hot spare for $hs{$LINE[$#LINE]}\n"; } } elsif ($LINE[1] eq "2") { # print " line is a two stripe metadevice\n"; for ($i=0;$i<$LINE[2];$i++) { # print " $LINE[$i+3]\n"; # $idx = substr($LINE[$i+3],9); # I just uncommented the two uncommented lines below $idx = $LINE[$i+3]; # print " idx = $idx\n"; $md{$idx} = $LINE[0]; # print " metadevice for $idx is $md{$idx}\n"; } $remainingstripes = 1; $omd = $LINE[0]; } elsif ($LINE[1] eq "-r") { # print " line is a RAID metadevice\n"; $seensw = 0; for ($i=2;$i<=$#LINE;$i++) { # print " $LINE[$i]\n"; if (substr($LINE[$i],0,1) eq "-") { $seensw = 1 } if ($seensw == 0) { $idx = $LINE[$i]; # print " idx = $idx\n"; $md{$idx} = $LINE[0]; # print " metadevice for $idx is $md{$idx}\n"; } } } elsif (substr($LINE[0],0,3) eq "hsp") { # print " line is a hot spare metadevice\n"; for ($i=1;$i<=$#LINE;$i++) { # print " $LINE[$i]\n"; $idx = substr($LINE[$i],9); $idx = $LINE[$i]; # print " idx = $idx\n"; $md{$idx} = $LINE[0]; # print " metadevice for $idx is $md{$idx}\n"; } } elsif ($LINE[1] eq "-m") { # print " line is a metamirror\n"; # if ($#LINE != 3) { # die " $0: do not understand metamirror '$_' from metastat\n"; # } # $mm{$LINE[2]} = $LINE[0]; for ($i=2;$i<=$#LINE;$i++) { $mm{$LINE[$i]} = $LINE[0]; } # print " metamirror for $LINE[2] is $mm{$LINE[2]}\n"; } elsif ($LINE[1] eq "-t") { # print " line is a metatrans\n"; # if ($#LINE != 2) { if ($#LINE > 3) { # number 3 could be logging device die " $0: do not understand metatrans '$_' from metastat\n"; } $mt{$LINE[2]} = $LINE[0]; # print " metatrans for $LINE[2] is $mt{$LINE[2]}\n"; if ($mtl{$LINE[3]}) { $mtl{$LINE[3]} = $mtl{$LINE[3]}.", ".$LINE[0]; } else { $mtl{$LINE[3]} = $LINE[0]; } # print " metatrans log for $LINE[3] is $mtl{$LINE[3]}\n"; } else { chop; die " $0: do not understand '$_' from metastat\n"; } } close(METASTAT); } # Here we go: &checkOs; if ($OSTYPE eq "unknown") {die "$0: can't run on $OS\n";} if ($OSTYPE eq "SunOS") { $FORMAT = "/usr/etc/format"; $FSTAB = "/etc/fstab"; $MTAB = "/etc/mtab"; $A = "a"; $B = "b"; $C = "c"; $D = "d"; $E = "e"; $F = "f"; $G = "g"; $H = "h"; } if ($OSTYPE eq "Solaris") { # warn "$0: under construction for $OS\n"; $FORMAT = "/etc/format"; $FSTAB = "/etc/vfstab"; $MTAB = "/etc/mnttab"; $A = "s0"; $B = "s1"; $C = "s2"; $D = "s3"; $E = "s4"; $F = "s5"; $G = "s6"; $H = "s7"; } # See what structures are around... # First, try format: &tryFormat; # Now check fstab. (format doesn't know about rf's and may not see # everthing else if format.dat is not up-to-date) &checkFstab; # look in mtab to find differences from fstab's impression and unknown # disks not in fstab yet. &lookInMtab; # unused b partition on disk with root is default swap (and therefore # used) &markSwap; # if optical disk software exists, check for cache partitions if (-e "/usr/local/etc/vllsset") { &checkOptical; } # if metadisk software exists, check for metadisk partitions if (-e "/usr/opt/SUNWmd/sbin/metastat") { &checkMeta; } # use dkinfo (or prtvtoc) to get disk partitioning and size &getPartitioning; # Here's the main analysis/output loop: $cur=0; while ($cur<$ndev) { $dev = $devices[$cur]; if ($devprinted++) { print "\n"; } print "$dev:"; if ($names[$cur]) { print " $names[$cur]\n"; } else { # if we don't have label and rf, ask rfutil if (substr($dev,0,2) eq "rf") { print " "; $tmp = "/dev/r".$dev.$C; $names[$cur] = ""; $cyl[$cur] = ""; $sec[$cur] = ""; $hd[$cur] = ""; do tryRfutil($tmp); } else { print " [Format doesn't see this disk]\n"; } } # verify c partition is whole disk if (($start{$dev.$C} != 0) || ($cyls{$dev.$C} != $size{$dev})) { print " *** c partition is not whole device ***\n"; print " dev = $dev; start = $start{$dev.$C}; cyls = $cyls{$dev.$C}; size = $size{$dev}\n"; } print "\n"; # disk type printed, now display all allocated space $bot = 0; $top = $size{$dev}; # note whether c partition is actually in use if ($fsmpt{$dev.$C} || $mtmpt{$dev.$C} || $md{$dev.$C}) { # print " debug: $dev$C is used\n"; $cpartused = 1 } else { $cpartused = 0; # print " debug: $dev$C is not used\n"; } # add up all allocated, unaccounted for cylinders $sum = 0; foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) { $sum += $unacc{$dev.$p} } # and account for all space on disk, and all allocated cylinders while(($bot < $top) || ($sum != 0)) { $idx = ""; # find a non-null partition beginning at bottom of remaining space foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) { if ($unacc{$dev.$p} == 0) { next } if ($start{$dev.$p} <= $bot) { $idx = $dev.$p; $curp = $p; last; } } if ($idx eq "") { # didn't find one. display an unallocated "partition" $nbot = $top; foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) { if ($unacc{$dev.$p} == 0) { next } if ($start{$dev.$p} < $nbot) { $nbot = $start{$dev.$p}; } } print " $dev?"; printf " %4d %4d [unallocated]\n",$bot,$nbot-$bot; $bot = $nbot; # I suspect this is not necessary: # $sum = 0; # foreach $p ("a","b","c","d","e","f","g","h") { # $sum += $unacc{$dev.$p} # } next; } # display next partition, starting cylinder and size print " $idx"; printf " %4d %4d",$start{$idx},$cyls{$idx}; # how big it is if ($cyl[$cur]) { $sec_per_cyl = $sec[$cur] * $hd[$cur]; $sec = $cyls{$idx} * $sec_per_cyl; $nmb = $sec / 2048; #print " ",$sec[$cur]," * ",$hd[$cur]," * ",$cyls{$idx}," / 2048 "; printf " %4dMB",$nmb; } # where it's mounted if ($fsmpt{$idx} eq $mtmpt{$idx}) { print " $fsmpt{$idx}"; if ($fsmpt{$idx} eq "") { if ($md{$idx} ne "") { if ($mt{$md{$idx}} ne "") { print "(part of mtrans $mt{$md{$idx}}) "; # $fsmpt{$mt{$md{$idx}}} = "X"; if ($fsmpt{$mt{$md{$idx}}} eq $mtmpt{$mt{$md{$idx}}}) { print "$fsmpt{$mt{$md{$idx}}}" } else { print " ("; if ($fsmpt{$mt{$md{$idx}}}) { print "should be mounted on $fsmpt{$mt{$md{$idx}}}"; } if ($fsmpt{$mt{$md{$idx}}} && $mtmpt{$mt{$md{$idx}}}) { print ", "; } if ($mtmpt{$mt{$md{$idx}}}) { print "mounted on $mtmpt{$mt{$md{$idx}}}"; } print ")"; } } elsif ($mm{$md{$idx}} ne "") { print "(sub of mmirror $mm{$md{$idx}}) "; if ($mtl{$mm{$md{$idx}}}) { print "(logging $mtl{$mm{$md{$idx}}})"; } else { print "(part of mtrans $mt{$mm{$md{$idx}}}) "; if ($fsmpt{$mt{$mm{$md{$idx}}}} eq $mtmpt{$mt{$mm{$md{$idx}}}}) { print "$fsmpt{$mt{$mm{$md{$idx}}}}" } else { print " ("; if ($fsmpt{$mt{$mm{$md{$idx}}}}) { print "should be mounted on $fsmpt{$mt{$mm{$md{$idx}}}}"; } if ($fsmpt{$mt{$mm{$md{$idx}}}} && $mtmpt{$mt{$mm{$md{$idx}}}}) { print ", "; } if ($mtmpt{$mt{$mm{$md{$idx}}}}) { print "mounted on $mtmpt{$mt{$mm{$md{$idx}}}}"; } print ")"; } } } else { if ($hs{$md{$idx}}) { print "(hspare for $hs{$md{$idx}})"; } else { print "(part of mdevice $md{$idx}) [unused]"; } } } elsif ($cmpt{$idx}) { print "(cache for $cmpt{$idx}"; if ($mtmpt{$cmpt{$idx}}) { print " which is mounted on $mtmpt{$cmpt{$idx}}"; } print ")"; } elsif ($idx eq $dswap) { print "swap"; } elsif ($curp ne $C) { print "[unused]"; } } } elsif ($fsmpt{$idx} eq "swap") { print " swap"; } else { # show discrepancy between fstab and mtab print " ("; if ($fsmpt{$idx}) { print "should be mounted on $fsmpt{$idx}"; } if ($fsmpt{$idx} && $mtmpt{$idx}) { print ", "; } if ($mtmpt{$idx}) { print "mounted on $mtmpt{$idx}"; } print ")"; } # now determine any overlaps between partitions $overlap = ""; $danger = ""; foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) { # null partitions don't overlap if ($cyls{$dev.$p} == 0) { next } # don't check against self if ($idx eq $dev.$p) { next } # or unused c partition if ($idx eq $dev.$C && ! $cpartused) { next } $tidx = $dev.$p; if ( ($start{$idx} == $start{$tidx}) || (($start{$idx} < $start{$tidx}) && ($end{$idx} > $start{$tidx})) || (($start{$idx} > $start{$tidx}) && ($start{$idx} < $end{$tidx})) ) { if ($p ne $C || $cpartused) { if ($overlap) { $overlap .= ","; } $overlap .= " ".$p; # if both overlapping partitions are in use, draw attention to it!!! if ($used{$idx} && $used{$tidx}) { $danger = " [DANGER!]"; } } } } if ($overlap) { print " overlaps$overlap $danger"; } print "\n"; # adjust bottom of accounted for disk space if not unused c partition if ($idx ne $dev.$C || $cpartused) { $bot = $start{$idx} + $unacc{$idx}; } # we've now accounted for this structure $unacc{$idx} = 0; # sum up unaccounted for partitions $sum = 0; foreach $p ($A,$B,$C,$D,$E,$F,$G,$H) { $sum += $unacc{$dev.$p} } # loop back until done } # now on to the next disk... $cur++; }