/[smeserver]/rpms/e-smith-backup/sme10/e-smith-backup-2.6.0-bz9299-consoleBackupBlockDevice.patch
ViewVC logotype

Annotation of /rpms/e-smith-backup/sme10/e-smith-backup-2.6.0-bz9299-consoleBackupBlockDevice.patch

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (hide annotations) (download)
Wed Apr 12 20:19:30 2017 UTC (7 years, 7 months ago) by unnilennium
Branch: MAIN
CVS Tags: e-smith-backup-2_6_0-8_el7_sme
* Wed Apr 12 2017 Jean-Philipe Pialasse <tests@pialasse.com> 2.6.0-8.sme
- fix removable device detection [SME: 9299]

1 unnilennium 1.1 --- e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/console/perform_backup.pm.devdetection 2017-02-17 14:37:15.000000000 -0500
2     +++ e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/console/perform_backup.pm 2017-02-24 14:46:06.777798717 -0500
3     @@ -6,8 +6,14 @@
4     use esmith::util;
5     use Locale::gettext;
6     use esmith::Backup;
7     -#use Filesys::DiskFree;
8     -#use Sys::Filesystem;
9     +use Carp;
10     +use feature qw( say );
11     +use esmith::BlockDevices;
12     +use POSIX qw(:sys_wait_h strftime);
13     +use File::stat;
14     +use Taint::Util;
15     +
16     +my $EMPTY = q{};
17    
18     sub new
19     {
20     @@ -43,22 +49,10 @@
21     my $fh = shift;
22     my @backup_list = esmith::Backup->restore_list;
23     my @backup_excludes = esmith::Backup->excludes;
24     -
25     - unless (open(DU, "-|"))
26     - {
27     - open(STDERR, ">/dev/null");
28     - exec qw(/usr/bin/du -sb), map { "/$_" } @backup_list;
29     - }
30     - my $backup_size = 0;
31     - while (<DU>)
32     - {
33     - next unless (/^(\d+)/);
34     - $backup_size += $1;
35     - }
36     - close DU;
37     + my $backup_size = backupSize (@backup_list);
38    
39     open(OLDSTDOUT, ">&STDOUT");
40     - unless (open(STDOUT, ">/mnt/bootstrap-console-backup/smeserver.tgz"))
41     + unless (open(STDOUT, ">$device/smeserver.tgz"))
42     {
43     return gettext("Could not create backup file on device").": $!\n";
44     }
45     @@ -133,20 +127,85 @@
46     my ($self, $console, $db) = @_;
47     my @backup_list = esmith::Backup->restore_list;
48     my @backup_excludes = esmith::Backup->excludes;
49     + my $compressionLevel = $db->get_prop('backupconsole', 'CompressionLevel') || '-6';
50     + my $mountpoint = $db->get_prop('backupconsole', 'Mountpoint') || '/mnt/bootstrap-console-backup';
51     + my $allowMounted = $db->get_prop('backupconsole', 'AllowMounted') || 'disabled'; ### For future use
52    
53     $ENV{PATH} = "/bin:/usr/bin";
54     $ENV{HOME} = "/root";
55    
56     - my $compressionLevel = $db->get_prop("backupconsole", "CompressionLevel") || "-6";
57     + my $devices = esmith::BlockDevices->new ('mount' => $mountpoint, 'allowmount' => $allowMounted);
58     +
59     + INITIATE_BACKUP:
60     my ($rc, $choice) = $console->yesno_page
61     (
62     title => gettext("Create Backup to removable media"),
63     defaultno => 1,
64     - text =>
65     - gettext("Do you wish to create backup on removable media?"),
66     - );
67     - return unless $rc == 0;
68     + text => gettext('Do you wish to create a backup on removable media?')."\n\n".
69     + gettext('Insert removable media before proceeding.')."\n".
70     + gettext('It may take many seconds to scan for media.'),
71     + );
72     + if ($rc != 0)
73     + {
74     + $devices->destroy;
75     + return;
76     + }
77     + ### determine which filesystems are valid or not for backups
78     + # check expected backup size
79     + my $backup_size = backupSize (@backup_list);
80     + # validate each filesystem
81     + my ($valid, $invalid) = $devices->checkBackupDrives ($backup_size);
82     + my $text = $EMPTY;
83     +
84     + if (${$invalid}[0]) # If there are filesystems that are not valid.
85     + {
86     + $text .= gettext ('These filesystems are not valid:')."\n";
87     + foreach my $drive (sort @{$invalid})
88     + {
89     + $text .= "$drive ".$devices->desc($drive).' '.gettext ('Reason').': '.$devices->reason($drive)."\n";
90     + }
91     + $text .= "\n";
92     + }
93     + unless (${$valid}[0]) # Unless a device is found show error page
94     + {
95     + my $title = gettext('No valid backup device found').' '.gettext('size').' '.esmith::BlockDevices::scaleIt($backup_size);
96     + $text .= "\n$title, ".gettext('please try again');
97     + ($rc, $choice) = $console->yesno_page
98     + (
99     + title => $title,
100     + text => $text,
101     + left => gettext('Try again'),
102     + right => gettext('Cancel'),
103     + );
104     + if ($rc == 0) # Try Again
105     + {
106     + goto INITIATE_BACKUP;
107     + }
108     + else
109     + {
110     + $devices->destroy;
111     + return;
112     + }
113     + }
114     + $text .= gettext ('The following are valid for backup').' (';
115     + $text .= gettext ('size').' '.esmith::BlockDevices::scaleIt($backup_size).')';
116    
117     + #ToDo when valid + invalid > 13 then may need to limit the information
118     + my @args = map { $_ => $devices->desc($_) } @{$valid};
119     +
120     + # Display the available backup destinations.
121     + ($rc, $choice) = $console->menu_page
122     + (
123     + title => gettext('Choose device to use for backup').' '.gettext('size').' '.esmith::BlockDevices::scaleIt($backup_size),
124     + text => $text,
125     + argsref => \@args,
126     + left => gettext('Cancel'),
127     + right => gettext('OK'),
128     + );
129     + goto INITIATE_BACKUP unless ($rc == 0);
130     + untaint $choice;
131     + $devices->mount ($choice); # mount the chosen filesystem
132     +
133     if (@backup_excludes) {
134     my $backupexclude = join ("\n/", sort @backup_excludes);
135     ($rc, $choice) = $console->yesno_page
136     @@ -159,124 +218,44 @@
137     return unless $rc == 0;
138     }
139    
140     - INITIATE_BACKUP:
141     - ($rc, $choice) = $console->yesno_page
142     - (
143     - title => gettext("Insert media to use for backup"),
144     - left => gettext("Next"),
145     - right => gettext("Cancel"),
146     - text =>
147     - gettext("Insert removable media, then hit the enter key."),
148     - );
149     - return unless $rc == 0;
150     - sleep(3);
151     - my @dirs = ();
152     - my @labels = ();
153     - foreach my $udi (qx(hal-find-by-property --key volume.fsusage --string filesystem)) {
154     - $udi =~ m/^(\S+)/;
155     -
156     - my $is_readonly = qx(hal-get-property --udi $1 --key volume.is_mounted_read_only);
157     -
158     - if ($is_readonly eq "false\n") {
159     -
160     - my $is_mounted = qx(hal-get-property --udi $1 --key volume.is_mounted);
161     -
162     - if ($is_mounted eq "false\n") {
163     - my $blkdev = qx(hal-get-property --udi $1 --key block.device);
164     - $blkdev =~ m/^(\S+)/;
165     - push @dirs, $1;
166     - }
167     - if ($is_mounted eq "false\n") {
168     - my $vollbl = qx(hal-get-property --udi $1 --key volume.label);
169     - $vollbl =~ m/^(\S+)/;
170     - if ($vollbl =~ /^\s/) {$vollbl = 'nolabel';}
171     - chomp $vollbl;
172     - push @labels, lc($vollbl);
173     - }
174     - }
175     - }
176     - unless ($dirs[0])
177     - {
178     - ($rc, $choice) = $console->message_page
179     - (
180     - title => gettext("Writable backup medium not found"),
181     - right => gettext("Back"),
182     - text =>
183     - gettext("No removable and/or writable media or device found"),
184     - );
185     - goto INITIATE_BACKUP;
186     - }
187     - mkdir("/mnt/bootstrap-console-backup");
188     -
189     - my $device = $dirs[0];
190     - if (defined $dirs[1])
191     - {
192     - my $count=1;
193     - my @args = map { $count++ . '.' => $_ } @dirs;
194     -
195     - my ($rc, $choice) = $console->menu_page
196     - (
197     - title => gettext("Choose device to use for backup"),
198     - text => ("@dirs \n @labels"),
199     - argsref => \@args,
200     - left => gettext("Cancel"),
201     - right => gettext("OK"),
202     - );
203     - goto INITIATE_BACKUP unless ($rc == 0);
204     - my %args_hash = ( @args );
205     - $device = $args_hash{$choice};
206     - }
207     - unless ( system("/bin/mount", "$device", "/mnt/bootstrap-console-backup") == '0' )
208     - {
209     - ($rc, $choice) = $console->message_page
210     - (
211     - title => gettext("No mountable backup medium"),
212     - right => gettext("Back"),
213     - text =>
214     - gettext("Unable to mount removable media, please check the file system format (default : Vfat,Ext2,Ext3,Ext4)"),
215     - );
216     - goto INITIATE_BACKUP;
217     - }
218     -
219     - use File::stat;
220     - my $st = stat("/mnt/bootstrap-console-backup/smeserver.tgz");
221     - if ($st)
222     - {
223     -# TODO
224     -# old backup exists - what do we want to do with it?
225     - my $size = $st->size;
226     - }
227     -
228     $console->infobox(
229     title => gettext("Preparing for backup"),
230     text => gettext("Please stand by while the system is prepared for backup..."),
231     );
232    
233     - my $backup_size = 0;
234     system("/sbin/e-smith/signal-event", "pre-backup");
235     +
236     + $console->gauge(make_backup_callback($mountpoint,$compressionLevel), 'title' => gettext('Creating backup file'));
237     +
238     + $devices->destroy;
239     +
240     + system("/sbin/e-smith/signal-event", 'post-backup');
241     + $console->message_page
242     + (
243     + title => gettext('Backup complete'),
244     + text => gettext('Remove backup media.'),
245     + );
246     + return;
247     +}
248     +
249     +
250     +sub backupSize
251     +{
252     + my $size;
253     +
254     unless (open(DU, "-|"))
255     {
256     open(STDERR, ">/dev/null");
257     - exec qw(/usr/bin/du -sb), map { "/$_" } @backup_list;
258     + exec qw(/usr/bin/du -sb), map { "/$_" } @_;
259     }
260     while (<DU>)
261     {
262     next unless (/^(\d+)/);
263     - $backup_size += $1;
264     + $size += $1;
265     }
266     close DU;
267     -
268     - $console->gauge(make_backup_callback("/mnt/bootstrap-console-backup",$compressionLevel), 'title' => gettext("Creating backup file"));
269     -
270     - system("/bin/umount", "/mnt/bootstrap-console-backup");
271     - rmdir("/mnt/bootstrap-console-backup");
272     - system("/sbin/e-smith/signal-event", "post-backup");
273     - ($rc, $choice) = $console->message_page
274     - (
275     - title => gettext("Backup complete"),
276     - text =>
277     - gettext("Remove removable media, then hit the enter key."),
278     - );
279     +
280     + return $size;
281     }
282    
283     #use esmith::console;
284     --- e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/console/perform_restore.pm.devdetection 2017-02-17 14:37:15.000000000 -0500
285     +++ e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/console/perform_restore.pm 2017-02-20 17:25:38.438280203 -0500
286     @@ -4,6 +4,12 @@
287     use esmith::ConfigDB;
288     use esmith::console;
289     use Locale::gettext;
290     +use Carp;
291     +use feature qw( say );
292     +use esmith::BlockDevices;
293     +use Taint::Util;
294     +
295     +my $EMPTY = q{};
296    
297     sub new
298     {
299     @@ -18,7 +24,6 @@
300     return $self;
301     }
302    
303     -
304     sub name
305     {
306     return $_[0]->{name};
307     @@ -32,137 +37,164 @@
308     sub doit
309     {
310     my ($self, $console, $db) = @_;
311     + my $compressionLevel = $db->get_prop('backupconsole', 'CompressionLevel') || '-6';
312     + my $mountpoint = $db->get_prop('backupconsole', 'Mountpoint') || '/mnt/bootstrap-console-backup';
313     + my $allowMounted = $db->get_prop('backupconsole', 'AllowMounted') || 'disabled'; ### For future use
314     + my $restoreMaxDepth = $db->get_prop('backupconsole', 'MaxDepth') || 1; ### For future use
315     + my %found;
316     + my $backupcount = 0;
317     + my $backupdrive; # Which filesystem holds the backup
318     + my $backupfile; # full path to the backup
319     + my ($time, $size); # time and size of chosen backup
320     +
321     return if (($db->get_prop('bootstrap-console', 'Run') eq 'no') && $self->{bootstrap} ); # regular reboot
322     if ($db->get_prop('bootstrap-console', 'Run') eq 'yes') # called from bootstrap console
323     {
324     return if ($db->get_value('PasswordSet') eq 'yes'); # too late
325     }
326     return if ($db->get_prop('bootstrap-console', 'Restore') eq 'disabled');
327     +
328     + my $devices = esmith::BlockDevices->new ('mount' => $mountpoint, 'allowmount' => $allowMounted);
329     +
330     + INITIATE_RESTORE:
331     my ($rc, $choice) = $console->yesno_page
332     (
333     title => gettext("Restore From Backup"),
334     defaultno => 1,
335     - text =>
336     - gettext("Do you wish to restore from backup?"),
337     - );
338     - return unless $rc == 0;
339     - mkdir("/mnt/bootstrap-console-backup");
340     - system("/etc/init.d/messagebus", "start");
341     - system("/etc/init.d/haldaemon", "start");
342     - INITIATE_RESTORE:
343     - ($rc, $choice) = $console->yesno_page
344     - (
345     - title => gettext("Insert media containing backup"),
346     - left => gettext("Next"),
347     - right => gettext("Cancel"),
348     - text =>
349     - gettext("Insert removable media containing your backup file, then hit the enter key."),
350     - );
351     - unless ($rc == 0) {
352     - system("/etc/init.d/haldaemon", "stop");
353     - system("/etc/init.d/messagebus", "stop");
354     - rmdir("/mnt/bootstrap-console-backup");
355     + text => gettext('Do you wish to restore from backup?')."\n\n".
356     + gettext('Insert removable media before proceeding.')."\n".
357     + gettext('It may take many seconds to scan for media.'),
358     + ); # Buttons are Yes & No
359     + if ($rc != 0) # choice was not Yes
360     + {
361     + $devices->destroy;
362     return;
363     }
364     - sleep(3);
365     - my @dirs;
366     - @dirs = ();
367     - foreach my $udi (qx(hal-find-by-property --key volume.fsusage --string filesystem)) {
368     - $udi =~ m/^(\S+)/;
369     - my $is_mounted = qx(hal-get-property --udi $1 --key volume.is_mounted);
370     -
371     - if ($is_mounted eq "false\n") {
372     - my $blkdev = qx(hal-get-property --udi $1 --key block.device);
373     - $blkdev =~ m/^(\S+)/;
374     - push @dirs, $1;
375     + ### determine which filesystems are valid or not for backups
376     +
377     + # validate each filesystem
378     + my ($valid, $invalid) = $devices->checkBackupDrives ($EMPTY);
379     + my $text = $EMPTY;
380     +
381     + if (${$valid}[0]) # There are filesystems that could hold a backup.
382     + {
383     + $text .= gettext ('These filesystems could hold backups')."\n";
384     + foreach my $drive (sort @{$valid})
385     + {
386     + $text .= "$drive ".$devices->desc($drive)."\n";
387     + $devices->findBackup ($drive, \%found, $restoreMaxDepth, \$backupcount);
388     }
389     + $text .= "\n";
390     }
391     - unless ($dirs[0])
392     - {
393     - ($rc, $choice) = $console->message_page
394     - (
395     - title => gettext("Backup medium not found"),
396     - right => "Try again",
397     - text =>
398     - gettext("No removable media or device found"),
399     - );
400     - goto INITIATE_RESTORE;
401     - }
402     - my $device = $dirs[0];
403     - if (defined $dirs[1])
404     - {
405     - my $count=1;
406     - # FIXME use better regexp
407     - my @args = map { /(.*)/; $count++ . '.' => $1 } @dirs;
408    
409     - my ($rc, $choice) = $console->menu_page
410     - (
411     - title => gettext("Choose device to restore from"),
412     - text => gettext("Please select which device contains the backup file you wish to restore from."),
413     - argsref => \@args,
414     - left => gettext("Cancel"),
415     - right => gettext("OK"),
416     - );
417     + unless ($backupcount) # Unless a valid backup is found show error page
418     + {
419     + if (${$invalid}[0]) # If there are filesystems that are not valid.
420     + {
421     + $text .= gettext ('These filesystems are not valid:')."\n";
422     + foreach my $drive (sort @{$invalid})
423     + {
424     + $text .= "$drive ".$devices->desc($drive).' '.gettext ('Reason').': '.$devices->reason($drive)."\n";
425     + }
426     + $text .= "\n";
427     + }
428     + my $title = gettext('No valid backup device found');
429     + $text .= "\n$title, ".gettext('please try again');
430     + ($rc, $choice) = $console->yesno_page
431     + (
432     + title => $title,
433     + text => $text,
434     + left => gettext('Try again'),
435     + right => gettext('Cancel'),
436     + );
437     + if ($rc == 0) # Try Again
438     + {
439     + goto INITIATE_RESTORE;
440     + }
441     + else
442     + {
443     + $devices->destroy;
444     + return;
445     + }
446     + }
447     + # %found contains $backupcount backups.
448     + if ($backupcount == 1)
449     + {
450     + # One backup found, so simple yes/no choice
451     + $backupdrive = $found{1}{device}; # Find the (only) device that a backup was found on
452     + $backupfile = $found{1}{path}; # find the actual backup
453     + $time = gettext('Date') .' '. $found{1}{time};
454     + $size = gettext('Size') .' '. $found{1}{sizeH};
455     +
456     + ($rc, $choice) = $console->yesno_page
457     + (
458     + title => gettext('Start restore from backup'),
459     + text =>
460     + gettext('Backup found on device').
461     + "\n$backupdrive ".$devices->desc($backupdrive)."\n\n".
462     + gettext('Backup details').
463     + "\n$backupfile $size $time\n\n\n".
464     + gettext('Do you wish to restore from this file?'),
465     + );
466     goto INITIATE_RESTORE unless ($rc == 0);
467     - my %args_hash = ( @args );
468     - $device = $args_hash{$choice};
469     + $size = $found{1}{size};
470     }
471     - system("/bin/mount", "$device", "/mnt/bootstrap-console-backup");
472     - sleep(1);
473     -
474     - unless (-f "/mnt/bootstrap-console-backup/smeserver.tgz")
475     + else # Multiple backups found so display a choice
476     {
477     - system("/bin/umount", "$device");
478     - ($rc, $choice) = $console->message_page
479     - (
480     - title => gettext("Backup file not found"),
481     - right => "Try again",
482     - text =>
483     - gettext("No backup file found"),
484     - );
485     - goto INITIATE_RESTORE;
486     - }
487     - use File::stat;
488     - my $st = stat("/mnt/bootstrap-console-backup/smeserver.tgz");
489     - my $size = $st->size;
490     -
491     - ($rc, $choice) = $console->yesno_page
492     - (
493     - title => gettext("Start restore from backup"),
494     - text =>
495     - gettext("Backup file found:") . " smeserver.tgz ($device) " .
496     - gettext("size") . " $size " . gettext("bytes") .
497     - "\n\n" .
498     - gettext("Do you wish to restore from this file?"),
499     - );
500     - unless ($rc == 0) {
501     - system("/bin/umount", "$device");
502     - goto INITIATE_RESTORE;
503     + $text = gettext ('Backups found on these devices')."\n";
504     + foreach my $backupfound (sort keys %found)
505     + {
506     + $backupdrive = $found{$backupfound}{device};
507     + if (($backupfound == 1) || ($found{$backupfound}{device} ne $found{$backupfound-1}{device}))
508     + {
509     + $text.= "$backupdrive ".$devices->desc($backupdrive)."\n";
510     + }
511     + }
512     + my @args = map { $_ => "$found{$_}{device} $found{$_}{path} $found{$_}{sizeH} $found{$_}{time}" } sort keys %found;
513     + ($rc, $choice) = $console->menu_page
514     + (
515     + title => gettext('Start restore from backup'),
516     + text =>
517     + "$text\n".
518     + gettext ('Please select the backup that you wish to restore from.'),
519     + argsref => \@args,
520     + left => gettext('Cancel'),
521     + right => gettext('OK'),
522     + );
523     + goto INITIATE_RESTORE unless ($rc == 0);
524     + untaint $choice;
525     + $backupdrive = $found{$choice}{device};
526     + $size = $found{$choice}{size};
527     }
528     +
529     + $devices->mount ($backupdrive); # mount the chosen filesystem
530     + sleep(1); # Some mounts take time to become active
531     +
532     + ###ToDo This section has no error checking
533     + ###ToDo 'Restoring data is not localized
534     + # Execute the restore
535     system("/sbin/e-smith/signal-event", "pre-restore");
536     - system("(cd / ; cat /mnt/bootstrap-console-backup/smeserver.tgz |
537     + system("(cd / ; cat $mountpoint/smeserver.tgz |
538     pv -n -s $size |
539     gunzip |
540     tar xf - > /dev/null ) 2>&1 |
541     dialog --backtitle 'Restoring data' --guage 'Progress' 7 70");
542     - system("/bin/umount", "$device");
543     - system("/etc/init.d/haldaemon", "stop");
544     - system("/etc/init.d/messagebus", "stop");
545     - rmdir("/mnt/bootstrap-console-backup");
546     - system("/sbin/e-smith/signal-event", "post-upgrade");
547     -
548     +
549     + # Restore complete, now clean-up
550     + $devices->destroy;
551     + system("/sbin/e-smith/signal-event", "post-upgrade");
552     unless ( $self->{bootstrap} )
553     {
554     - $db->set_prop("bootstrap-console", "Run", "yes");
555     + $db->set_prop("bootstrap-console", "Run", "yes");
556     $db->set_prop("bootstrap-console", "ForceSave", "yes");
557     $db->set_prop("bootstrap-console", "Restore", "disabled");
558     -
559     - system("/usr/bin/tput", "clear");
560     - system("/sbin/e-smith/signal-event", "reboot");
561     -
562     - # A bit of a hack to avoid the console restarting before the
563     - # reboot takes effect.
564     -
565     +
566     + system("/usr/bin/tput", "clear");
567     + system("/sbin/e-smith/signal-event", "reboot");
568     +
569     + # A bit of a hack to avoid the console restarting before the
570     + # reboot takes effect.
571     +
572     sleep(600);
573     }
574     return;
575     --- e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/BlockDevices.pm.devdetection 2017-02-24 14:39:00.743982759 -0500
576     +++ e-smith-backup-2.6.0/root/usr/share/perl5/vendor_perl/esmith/BlockDevices.pm 2017-02-24 14:47:46.334675319 -0500
577     @@ -0,0 +1,503 @@
578     +#----------------------------------------------------------------------
579     +# Copyright 2015 Ian Wells
580     +# This program is free software; you can redistribute it and/or
581     +# modify it under the same terms as Perl itself.
582     +#----------------------------------------------------------------------
583     +
584     +package esmith::BlockDevices;
585     +
586     +use strict;
587     +use warnings;
588     +use English '-no_match_vars';
589     +use Carp;
590     +use File::Path qw(make_path remove_tree);
591     +use POSIX qw(:sys_wait_h strftime);
592     +use Locale::gettext;
593     +use File::stat;
594     +use v5.10.1;
595     +use Taint::Util;
596     +use Readonly;
597     +use File::Find;
598     +
599     +use vars qw($VERSION @ISA @EXPORT_OK);
600     +
601     +@ISA = qw(Exporter);
602     +
603     +=head1 NAME
604     +
605     +esmith::BlockDevices - Module to handle block devices
606     +
607     +=head1 SYNOPSIS
608     +
609     + use esmith::BlockDevices;
610     + my $devices = BlockDevices->new ();
611     +
612     +=head1 DESCRIPTION
613     +
614     +This module provides an abstracted interface to the
615     +block devices used for backup/restore
616     +
617     +=cut
618     +
619     +my $EMPTY = q{};
620     +
621     +sub new
622     +{
623     + my $class = shift;
624     + my $self = {
625     + _blox => lsblk(),
626     + mount => findValidFS(),
627     + _fstype => $EMPTY,
628     + allowmount => $EMPTY,
629     + @_,
630     + };
631     + bless $self, $class;
632     + return $self;
633     +}
634     +
635     +sub lsblk
636     +{
637     +#ToDo add some comments
638     + my %blox; # a hash to hold the device information
639     +
640     + my $short = qx(/bin/lsblk -sdn -o KNAME);
641     + my @long = qx(/bin/lsblk -P -b -o KNAME,MAJ:MIN,RM,RO,TYPE,MOUNTPOINT,FSTYPE,LABEL,UUID,MODEL,SIZE,STATE,MODE,OWNER,GROUP);
642     + # Not all of this information may be needed currently, but it does not affect the processing time
643     + untaint ($short);
644     + untaint (@long);
645     +
646     + my $devicemodel= $EMPTY;
647     +
648     + for (@long)
649     + {
650     + my @line = split /\"\s/s;
651     + my $name;
652     + if ($line[0] =~ /KNAME=\"(.*)/s)
653     + {
654     + $name = $1;
655     + }
656     + else {carp 'Could not match KNAME'; last;} # should never occur.
657     +
658     + $blox{$name}{tip} = ($short =~ m/^$name$/sm) ? 1 : 0;
659     +
660     + for (@line)
661     + {
662     + my ($key,$value) = split /=/s;
663     + $value =~ s/\"//sg;
664     + $blox{$name}{$key} = trim($value);
665     + }
666     + if ($blox{$name}{TYPE} =~ /rom|disk/s)
667     + {
668     + $devicemodel = $blox{$name}{MODEL};
669     + }
670     + else
671     + {
672     + $blox{$name}{MODEL} = trim($devicemodel);
673     + }
674     + $blox{$name}{SIZEH} = scaleIt($blox{$name}{SIZE});
675     + }
676     + return \%blox;
677     +}
678     +
679     +sub findValidFS
680     +{
681     +# Find all filesystem types that are supported
682     + my %fs; # a hash to hold the supported filesystem type information
683     +
684     + my @cmd = `cat /proc/filesystems`;
685     + foreach (@cmd)
686     + {
687     + if (/.*\/(.*?)\.ko/s){$fs {$1}=$1;}
688     + }
689     + @cmd = `ls -1 /lib/modules/\$(uname -r)/kernel/fs/*/*ko`;
690     + foreach (@cmd)
691     + {
692     + if (/.*\/(.*?)\.ko/s){$fs {$1}=$1;}
693     + }
694     + return \%fs;
695     +}
696     +
697     +sub scanBlocks
698     +{
699     + # Scan all the block devices
700     + # This takes some seconds on systems with many filesystems
701     + my ($self) = @_;
702     + $self->{_blox} = lsblk;
703     + $self->{_fstype} = findValidFS;
704     + return;
705     +}
706     +
707     +sub list
708     +{
709     + my ($self) = @_;
710     + my @dirs=();
711     + my $hashref = $self->{_blox};
712     +
713     + foreach my $drive (keys %{$hashref})
714     + {
715     + push @dirs, $drive;
716     + }
717     +
718     + return @dirs;
719     +}
720     +
721     +sub checkBackupDriveSize
722     +{
723     + my ($self,$drive, $size) = @_;
724     + my $hashref = $self->{_blox};
725     + my $sz = $EMPTY;
726     + my $mntdir = $self->{mount};
727     + Readonly my $VFAT_LIMIT => 2147483648;
728     + Readonly my $KBYTE => 1024;
729     +
730     + # size > drive size
731     + if ($size > $hashref->{$drive}{SIZE})
732     + {
733     + return 1; # filesystem too small
734     + }
735     +
736     + # FAT32 drive and size > 2G
737     + if (($size > $VFAT_LIMIT) && ($hashref->{$drive}{FSTYPE} eq 'vfat'))
738     + {
739     + return 2; # filesystem vfat limit
740     + }
741     +
742     +#ToDo add a check here to see if mounting is allowed by db value
743     +
744     + # check mount and find actual size
745     + if ($self->mountable ($drive)) # Only check filesystems that appear mountable
746     + {
747     + $self->mount ($drive);
748     + my $filesize = -s "$mntdir/smeserver.tgz";
749     +
750     + # Check free disk space
751     + my $df = qx(/usr/bin/df -P \"$mntdir\");
752     + if ($df =~ /(\S+)\s+(\S+)\s+(\S+)\s+(\d*%)/s)
753     + {
754     + my $dsize = ($3 * $KBYTE) + ($filesize //= 0);
755     + if ($size > $dsize) # not enough space
756     + {
757     + $sz = 3; # filesystem has too little free space
758     + }
759     + }
760     + else # fail (never seen in testing)
761     + {
762     + $sz = 4; # Failed to get disk size
763     + }
764     + $self->unmount;
765     + }
766     + return $sz;
767     +}
768     +
769     +# Check each block device
770     +# Return two arrays, valid drives, invalid drives
771     +sub checkBackupDrives
772     +{
773     + my ($self,$bsize) = @_;
774     + my @valid = ();
775     + my @invalid = ();
776     + $self->scanBlocks; # scan all block devices
777     + my $hashref = $self->{_blox};
778     + my $allowmount = $self->{'allowmount'}; # Are mounted drives allowed in $checks.
779     + my $checks = 'UU RO FS'; # These checks are always valid
780     + $checks .= ' MO' if ($allowmount eq 'enabled');
781     + $checks .= ' SZ' if ($bsize); # Only run the size check when a valid size is given
782     +
783     + foreach my $drive (keys %{$hashref})
784     + {
785     + $hashref->{$drive}{REASON} = $EMPTY; # Reason for a filesystem being (in)valid
786     + next unless $hashref->{$drive}{tip}; #Ignore drives that have child filesystems
787     +
788     + # drives mounted on /, /boot, or [SWAP] are never valid for backups
789     + next if ($hashref->{$drive}{MOUNTPOINT} =~ /^\/boot$|^\[SWAP\]$|^\/$/s );
790     +
791     + # validate each filesystem against the checks
792     + foreach my $check (split / /s, $checks)
793     + {
794     + for ($check)
795     + {
796     + if (/^UU/si) # No UUID
797     + {
798     + $hashref->{$drive}{REASON} .='UU ' unless $self->uuid ($drive); last;
799     + }
800     + if (/^RO/si) # Read Only
801     + {
802     + $hashref->{$drive}{REASON} .='RO ' if $self->readonly ($drive); last;
803     + }
804     + if (/^FS/si) # Invalid filesystem
805     + {
806     + $hashref->{$drive}{REASON} .='FS ' unless $self->validFS ($drive); last;
807     + }
808     + if (/^MO/si) # Mounted
809     + {
810     + $hashref->{$drive}{REASON} .='MO ' if $self->mountpoint ($drive); last;
811     + }
812     + if (/^SZ/si) # filesystem size, this includes mounting to check free space
813     + {
814     + $hashref->{$drive}{REASON} .='SZ ' if $self->checkBackupDriveSize ($drive, $bsize);
815     + #ToDo the return value contains the reason why there is insufficient space, but this is not used yet.
816     + last;
817     + }
818     + { carp "not supported yet in checkBackupDrives: $check"; } # Should never be seen
819     + }
820     + }
821     + if ($hashref->{$drive}{REASON})
822     + {
823     + push @invalid, $drive;
824     + }
825     + else
826     + {
827     + push @valid, $drive;
828     + }
829     + }
830     + return (\@valid, \@invalid);
831     +}
832     +
833     +sub findBackup
834     +{
835     + my ($self, $kname, $foundref, $maxDepth, $count) = @_;
836     + my $hashref = $self->{_blox};
837     + my $mountpoint = $self->{'mount'};
838     + my $file = 'smeserver.tgz';
839     +
840     + $self->mount ($kname);
841     + sleep 1;
842     +
843     + # start with the absolute path
844     + my $findRoot = Cwd::realpath($mountpoint);
845     +
846     + # determine the depth of our beginning directory
847     + my $begDepth = 1 + grep { length } File::Spec->splitdir($findRoot);
848     +
849     + find (
850     + {
851     + preprocess => sub
852     + { @_ if (scalar File::Spec->splitdir($File::Find::dir) - $begDepth) <= $maxDepth },
853     + wanted => sub
854     + {
855     + if (($_ =~ m/^$file/s) && ($File::Find::name =~ qr|^([-+@\w\s./:\\]+)$| )) # if matching the backup name
856     + {
857     + $$count++;
858     + my $sb = stat $1;
859     + ${$foundref}{$$count}{count}=$$count;
860     + ${$foundref}{$$count}{device}=$kname;
861     + ${$foundref}{$$count}{path} = $1;
862     + ${$foundref}{$$count}{path} =~ s/$mountpoint//; #strip off the mountpoint
863     + ${$foundref}{$$count}{path} =~ s/$file//; #strip off the filename
864     + ${$foundref}{$$count}{size}=$sb->size; # size in bytes
865     + ${$foundref}{$$count}{sizeH}=scaleIt($sb->size); # human readable size
866     + ${$foundref}{$$count}{time}=strftime '%d %b %g %H:%M', localtime $sb->mtime;
867     + }
868     + },
869     + untaint => 1,
870     + untaint_pattern => qr|^([-+@\w\s./:\\]+)$|,
871     + untaint_skip =>1,
872     + },
873     + $findRoot
874     + );
875     +
876     + $self->unmount;
877     + return;
878     +}
879     +
880     +sub desc # brief description of a filesystem
881     +{
882     + my ($self,$kname) = @_;
883     + my $hashref = $self->{_blox};
884     +
885     + my $model = $hashref->{$kname}{MODEL};
886     + my $label = $hashref->{$kname}{LABEL} || gettext('no label');
887     + my $size = $hashref->{$kname}{SIZEH};
888     +
889     + return "$label $model $size";
890     +}
891     +
892     +
893     +# Given the KNAME check if the filesystem.could be mountable
894     +# Check that there are no children, i.e. a tip
895     +# Check that it has a UUID, Filesystem,
896     +sub mountable
897     +{
898     + my ($self,$kname) = @_;
899     + my $hashref = $self->{_blox};
900     + return ($hashref->{$kname}{tip} && $hashref->{$kname}{UUID} && _isFS ($hashref->{$kname}{FSTYPE})) ? 1 : $EMPTY;
901     +}
902     +
903     +# Given the KNAME check if the filesystem.is read-only
904     +# returns 1 for Read-Only and $EMPTY for R-W
905     +sub readonly
906     +{
907     + my ($self,$kname) = @_;
908     + my $hashref = $self->{_blox};
909     + return ($hashref->{$kname}{RO}) ? 1 : $EMPTY;
910     +}
911     +
912     +sub mountpoint
913     +{
914     + my ($self,$kname) = @_;
915     + my $hashref = $self->{_blox};
916     + return ($hashref->{$kname}{MOUNTPOINT});
917     +}
918     +
919     +sub uuid
920     +{
921     + my ($self,$kname) = @_;
922     + my $hashref = $self->{_blox};
923     + return ($hashref->{$kname}{UUID});
924     +}
925     +
926     +sub model
927     +{
928     + my ($self,$kname) = @_;
929     + my $hashref = $self->{_blox};
930     + return ($hashref->{$kname}{MODEL});
931     +}
932     +
933     +# Given the KNAME return the label
934     +# returns 'no label' if none found
935     +sub label
936     +{
937     + my ($self,$kname) = @_;
938     + my $hashref = $self->{_blox};
939     + return ($hashref->{$kname}{LABEL}) || gettext('no label');
940     +}
941     +
942     +sub size
943     +{
944     + my ($self,$kname) = @_;
945     + my $hashref = $self->{_blox};
946     + return ($hashref->{$kname}{SIZE});
947     +}
948     +
949     +# Given a filesystem.(eg sr0) check if it's filesystem type is allowed
950     +sub validFS
951     +{
952     + my ($self,$kname) = @_;
953     + my $hashref = $self->{_blox};
954     + my $fsref = $self->{_fstype};
955     + return ($fsref->{$hashref->{$kname}{FSTYPE}}) || $EMPTY;
956     +}
957     +
958     +# Given a filesystem.type (eg vfat) check if it is allowed
959     +sub _isFS
960     +{
961     + my ($filesystem) = @_;
962     + return $EMPTY unless $filesystem;
963     +
964     + my $fsref = findValidFS;
965     + return ($fsref->{$filesystem}) || $EMPTY;
966     +}
967     +
968     +# Return the reason string which indicates why a drive is (in)valid
969     +sub reason
970     +{
971     + my ($self,$kname) = @_;
972     + my $hashref = $self->{_blox};
973     + return ($hashref->{$kname}{REASON});
974     +}
975     +
976     +# Given the KNAME mount the filesystem, example
977     +# system ('/bin/mount', '-t', 'vfat', '-U', '9891-4C8A', '/tmp/mnt');
978     +sub mount
979     +{
980     + my ($self, $kname) = @_;
981     + my $hashref = $self->{_blox};
982     +
983     + $self->createMountpoint;
984     +
985     + system ('/bin/mount', '-t', $hashref->{$kname}{FSTYPE}, '-U', $hashref->{$kname}{UUID}, $self->{mount}) == 0
986     + or croak (gettext('Failed to mount')." $self->{mount},$hashref->{$kname}{FSTYPE},$hashref->{$kname}{UUID}: $?");
987     + return;
988     +}
989     +
990     +# Unmount the block device
991     +sub unmount
992     +{
993     + my $self = shift;
994     + system('/bin/umount', $self->{mount}) == 0
995     + or croak (gettext('Failed to unmount')." $self->{mount}: $?");
996     + return;
997     +}
998     +
999     +# Create the mountpoint directory
1000     +# Error if already mounted
1001     +sub createMountpoint
1002     +{
1003     + my $self = shift;
1004     + my $mount = $self->{mount};
1005     +
1006     + # Check if the mountpoint is in use
1007     + if (!checkMount ($mount))
1008     + {
1009     + # Try to unmount, will die if fails
1010     + $self->unmount;
1011     + }
1012     +
1013     + if ($mount && ! -d $mount)
1014     + {
1015     + eval {make_path($mount)};
1016     + croak (gettext('Error while creating')." $mount $EVAL_ERROR".gettext('Maybe insufficient permissions.')) if $EVAL_ERROR;
1017     + }
1018     + return;
1019     +}
1020     +
1021     +sub destroy
1022     +{
1023     +# cleanup, unmount and remove mountpoint
1024     +
1025     + my $self = shift;
1026     + my $mount = $self->{mount};
1027     +
1028     +
1029     + # Check if the mountpoint is in use
1030     + if (!checkMount ($mount))
1031     + {
1032     + $self->unmount;
1033     + }
1034     +
1035     + if ($mount && -d $mount)
1036     + {
1037     + eval {remove_tree($mount)};
1038     + croak (gettext('Error while deleting')." $mount $EVAL_ERROR") if $EVAL_ERROR;
1039     + }
1040     + return;
1041     +}
1042     +
1043     +
1044     +### The following subroutines are not specific to block devices
1045     +sub scaleIt {
1046     + Readonly my $KBYTE => 1024;
1047     + my( $size, $n ) =( shift, 0 );
1048     + ++$n and $size /= $KBYTE until $size < $KBYTE;
1049     + if ($size >= 1000){++$n ; $size /= $KBYTE;}
1050     + return sprintf "%.3g %s",
1051     + $size, ( qw[ bytes KB MB GB TB] )[ $n ];
1052     +}
1053     +
1054     +sub checkMount
1055     +{
1056     + # check if $mountdir is mounted
1057     + my $mountdir = shift;
1058     + $|=1; # Auto-flush
1059     +
1060     + # copy STDOUT to another filehandle
1061     + open (my $STDOLD, '>&', STDOUT);
1062     +
1063     + open(STDOUT, ">/dev/null");
1064     + if ( open(MOUNTDIR, "|-", "/bin/findmnt", $mountdir)){;}
1065     +
1066     + # restore STDOUT
1067     + open (STDOUT, '>&', $STDOLD);
1068     +
1069     + return (!close(MOUNTDIR));
1070     +}
1071     +
1072     +# remove leading and trailing spaces from a string
1073     +# this should be moved to a util library.
1074     +sub trim
1075     +{
1076     + my ($string) = @_;
1077     + $string =~ s/^\s+|\s+$//g;
1078     + return $string;
1079     +}
1080     +1;

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed