smeserver-manager/root/usr/share/smanager/lib/SrvMngr/Controller/Backup.pm
Brian Read 891fc73ce3 * Tue Apr 30 2024 Brian Read <brianr@koozali.org> 11.0.0-7.sme
- Remove use of hel command and replace by call to BlockDevices lib [SME: 12644]
2024-04-30 18:23:52 +01:00

2165 lines
58 KiB
Perl

package SrvMngr::Controller::Backup;
#----------------------------------------------------------------------
# heading : System
# description : Backup or restore
# navigation : 4000 200
# Copyright (C) 2002 Mitel Networks Corporation
#----------------------------------------------------------------------
# routes : end
# for information - routes
# $if_admin->get('/backup')->to('backup#main')->name('backup');
# $if_admin->post('/backup')->to('backup#do_display')->name('backupd');
# $if_admin->get('/backupd')->to('backup#do_display')->name('backupc');
# $if_admin->post('/backupd')->to('backup#do_update')->name('backupu');
use strict;
use warnings;
use Mojo::Base 'Mojolicious::Controller';
use utf8;
use Locale::gettext;
use SrvMngr::I18N;
use SrvMngr qw(theme_list init_session ip_number_or_blank);
use Quota;
use esmith::ConfigDB;
use esmith::AccountsDB;
use esmith::util;
use File::Basename;
use File::Find;
use File::Path qw(make_path remove_tree);
use esmith::Backup;
use esmith::BackupHistoryDB;
use esmith::util;
use esmith::lockfile;
use esmith::BlockDevices;
use constant DEBUG => $ENV{MOJO_SMANAGER_DEBUG} || 0;
our $cdb = esmith::ConfigDB->open || die "Couldn't open config db";
our $adb = esmith::AccountsDB->open || die "Couldn't open accounts db";
our $rdb = esmith::ConfigDB->open('/etc/e-smith/restore')
|| die "Couldn't open restore db";
my $es_backup = new esmith::Backup or die "Couldn't create Backup object\n";
my @directories = $es_backup->restore_list;
@directories = grep { -e "/$_" } @directories;
my @backup_excludes = $es_backup->excludes;
# Unbuffer standard output so that files and directories are listed as
# they are restored
$| = 1;
# Store away current gid of 'www' group.
my $www_gid = getgrnam("www");
sub main {
my $c = shift;
$c->app->log->info($c->log_req);
my %bac_datas = ();
my $title = $c->l('bac_BACKUP_TITLE');
my $notif;
$bac_datas{'function'} = 'desktop_backup';
my ($tarsize, $dumpsize, undef, undef) = $c->CalculateSizes();
my $module = $cdb->get('backup');
if ($module) {
$module = $module->prop('Program');
}
# The default e-smith backup program is flexbackup.
unless (defined $module) {
$module = "flexbackup";
} elsif ($module eq '') {
$module = "flexbackup";
}
$bac_datas{'tarsize'} = $tarsize;
$bac_datas{'dumpsize'} = $dumpsize;
$bac_datas{'module'} = $module;
if ($tarsize =~ /Tb/ or $tarsize =~ /(\d+)Gb/ and $1 >= 2) {
$notif = $c->l("bac_BACKUP_DESKTOP_TOO_BIG").' : '.$tarsize;
}
my $rec = $cdb->get('backup');
my ($backup_status, $backupwk_status) = 'disabled';
if ($rec) {
$backup_status = $rec->prop('status') || 'disabled';
}
if ($backup_status eq "enabled") {
$bac_datas{'backupTime'} = $rec->prop('backupTime');
$bac_datas{'reminderTime'} = $rec->prop('reminderTime');
}
$rec = $cdb->get('backupwk');
if ($rec) {
$backupwk_status = $rec->prop('status') || 'disabled';
}
if ($backupwk_status eq "enabled") {
$bac_datas{'backupwkTime'} = $rec->prop('BackupTime');
}
$bac_datas{'backupStatus'} = $backup_status;
$bac_datas{'backupwkStatus'} = $backupwk_status;
$c->stash( warning => $notif ) if ( $notif );
$c->stash( title => $title, bac_datas => \%bac_datas );
$c->render(template => 'backup');
};
sub do_display {
my $c = shift;
$c->app->log->info($c->log_req);
my $rt = $c->current_route;
my ($res, $result) = '';
my $function = $c->param('Function');
if ($function =~ /^(\S+)$/) {
$function = $1;
} elsif ($function =~ /^\s*$/) {
$function = "zoverall";
} else {
$result = $c->l('bac_INVALID_FUNCTION') . $function;
$function = undef;
}
DEBUG && warn("do_display $function");
my %bac_datas = ();
$bac_datas{'function'} = $function;
my $title = $c->l('bac_BACKUP_TITLE');
my $dest = '';
if ( $function eq 'desktop_backup' ) {
my $CompressionLevel = $cdb->get_prop("backupconsole", "CompressionLevel") || "-6";
my @exclude = map (" --exclude=$_",@backup_excludes);
$c->stash( compressionlevel => $CompressionLevel, exclude => \@exclude, directories => \@directories );
# streaming download in template
return $c->render("/backdown");
}
if ( $function eq 'tape_configure' ) {
$bac_datas{'status'} = 'unchecked';
my $backupTime = "2:00";
my $rec = $cdb->get('backup');
if ($rec) {
$backupTime = $rec->prop('backupTime') || "2:00";
my $backup_status = $rec->prop('status');
if (defined $backup_status && $backup_status eq "enabled") {
$bac_datas{'status'} = "checked";
}
}
($bac_datas{backupAMPM}, $bac_datas{reminderAMPM}) = 'AM';
($bac_datas{backupHour}, $bac_datas{backupMin}) = split (":", $backupTime, -1);
if ($bac_datas{backupHour} > 11) {
if ($bac_datas{backupHour} > 12) {
$bac_datas{backupHour} -= 12;
}
$bac_datas{backupAMPM} = 'PM';
}
# Obtain time for reminder notice from the backup cron template
my $reminderTime = "14:00";
if ($rec) {
$reminderTime = $rec->prop('reminderTime') || "14:00";
}
($bac_datas{reminderHour}, $bac_datas{reminderMin}) = split (":", $reminderTime, -1);
if ($bac_datas{reminderHour} > 12) {
$bac_datas{reminderHour} -= 12;
$bac_datas{reminderAMPM} = 'PM';
}
}
if ( $function eq 'workstn_configure' ) {
my $rec = $cdb->get('backupwk');
$bac_datas{vfstype} = $rec->prop('VFSType') || 'cifs';
$bac_datas{status} = $rec->prop('status');
}
if ( $function eq 'workstn_configure1' ) {
$bac_datas{vfstype} = $c->param('VFSType');
$bac_datas{'status'} = '';
$bac_datas{ampm} = 'AM';
$bac_datas{min} = '';
$bac_datas{hour} = '';
$bac_datas{login} = 'backup';
$bac_datas{password} = 'backup';
$bac_datas{station} = 'host';
$bac_datas{folder} = 'share';
$bac_datas{mount} = '';
$bac_datas{setsNumber} = '';
$bac_datas{filesinset} = '';
$bac_datas{timeout} = '';
$bac_datas{incOnlyTimeout} = '';
$bac_datas{compression} = '';
$bac_datas{dof} = '';
# Obtain backup informations from configuration
my $rec = $cdb->get('backupwk');
my $Time = '2:00';
if ($rec) {
$Time = $rec->prop('BackupTime') || '2:00';
$bac_datas{login} = $rec->prop('Login') || 'backup';
$bac_datas{password} = $rec->prop('Password') || 'backup';
$bac_datas{station} = $rec->prop('SmbHost') || 'host';
$bac_datas{folder} = $rec->prop('SmbShare') || 'share';
$bac_datas{mount} = $rec->prop('Mount') || '';
$bac_datas{setsNumber} = $rec->prop('SetsMax') || '1';
$bac_datas{filesinset} = $rec->prop('DaysInSet') || '1';
$bac_datas{timeout} = $rec->prop('Timeout') || '12';
$bac_datas{incOnlyTimeout} = $rec->prop('IncOnlyTimeout') || 'yes';
$bac_datas{compression} = $rec->prop('Compression') || '0';
$bac_datas{dof} = (defined $rec->prop('FullDay')) ? $rec->prop('FullDay') : '7';
}
($bac_datas{hour}, $bac_datas{min}) = split (':', $Time, -1);
if ( $bac_datas{hour} > 12 ) {
$bac_datas{hour} -= 12;
$bac_datas{ampm} = 'PM';
}
my $backupwk_status;
if ($rec) {
$backupwk_status = $rec->prop('status');
}
if (defined $backupwk_status && $backupwk_status eq 'enabled') {
$bac_datas{status} = 'checked';
}
if (defined $bac_datas{incOnlyTimeout} && $bac_datas{incOnlyTimeout} eq 'yes') {
$bac_datas{incOnlyTimeout} = 'checked';
}
}
if ( $function eq 'workstn_verify' ) {
my $rec = $cdb->get('backupwk');
if ( $rec) {
$bac_datas{status} = $rec->prop('status') || 'disabled';
}
}
if ( $function eq 'workstn_verify1' ) {
$res = '';
if ( ! $result ) {
$bac_datas{function} = $function;
}
}
if ( $function eq 'workstn_restore' ) {
my $rec = $cdb->get('backupwk');
if ( $rec) {
$bac_datas{status} = $rec->prop('status') || 'disabled';
}
}
$dest = "back_$function";
$c->stash( error => $result );
$c->stash( title => $title, bac_datas => \%bac_datas );
return $c->render( template => $dest );
};
sub do_update {
my $c = shift;
$c->app->log->info($c->log_req);
my $rt = $c->current_route;
my $function = $c->param('Function');
DEBUG && warn("do_update $function");
my %bac_datas = ();
$bac_datas{function} = $function;
my $title = $c->l('bac_BACKUP_TITLE');
my ($dest, $res, $result) = '';
if ( $function eq 'desktop_backup' ) {
# should not happen !! no desktop_backup template !!
$result .= ' ** Function error for desktop backup ** !';
}
if ( $function eq 'tape_configure' ) {
my $status = $c->param('Tapebackup');
my $backupHour = $c->param('BackupHour');
my $backupMin = $c->param('BackupMin');
my $bampm = $c->param('BackupAMPM');
my $reminderHour = $c->param('ReminderHour');
my $reminderMin = $c->param('ReminderMin');
my $rampm = $c->param('ReminderAMPM');
if (defined $status && $status eq "on") {
if ($backupHour =~ /^(.*)$/) {
$backupHour = $1;
} else {
$backupHour = "12";
}
if (($backupHour < 1) || ($backupHour > 12)) {
$result .= $c->l('bac_ERR_INVALID_HOUR').$backupHour.
' '.$c->l('bac_BETWEEN_0_AND_12').' ';
}
if ($backupMin =~ /^(.*)$/) {
$backupMin = $1;
} else {
$backupMin = "0";
}
if (($backupMin < 0) || ($backupMin > 59)) {
$result .= $c->l('bac_ERR_INVALID_MINUTE').$backupMin.
' '.$c->l('bac_BETWEEN_0_AND_59').' ';
}
if ($reminderHour =~ /^(.*)$/) {
$reminderHour = $1;
} else {
$reminderHour = "12";
}
if (($reminderHour < 1) || ($reminderHour > 12)) {
$result .= $c->l('bac_ERR_INVALID_REMINDER_HOUR').$reminderHour.
' '.$c->l('bac_BETWEEN_0_AND_12').' ';
}
if ($reminderMin =~ /^(.*)$/) {
$reminderMin = $1;
} else {
$reminderMin = "0";
}
if (($reminderMin < 0) || ($reminderMin > 59)) {
$result .= $c->l('bac_ERR_INVALID_REMINDER_MINUTE').$reminderMin.
' '.$c->l('bac_BETWEEN_0_AND_59').' ';
}
} else {
# service disabled no controls
}
##$result .= ' ** Blocked for testing ** !';
$res = '';
if ( ! $result ) {
$res = $c->tapeBackupConfig($status, $backupHour,
$backupMin, $bampm, $reminderHour, $reminderMin, $rampm);
$result .= $res unless $res eq 'OK';
if ( ! $result ) {
if (defined $status && $status eq "on") {
$result .= ($c->l('bac_SUCCESSFULLY_ENABLED_TAPE').' '.
$c->l('bac_WITH_BACKUP_TIME')."$backupHour:$backupMin".' '.
$c->l('bac_WITH_REMINDER_TIME')."$reminderHour:$reminderMin");
} else {
$result .= $c->l('bac_SUCCESSFULLY_DISABLED');
}
$cdb->reload;
}
}
}
if ( $function eq 'tape_restore' ) {
my $lock_file = "/var/lock/subsys/e-smith-restore";
my $file_handle = &esmith::lockfile::LockFileOrReturn($lock_file);
unless ($file_handle) {
$result .= $c->l('bac_UNABLE_TO_RESTORE_CONF').' '.
$c->l('bac_ANOTHER_RESTORE_IN_PROGRESS');
}
##$result .= ' ** Blocked for testing ** !';
$res = '';
if ( ! $result ) {
$res = $c->tapeRestore( $lock_file, $file_handle );
$result .= $res unless $res eq 'OK';
#if ( ! $result ) {
#$result = $c->l('bac_SUCCESS');
#}
}
}
if ( $function eq 'workstn_configure' ) {
# should not happen !!
$result .= ' ** Function error for workstation configure *** !';
}
if ( $function eq 'workstn_configure1' ) {
#$result .= ' ** Blocked for testing ** !';
$res = '';
if ( ! $result ) {
$res = $c->updateWorkstnBackupConfig();
if ( ($result = $res) =~ s|^#OK#|| ) {
$res = 'OK';
$cdb->reload;
}
}
}
if ( $function eq 'workstn_verify' ) {
# should not happen !!
$result .= ' ** Function error for workstation verify *** !';
}
if ( $function eq 'workstn_verify1' ) {
##$result .= ' ** Blocked for testing ** !';
$res = 'OK';
$result = '';
}
if ( $function eq 'workstn_restore' ) {
##$result .= ' ** Blocked for testing ** !';
$res = 'NOK';
if ( ! $result ) {
$res = $c->workstnRestore();
if ( ($result = $res) =~ s|^#OK#|| ) {
$bac_datas{restore_log} = $result;
$res = 'OK';
} else {
$c->stash( error => $result );
}
$bac_datas{function} = 'workstn_restore1';
$res = 'NEXT';
}
}
if ( $function eq 'workstn_restore1' ) {
my $state = 'unknown';
my $rec = $rdb->get('restore');
if ($rec) {
$state = $rec->prop('state') || 'unknown';
}
$result .= "Restore state unexpected: $state" if ($state ne 'complete');
$res = 'NOK';
if ( ! $result ) {
$res = $c->performReboot();
if ( ($result = $res) =~ s|^#OK#|| ) {
$res = 'OK';
} else {
$c->stash( error => $result );
}
}
}
if ( $function eq 'workstn_sel_restore' ) {
my $backupset = $c->param('Backupset');
my $filterexp = $c->param('Filterexp');
if ($filterexp =~ /^(.*)$/) {
$filterexp = $1;
} else {
$filterexp = '';
}
#$result .= ' ** Blocked for testing 1 ** !';
$res = '';
if ( ! $result ) {
$bac_datas{function} = 'workstn_sel_restore1';
$bac_datas{backupset} = $backupset;
$bac_datas{filterexp} = $filterexp;
$res = 'NEXT';
}
}
if ( $function eq 'workstn_sel_restore1' ) {
$bac_datas{backupset} = $c->param('Backupset');
$bac_datas{filterexp} = $c->param('Filterexp');
my @restorefiles = @{$c->every_param('Restorefiles')};
my $seldatebefore = $c->param('Seldatebefore');
if ($seldatebefore =~ /^(.*)$/) {
$seldatebefore = $1;
} else {
$result .= 'Unsecure data : ' . $seldatebefore;
}
my $tymd =
qr/((19|20)\d\d\/(?=\d\d\/\d\d-))?((0?[1-9]|1[0-2])\/(?=\d\d-))?((31|[123]0|[012]?[1-9])-)?/;
my $thms =
qr/([01]?[0-9]|2[0-3]):([0-5][0-9])(:[0-5][0-9])?/;
$result .= " $seldatebefore : " . $c->l('bac_ERR_INVALID_SELDATE')
unless (($seldatebefore =~ m/^$tymd$thms$/) || ($seldatebefore eq ""));
##$result .= ' ** Blocked for testing 2 ** !';
$res = '';
if ( ! $result ) {
$res = $c->performWorkstnSelRestore( $seldatebefore, \@restorefiles ); # restore log returned
if ( ($result = $res) =~ s|^#OK#|| ) {
$bac_datas{restore_log} = $result;
$res = 'OK';
} else {
$c->stash( error => $result );
}
$bac_datas{function} = 'workstn_sel_restore2';
$res = 'NEXT';
}
}
if ( $function eq 'workstn_sel_restore2' ) {
##$result .= ' ** Blocked for testing 3 ** !';
$res = 'OK';
$result = '';
}
# common part for all functions
if ($res ne 'OK') {
if ($res eq 'NEXT') {
$dest = 'back_' . $bac_datas{"function"};
} else {
$c->stash( error => $result );
$dest = "back_$function";
}
$c->stash( title => $title, bac_datas => \%bac_datas );
return $c->render($dest);
}
my $message = "'Backup' $function updates DONE";
$c->app->log->info($message);
$c->flash(success => $result);
$c->redirect_to('backup');
};
sub tapeBackupConfig {
my ($c, $status, $backupHour, $backupMin, $bampm,
$reminderHour, $reminderMin, $rampm) = @_;
if (defined $status && $status eq "on") {
$backupMin = sprintf("%02d", $backupMin);
if ($bampm =~ /^(.*)$/) {
$bampm = $1;
} else {
$bampm = "AM";
}
# convert to 24 hour time
$backupHour = $backupHour % 12;
if ($bampm eq "PM") {
$backupHour = $backupHour + 12;
}
$reminderMin = sprintf("%02d", $reminderMin);
if ($rampm =~ /^(.*)$/) {
$rampm = $1;
} else {
$rampm = "AM";
}
# convert to 24 hour time
$reminderHour = $reminderHour % 12;
if ($rampm eq "PM") {
$reminderHour = $reminderHour + 12;
}
# variables passed validity checks, set configuration database values
my $oldUnsav = $cdb->get('UnsavedChanges')->value;
my $rec = $cdb->get('backup');
unless (defined $rec) {
$rec = $cdb->new_record('backup', {type=>'service'});
}
$rec->set_prop('status', 'enabled');
my $module = $rec->prop('Program');
# The default e-smith backup program is flexbackup.
unless (defined $module) {
$module = "flexbackup";
} elsif ($module eq '') {
$module = "flexbackup";
}
$rec->set_prop('Program', $module);
$rec->set_prop('backupTime', "$backupHour:$backupMin");
$rec->set_prop('reminderTime', "$reminderHour:$reminderMin");
$cdb->get('UnsavedChanges')->set_value($oldUnsav);
system("/sbin/e-smith/signal-event", "conf-backup") == 0
or return ($c->l('bac_ERR_CONF_BACKUP'),"\n");
return 'OK';
} else {
# set service to disabled
my $oldUnsav = $cdb->get('UnsavedChanges')->value;
my $rec = $cdb->get('backup');
unless ($rec) {
$rec = $cdb->new_record('backup', {type=>'service'});
}
$rec->set_prop('status', 'disabled');
$cdb->get('UnsavedChanges')->set_value($oldUnsav);
system("/sbin/e-smith/signal-event", "conf-backup") == 0
or return ($c->l('bac_ERR_CONF_BACKUP')."\n");
return 'OK';
}
return undef;
}
sub tapeRestore {
my ($c, $lock_file, $file_handle) = @_;
my $rec = $rdb->get('restore');
$rec->set_prop('state', 'running');
$rec->set_prop('start', time);
my $child;
if ($child = fork) {
# Parent
$SIG{'CHLD'} = 'IGNORE';
&esmith::lockfile::UnlockFile($file_handle);
return 'OK';
} elsif (defined $child) {
# Child
# Re-establish the lock. Wait till it is relinquished by the parent.
$file_handle = &esmith::lockfile::LockFileOrWait($lock_file);
# Close STDOUT so that the web server connection is closed.
close STDOUT;
# Now reopen STDOUT for the child. Redirect it to STDERR.
open(STDOUT, ">&STDERR");
unless(system("/sbin/e-smith/signal-event", "pre-restore") == 0) {
$rec->set_prop('errmsg', $c->l('bac_ERR_PRE_RESTORE'));
$rec->delete_prop('state');
die ($c->l('bac_ERR_PRE_RESTORE'),"\n");
}
unless(system("/sbin/e-smith/signal-event", "restore-tape") == 0) {
$rec->set_prop('errmsg', $c->l('bac_ERR_RESTORING_FROM_TAPE'));
$rec->delete_prop('state');
die ($c->l('bac_ERR_RESTORING_FROM_TAPE')."\n");
}
#----------------------------------------
# regenerate configuration files
#----------------------------------------
unless(system("/usr/sbin/groupmod", "-g", "$www_gid", "www") == 0) {
$rec->set_prop('errmsg', $rec->prop('errmsg').', '.
$c->l('bac_ERR_RESTORING_GID'));
warn ($c->l('bac_ERR_RESTORING_GID')."\n");
}
unless(system("/usr/sbin/usermod", "-g", "$www_gid", "www") == 0) {
$rec->set_prop('errmsg', $rec->prop('errmsg').', '.
$c->l('bac_ERR_RESTORING_INITIAL_GRP'));
warn ($c->l('bac_ERR_RESTORING_INITIAL_GRP')."\n");
}
esmith::util::backgroundCommand(0,
"/sbin/e-smith/signal-event", "post-upgrade");
#unless(system("/sbin/e-smith/signal-event", "post-upgrade") == 0) {
# $rec->set_prop('errmsg', $rec->prop('errmsg').', '.
# $c->l('bac_ERR_UPDATING_CONF_AFTER_TAPE_RESTORE'));
# $rec->delete_prop('state');
# die ($c->l('bac_ERR_UPDATING_CONF_AFTER_TAPE_RESTORE'));
#}
my $finish = time;
$rec->set_prop('state', 'complete');
$rec->set_prop('finish', $finish);
my $start = $rec->prop('start');
$start = scalar localtime($start);
$finish = scalar localtime($finish);
&esmith::lockfile::UnlockFile($file_handle);
exit;
} else {
# Error
$rec->delete_prop('state');
$rec->set_prop('errmsg', $c->l('bac_COULD_NOT_FORK'));
die ($c->l("bac_COULD_NOT_FORK")." $!\n");
}
}
sub workstnBackupConfig {
# called by template
my ($c) = @_;
my $out;
my $backupwk_status;
my $enabledIncOnlyTimeout = "";
my $backupwkLogin = 'backup';
my $backupwkPassword = 'backup';
my $backupwkStation = 'host';
my $backupwkFolder = 'share';
my $backupwkMount = '/mnt/smb';
my $setsNumber;
my $filesinset;
my $backupwkTime;
my $backupwkTimeout;
my $backupwkIncOnlyTimeout;
my $VFSType;
my $compression;
my $dof;
my @dlabels = split(' ', $c->l('bac_DOW'));
# Obtain backup informations from configuration
my $rec = $cdb->get('backupwk');
if ($rec) {
$backupwkTime = $rec->prop('BackupTime') || '2:00';
$backupwkLogin = $rec->prop('Login') || 'backup';
$backupwkPassword = $rec->prop('Password') || 'backup';
$backupwkStation = $rec->prop('SmbHost') || 'host';
$backupwkFolder = $rec->prop('SmbShare') || 'share';
$backupwkMount = $rec->prop('Mount') || '/mnt/smb';
$VFSType = $rec->prop('VFSType') || 'cifs';
$setsNumber = $rec->prop('SetsMax') || '1';
$filesinset = $rec->prop('DaysInSet') || '1';
$backupwkTimeout = $rec->prop('Timeout') || '12';
$backupwkIncOnlyTimeout = $rec->prop('IncOnlyTimeout') || 'yes';
$compression = $rec->prop('Compression') || '0';
$dof = (defined $rec->prop('FullDay')) ? $rec->prop('FullDay') : '7';
$backupwk_status = $rec->prop('status');
}
if ($rec) {
if ($VFSType eq 'usb') {
$out .= $c->l('bac_WORKSTN_BACKUP_USB').' '.$backupwkFolder.'<br/>';
} elsif ($VFSType eq 'mnt') {
$out .= $c->l('bac_WORKSTN_BACKUP_MNT').' '.$backupwkMount.'<br/>';
} else {
$out .= $c->l('bac_WORKSTN_BACKUP_HOST').' '.$backupwkStation.' ';
$out .= $c->l('bac_WORKSTN_BACKUP_VFSTYPE').' '.$VFSType.'<br/>';
$out .= $c->l('bac_WORKSTN_BACKUP_SHARE').' '.$backupwkFolder.'<br/>';
}
if ($VFSType eq 'cifs') {
$out .= $c->l('bac_LOGIN').' '.$backupwkLogin.'<br/>';
$out .= $c->l('PASSWORD').' ********<br/>';
}
$out .= $c->l('bac_WORKSTN_BACKUP_SETSNUM').' '.$setsNumber.'<br/>';
$out .= $c->l('bac_WORKSTN_BACKUP_DAYSINSET').' '.$filesinset.'<br/>';
$out .= $c->l('bac_WORKSTN_BACKUP_COMPRESSION').' '.$compression.'<br/>';
$out .= $c->l('bac_WORKSTN_BACKUP_TOD').' '.$backupwkTime.'<br/>';
$out .= $c->l('bac_WORKSTN_BACKUP_TIMEOUT').' '.$backupwkTimeout.' '.$c->l('bac_HOURS');
if ( $backupwkIncOnlyTimeout eq 'yes' ) {
$out .= $c->l('bac_WORKSTN_BACKUP_INCONLY_TIMEOUT')
}
$out .= '<br/>';
if ( $dof eq '7' ) {
$out .= $c->l('bac_WORKSTN_FULL_BACKUP_EVERYDAY').'<br/>';
} else {
$out .= $c->l('bac_WORKSTN_FULL_BACKUP_DAY').' '.$dlabels[$dof].'<br/>';
}
} else {
$out = $c->l('bac_WORKSTN_BACKUP_NOT_CONFIGURED');
}
return $out;
}
sub workstnVerify {
my ($c) = @_;
my $out;
my $backupwkrec = $cdb->get('backupwk');
my $smbhost = $backupwkrec->prop('SmbHost');
my $smbshare = $backupwkrec->prop('SmbShare');
my $mntdir = $backupwkrec->prop('Mount') || '/mnt/smb';
my $key;
my $error_message;
my $id = $backupwkrec->prop('Id') ||
$cdb->get('SystemName')->value .'.'. $cdb->get('DomainName')->value;
my $err;
my $VFSType = $backupwkrec->prop('VFSType') || 'cifs';
my $verifyref = $c->param ('Backupset');
$mntdir = "/$smbshare" if ( $VFSType eq 'usb' );
# Mounting backup shared folder
$error_message = $c->bmount($mntdir,$smbhost,$smbshare,$VFSType);
if ($error_message) {
return $error_message .' '. $id;
}
# Test if backup subdirectory for our server
my $mntbkdir = $mntdir . "/$id";
unless (-d $mntbkdir) {
$error_message = $c->l('bac_ERR_NO_HOST_DIR')."\n";
$error_message .= $c->bunmount($mntdir,$VFSType);
return $error_message .' '. $id;
}
my $fullverify = $c->param('Verifyall') || '';
if ( $fullverify eq "on" ) {
# Test all backups needed to full restore
my %backupsetfiles = ();
my @restorefiles;
my $set = $verifyref;
$set =~ s/\/[^\/]*$//;
my $backupsetlist = sub {
if ( $_ =~ /\.dar/ ) {
my $backupref = $File::Find::name;
$backupref =~ s/\.[0-9]+\.dar//;
$_ =~ s/\..*\.dar//;
$_ =~ s/.*-//;
$backupsetfiles{$_} = $backupref;
}
};
# find list of available backups and verify
# it contains all backups needed for full restore
find { wanted => \&$backupsetlist, untaint => 1, untaint_pattern => qr|^([-+@\w\s./]+)$| }, $set ;
my $key;
my $num = 0;
foreach $key (sort keys %backupsetfiles)
{
push @restorefiles, $backupsetfiles{$key};
if ( $num == 0 ) {
unless ( $backupsetfiles{$key} =~ /\/full-/ ) {
$out .= $c->l('bac_ERR_NO_FULL_BACKUP');
return $out;
}
} else {
my $numf = sprintf("%03d", $num);
unless ( $backupsetfiles{$key} =~ /\/inc-$numf-/ ) {
$out .= $c->l('bac_ERR_NO_INC_BACKUP') . " " . $numf;
return $out;
}
}
$num++;
last if ( $backupsetfiles{$key} eq $verifyref );
}
if (open(RD, "-|")) {
$out .= '<b>'.$c->l('bac_TESTING_NEEDED_BACKUPS_FOR_RESTORE').'</b><UL>';
while (<RD>) {
$out .= "<li>$_</li>";
}
$out .= '</UL>';
my $message;
$out .= '<b>';
if (!close RD) {
$out .= $c->l('bac_RESTORE_VERIFY_FAILED');
} else {
$out .= $c->l('bac_VERIFY_COMPLETE');
}
$out .= '</b>';
} else {
select(STDOUT);
$| = 1;
my $file;
foreach $file (@restorefiles) {
if ($file =~ /^(.*)$/) {
$file = $1;
} else {
$error_message = "Unsecure data : $file\n";
$error_message .= $c->bunmount($mntdir,$VFSType);
die ($error_message);
}
print $c->l('bac_TESTED_BACKUP') .' '. $file;
system ("/usr/bin/dar", "-Q", "--test", "$file", "--noconf");
}
$error_message = $c->bunmount($mntdir,$VFSType);
die($error_message) if $error_message;
exit(0);
}
#return;
} else {
# verify selected backup only
# and display files saved in the backup
my $backupkey = $verifyref;
if ($backupkey =~ /^(.*)$/) {
$backupkey = $1;
} else {
$error_message = "Unsecure data : $backupkey\n";
$error_message .= $c->bunmount($mntdir,$VFSType);
die ($error_message);
}
if (open(RD, "-|")) {
$out .= '<b>'.$c->l('bac_FILES_IN_BACKUP').'</b><UL>';
my $complete = 0;
while (<RD>) {
$complete++ if /etc\/samba\/smbpasswd$/;
$out .= "<li>$_</li>";
}
$out .= '</UL>';
my $status = close RD ?
($complete ?
$c->l('bac_VERIFY_COMPLETE') :
$c->l('bac_BACKUP_FILE_INCOMPLETE'))
: ($c->l('bac_ERROR_READING_FILE').' : '.$backupkey);
$out .= "<b> $status </b>";
} else {
select(STDOUT);
$| = 1;
system ("/usr/bin/dar", "-Q", "--list", "$backupkey", "--noconf") == 0
or die ($c->l('bac_ERR_EXTRACT')." : ".$!);
$error_message = $c->bunmount($mntdir,$VFSType);
die($error_message) if $error_message;
exit(0);
}
}
$error_message .= $c->bunmount($mntdir,$VFSType);
return $out;
}
sub workstnRestore {
my ($c) = @_;
my $out;
my $restoreref = $c->param('Backupset');
my $set = $restoreref;
$set =~ s/\/[^\/]*$//;
my %backupsetfiles = ();
my @restorefiles;
my $backupsetlist = sub {
if ( $_ =~ /\.dar/ ) {
my $backupref = $File::Find::name;
$backupref =~ s/\.[0-9]+\.dar//;
$_ =~ s/\..*\.dar//;
$_ =~ s/.*-//;
$backupsetfiles{$_} = $backupref;
}
};
my $lock_file = "/var/lock/subsys/e-smith-restore";
my $file_handle = &esmith::lockfile::LockFileOrReturn($lock_file);
unless ($file_handle) {
return "$c->l('bac_RESTORE_CANNOT_PROCEED') <br> ($c->l('bac_ANOTHER_RESTORE_IN_PROGRESS')";
}
my $backupwkrec = $cdb->get('backupwk');
my $id = $backupwkrec->prop('Id')
|| $cdb->get('SystemName')->value . "." . $cdb->get('DomainName')->value;
my $mntdir = $backupwkrec->prop('Mount') || '/mnt/smb';
my $VFSType = $backupwkrec->prop('VFSType') || 'cifs';
my $smbhost = $backupwkrec->prop('SmbHost');
my $smbshare = $backupwkrec->prop('SmbShare');
$mntdir = "/$smbshare" if ( $VFSType eq 'usb' );
my $err;
my $error_message;
# Mounting backup shared folder
$error_message = $c->bmount($mntdir,$smbhost,$smbshare,$VFSType);
if ($error_message) {
return "$c->l('bac_RESTORE_CANNOT_PROCEED') $error_message : $id";
}
# Test if backup subdirectory for our server
my $mntbkdir = $mntdir . "/$id";
unless (-d $mntbkdir) {
$error_message = $c->l('bac_ERR_NO_HOST_DIR')."\n";
$error_message .= $c->bunmount($mntdir,$VFSType);
return "$c->l('bac_RESTORE_CANNOT_PROCEED') $error_message : $id";
}
# finding list of available backups
# and verifying all needed backup files are available
find { wanted => \&$backupsetlist, untaint => 1, untaint_pattern => qr|^([-+@\w\s./]+)$| }, $set ;
my $key;
my $num = 0;
foreach $key (sort keys %backupsetfiles) {
push @restorefiles, $backupsetfiles{$key};
if ( $num == 0 ) {
unless ( $backupsetfiles{$key} =~ /\/full-/ ) {
return "$c->l('bac_RESTORE_CANNOT_PROCEED') $c->l('bac_ERR_NO_FULL_BACKUP')";
}
} else {
my $numf = sprintf("%03d", $num);
unless ( $backupsetfiles{$key} =~ /\/inc-$numf-/ ) {
return "$c->l('bac_RESTORE_CANNOT_PROCEED') $c->l('bac_ERR_NO_INC_BACKUP') . $numf";
}
}
$num++;
last if ( $backupsetfiles{$key} eq $restoreref );
}
# backup is online, restoring now
my $rec = $rdb->get('restore');
$rec->set_prop('state','running');
$rec->set_prop('start', time);
$cdb->get('bootstrap-console')->set_prop('Run', 'yes');
unless (system("/sbin/e-smith/signal-event", "pre-restore") == 0) {
return "$c->l('bac_OPERATION_STATUS_REPORT') $c->l('bac_ERR_PRE_RESTORE')";
}
$| = 1;
if (open(RD, "-|")) {
#-----------------------------------------------------
# restore system from uploaded workstation backup file
#-----------------------------------------------------
$out .= $c->l('bac_FILES_HAVE_BEEN_RESTORED')."\n";
$out .= '<UL>';
my $complete = 0;
while (<RD>) {
$complete++ if /etc\/samba\/smbpasswd$/;
$out .= "<li>$_</li>\n";
}
$out .= '</UL>';
my $message;
if (!close RD) {
$message = $c->l('bac_RESTORE_FAILED_MSG');
} else {
#-----------------------------------------------------
# if restore completed, regenerate configuration files
#-----------------------------------------------------
if ($complete) {
$out .= $c->l('bac_RESTORE_COMPLETE');
system("/usr/sbin/groupmod", "-g", "$www_gid", "www") == 0
or warn ($c->l('bac_ERR_RESTORING_GID')."\n");
system("/usr/sbin/usermod", "-g", "$www_gid", "www") == 0
or warn ($c->l('bac_ERR_RESTORING_INITIAL_GRP')."\n");
esmith::util::backgroundCommand(0,
"/sbin/e-smith/signal-event", "post-upgrade");
# system("/sbin/e-smith/signal-event", "post-upgrade") == 0
# or die ($c->l('bac_ERROR_UPDATING_CONFIGURATION')."\n");
} else {
$message = $c->l('bac_RESTORE_FAILED');
}
}
return $message if $message;
$rec->set_prop('state', 'complete');
$rec->set_prop('finish', time);
&esmith::lockfile::UnlockFile($file_handle);
} else {
select(STDOUT);
$| = 1;
my $file;
foreach $file (@restorefiles) {
if ($file =~ /^(.*)$/) {
$file = $1;
} else {
$error_message = "Unsecure data : $file\n";
$error_message .= $c->bunmount($mntdir,$VFSType);
die ($error_message);
}
system ("/usr/bin/dar", "-Q", "-x", "$file", "-v", "-N", "-R", "/", "-wa");
}
$error_message = $c->bunmount($mntdir,$VFSType);
die($error_message) if $error_message;
exit(0);
}
$rdb->reload;
$error_message .= $c->bunmount($mntdir,$VFSType);
return '#OK#' . $out;
}
sub workstnSelRestore() {
my $c = shift;
my $rec = $cdb->get('backupwk');
my %backupfiles = ();
my $mntdir = $rec->prop('Mount') || '/mnt/smb';
my $mntbkdir;
my $key;
my $id = $rec->prop('Id') ||
$cdb->get('SystemName')->value . '.' . $cdb->get('DomainName')->value;
my %blabels = ();
my @blabels;
my $backups = 0;
my $filterexp;
my $VFSType = $rec->prop('VFSType') || 'cifs';
my $smbhost = $rec->prop('SmbHost');
my $smbshare = $rec->prop('SmbShare');
$mntdir = "/$smbshare" if ( $VFSType eq 'usb' );
my $err;
my $error_message;
my $setbackuplist = sub {
if ( $_ =~ /\.dar/ )
{
my $dir = $File::Find::dir;
my $backupref;
$dir =~ s/$mntbkdir\///;
$_ =~ s/\..*\.dar//;
$backupref = $_;
$_ =~ s/.*-//;
@{$backupfiles{$_}}[0] = $dir;
@{$backupfiles{$_}}[1] = $backupref;
}
};
# Mounting backup shared folder
$error_message = $c->bmount($mntdir,$smbhost,$smbshare,$VFSType);
if ($error_message) {
return ($error_message, $id);
}
# Test if backup subdirectory for our server
$mntbkdir = $mntdir . "/$id";
unless (-d $mntbkdir) {
$error_message = $c->l('bac_ERR_NO_HOST_DIR')."\n";
$error_message .= $c->bunmount($mntdir,$VFSType);
return ($error_message, $id);
}
my $catalog = "$mntbkdir/dar-catalog";
my $i = 0;
my $j = 0;
my @bknum;
my @setd;
my @bkname;
# update backups list from current catalog
open(DAR_LIST, "/usr/bin/dar_manager -B $catalog -l |") ;
$i = 0;
while (<DAR_LIST>)
{
next unless m/set/;
chomp;
($bknum[$i], $setd[$i], $bkname[$i]) = split(' ', $_, 3);
$i++;
}
close (DAR_LIST);
# set drop down list of backups
push @blabels, "0";
$blabels{"0"} = $c->l('ALL_BACKUPS');
$j = 0;
while ($j < $i)
{
push @blabels, $bknum[$j];
$blabels{$bknum[$j]} = $bkname[$j];
$j++
}
}
sub updateWorkstnBackupConfig {
my ($c) = @_;
my $status = $c->param('Workstnbackup') || "";
my $inconly = $c->param('IncOnlyTimeout');
my $dof = $c->param('Dof');
my $ampm;
my $incOnlyTimeout;
my $rec = $cdb->get('backupwk');
unless (defined $rec) {
$rec = $cdb->new_record('backupwk', {type=>'service'});
}
my $backupwkMount = $rec->prop('Mount') || '/mnt/smb';
unless ( $status eq 'on') {
# set service to disabled
my $old = $cdb->get('UnsavedChanges')->value;
$rec->set_prop('status', 'disabled');
$cdb->get('UnsavedChanges')->set_value($old);
system("/sbin/e-smith/signal-event", "conf-backup") == 0
or die($c->l('bac_ERR_CONF_BACKUP')."\n");
return '#OK#'.$c->l('bac_SUCCESSFULLY_DISABLED_WORKSTN');
}
#--------------------------------------------------
# Untaint parameters and check for validity
#--------------------------------------------------
my $VFSType = $c->param('VFSType');
if ( $VFSType eq 'nousb') {
return $c->l('bac_ERR_NO_USB_DISK')
}
if ( $VFSType eq 'nomnt') {
return $c->l('bac_ERR_NO_MOUNTED_DISK')
}
my $backupwkStation = $c->param('BackupwkStation');
if ( $VFSType =~ m/usb|mnt/s) { $backupwkStation = 'localhost' }
if ($backupwkStation =~ /^\s*(\S+)\s*$/) {
$backupwkStation = $1;
} else {
$backupwkStation = "";
}
if ( $backupwkStation eq "" ) {
return $c->l('bac_ERR_INVALID_WORKSTN')
}
my $backupwkFolder = $c->param('BackupwkFolder');
if ($backupwkFolder =~ /^(.*)$/) {
$backupwkFolder = $1;
} else {
$backupwkFolder = '';
}
if ( $VFSType eq 'usb' ) {
$backupwkFolder = 'media/' . $backupwkFolder;
}
if ( $VFSType eq 'mnt' ) {
$backupwkMount = $backupwkFolder;
if (checkMount ($backupwkMount)){$backupwkFolder = '';}
} else {
$backupwkFolder =~ s/^\///; # remove leading /
}
if ( $backupwkFolder eq '') {
return $c->l('bac_ERR_INVALID_FOLDER')
}
my $backupwkLogin = $c->param('BackupwkLogin') || '';
if ($backupwkLogin =~ /^(.*)$/) {
$backupwkLogin = $1;
} else {
$backupwkLogin = "";
}
if ( ( $backupwkLogin eq "" ) && ( $VFSType eq 'cifs' ) ) {
return $c->l('bac_ERR_INVALID_LOGIN')
}
my $backupwkPassword = $c->param('BackupwkPassword') || '';
if ($backupwkPassword =~ /^(.*)$/) {
$backupwkPassword = $1;
} else {
$backupwkPassword = "";
}
if ( ( $backupwkPassword eq "" ) && ( $VFSType eq 'cifs' ) ) {
return $c->l('bac_ERR_INVALID_PASSWORD')
}
my $setsNumber = $c->param('SetsNumber');
unless ( $setsNumber > 0 ) {
return $c->l('bac_ERR_INVALID_SETS_NUMBERFOLDER')
}
my $filesinset = $c->param('Filesinset');
unless ( $filesinset > 0 ) {
return $c->l('bac_ERR_INVALID_FILES_IN_SET_NUMBER')
}
my $timeout = $c->param('BackupwkTimeout');
if (( $timeout eq '') || ( $timeout == 0 )) {$timeout = 24 }
if (( $timeout < 1 ) || ( $timeout > 24 )) {
return $c->l('bac_ERR_INVALID_TIMEOUT')
}
if (defined $inconly && $inconly eq 'on') {
$incOnlyTimeout = 'yes';
} else {
$incOnlyTimeout = 'no';
}
my $compression = $c->param('Compression');
if (( $compression < 0 ) || ( $compression > 9 )) {
return $c->l('bac_ERR_INVALID_COMPRESSION')
}
$rec->set_prop('SmbHost', $backupwkStation);
$rec->set_prop('SmbShare', $backupwkFolder);
$rec->set_prop('Mount', $backupwkMount);
$rec->set_prop('Login', $backupwkLogin);
$rec->set_prop('Password', $backupwkPassword);
$rec->set_prop('SetsMax', $setsNumber);
$rec->set_prop('DaysInSet', $filesinset);
$rec->set_prop('Timeout', $timeout);
$rec->set_prop('IncOnlyTimeout', $incOnlyTimeout);
$rec->set_prop('Compression', $compression);
$rec->set_prop('FullDay', $dof);
$rec->set_prop('VFSType', $VFSType);
my $module = $rec->prop('Program');
# The default workstation backup program is dar.
unless (defined $module) {
$module = 'dar';
} elsif ($module eq '') {
$module = 'dar';
}
$rec->set_prop('Program', $module);
my $backupwkHour = $c->param('BackupwkHour');
if ($backupwkHour =~ /^(.*)$/) {
$backupwkHour = $1;
} else {
$backupwkHour = '12';
}
if (($backupwkHour < 0) || ($backupwkHour > 12)) {
return $c->l('bac_ERR_INVALID_HOUR').$backupwkHour.
$c->l('bac_BETWEEN_0_AND_12')
}
my $backupwkMin = $c->param('BackupwkMin');
if ($backupwkMin =~ /^(.*)$/) {
$backupwkMin = $1;
} else {
$backupwkMin = '0';
}
if (($backupwkMin < 0) || ($backupwkMin > 59)) {
return $c->l('bac_ERR_INVALID_MINUTE').$backupwkMin.
$c->l('bac_BETWEEN_0_AND_59');
}
$backupwkMin = sprintf("%02d", $backupwkMin);
$ampm = $c->param('BackupwkAMPM');
if ($ampm =~ /^(.*)$/) {
$ampm = $1;
} else {
$ampm = 'AM';
}
# convert to 24 hour time
$backupwkHour = $backupwkHour % 12;
if ($ampm eq 'PM') {
$backupwkHour = $backupwkHour + 12;
}
# variables passed validity checks, set configuration database values
my $old = $cdb->get('UnsavedChanges')->value;
$rec->set_prop('status', 'enabled');
$rec->set_prop('BackupTime', "$backupwkHour:$backupwkMin");
$cdb->get('UnsavedChanges')->set_value($old);
system("/sbin/e-smith/signal-event", "conf-backup") == 0
or die($c->l('bac_ERR_CONF_BACKUP'),"\n");
# we test if the remote host is reachable, else we simply display a warning
if ( $VFSType =~ m/cifs|nfs/s ) {
my $error_message = vmount($backupwkStation,$backupwkFolder,$backupwkMount,$VFSType);
if (! $error_message) {
$c->bunmount($backupwkMount,$VFSType);
} elsif ($error_message) {
return $c->l('bac_ERROR_WHEN_TESTING_REMOTE_SERVER'). "<br>$error_message";
}
}
return '#OK#'.$c->l('bac_SUCCESSFULLY_ENABLED_WORKSTN'). '<br>'.
$c->l('bac_WITH_BACKUP_TIME')." $backupwkHour:$backupwkMin";
}
sub performWorkstnSelRestore {
my ($c, $seldatebefore, $restorefiles) = @_;
my $out;
my @restorelist;
foreach ( @{$restorefiles} ) {
push @restorelist, "\"".$1."\"" if ($_ =~ /^(.*)$/);
}
my $backupwkrec = $cdb->get('backupwk');
my $id = $backupwkrec->prop('Id') ||
$cdb->get('SystemName')->value . "." . $cdb->get('DomainName')->value;
my $mntdir = $backupwkrec->prop('Mount') || '/mnt/smb';
my $VFSType = $backupwkrec->prop('VFSType') || 'cifs';
my $smbhost = $backupwkrec->prop('SmbHost');
my $smbshare = $backupwkrec->prop('SmbShare');
my $err;
my $error_message;
$mntdir = "/$smbshare" if ( $VFSType eq 'usb' );
# Mounting backup shared folder
$error_message = $c->bmount($mntdir,$smbhost,$smbshare,$VFSType);
if ($error_message) {
$error_message .= " : $id";
return $error_message;
}
# Test if backup subdirectory for our server
my $mntbkdir = $mntdir . "/$id";
unless (-d $mntbkdir) {
$error_message = $c->l('bac_ERR_NO_HOST_DIR')."\n";
$error_message .= $c->bunmount($mntdir,$VFSType);
$error_message .= " : $id";
return $error_message;
}
# backup is online, restoring now
$| = 1;
my $restorerr;
if (open(RD, "-|")) {
#-----------------------------------------------------
# restore system from uploaded workstation backup file
#-----------------------------------------------------
$out .= "<b>". $c->l('bac_FILES_HAVE_BEEN_RESTORED') ."</b> \n";
$out .= '<UL>';
while (<RD>) {
$out .= "<li>$_</li>\n";
}
$out .= '</UL>';
my $message;
if (!close RD) {
$message = $c->l('bac_RESTORE_FAILED_MSG');
} else {
if ($restorerr) {
$message = $c->l('bac_RESTORE_FAILED');
} else {
$message = $c->l('bac_RESTORE_COMPLETE');
}
}
$out .= "<b>$message </b>\n";
} else {
select(STDOUT);
$| = 1;
if ($seldatebefore) {
$restorerr = system("/usr/bin/dar_manager -B \"$mntbkdir/dar-catalog\" -Q -w $seldatebefore -e '-v -N -R / -w' -r @restorelist");
} else {
$restorerr = system("/usr/bin/dar_manager -B \"$mntbkdir/dar-catalog\" -Q -k -e '-v -N -R / -w' -r @restorelist");
}
$error_message = $c->bunmount($mntdir,$VFSType);
die($error_message) if $error_message;
exit(0);
}
return "#OK#" . $out;
}
sub performReboot {
my ($c) = @_;
#print "$c->l('bac_SERVER_REBOOT')";
#print "$c->l('bac_SERVER_WILL_REBOOT')";
esmith::util::backgroundCommand( 2, "/sbin/e-smith/signal-event", "reboot" );
return "#OK#" . $c->l('bac_SERVER_WILL_REBOOT');
}
sub get_VFSType_options {
my $c = shift;
return [[ $c->l('cifs') => 'cifs' ], [ $c->l('nfs') => 'nfs' ],
[ $c->l('local removable disk') => 'usb' ], [ $c->l('Mounted disk') => 'mnt' ]];
}
sub get_dow_list {
my $c = shift;
my @list = ();
my @dlabels = split(' ', $c->l('bac_DOW'));
my $i = 0;
foreach (@dlabels) {
push @list, [ "$_" => "$i" ];
$i++;
}
# put 'everyday' first
my $lastr = pop @list;
unshift @list, $lastr;
return \@list;
}
sub get_BackupwkDest_options {
my ($c, $VFSType) = @_;
my @usbdisks = ();
if ( $VFSType eq 'usb' ) {
my $devices = esmith::BlockDevices->new ('allowmount' => 'disabled');
my ($valid, $invalid) = $devices->checkBackupDrives(0);
if ( ${$valid}[0] ) {
foreach ( @{$valid} ) {
push @usbdisks, $devices->label($_);
}
}
if (!$usbdisks[0]){
push (@usbdisks,$c->l('bac_No suitable local devices found'));
}
$devices->destroy;
#foreach my $udi (qx(hal-find-by-property --key volume.fsusage --string filesystem)) {
#$udi =~ m/^(\S+)/;
#my $is_mounted = qx(hal-get-property --udi $1 --key volume.is_mounted);
#if ($is_mounted eq "false\n") {
#my $vollbl = qx(hal-get-property --udi $1 --key volume.label);
#$vollbl =~ m/^(\S+)/;
#if ($vollbl =~ /^\s/) {$vollbl = 'nolabel';}
#chomp $vollbl;
#push @usbdisks, $vollbl;
#}
#}
# return undef unless ($usbdisks[0]);
}
if ( $VFSType eq 'mnt' ) {
@usbdisks = findmnt ();
# return undef unless ($usbdisks[0]);
}
return \@usbdisks
}
sub get_function_options {
my $c = shift;
return [[ $c->l('bac_DESKTOP_BACKUP') => 'desktop_backup' ],
[ $c->l('bac_TAPE_CONFIGURE') => 'tape_configure' ],
[ $c->l('bac_TAPE_RESTORE') => 'tape_restore' ],
[ $c->l('bac_WORKSTN_CONFIGURE') => 'workstn_configure' ],
[ $c->l('bac_WORKSTN_VERIFY') => 'workstn_verify' ],
[ $c->l('bac_WORKSTN_RESTORE') => 'workstn_restore' ],
[ $c->l('bac_WORKSTN_SEL_RESTORE') => 'workstn_sel_restore']];
}
sub get_shared_folder_to_verify () {
my ($c) = @_;
my $rec = $cdb->get('backupwk');
return undef unless $rec;
my $id = $rec->prop('Id') || $cdb->get('SystemName')->value . "." . $cdb->get('DomainName')->value;
my $smbhost = $rec->prop('SmbHost');
my $smbshare = $rec->prop('SmbShare');
return "$smbhost/$smbshare/$id";
}
sub get_Backupset_options () {
my ($c) = @_;
my $rec = $cdb->get('backupwk');
return undef unless $rec;
my %backupfiles = ();
my $mntdir = $rec->prop('Mount') || '/mnt/smb';
my $id = $rec->prop('Id') || $cdb->get('SystemName')->value . "." . $cdb->get('DomainName')->value;
my $smbhost = $rec->prop('SmbHost');
my $smbshare = $rec->prop('SmbShare');
my $mntbkdir;
my $key;
my $VFSType = $rec->prop('VFSType') || 'cifs';
my $err;
$mntdir = "/$smbshare" if ( $VFSType eq 'usb' );
my $setbackuplist = sub {
if ( $_ =~ /\.dar/ ) {
my $dir = $File::Find::dir;
my $backupref;
$dir =~ s/$mntbkdir\///;
$_ =~ s/\..*\.dar//;
$backupref = $_;
$_ =~ s/.*-//;
@{$backupfiles{$_}}[0] = $dir;
@{$backupfiles{$_}}[1] = $backupref;
}
};
# Mounting backup shared folder
my $error_message = $c->bmount($mntdir,$smbhost,$smbshare,$VFSType);
return [] if $error_message;
# Test if backup subdirectory for our server
$mntbkdir = $mntdir . "/$id";
unless (-d $mntbkdir) {
$error_message .= $c->bunmount($mntdir,$VFSType);
return [];
}
# Finding existing backups
find { wanted => \&$setbackuplist, untaint => 1, untaint_pattern => qr|^([-+@\w\s./]+)$|}, $mntbkdir ;
my %blabels = ();
my @list;
foreach $key (sort keys %backupfiles) {
my $labkey = $mntbkdir . '/' . $backupfiles{$key}[0] . '/' . $backupfiles{$key}[1];
$blabels{$labkey} = $backupfiles{$key}[1] . " (" . $backupfiles{$key}[0] . ")";
push @list, [ "$blabels{$labkey}" => "$labkey" ];
}
$error_message .= $c->bunmount($mntdir,$VFSType);
return \@list;
}
sub get_Restoreset_options () {
my ($c) = @_;
my $rec = $cdb->get('backupwk');
return [] unless $rec;
my %backupfiles = ();
my $mntdir = $rec->prop('Mount') || '/mnt/smb';
my $id = $rec->prop('Id') || $cdb->get('SystemName')->value . "." . $cdb->get('DomainName')->value;
my $smbhost = $rec->prop('SmbHost');
my $smbshare = $rec->prop('SmbShare');
my $mntbkdir;
my $key;
my $VFSType = $rec->prop('VFSType') || 'cifs';
my $err;
$mntdir = "/$smbshare" if ( $VFSType eq 'usb' );
my $setbackuplist = sub {
if ( $_ =~ /\.dar/ ) {
my $dir = $File::Find::dir;
my $backupref;
$dir =~ s/$mntbkdir\///;
$_ =~ s/\..*\.dar//;
$backupref = $_;
$_ =~ s/.*-//;
@{$backupfiles{$_}}[0] = $dir;
@{$backupfiles{$_}}[1] = $backupref;
}
};
# Mounting backup shared folder
my $error_message = $c->bmount($mntdir,$smbhost,$smbshare,$VFSType);
return [] if $error_message;
# Test if backup subdirectory for our server
$mntbkdir = $mntdir . "/$id";
unless (-d $mntbkdir) {
$error_message .= $c->bunmount($mntdir,$VFSType);
return [];
}
my $catalog = "$mntbkdir/dar-catalog";
my $i = 0; my $j = 0;
my @bknum; my @setd; my @bkname;
# update backups list from current catalog
open(DAR_LIST, "/usr/bin/dar_manager -B $catalog -l |") ;
$i = 0;
while (<DAR_LIST>) {
next unless m/set/;
chomp;
($bknum[$i], $setd[$i], $bkname[$i]) = split(' ', $_, 3);
$i++;
}
close (DAR_LIST);
my @list;
# set drop down list of backups
push @list, [ $c->l('bac_ALL_BACKUPS') => "0" ];
$j = 0;
while ($j < $i) {
push @list, [ $bkname[$j] => "$bknum[$j]" ];
$j++
}
$error_message .= $c->bunmount($mntdir,$VFSType);
return \@list;
}
sub get_Restorefiles_options {
my ($c, $filterexp, $backupkey) = @_;
my $rgfilter;
if ($filterexp =~ /^(.*)$/) {
$filterexp = $1;
$rgfilter = qr/$filterexp/;
} else {
$filterexp = "";
}
if ($backupkey =~ /^(.*)$/) {
$backupkey = $1;
} else {
die('Unsecure data : ' . $backupkey);
}
my $seldatebf;
my $backupwkrec = $cdb->get('backupwk');
my $smbhost = $backupwkrec->prop('SmbHost');
my $smbshare = $backupwkrec->prop('SmbShare');
my $mntdir = $backupwkrec->prop('Mount') || '/mnt/smb';
my $key;
my $id = $backupwkrec->prop('Id') ||
$cdb->get('SystemName')->value . "." . $cdb->get('DomainName')->value;
my @flabels;
my %flabels = ();
my $VFSType = $backupwkrec->prop('VFSType') || 'cifs';
my $err;
my $error_message;
$mntdir = "/$smbshare" if ( $VFSType eq 'usb' );
# Mounting backup shared folder
$error_message = $c->bmount($mntdir,$smbhost,$smbshare,$VFSType);
if ($error_message) {
warn "Backup - restore files: $error_message, $id \n";
return undef;
}
# Test if backup subdirectory for our server
my $mntbkdir = $mntdir . "/$id";
unless (-d $mntbkdir) {
$error_message = $c->l('bac_ERR_NO_HOST_DIR')."\n";
$error_message .= $c->bunmount($mntdir,$VFSType);
warn "Backup - restore files: $error_message, $id \n";
return undef;
}
# Read wanted file list from selected backup
if (open(RD, "-|")) {
my $regex = qr/\[.*\] */;
while (<RD>) {
chomp;
$_ =~ s/$regex//;
if ($filterexp) {next unless m/$rgfilter/};
push @flabels, $_;
}
my $status = close RD ?
$c->l('bac_READ_COMPLETE')
: ($c->l('bac_ERROR_READING_FILE').' : '.$backupkey);
} else {
select(STDOUT);
$| = 1;
system("/usr/bin/dar_manager", "-B", "$mntbkdir/dar-catalog", "-u", "$backupkey") == 0
or die ($c->l('bac_ERR_EXTRACT')." : ".$!);
$error_message = $c->bunmount($mntdir,$VFSType);
die($error_message) if $error_message;
exit(0);
}
$error_message .= $c->bunmount($mntdir,$VFSType);
return \@flabels;
}
sub CalculateSizes () {
my $c = shift;
#------------------------------------------------------------
# figure out the size of the tar file.
#------------------------------------------------------------
my $tarsize = 0;
# It takes way too much time to do a du on /home/e-smith. So we'll
# estimate the current size.
# We do this by checking the quota used by each user on the system.
# Get a $dev value appropriate for use in Quota::query call.
my $dev = Quota::getqcarg("/home/e-smith/files");
foreach my $user ($adb->users())
{
my $name = $user->key;
my $uid = getpwnam($name);
unless ($uid)
{
warn ($c->l('bac_NO_UID_FOR_NAME').$name."\n");
# We shouldn't ever get here. If we do, we can't get
# the quota value for this user, so we just skip to
# the next one.
next;
}
# Get current quota settings.
my ($blocks) = Quota::query($dev, $uid, 0);
$tarsize += $blocks;
}
# We add to this the size of root owned firectories, estimated using du.
# If this takes too long, then the admin only has his or
# herself to blame!
# Remove /home/e-smith from backup list, and make paths absolute
my @list = map { "/$_" } grep { !/home\/e-smith/ } @directories;
open(DU, "-|")
or exec '/usr/bin/du', '-s', @list;
while (<DU>) {
my ($du) = split(/\s+/);
$tarsize += $du;
}
close DU;
$tarsize = showSize($tarsize);
#------------------------------------------------------------
# figure out the size of the dump files
#------------------------------------------------------------
my $dumpsize = 0;
open(DF, "-|")
or exec '/bin/df', '-P', '-t', 'ext4', '-t', 'xfs';
while (<DF>) {
next unless (/^\//);
(undef, undef, my $s, undef) = split(/\s+/, $_);
$dumpsize += $s;
}
# increase size by 10% to cope with dump overhead.
$dumpsize *= 1.1;
close DF;
$dumpsize = showSize($dumpsize);
#------------------------------------------------------------
# how much free space is in /tmp
#------------------------------------------------------------
my $tmpfree = 0;
my $halffree = 0;
open(DF, "-|")
or exec '/bin/df', '-P', '-t', 'ext4', '-t', 'xfs', '/tmp';
while (<DF>) {
next unless (/^\//);
(undef, undef, undef, my $s) = split(/\s+/, $_);
$tmpfree += $s;
}
close DF;
$halffree = $tmpfree / 2;
$tmpfree = showSize($tmpfree);
$halffree = showSize($halffree);
return ($tarsize, $dumpsize, $tmpfree, $halffree);
}
sub showSize {
# convert size to Mb or Gb or Tb :) Remember, df reports in kb.
my $size = shift;
my $Mb = 1024;
my $Gb = $Mb * $Mb;
my $Tb = $Mb * $Mb * $Mb;
if ($size >= $Tb) {
$size /= $Tb;
$size = int($size) . "Tb";
} elsif ($size >= $Gb) {
$size /= $Gb;
$size = int($size) . "Gb";
} elsif ($size >= $Mb) {
$size /= $Mb;
$size = int($size) . "Mb";
} else {
$size .= "kb";
}
return $size;
}
sub desktopBackupRecordStatus {
my ($backup, $phase, $status) = @_;
my $now = time();
warn("Backup terminated: $phase failed - status: $status\n");
$backup->set_prop('EndEpochTime', "$now");
$backup->set_prop('Result', "$phase:$status");
}
sub dmount {
# mount dar unit according to dar-workstation configuration
# return nothing if mount successfull
my ($host,$share,$mountdir,$login,$password,$VFSType) = @_;
if ($VFSType eq 'cifs')
{
return ( qx(/bin/mount -t cifs "//$host/$share" $mountdir -o credentials=/etc/dar/CIFScredentials,nounix 2>&1) );
}
elsif ($VFSType eq 'nfs') {
return ( qx(/bin/mount -t nfs -o nolock "$host:/$share" $mountdir 2>&1) );
}
elsif ($VFSType eq 'usb') {
my $device = "";
my $vollbl = "";
my $devices = esmith::BlockDevices->new ('allowmount' => 'disabled');
my ($valid, $invalid) = $devices->checkBackupDrives(0);
if ( ${$valid}[0] ) {
foreach ( @{$valid} ) {
$vollbl = $devices->label($_);
if ( $share eq "media/$vollbl" ) {
$device = "/dev/$_";
}
}
}
$devices->destroy;
return ( qx (mount $device /$share 2>&1) );
#-------------------------------------------------------------------------------------------------------
#my $device = "";
#my $blkdev = "";
#my $vollbl = "";
#foreach my $udi (qx(hal-find-by-property --key volume.fsusage --string filesystem)) {
#$udi =~ m/^(\S+)/;
#my $is_mounted = qx(hal-get-property --udi $1 --key volume.is_mounted);
#if ($is_mounted eq "false\n") {
#$blkdev = qx(hal-get-property --udi $1 --key block.device);
#if ($blkdev =~ m/^(\S+)/) {$blkdev = $1;}
#}
#if ($is_mounted eq "false\n") {
#$vollbl = qx(hal-get-property --udi $1 --key volume.label);
#$vollbl =~ m/^(\S+)/;
#if ($vollbl =~ /^\s/) {$vollbl = 'nolabel';}
#}
#chomp $vollbl;
#chomp $blkdev;
#$vollbl = "media/$vollbl";
#if ($vollbl eq $share) {
#$device = $blkdev;
#}
#}
#return ( qx(/bin/mount $device "/$share" 2>&1) );
#-------------------------------------------------------------------------------------------------------
} else {
return ("Error while mounting $host/$share : $VFSType not supported.\n");
}
}
sub checkMount {
# check if $mountdir is mounted
my $mountdir = shift;
$|=1; # Auto-flush
# copy STDOUT to another filehandle
open (my $STDOLD, '>&', STDOUT);
open(STDOUT, ">/dev/null");
if ( open(MOUNTDIR, "|-", "/bin/findmnt", $mountdir)){;}
# restore STDOUT
open (STDOUT, '>&', $STDOLD);
return (!close(MOUNTDIR));
}
sub bmount {
my ($c, $mntdir, $host, $share, $VFSType) = @_;
# verify backup directory not already mounted
if (!checkMount ($mntdir)) {
return if ($VFSType eq 'mnt');
return ($c->l('bac_ERR_ALREADY_MOUNTED'));
} else {
if ($VFSType eq 'mnt') {
return ($c->l('bac_ERR_NOT_MOUNTED'));
}
}
# create the directory mount point if it does not exist
my $err = createTree ($mntdir);
return ($c->l('bac_ERR_MOUNTING_SMBSHARE') . "<//$host/$share>\n" . $err) if $err;
# mount the backup directory
$err = dmount($host,$share,$mntdir,'','',$VFSType);
return ($c->l('bac_ERR_MOUNTING_SMBSHARE') . "<//$host/$share>\n" . $err) if $err;
# verify $mntdir is mounted
if (checkMount ($mntdir)) {
# The mount should have suceeded, but sometimes it needs more time,
# so sleep and then check again.
sleep 5;
if (checkMount ($mntdir)) {
return ($c->l('bac_ERR_NOT_MOUNTED'));
}
}
return;
}
sub bunmount {
my ($c, $mount, $type) = @_;
return if ($type eq 'mnt'); # Don't unmount for type 'mnt'
if (!checkMount ($mount)) {
system('/bin/umount', '-f', $mount) == 0
or return ($c->l('bac_ERR_WHILE_UNMOUNTING'));
}
return;
}
sub findmnt {
my @mntin = qx( findmnt -n -l -o TARGET );
my @mntout;
foreach my $mount (@mntin) {
next if ($mount =~ m/^\/proc|^\/dev|^\/sys|^\/boot/s);
chomp $mount;
next if ($mount eq '/');
push @mntout, $mount;
}
return @mntout;
}
sub createTree {
my $tree = shift;
if (! -d "$tree") {
eval {make_path("$tree")};
return ("Error while creating $tree : $@. Maybe insufficient rights directory.\n") if $@;
}
return;
}
sub vmount {
#Used to test if the remote share is mountable when you save settings in database
# mount dar unit according to dar-workstation configuration in order to test the remote host
# return nothing if mount successfull
my ($host,$share,$mountdir,$VFSType) = @_;
if ($VFSType eq 'cifs') {
return ( qx(/bin/mount -t cifs "//$host/$share" $mountdir -o credentials=/etc/dar/CIFScredentials,nounix 2>&1) );
} elsif ($VFSType eq 'nfs') {
return ( qx(/bin/mount -t nfs -o nolock,timeo=30,retrans=1,retry=0 "$host:/$share" $mountdir 2>&1) );
}
}
1;