2 # Functions for managing RAID
4 BEGIN { push(@INC, ".."); };
7 &foreign_require("fdisk");
9 open(MODE, "$module_config_directory/mode");
10 chop($raid_mode = <MODE>);
13 %container = ( 'raiddev', 1,
17 # Returns a list of allowed RAID levels
20 if ($raid_mode eq "mdadm") {
21 return ( 0, 1, 4, 5, 6, 10 );
24 return ( 0, 1, 4, 5 );
29 # Read information about active RAID devices. Returns a hash indexed by
30 # device name (like /dev/md0), with each value being an array reference
31 # containing status level disks blocks resync disk-info
34 # Read the mdstat file
37 open(MDSTAT, $config{'mdstat'});
39 if (/^(md\d+)\s*:\s+(\S+)\s+(\S+)\s+(.*)\s+(\d+)\s+blocks\s*(.*)resync=(\d+)/) {
40 $mdstat{$lastdev = "/dev/$1"} = [ $2, $3, $4, $5, $7, $6 ];
42 elsif (/^(md\d+)\s*:\s+(\S+)\s+(\S+)\s+(.*)\s+(\d+)\s+blocks\s*(.*)/) {
43 $mdstat{$lastdev = "/dev/$1"} = [ $2, $3, $4, $5, undef, $6 ];
45 elsif (/^(md\d+)\s*:\s+(\S+)\s+(\S+)\s+(.*)/) {
46 $mdstat{$lastdev = "/dev/$1"} = [ $2, $3, $4 ];
48 if (/\s+(\d+)\s+blocks\s*(.*)resync=(\d+)/) {
49 $mdstat{$lastdev}->[3] = $1;
50 $mdstat{$lastdev}->[4] = $3;
51 $mdstat{$lastdev}->[5] = $2;
53 elsif (/\s+(\d+)\s+blocks\s*(.*)/) {
54 $mdstat{$lastdev}->[3] = $1;
55 $mdstat{$lastdev}->[5] = $2;
64 # Parse the raid config file into a list of devices
67 local ($raiddev, $device, %mdstat);
68 return \@get_raidtab_cache if (scalar(@get_raidtab_cache));
69 %mdstat = &get_mdstat();
71 if ($raid_mode eq "raidtools") {
72 # Read the raidtab file
74 open(RAID, $config{'raidtab'});
78 if (/^\s*(\S+)\s+(\S+)/) {
79 local $dir = { 'name' => lc($1),
83 if ($dir->{'name'} =~ /^(raid|spare|parity|failed)-disk$/) {
84 push(@{$device->{'members'}}, $dir);
85 $device->{'eline'} = $lnum;
86 $raiddev->{'eline'} = $lnum;
88 elsif ($dir->{'name'} eq 'raiddev') {
89 $dir->{'index'} = scalar(@get_raidtab_cache);
90 push(@get_raidtab_cache, $dir);
93 push(@{$raiddev->{'members'}}, $dir);
94 $raiddev->{'eline'} = $lnum;
96 if ($dir->{'name'} eq 'device') {
99 elsif ($dir->{'name'} eq 'raiddev') {
101 local $m = $mdstat{$dir->{'value'}};
102 $dir->{'active'} = $m->[0] =~ /^active/;
103 $dir->{'level'} = $m->[1] =~ /raid(\d+)/ ? $1 : $m->[1];
104 $dir->{'devices'} = [
105 map { /(\S+)\[\d+\](\((.)\))?/;
106 $3 eq 'F' ? () : ("/dev/$1") }
107 split(/\s+/, $m->[2]) ];
108 $dir->{'size'} = $m->[3];
109 $dir->{'resync'} = $m->[4];
110 $dir->{'errors'} = &disk_errors($m->[5]);
118 # Fake up the same format from mdadm output
120 foreach $m (sort { $a cmp $b } keys %mdstat) {
121 local $md = { 'value' => $m,
123 'index' => scalar(@get_raidtab_cache) };
124 local $mdstat = $mdstat{$md->{'value'}};
125 $md->{'active'} = $mdstat->[0] =~ /^active/;
126 $md->{'level'} = $mdstat->[1] =~ /raid(\d+)/ ? $1 : $mdstat->[1];
128 map { /(\S+)\[\d+\](\((.)\))?/;
129 $3 eq 'F' ? () : (&convert_to_hd("/dev/$1")) }
130 split(/\s+/, $mdstat->[2]) ];
131 $md->{'size'} = $mdstat->[3];
132 $md->{'resync'} = $mdstat->[4];
133 $md->{'errors'} = &disk_errors($mdstat->[5]);
134 open(MDSTAT, "mdadm --detail $m |");
136 if (/^\s*Raid\s+Level\s*:\s*(\S+)/) {
139 push(@{$md->{'members'}}, { 'name' => 'raid-level',
142 elsif (/^\s*Persistence\s*:\s*(.*)/) {
143 push(@{$md->{'members'}},
144 { 'name' => 'persistent-superblock',
145 'value' => $1 =~ /is\s+persistent/ });
147 elsif (/^\s*State\s*:\s*(.*)/) {
150 elsif ((/^\s*Rebuild\s+Status\s*:\s*(\d+)\s*\%/) || (/^\s*Reshape\s+Status\s*:\s*(\d+)\s*\%/)) {
151 $md->{'rebuild'} = $1;
153 elsif (/^\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+|\-)\s+(.*\S)\s+(\/\S+)/) {
155 local $device = { 'name' => 'device',
158 push(@{$device->{'members'}},
159 { 'name' => $5 eq 'spare' ? 'spare-disk'
162 push(@{$md->{'members'}}, $device);
164 elsif (/^\s+(Chunk\s+Size|Rounding)\s+:\s+(\d+)/i) {
165 push(@{$md->{'members'}},
166 { 'name' => 'chunk-size',
171 open(MDSTAT, $config{'mdstat'});
173 if (/^.*finish=(\S+)min/){
174 $md->{'remain'} = $1;
176 if (/^.*speed=(\S+)K/){
181 push(@get_raidtab_cache, $md);
184 # Merge in info from mdadm.conf
185 local $lref = &read_file_lines($config{'mdadm'});
186 foreach my $l (@$lref) {
187 if ($l =~ /^ARRAY\s+(\S+)\s*(.*)/) {
189 local %opts = map { split(/=/, $_, 2) }
191 local ($md) = grep { $_->{'value'} eq $dev }
194 push(@{$md->{'members'}},
195 { 'name' => 'spare-group',
196 'value' => $opts{'spare-group'} });
201 return \@get_raidtab_cache;
204 # disk_errors(string)
205 # Converts an mdstat errors string into an array of disk statuses
208 if ($_[0] =~ /\[([0-9\/]+)\].*\[([A-Z_]+)\]/i) {
209 local ($idxs, $errs) = ($1, $2);
210 local @idxs = split(/\//, $idxs);
211 local @errs = split(//, $errs);
212 #if (@idxs == @errs) {
213 # return [ map { $errs[$_-1] } @idxs ];
222 &lock_file($raid_mode eq "raidtools" ? $config{'raidtab'} : $config{'mdadm'});
225 sub unlock_raid_files
227 &unlock_file($raid_mode eq "raidtools" ? $config{'raidtab'} : $config{'mdadm'});
231 # Create a new raid set in the configuration file
234 if ($raid_mode eq "raidtools") {
235 # Add to /etc/raidtab
236 local $lref = &read_file_lines($config{'raidtab'});
237 $_[0]->{'line'} = @$lref;
238 push(@$lref, &directive_lines($_[0]));
239 $_[0]->{'eline'} = @$lref - 1;
243 # Add to /etc/mdadm.conf
244 local ($d, @devices);
245 foreach $d (&find("device", $_[0]->{'members'})) {
246 push(@devices, $d->{'value'});
248 local $sg = &find_value("spare-group", $_[0]->{'members'});
249 local $lref = &read_file_lines($config{'mdadm'});
250 local $lvl = &find_value('raid-level', $_[0]->{'members'});
251 $lvl = $lvl =~ /^\d+$/ ? "raid$lvl" : $lvl;
252 push(@$lref, "DEVICE ".
253 join(" ", map { &device_to_volid($_) } @devices));
254 push(@$lref, "ARRAY $_[0]->{'value'} level=$lvl devices=".
256 ($sg ? " spare-group=$sg" : ""));
263 # Delete a raid set from the config file
266 if ($raid_mode eq "raidtools") {
267 # Remove from /etc/raidtab
268 local $lref = &read_file_lines($config{'raidtab'});
269 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1);
270 &flush_file_lines($config{'raidtab'});
274 &system_logged("mdadm --zero-superblock ".
275 "$_[0]->{'value'} >/dev/null 2>&1");
277 # Zero out component superblocks
278 my @devs = &find('device', $_[0]->{'members'});
280 if (&find('raid-disk', $d->{'members'}) ||
281 &find('parity-disk', $d->{'members'}) ||
282 &find('spare-disk', $d->{'members'})) {
283 &system_logged("mdadm --zero-superblock ".
284 "$d->{'value'} >/dev/null 2>&1");
288 # Remove from /etc/mdadm.conf
289 local ($d, %devices);
290 foreach $d (&find("device", $_[0]->{'members'})) {
291 $devices{$d->{'value'}} = 1;
293 local $lref = &read_file_lines($config{'mdadm'});
295 for($i=0; $i<@$lref; $i++) {
296 if ($lref->[$i] =~ /^ARRAY\s+(\S+)/ && $1 eq $_[0]->{'value'}) {
297 splice(@$lref, $i--, 1);
299 elsif ($lref->[$i] =~ /^DEVICE\s+(.*)/) {
300 local @olddevices = split(/\s+/, $1);
301 local @newdevices = grep { !$devices{$_} } @olddevices;
303 $lref->[$i] = "DEVICE ".join(" ", @newdevices);
306 splice(@$lref, $i--, 1);
310 &flush_file_lines($config{'mdadm'});
315 # device_to_volid(device)
316 # Given a device name like /dev/sda1, convert it to a volume ID if possible.
317 # Otherwise return the device name.
322 #return &fdisk::get_volid($dev) || $dev;
325 # make_raid(&raid, force, [missing], [assume-clean])
326 # Call mkraid or mdadm to make a raid set for real
329 if (!-r $_[0]->{'value'} && $_[0]->{'value'} =~ /\/md(\d+)$/) {
330 # Device file is missing - create it now
331 &system_logged("mknod $_[0]->{'value'} b 9 $1");
333 if ($raid_mode eq "raidtools") {
334 # Call the raidtools mkraid command
335 local $f = $_[1] ? "--really-force" : "";
336 local $out = &backquote_logged("mkraid $f $_[0]->{'value'} ".
338 return $? ? &text($out =~ /force/i ? 'eforce' : 'emkraid',
343 # Call the complete mdadm command
344 local $lvl = &find_value("raid-level", $_[0]->{'members'});
346 local $chunk = &find_value("chunk-size", $_[0]->{'members'});
347 local $mode = &find_value("persistent-superblock", $_[0]->{'members'}) ? "create" : "build";
348 local $layout = &find_value("parity-algorithm", $_[0]->{'members'});
349 local ($d, @devices, @spares, @parities);
350 foreach $d (&find("device", $_[0]->{'members'})) {
351 if (&find("raid-disk", $d->{'members'})) {
352 push(@devices, $d->{'value'});
354 elsif (&find("spare-disk", $d->{'members'})) {
355 push(@spares, $d->{'value'});
357 elsif (&find("parity-disk", $d->{'members'})) {
358 push(@parities, $d->{'value'});
361 local $cmd = "mdadm --$mode --level $lvl --chunk $chunk";
363 push(@devices, "missing");
365 $cmd .= " --layout $layout" if ($layout);
366 $cmd .= " --raid-devices ".scalar(@devices);
367 $cmd .= " --spare-devices ".scalar(@spares) if (@spares);
368 $cmd .= " --force" if ($_[1]);
369 $cmd .= " --assume-clean" if ($_[3]);
371 $cmd .= " $_[0]->{'value'}";
372 foreach $d (@devices, @parities, @spares) {
375 local $out = &backquote_logged("$cmd 2>&1 </dev/null");
377 return $? ? &text('emdadmcreate', "<pre>$out</pre>") : undef;
381 # readwrite_raid(&raid)
382 # Set RAID mode to read/write.
385 local $cmd = "mdadm --readwrite $_[0]->{'value'}";
386 local $out = &backquote_logged("$cmd 2>&1 </dev/null");
391 # Shut down a RAID set permanently
394 if ($raid_mode eq "raidtools") {
395 &deactivate_raid($_[0]) if ($_[0]->{'active'});
398 local $out = &backquote_logged("mdadm --stop $_[0]->{'value'} 2>&1");
399 &error(&text('emdadmstop', "<tt>$out</tt>")) if ($?);
403 # activate_raid(&raid)
404 # Activate a raid set, which has previously been deactivated
407 if ($raid_mode eq "raidtools") {
408 local $out = &backquote_logged("raidstart $_[0]->{'value'} 2>&1");
409 &error(&text('eraidstart', "<tt>$out</tt>")) if ($?);
413 # deactivate_raid(&raid)
414 # Deactivate a raid set, without actually deleting it
417 if ($raid_mode eq "raidtools") {
418 # Just stop the raid set
419 local $out = &backquote_logged("raidstop $_[0]->{'value'} 2>&1");
420 &error(&text('eraidstop', "<tt>$out</tt>")) if ($?);
424 # add_partition(&raid, device)
425 # Adds a device to some RAID set, both in the config file and for real
428 if ($raid_mode eq "mdadm") {
429 # Call mdadm command to add
430 local $out = &backquote_logged(
431 "mdadm --manage $_[0]->{'value'} --add $_[1] 2>&1");
432 &error(&text('emdadmadd', "<tt>$out</tt>")) if ($?);
434 # Add device to mdadm.conf
435 local $lref = &read_file_lines($config{'mdadm'});
436 local ($i, $done_device);
437 for($i=0; $i<@$lref; $i++) {
438 if ($lref->[$i] =~ /^DEVICE\s+/ && !$done_device) {
439 $lref->[$i] .= " $_[1]";
442 elsif ($lref->[$i] =~ /^ARRAY\s+(\S+)/ &&
443 $1 eq $_[0]->{'value'}) {
444 $lref->[$i] =~ s/(\s)devices=(\S+)/${1}devices=${2},$_[1]/;
452 # grow(&raid, totaldisks)
453 # Grows a RAID set to contain totaldisks active partitions
456 if ($raid_mode eq "mdadm") {
457 # Call mdadm command to add
458 $cmd="mdadm --grow $_[0]->{'value'} -n $_[1] 2>&1";
459 local $out = &backquote_logged(
461 &error(&text('emdadmgrow', "<tt>'$cmd' -> $out</tt>")) if ($?);
465 # convert_raid(&raid, oldcount, newcount, level)
466 # Converts a RAID set to a defferent level RAID set
469 if ($raid_mode eq "mdadm") {
471 # Call mdadm command to convert
472 $cmd="mdadm --grow $_[0]->{'value'} --level $_[3]";
473 $grow_by = $_[2] - $_[1];
475 $raid_device_short = $_[0]->{'value'};
476 $raid_device_short =~ s/\/dev\///;
477 $date = `date \+\%Y\%m\%d-\%H\%M`;
479 $cmd .= " --backup-file /tmp/convert-$raid_device_short-$date";
481 $cmd .= " -n $_[2] 2>&1";
483 local $out = &backquote_logged(
485 &error(&text('emdadmgrow', "<tt>'$cmd' -> $out</tt>")) if ($?);
488 $newcount = $_[1] - 1;
489 $cmd="mdadm --grow $_[0]->{'value'} --level $_[3] -n $newcount";
490 $raid_device_short = $_[0]->{'value'};
491 $raid_device_short =~ s/\/dev\///;
492 $date = `date \+\%Y\%m\%d-\%H\%M`;
494 $cmd .= " --backup-file /tmp/convert-$raid_device_short-$date";
495 local $out = &backquote_logged(
497 &error(&text('emdadmgrow', "<tt>'$cmd' -> $out</tt>")) if ($?);
502 # remove_partition(&raid, device)
503 # Removes a device from some RAID set, both in the config file and for real
506 if ($raid_mode eq "mdadm") {
507 # Call mdadm commands to fail and remove
508 local $out = &backquote_logged(
509 "mdadm --manage $_[0]->{'value'} --fail $_[1] 2>&1");
510 &error(&text('emdadfail', "<tt>$out</tt>")) if ($?);
511 local $out = &backquote_logged(
512 "mdadm --manage $_[0]->{'value'} --remove $_[1] 2>&1");
513 &error(&text('emdadremove', "<tt>$out</tt>")) if ($?);
515 # Remove device from mdadm.conf
516 local $lref = &read_file_lines($config{'mdadm'});
517 local ($i, $done_device);
518 for($i=0; $i<@$lref; $i++) {
519 if ($lref->[$i] =~ /^DEVICE\s+(.*)/) {
520 local @olddevices = split(/\s+/, $1);
521 local @newdevices = grep { $_ ne $_[1] } @olddevices;
523 $lref->[$i] = "DEVICE ".join(" ", @newdevices);
526 splice(@$lref, $i--, 1);
529 elsif ($lref->[$i] =~ /^ARRAY\s+(\S+)/ &&
530 $1 eq $_[0]->{'value'}) {
531 $lref->[$i] =~ s/((=)|,)\Q$_[1]\E/$2/;
539 # remove_detached(&raid)
540 # Removes detached device(s) from some RAID set
543 if ($raid_mode eq "mdadm") {
544 # Call mdadm commands to remove
545 local $out = &backquote_logged(
546 "mdadm --manage $_[0]->{'value'} --remove detached 2>&1");
547 &error(&text('emdadremove', "<tt>$out</tt>")) if ($?);
551 # directive_lines(&directive, indent)
554 local @rv = ( "$_[1]$_[0]->{'name'}\t$_[0]->{'value'}" );
555 foreach $m (@{$_[0]->{'members'}}) {
556 push(@rv, &directive_lines($m, $_[1]."\t"));
565 foreach $c (@{$_[1]}) {
566 if ($c->{'name'} eq $_[0]) {
570 return @rv ? wantarray ? @rv : $rv[0]
571 : wantarray ? () : undef;
574 # find_value(name, &array)
578 @v = &find($_[0], $_[1]);
579 if (!@v) { return undef; }
580 elsif (wantarray) { return map { $_->{'value'} } @v; }
581 else { return $v[0]->{'value'}; }
584 # device_status(device)
585 # Returns an array of directory, type, mounted
588 @mounted = &mount::list_mounted() if (!@mounted);
589 @mounts = &mount::list_mounts() if (!@mounts);
590 local $label = &fdisk::get_label($_[0]);
591 local $volid = &fdisk::get_volid($_[0]);
593 local ($mounted) = grep { &same_file($_->[1], $_[0]) ||
594 $_->[1] eq "LABEL=$label" ||
595 $_->[1] eq "UUID=$volid" } @mounted;
596 local ($mount) = grep { &same_file($_->[1], $_[0]) ||
597 $_->[1] eq "LABEL=$label" ||
598 $_->[1] eq "UUID=$volid" } @mounts;
599 if ($mounted) { return ($mounted->[0], $mounted->[2], 1,
600 &indexof($mount, @mounts),
601 &indexof($mounted, @mounted)); }
602 elsif ($mount) { return ($mount->[0], $mount->[2], 0,
603 &indexof($mount, @mounts)); }
604 if (!scalar(@physical_volumes)) {
605 @physical_volumes = ();
606 foreach $vg (&lvm::list_volume_groups()) {
607 push(@physical_volumes,
608 &lvm::list_physical_volumes($vg->{'name'}));
611 foreach $pv (@physical_volumes) {
612 return ( $pv->{'vg'}, "lvm", 1)
613 if ($pv->{'device'} eq $_[0]);
618 # find_free_partitions(&skip, showtype, showsize)
619 # Returns a list of options, suitable for ui_select
620 sub find_free_partitions
622 &foreign_require("fdisk");
623 &foreign_require("mount");
624 &foreign_require("lvm");
625 local %skip = map { $_, 1 } @{$_[0]};
628 local $conf = &get_raidtab();
629 foreach $c (@$conf) {
630 foreach $d (&find_value('device', $c->{'members'})) {
636 foreach $d (&fdisk::list_disks_partitions()) {
637 foreach $p (@{$d->{'parts'}}) {
638 next if ($used{$p->{'device'}} || $p->{'extended'} ||
639 $skip{$p->{'device'}});
640 local @st = &device_status($p->{'device'});
642 $tag = &fdisk::tag_name($p->{'type'});
643 $p->{'blocks'} =~ s/\+$//;
644 push(@disks, [ $p->{'device'},
646 ($tag && $_[1] ? " ($tag)" : "").
648 $d->{'cylsize'} ? " (".&nice_size($d->{'cylsize'}*($p->{'end'} - $p->{'start'} + 1)).")" :
649 " ($p->{'blocks'} $text{'blocks'})") ]);
651 if (!@{$d->{'parts'}} &&
652 !$used{$d->{'device'}} && !$skip{$d->{'device'}}) {
653 # Raw disk has no partitions - add it as an option
654 push(@disks, [ $d->{'device'},
656 ($d->{'cylsize'} ? " (".&nice_size($d->{'cylsize'}*$d->{'cylinders'}).")" : "") ]);
659 foreach $c (@$conf) {
660 next if (!$c->{'active'} || $used{$c->{'value'}});
661 local @st = &device_status($c->{'value'});
662 next if (@st || $skip{$c->{'value'}});
663 push(@disks, [ $c->{'value'},
665 $c->{'value'} =~ /md(\d+)$/ ? "$1" : $c->{'value'}) ]);
668 foreach $vg (&lvm::list_volume_groups()) {
670 foreach $lv (&lvm::list_logical_volumes($vg->{'name'})) {
671 next if ($lv->{'perm'} ne 'rw' || $used{$lv->{'device'}} ||
672 $skip->{$lv->{'device'}});
673 local @st = &device_status($lv->{'device'});
675 push(@disks, [ $lv->{'device'},
676 &text('create_lvm', $lv->{'vg'}, $lv->{'name'}) ]);
679 return sort { $a->[0] cmp $b->[0] } @disks;
682 # convert_to_hd(device)
683 # Converts a device file like /dev/ide/host0/bus0/target1/lun0/part1 to
684 # /dev/hdb1, if it doesn't actually exist.
688 return $dev if (-r $dev);
689 if ($dev =~ /ide\/host(\d+)\/bus(\d+)\/target(\d+)\/lun(\d+)\/part(\d+)/) {
690 local ($host, $bus, $target, $lun, $part) = ($1, $2, $3, $4, $5);
691 return "/dev/".&fdisk::hbt_to_device($host, $bus, $target).$part;
698 %mdadm_notification_opts = map { $_, 1 } ( 'MAILADDR', 'MAILFROM', 'PROGRAM' );
700 # get_mdadm_notifications()
701 # Returns a hash from mdadm.conf notification-related settings to values
702 sub get_mdadm_notifications
704 local $lref = &read_file_lines($config{'mdadm'});
706 foreach my $l (@$lref) {
708 if ($l =~ /^(\S+)\s+(\S.*)/ && $mdadm_notification_opts{$1}) {
715 # save_mdadm_notifications(¬ifications)
716 # Updates mdadm.conf with settings from the given hash. Those set to undef
717 # are removed from the file.
718 sub save_mdadm_notifications
721 local $lref = &read_file_lines($config{'mdadm'});
723 for(my $i=0; $i<@$lref; $i++) {
726 local ($k, $v) = split(/\s+/, $l, 2);
727 if (exists($notif->{$k})) {
728 if (defined($notif->{$k})) {
729 $lref->[$i] = "$k $notif->{$k}";
732 splice(@$lref, $i--, 1);
737 foreach my $k (grep { !$done{$_} && defined($notif->{$_}) } keys %$notif) {
738 push(@$lref, "$k $notif->{$k}");
740 &flush_file_lines($config{'mdadm'});
744 # Returns the name of an init module action for mdadm monitoring, or undef if
748 if (&foreign_installed("init")) {
749 &foreign_require("init");
750 foreach my $a ("mdmonitor", "mdadm", "mdadmd") {
751 local $st = &init::action_status($a);
758 # get_mdadm_monitoring()
759 # Returns 1 if mdadm monitoring is enabled, 0 if not
760 sub get_mdadm_monitoring
762 local $act = &get_mdadm_action();
764 &foreign_require("init");
765 local $st = &init::action_status($act);
771 # save_mdadm_monitoring(enabled)
772 # Tries to enable or disable mdadm monitoring. Returns an error mesage
773 # if something goes wrong, undef on success
774 sub save_mdadm_monitoring
776 local ($enabled) = @_;
777 local $act = &get_mdadm_action();
779 &foreign_require("init");
781 &init::enable_at_boot($act);
782 &init::stop_action($act);
784 local ($ok, $err) = &init::start_action($act);
785 return $err if (!$ok);
788 &init::disable_at_boot($act);
789 &init::stop_action($act);
796 # If the update-initramfs command is installed, run it to update mdadm.conf
800 if (&has_command("update-initramfs")) {
801 &system_logged("update-initramfs -u >/dev/null 2>&1 </dev/null");
805 # get_mdadm_version()
806 # Returns the mdadm version number
807 sub get_mdadm_version
809 local $out = `mdadm --version 2>&1`;
810 local $ver = $out =~ /\s+v([0-9\.]+)/ ? $1 : undef;
811 return wantarray ? ( $ver, $out ) : $ver;