#!/usr/bin/perl use warnings; use strict; use Term::ANSIColor; use Getopt::Long; use Time::localtime; my %temp = ('warn' => 38, 'bad' => 42); my %color = ('good' => 'green', 'warn' => 'yellow', 'bad' => 'red'); my $devdir='/dev'; my $smartctl='/usr/sbin/smartctl'; my @udisks = (); my @disks = (); my %rotobois=(); my $CLEAR_LINE=`tput cub 40;tput el`; # https://stackoverflow.com/a/5047362 sub clean {my $text = shift; $text=~s/\n//g; $text=~s/\r//g;return $text;} sub ptime {my $min = localtime->min < 10 ? "0".localtime->min : localtime->min; my $hr = localtime->hour < 10 ? "0".localtime->hour : localtime->hour; return "$hr:$min";} # https://www.perl.com/article/37/2013/8/18/Catch-and-Handle-Signals-in-Perl/ $SIG{INT} = sub {printf("%s",$CLEAR_LINE);printf("\n%s\n\n",clean(colored("Caught ctrl+c - exiting",'red')));exit 0;}; opendir my($DH), $devdir or die $!; my @devicelist=readdir $DH; closedir $DH; for(@devicelist) { my $device =$_; # Directory listing includes . and .. but these aren't disks! if ($device eq "." or $device eq "..") {next;} # We don't' care about partitions if ($device =~ /sd[a-z][0-9]/) {next;} if ($device =~ /sd[a-z]/) { push(@udisks,$device); } } @disks=sort(@udisks); GetOptions("interval|i=i" => \( my $INTERVAL=300 ),"header|h=i" => \( my $HEADER_REPEAT = 24 ),) or die "command line arguments error"; #my $INTERVAL=300; #my $HEADER_REPEAT=24; printf("\n%s\n","Running script forever, press ctrl+c to exit.\nScript args:"); printf("%s\n%s\n\n","- Interval: $INTERVAL seconds","- Re-Header: $HEADER_REPEAT rows"); sub diskheader { my $rotoinv=keys %rotobois; if ($rotoinv > 0) { printf("%-10s", "24hr"); for (@disks) {printf("%-5s",$rotobois{"/dev/$_"});} printf("\n");} printf("%-10s", "Time"); for (@disks) {printf("%-5s",$_);} printf("\n"); } sub headerlines {printf("%-10s", "---------+"); for (@disks) {printf("%-5s","----+");} printf("\n");} diskheader(); headerlines(); my $headertracker=0; while (1) { printf("%s",$CLEAR_LINE); if ($headertracker >= $HEADER_REPEAT) {$headertracker=0;headerlines();diskheader();headerlines();} printf("%5s%5s",ptime(),""); for (@disks) { my $diskname="/dev/$_"; open my $cmd, '-|', "$smartctl -x $diskname"; while (my $line = <$cmd>) { if ($line =~ /0x05\s+0x008\s+\d+\s+(\d+)/) { my $disktemp=int($1); if ($disktemp > $temp{"bad"}) {printf(" %-5s ",clean(colored($disktemp, $color{"bad"})));} elsif($disktemp > $temp{"warn"}) {printf(" %-5s ",clean(colored($disktemp, $color{"warn"})));} else {printf(" %-5s ",clean(colored($disktemp, $color{"good"})));} } elsif ($line =~ /Rotation Rate:\s+(Solid State Device|\d+\s*rpm)/) { if ($line =~ /.*:\s+(\d+)\s+rpm/) {$rotobois{"$diskname"}="$1";} else {$rotobois{"$diskname"}="SSD";} } } close $cmd; } printf("\n"); printf("%s","Sleeping for $INTERVAL seconds..."); $headertracker=$headertracker+1; select()->flush(); # Heccin buffered I/O gets caulk blocked by the sleep. This forces a flush to STDIO sleep($INTERVAL); }