#!perl -w # # RaidTab -- encapsulate mdadm output # Copyright (C) 2005 Erik van Konijnenburg # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # NOTE: The mdadm --detail shows only devices that are running, # and does not distinguish between partitionable and non-partitionable # devices. # use strict; use warnings; use Base; use Conf; use RaidDev; package RaidTab; my $raidTab = undef; # # joinStanzas -- given a list of lines, return list where indented # continuation lines are joined with predecessor. # sub joinStanzas ($) { my ($lines) = @_; my @result = (); my $buf; for my $line (@{$lines}) { if ($line =~ /^\s+/) { $buf = $buf . $line; } else { if (defined ($buf)) { push @result, $buf; } $buf = $line; } } if (defined ($buf)) { push @result, $buf; } return [ @result ]; } # # init -- initialise table of all known raid devices. # # Sigh. Mdadm 1.9.0 has devices= in normal scan output, # in 1.12.0 this info is only produced if --verbose option # is given. With 1.9.0, the -v option produces completely # different output, that 1.12.0 only produces if -v is given # twice. We first try to retrieve devices= without -v, # if that doesn't work, retry with -v. See Debian Bug#324774. # sub init () { if (defined ($raidTab)) { return; } $raidTab = []; my ($rc, $lines) = Base::runCmd (missingOk => 1, cmd => ['/sbin/mdadm', '--detail', '--scan']); if (! defined ($lines)) { return; } $lines = joinStanzas ($lines); if (@{$lines} <= 0) { # no output and no error: done. return; } if ($lines->[0] !~ /\sdevices=/) { # output, but without the devices part, # lets retry. # Note: don't be deceived by the num-devices=... clause. ($rc, $lines) = Base::runCmd (cmd => ['/sbin/mdadm', '--detail', '--scan', '--verbose']); $lines = joinStanzas ($lines); } for my $line (@{$lines}) { processLine ($line); } } sub processLine ($) { my ($line) = @_; my @fields = split (/\s+/, $line); if ($fields[0] ne "ARRAY") { Base::fatal ("Expected ARRAY keyword in mdadm output"); } my $path = $fields[1]; my $uuid; my $level; my $devices; for my $i (2 .. $#fields) { if ($fields[$i] =~ /^uuid=(.+)$/i) { if (defined ($uuid)) { Base::fatal ("duplicate uuid attribute in mdadm output"); } $uuid = $1; } elsif ($fields[$i] =~ /^super-minor=(\d+)$/i) { # nothing } elsif ($fields[$i] =~ /^devices=(.+)$/i) { if (defined ($devices)) { Base::fatal ("duplicate devices attribute in mdadm output"); } $devices = $1; } elsif ($fields[$i] =~ /^level=(.+)$/i) { if (defined ($level)) { Base::fatal ("duplicate level attribute in mdadm output"); } $level = $1; } elsif ($fields[$i] =~ /^num-devices=(\d+)$/i) { # nothing } elsif ($fields[$i] =~ /^spare-group=(.+)$/i) { # nothing } elsif ($fields[$i] =~ /^auto=(.+)$/i) { # nothing } elsif ($fields[$i] =~ /^spares=(\d+)$/i) { # nothing } elsif ($fields[$i] =~ /^name=(.+)$/i) { # nothing } else { my $pair = $fields[$i]; Base::fatal ("Unknown attribute $pair in mdadm output"); } } if (! defined($path)) { Base::fatal ("Missing device field in mdadm output"); } if (! defined($uuid)) { Base::fatal ("Missing uuid attribute in mdadm output"); } if (! defined($level)) { Base::fatal ("Missing level attribute in mdadm output"); } if (! defined($devices)) { Base::fatal ("Missing devices attribute in mdadm output"); } my $devno = Base::devno ($path); if (! defined($devno)) { Base::fatal ("Device '$path' in mdadm output: cant find device major/minor number"); } my $descr = RaidDev->new ( path => $path, devno => $devno, uuid => $uuid, level => $level, devices => [split (/,/, $devices)], ); push @{$raidTab}, $descr; } sub all () { init; return $raidTab; } sub findByPath ($) { my ($path) = @_; for my $rd (@{all()}) { if ($rd->path() eq $path) { return $rd; } } return undef; } sub findByDevno ($) { my ($devno) = @_; for my $rd (@{all()}) { if ($rd->devno() eq $devno) { return $rd; } } return undef; } 1;