Copyediting, fixed up syntax (bumped to perl 5.10), more terminal support (via tput), fixed termios (no more stty\!), added lots of safety (--notreally flag, one-off confirmation phrase)
This commit is contained in:
parent
b2ae81bcf1
commit
bf8ab31fe5
195
wipe.pl
195
wipe.pl
@ -3,35 +3,62 @@ use strict;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use Digest::MD5 "md5_base64";
|
use Digest::MD5 "md5_base64";
|
||||||
use POSIX;
|
use POSIX;
|
||||||
|
use v5.10;
|
||||||
|
|
||||||
# create our random data and store it in ram so we can keep reusing it
|
# set CONFIRMATION_PHRASE to anything falsy (like zero) if you are a fast and
|
||||||
open (my $drand, "<", "/dev/urandom");
|
# loose badass and you don't need no stinkin' safety net.
|
||||||
|
use constant CONFIRMATION_PHRASE => "I am a pretty pink pineapple!";
|
||||||
|
|
||||||
|
if ($>) {
|
||||||
|
say "This program requires root, sorry.";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $termios = POSIX::Termios->new;
|
||||||
|
$termios->getattr(fileno(STDIN));
|
||||||
|
# fetch this value once and cache it
|
||||||
|
my $startup_lflag = $termios->getlflag();
|
||||||
|
|
||||||
|
# define some colors for convenience
|
||||||
|
my %color = (
|
||||||
|
# use tput to support anything with a terminfo entry
|
||||||
|
"brightred" => `tput setaf 9`,
|
||||||
|
"red" => `tput setaf 1`,
|
||||||
|
"green" => `tput setaf 2`,
|
||||||
|
"reset" => `tput sgr0`
|
||||||
|
);
|
||||||
|
|
||||||
|
my $notreally = grep /--notreally/, @ARGV;
|
||||||
|
if (!$notreally && CONFIRMATION_PHRASE) {
|
||||||
|
say "$color{brightred}WARNING$color{reset}: This program will $color{brightred}COMPLETELY DESTROY ALL DATA ON ALL SYSTEM DRIVES$color{reset}";
|
||||||
|
print 'Please type "'.CONFIRMATION_PHRASE.qq(" if you are $color{brightred}CERTAIN$color{reset} you want to continue: );
|
||||||
|
chomp(my $confirm = <STDIN>);
|
||||||
|
do { say; exit } unless CONFIRMATION_PHRASE eq $confirm;
|
||||||
|
}
|
||||||
|
|
||||||
|
# set termios lflag to not echo keystrokes, and take us out of line mode
|
||||||
|
$termios->setlflag($startup_lflag & ~(ECHO | ECHOK | ICANON));
|
||||||
|
$termios->setattr(fileno(STDIN), TCSANOW);
|
||||||
|
|
||||||
|
END { # reset termios lflag on exit
|
||||||
|
$termios->setlflag($startup_lflag);
|
||||||
|
$termios->setattr(fileno(STDIN), TCSANOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
# create our random data and store it in RAM so we can keep reusing it
|
||||||
|
open(my $drand, "<", "/dev/urandom");
|
||||||
my $clobber;
|
my $clobber;
|
||||||
read($drand, $clobber, 10240000);
|
read($drand, $clobber, 10240000);
|
||||||
close($drand);
|
close($drand);
|
||||||
my $clobsum = md5_base64($clobber);
|
my $clobsum = md5_base64($clobber);
|
||||||
|
|
||||||
# define some colors for conviniance
|
|
||||||
my %color = (
|
|
||||||
"red" => "\e[0;31m",
|
|
||||||
"green" => "\e[0;32m",
|
|
||||||
"reset" => "\e[0m"
|
|
||||||
);
|
|
||||||
my @failed = ();
|
my @failed = ();
|
||||||
my @good = ();
|
my @good = ();
|
||||||
my $term = POSIX::Termios->new;
|
|
||||||
|
|
||||||
# for providing a waiting prompt
|
# for providing a waiting prompt
|
||||||
sub anykey {
|
sub anykey {
|
||||||
print shift;
|
print shift;
|
||||||
my $key;
|
return getc(STDIN);
|
||||||
my $oldflag = $term->getlflag;
|
|
||||||
$term->setlflag(ICANON);
|
|
||||||
system "stty", '-icanon', 'eol', "\001"; #ISNT POSIX compliant
|
|
||||||
$key = getc(STDIN);
|
|
||||||
$term->setlflag($oldflag);
|
|
||||||
system "stty", 'icanon', 'eol', "^@";
|
|
||||||
return $key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# to handle errors without using die
|
# to handle errors without using die
|
||||||
@ -43,7 +70,7 @@ sub warning {
|
|||||||
# home made uniq function
|
# home made uniq function
|
||||||
sub uniq {
|
sub uniq {
|
||||||
my %seen;
|
my %seen;
|
||||||
return grep { !$seen{$_}++} @_;
|
return grep { !$seen{$_}++ } @_;
|
||||||
}
|
}
|
||||||
|
|
||||||
# To get the list of devices we will be cleaning
|
# To get the list of devices we will be cleaning
|
||||||
@ -57,10 +84,10 @@ sub getdisks {
|
|||||||
chomp( $serial );
|
chomp( $serial );
|
||||||
chomp( $model );
|
chomp( $model );
|
||||||
if ( $serial && $model ) {
|
if ( $serial && $model ) {
|
||||||
print $devicepath."\t".$serial."\n";
|
say "$devicepath\t$serial";
|
||||||
$listhash{$_}{"path"} = $devicepath;
|
$listhash{$_}{path} = $devicepath;
|
||||||
$listhash{$_}{"serial"} = $serial;
|
$listhash{$_}{serial} = $serial;
|
||||||
$listhash{$_}{"model"} = $model;
|
$listhash{$_}{model} = $model;
|
||||||
} else { next; }
|
} else { next; }
|
||||||
}
|
}
|
||||||
return %listhash;
|
return %listhash;
|
||||||
@ -71,27 +98,36 @@ sub wipethemdrives {
|
|||||||
my $diskid = shift;
|
my $diskid = shift;
|
||||||
my $disks = shift;
|
my $disks = shift;
|
||||||
# Write
|
# Write
|
||||||
print $disks->{$diskid}{"path"}." - Serial:".$disks->{$diskid}{"serial"}." is being wiped...\n";
|
say "$disks->{$diskid}{path} - Serial:$disks->{$diskid}{serial} is being wiped...";
|
||||||
open(my $diskw, ">", $disks->{$diskid}{"path"}) or return warning("could not open ".$disks->{$diskid}{"path"});
|
my $disksum;
|
||||||
print $diskw $clobber;
|
if ($notreally) {
|
||||||
close($diskw);
|
print "Pretend wipe complete, pretending to check...";
|
||||||
system("sync");
|
$disksum = $clobsum;
|
||||||
print "Wipe attempt complete, checking...";
|
} else {
|
||||||
# Read
|
open(my $diskw, ">", $disks->{$diskid}{path}) or return warning("could not open $disks->{$diskid}{path}");
|
||||||
open(my $diskr, "<", $disks->{$diskid}{"path"}) or return warning("could not open ".$disks->{$diskid}{"path"});
|
print $diskw $clobber;
|
||||||
my $diskdata;
|
close($diskw);
|
||||||
read($diskr, $diskdata, 10240000);
|
system("sync");
|
||||||
my $disksum = md5_base64($diskdata);
|
print "Wipe attempt complete, checking...";
|
||||||
close($diskr);
|
# Read
|
||||||
# print "Disk Checksum: ".$disksum."\n";
|
open(my $diskr, "<", $disks->{$diskid}{path}) or return warning("could not open $disks->{$diskid}{path}");
|
||||||
|
my $diskdata;
|
||||||
|
read($diskr, $diskdata, 10240000);
|
||||||
|
$disksum = md5_base64($diskdata);
|
||||||
|
close($diskr);
|
||||||
|
}
|
||||||
|
# say "Disk Checksum: $disksum";
|
||||||
if ($disksum eq $clobsum) {
|
if ($disksum eq $clobsum) {
|
||||||
printf("%-37.37s %9.9s", "Checksum ",$color{"green"}."PASSED!!!".$color{"reset"});
|
say +("."x28)."Checksum $color{green}PASSED!!!$color{reset}";
|
||||||
# print $disks->{$diskid}{"path"}." is clean.\n";
|
# say "$disks->{$diskid}{path} is clean.";
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
# print "Wipe Checksum: ".$clobsum."\n";
|
# say "Wipe Checksum: $clobsum";
|
||||||
printf("%-37.37s %9.9s", "Checksum ",$color{"red"}."FAILED!!!".$color{"reset"});
|
say +("."x28)."Checksum $color{red}FAILED!!!$color{reset}";
|
||||||
# print $disks->{$diskid}{"path"}." did not take, it could be bad.\n";
|
# this code will not work on the majority of terminals,
|
||||||
|
# now that we support anything with a terminfo entry.
|
||||||
|
# printf("%-37.37s %9.9s\n", "Checksum ","$color{red}FAILED!!!$color{reset}");
|
||||||
|
# say "$disks->{$diskid}{path} did not take, it could be bad.";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,29 +137,30 @@ sub smartcheck {
|
|||||||
# Do our smart disk thing...
|
# Do our smart disk thing...
|
||||||
my $diskid = shift;
|
my $diskid = shift;
|
||||||
my $disks = shift;
|
my $disks = shift;
|
||||||
print "Checking S.M.A.R.T. variables...\n";
|
say"Checking S.M.A.R.T. variables...";
|
||||||
# print 'smartctl '.$disks->{$diskid}{"path"}.' -A -f hex,id | grep "^0x"'."\n";
|
# say qq(smartctl $disks->{$diskid}{path} -A -f hex,id | grep "^0x");
|
||||||
(my $smartcommand = `smartctl $disks->{$diskid}{"path"} -A -f hex,id | grep "^0x"`) or (print $!."\ncould not check smart data, skipping\n" and return 1);
|
(my $smartcommand = `smartctl $disks->{$diskid}{path} -A -f hex,id | grep "^0x"`) or (say "$!\ncould not check smart data, skipping" and return 1);
|
||||||
for (split("\n",$smartcommand)) {
|
for (split("\n",$smartcommand)) {
|
||||||
my @smartdata = split(/\s+/,$_);
|
my @smartdata = split(/\s+/,$_);
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"id"} = $smartdata[0];
|
$disks->{$diskid}{smart}{$smartdata[0]}{id} = $smartdata[0];
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"name"} = $smartdata[1];
|
$disks->{$diskid}{smart}{$smartdata[0]}{name} = $smartdata[1];
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"flag"} = $smartdata[2];
|
$disks->{$diskid}{smart}{$smartdata[0]}{flag} = $smartdata[2];
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"value"} = $smartdata[3];
|
$disks->{$diskid}{smart}{$smartdata[0]}{value} = $smartdata[3];
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"worst"} = $smartdata[4];
|
$disks->{$diskid}{smart}{$smartdata[0]}{worst} = $smartdata[4];
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"thresh"} = $smartdata[5];
|
$disks->{$diskid}{smart}{$smartdata[0]}{thresh} = $smartdata[5];
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"type"} = $smartdata[6];
|
$disks->{$diskid}{smart}{$smartdata[0]}{type} = $smartdata[6];
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"updated"} = $smartdata[7];
|
$disks->{$diskid}{smart}{$smartdata[0]}{updated} = $smartdata[7];
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"when_failed"} = $smartdata[8];
|
$disks->{$diskid}{smart}{$smartdata[0]}{when_failed} = $smartdata[8];
|
||||||
$disks->{$diskid}{"smart"}{$smartdata[0]}{"raw_value"} = $smartdata[9];
|
$disks->{$diskid}{smart}{$smartdata[0]}{raw_value} = $smartdata[9];
|
||||||
}
|
}
|
||||||
close( $smartcommand );
|
close( $smartcommand );
|
||||||
for my $smartentry (sort {$a cmp $b} keys %{$disks->{$diskid}{"smart"}}) {
|
# these are incredibly strict requirements, you probably should be looking at the when_failed field instead...
|
||||||
if ($disks->{$diskid}{"smart"}{$smartentry}{"id"} =~ /0x05|0x07|0xc4|0xc5|0xc6|0xc7/) {
|
for my $smartentry (sort {$a cmp $b} keys %{$disks->{$diskid}{smart}}) {
|
||||||
printf("%-6.6s %-26.26s\t%-26.26s\t", $disks->{$diskid}{"smart"}{$smartentry}{"id"}, $disks->{$diskid}{"smart"}{$smartentry}{"name"}, $disks->{$diskid}{"smart"}{$smartentry}{"raw_value"});
|
if ($disks->{$diskid}{smart}{$smartentry}{id} =~ /0x05|0x07|0xc4|0xc5|0xc6|0xc7/) {
|
||||||
if ($disks->{$diskid}{"smart"}{$smartentry}{"raw_value"} =~ /0|0\/0/) {
|
printf("%-6.6s %-26.26s\t%-26.26s\t", $disks->{$diskid}{smart}{$smartentry}{id}, $disks->{$diskid}{smart}{$smartentry}{name}, $disks->{$diskid}{smart}{$smartentry}{raw_value});
|
||||||
print $color{"green"}."Passed".$color{"reset"}."\n";
|
if ($disks->{$diskid}{smart}{$smartentry}{raw_value} =~ /0|0\/0/) {
|
||||||
} else { print $color{"red"}."Failed".$color{"reset"}."\n"; return 0}
|
say "$color{green}Passed$color{reset}";
|
||||||
|
} else { say "$color{red}Failed$color{reset}"; return 0}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,36 +171,36 @@ sub smartcheck {
|
|||||||
while () {
|
while () {
|
||||||
my %disks = getdisks();
|
my %disks = getdisks();
|
||||||
my $batchcount = keys %disks;
|
my $batchcount = keys %disks;
|
||||||
print "About to wipe ".$batchcount." drives with the following serial numbers:\n";
|
say "$color{red}WARNING:$color{reset} This program will completely destroy all data on $batchcount drives with the following serial numbers:";
|
||||||
foreach my $diskid (sort {$disks{$a}{"serial"} cmp $disks{$b}{"serial"}} keys %disks) {
|
foreach my $diskid (sort {$disks{$a}{serial} cmp $disks{$b}{serial}} keys %disks) {
|
||||||
print $disks{$diskid}{"serial"}."\n";
|
say $disks{$diskid}{serial};
|
||||||
}
|
}
|
||||||
anykey("Hit any key to continue...\n");
|
exit unless "\n" eq anykey("Last chance to save your data, hit ENTER to DESTROY ALL DATA, or any other key to abort!\n");
|
||||||
foreach my $diskid (sort {$a cmp $b}keys %disks) {
|
foreach my $diskid (sort {$a cmp $b}keys %disks) {
|
||||||
# Disabling this because it just dosen't look good.
|
# Disabling this because it just dosen't look good.
|
||||||
# print "\e[0;0H\e[2J"; # move cursor to 0,0 and then clear all text below.
|
# print "\e[0;0H\e[2J"; # move cursor to 0,0 and then clear all text below.
|
||||||
print "="x33;
|
print "="x33;
|
||||||
printf '[ %-10s ]', $diskid;
|
printf '[ %-10s ]', $diskid;
|
||||||
print "="x33;
|
say "="x33;
|
||||||
print "\n";
|
$disks{$diskid}{wipe} = wipethemdrives($diskid,\%disks);
|
||||||
$disks{$diskid}{"wipe"} = wipethemdrives($diskid,\%disks);
|
if ($disks{$diskid}{wipe} == 1) {
|
||||||
if ($disks{$diskid}{"wipe"} == 1) {
|
$disks{$diskid}{smartpass} = smartcheck($diskid,\%disks);
|
||||||
$disks{$diskid}{"smartpass"} = smartcheck($diskid,\%disks);
|
if ($disks{$diskid}{smartpass} == 1) {
|
||||||
if ($disks{$diskid}{"smartpass"} == 1) {
|
# say "Smart looks good.";
|
||||||
# print "Smart looks good.\n";
|
push(@good, sprintf("%-26.26s\t%46.46s",$disks{$diskid}{serial},$disks{$diskid}{model}));
|
||||||
push(@good, sprintf("%-26.26s\t%46.46s",$disks{$diskid}{"serial"},$disks{$diskid}{"model"}));
|
} else { push(@failed, $disks{$diskid}{serial}); }
|
||||||
} else { push(@failed, $disks{$diskid}{"serial"}); }
|
} else { push(@failed, $disks{$diskid}{serial}); }
|
||||||
} else { push(@failed, $disks{$diskid}{"serial"}); }
|
|
||||||
print "="x80;
|
print "="x80;
|
||||||
print "\n"x5;
|
print "\n"x5;
|
||||||
}
|
}
|
||||||
print "These drives were successfully wiped and passed SMART, they may be put back into\nproduction:\n";
|
say "These drives were successfully wiped and passed SMART, they may be put back into\nproduction:";
|
||||||
print $color{"green"}.join("\n",sort {$a cmp $b} uniq(@good)).$color{"reset"};
|
print $color{green}.join("\n",sort {$a cmp $b} uniq(@good)).$color{reset};
|
||||||
anykey("\nHit any key to see the list of failed drives. Please also note any drives that\ndo not appear on ether list. Those should be tried again (if they do not show\nup after three times assume falure).");
|
anykey("\nHit any key to see the list of failed drives. Please also note any drives that\ndo not appear on either list. Those should be tried again (if they do not show\nup after three times assume failure).\n");
|
||||||
print "The folowing drives are bad, RMA or shred:\n";
|
say "$color{red}The following drives are bad, RMA or shred:";
|
||||||
print join("\n",sort {$a cmp $b} uniq(@failed));
|
say join("\n",sort {$a cmp $b} uniq(@failed));
|
||||||
|
print "$color{green}";
|
||||||
@failed = ();
|
@failed = ();
|
||||||
@good = ();
|
@good = ();
|
||||||
print $color{"green"}.("-"x34).$color{"red"}."\nWipe complete, insert more drives.\n".$color{"green"}.("-"x34).$color{"reset"}."\n";
|
say $color{green}.("-"x34).$color{red}."\nWipe complete, insert more drives.\n".$color{green}.("-"x34).$color{reset};
|
||||||
anykey("Hit any key when ready to continue...\n");
|
anykey("Hit any key when ready to continue...\n");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user