174 |
rodolico |
1 |
#! /usr/bin/env perl
|
|
|
2 |
|
|
|
3 |
# finds missing drives, telling you what bay they are in.
|
|
|
4 |
# Author: R. W. Rodolico
|
|
|
5 |
# Date: 2025-04-04
|
|
|
6 |
#
|
|
|
7 |
# run smartctl --scan to determine what drives are in the system, then
|
|
|
8 |
# again for each drive to get the serial number off of the drives.
|
|
|
9 |
# These end up in @active
|
|
|
10 |
#
|
|
|
11 |
# If there is stuff in STDIN, will assume that is the input and
|
|
|
12 |
# search it for lines which do not match the list of serial numbers
|
|
|
13 |
# from @active.
|
|
|
14 |
#
|
|
|
15 |
# If STDIN is not active, looks for either of the files 'drive_bays',
|
|
|
16 |
# then 'drives.tsv' in both /etc and /usr/local/etc. If one of those
|
|
|
17 |
# is found, will read it in, searching lines which do not match the list
|
|
|
18 |
# of serial numbers in @active
|
|
|
19 |
#
|
|
|
20 |
# Any matching lines (those not found in @active) are printed to the
|
|
|
21 |
# screen
|
|
|
22 |
#
|
|
|
23 |
use strict;
|
|
|
24 |
use warnings;
|
|
|
25 |
|
|
|
26 |
use IO::Select; # this appears to be available in base Perl installs
|
|
|
27 |
|
|
|
28 |
sub loadFile {
|
|
|
29 |
my ( $filename, $regex, @paths ) = @_;
|
|
|
30 |
my @return;
|
|
|
31 |
while ( my $path = shift @paths ) {
|
|
|
32 |
if ( -f "$path\/$filename" ) {
|
|
|
33 |
open DATA, "<$path\/$filename" or die "Could not open $path\/$filename: $!\n";
|
|
|
34 |
@return = grep{ ! /$regex/ } <DATA>;
|
|
|
35 |
chomp @return;
|
|
|
36 |
return @return;
|
|
|
37 |
}
|
|
|
38 |
}
|
|
|
39 |
return undef;
|
|
|
40 |
}
|
|
|
41 |
|
|
|
42 |
# get list of active drives from smartctl
|
|
|
43 |
my @drives = qx( smartctl --scan | cut -d' ' -f1 );
|
|
|
44 |
chomp @drives;
|
|
|
45 |
|
|
|
46 |
my @active;
|
|
|
47 |
# for each drive found, get its serial number. Note: using geom might be faster
|
|
|
48 |
foreach my $drive ( @drives ) {
|
|
|
49 |
push @active, qx( smartctl -a $drive | grep 'Serial Number:' | rev | cut -d' ' -f1 | rev );
|
|
|
50 |
}
|
|
|
51 |
chomp @active;
|
|
|
52 |
# build a regex that contains all of the serial numbers found
|
|
|
53 |
my $regex = '(' . join( '|', @active ) . ')';
|
|
|
54 |
my @lines; # a place to put our input
|
|
|
55 |
# turn off STDIN for blocking read
|
|
|
56 |
my $selector = IO::Select->new();
|
|
|
57 |
$selector->add(\*STDIN);
|
|
|
58 |
if ( $selector->can_read(0) ) { # This is true only if there is something on STDIN, ie a pipe
|
|
|
59 |
@lines = grep{ ! /$regex/ } <>;
|
|
|
60 |
}
|
|
|
61 |
# if STDIN didn't work, look for file drive_bays
|
|
|
62 |
@lines = &loadFile( 'drive_bays', $regex, '/etc', '/usr/local/etc' ) unless @lines;
|
|
|
63 |
# if we still didn't do it, try the file drives.tsv
|
|
|
64 |
@lines = &loadFile( 'drives.tsv', $regex, '/etc', '/usr/local/etc' ) unless @lines;
|
|
|
65 |
# OMG, nothing worked. Tell the user
|
|
|
66 |
push @lines, "Could not find STDIN, drive_bays or drives.tsv, no values" unless @lines;
|
|
|
67 |
|
|
|
68 |
chomp @lines;
|
|
|
69 |
print join("\n", @lines) . "\n";
|
|
|
70 |
1;
|