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'); } ## end sub main 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"); } ## end if ($function eq 'desktop_backup') 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"; } } ## end if ($rec) ($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'; } ## end if ($bac_datas{backupHour...}) # 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'; } } ## end if ($function eq 'tape_configure') if ($function eq 'workstn_configure') { my $rec = $cdb->get('backupwk'); $bac_datas{vfstype} = $rec->prop('VFSType') || 'cifs'; $bac_datas{status} = $rec->prop('status'); } ## end if ($function eq 'workstn_configure') 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'; } ## end if ($rec) ($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'; } } ## end if ($function eq 'workstn_configure1') if ($function eq 'workstn_verify') { my $rec = $cdb->get('backupwk'); if ($rec) { $bac_datas{status} = $rec->prop('status') || 'disabled'; } } ## end if ($function eq 'workstn_verify') if ($function eq 'workstn_verify1') { $res = ''; if (!$result) { $bac_datas{function} = $function; } } ## end if ($function eq 'workstn_verify1') if ($function eq 'workstn_restore') { my $rec = $cdb->get('backupwk'); if ($rec) { $bac_datas{status} = $rec->prop('status') || 'disabled'; } } ## end if ($function eq 'workstn_restore') $dest = "back_$function"; $c->stash(error => $result); $c->stash(title => $title, bac_datas => \%bac_datas); return $c->render(template => $dest); } ## end sub do_display 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 ** !'; } ## end if ($function eq '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') . ' '; } ## end if (($reminderHour < 1...)) 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') . ' '; } ## end if (($reminderMin < 0)...) } 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; } ## end if (!$result) } ## end if (!$result) } ## end if ($function eq 'tape_configure') 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'); #} } ## end if (!$result) } ## end if ($function eq 'tape_restore') if ($function eq 'workstn_configure') { # should not happen !! $result .= ' ** Function error for workstation configure *** !'; } ## end if ($function eq 'workstn_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; } } ## end if (!$result) } ## end if ($function eq 'workstn_configure1') if ($function eq 'workstn_verify') { # should not happen !! $result .= ' ** Function error for workstation verify *** !'; } ## end if ($function eq 'workstn_verify') if ($function eq 'workstn_verify1') { ##$result .= ' ** Blocked for testing ** !'; $res = 'OK'; $result = ''; } ## end if ($function eq 'workstn_verify1') 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'; } ## end if (!$result) } ## end if ($function eq 'workstn_restore') 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); } } ## end if (!$result) } ## end if ($function eq 'workstn_restore1') 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'; } ## end if (!$result) } ## end if ($function eq 'workstn_sel_restore') 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'; } ## end if (!$result) } ## end if ($function eq 'workstn_sel_restore1') if ($function eq 'workstn_sel_restore2') { ##$result .= ' ** Blocked for testing 3 ** !'; $res = 'OK'; $result = ''; } ## end if ($function eq 'workstn_sel_restore2') # 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); } ## end if ($res ne 'OK') my $message = "'Backup' $function updates DONE"; $c->app->log->info($message); $c->flash(success => $result); $c->redirect_to('backup'); } ## end sub do_update 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'; } ## end else [ if (defined $status &&...)] return undef; } ## end sub tapeBackupConfig 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"); } ## end unless (system("/sbin/e-smith/signal-event"...)) 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"); } ## end unless (system("/sbin/e-smith/signal-event"...)) #---------------------------------------- # 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"); } ## end else [ if ($child = fork) ] } ## end sub tapeRestore 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'); } ## end if ($rec) if ($rec) { if ($VFSType eq 'usb') { $out .= $c->l('bac_WORKSTN_BACKUP_USB') . ' ' . $backupwkFolder . '
'; } elsif ($VFSType eq 'mnt') { $out .= $c->l('bac_WORKSTN_BACKUP_MNT') . ' ' . $backupwkMount . '
'; } else { $out .= $c->l('bac_WORKSTN_BACKUP_HOST') . ' ' . $backupwkStation . ' '; $out .= $c->l('bac_WORKSTN_BACKUP_VFSTYPE') . ' ' . $VFSType . '
'; $out .= $c->l('bac_WORKSTN_BACKUP_SHARE') . ' ' . $backupwkFolder . '
'; } if ($VFSType eq 'cifs') { $out .= $c->l('bac_LOGIN') . ' ' . $backupwkLogin . '
'; $out .= $c->l('PASSWORD') . ' ********
'; } $out .= $c->l('bac_WORKSTN_BACKUP_SETSNUM') . ' ' . $setsNumber . '
'; $out .= $c->l('bac_WORKSTN_BACKUP_DAYSINSET') . ' ' . $filesinset . '
'; $out .= $c->l('bac_WORKSTN_BACKUP_COMPRESSION') . ' ' . $compression . '
'; $out .= $c->l('bac_WORKSTN_BACKUP_TOD') . ' ' . $backupwkTime . '
'; $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 .= '
'; if ($dof eq '7') { $out .= $c->l('bac_WORKSTN_FULL_BACKUP_EVERYDAY') . '
'; } else { $out .= $c->l('bac_WORKSTN_FULL_BACKUP_DAY') . ' ' . $dlabels[$dof] . '
'; } } else { $out = $c->l('bac_WORKSTN_BACKUP_NOT_CONFIGURED'); } return $out; } ## end sub workstnBackupConfig 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; } ## end unless (-d $mntbkdir) 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; } ## end if ($_ =~ /\.dar/) }; # 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; } } ## end else [ if ($num == 0) ] $num++; last if ($backupsetfiles{$key} eq $verifyref); } ## end foreach $key (sort keys %backupsetfiles) if (open(RD, "-|")) { $out .= '' . $c->l('bac_TESTING_NEEDED_BACKUPS_FOR_RESTORE') . ''; my $message; $out .= ''; if (!close RD) { $out .= $c->l('bac_RESTORE_VERIFY_FAILED'); } else { $out .= $c->l('bac_VERIFY_COMPLETE'); } $out .= ''; } 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"); } ## end foreach $file (@restorefiles) $error_message = $c->bunmount($mntdir, $VFSType); die($error_message) if $error_message; exit(0); } ## end else [ if (open(RD, "-|")) ] #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 .= '' . $c->l('bac_FILES_IN_BACKUP') . ''; my $status = close RD ? ( $complete ? $c->l('bac_VERIFY_COMPLETE') : $c->l('bac_BACKUP_FILE_INCOMPLETE') ) : ($c->l('bac_ERROR_READING_FILE') . ' : ' . $backupkey); $out .= " $status "; } 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); } ## end else [ if (open(RD, "-|")) ] } ## end else [ if ($fullverify eq "on")] $error_message .= $c->bunmount($mntdir, $VFSType); return $out; } ## end sub workstnVerify 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; } ## end if ($_ =~ /\.dar/) }; 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')
($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"; } ## end unless (-d $mntbkdir) # 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"; } } ## end else [ if ($num == 0) ] $num++; last if ($backupsetfiles{$key} eq $restoreref); } ## end foreach $key (sort keys %backupsetfiles) # 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 .= ''; 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'); } } ## end else [ if (!close RD) ] 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"); } ## end foreach $file (@restorefiles) $error_message = $c->bunmount($mntdir, $VFSType); die($error_message) if $error_message; exit(0); } ## end else [ if (open(RD, "-|")) ] $rdb->reload; $error_message .= $c->bunmount($mntdir, $VFSType); return '#OK#' . $out; } ## end sub workstnRestore 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; } ## end if ($_ =~ /\.dar/) }; # 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); } ## end unless (-d $mntbkdir) 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 () { next unless m/set/; chomp; ($bknum[$i], $setd[$i], $bkname[$i]) = split(' ', $_, 3); $i++; } ## end while () 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++; } ## end while ($j < $i) } ## end sub workstnSelRestore 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'); } ## end unless ($status eq 'on') #-------------------------------------------------- # 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') . "
$error_message"; } } ## end if ($VFSType =~ m/cifs|nfs/s) return '#OK#' . $c->l('bac_SUCCESSFULLY_ENABLED_WORKSTN') . '
' . $c->l('bac_WITH_BACKUP_TIME') . " $backupwkHour:$backupwkMin"; } ## end sub updateWorkstnBackupConfig 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; } ## end unless (-d $mntbkdir) # backup is online, restoring now $| = 1; my $restorerr; if (open(RD, "-|")) { #----------------------------------------------------- # restore system from uploaded workstation backup file #----------------------------------------------------- $out .= "" . $c->l('bac_FILES_HAVE_BEEN_RESTORED') . " \n"; $out .= ''; 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'); } } ## end else [ if (!close RD) ] $out .= "$message \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); } ## end else [ if (open(RD, "-|")) ] return "#OK#" . $out; } ## end sub performWorkstnSelRestore 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'); } ## end sub performReboot 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' ] ]; } ## end sub get_VFSType_options 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; } ## end sub get_dow_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($_); } } ## end if (${$valid}[0]) 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]); } ## end if ($VFSType eq 'usb') if ($VFSType eq 'mnt') { @usbdisks = findmnt(); # return undef unless ($usbdisks[0]); } ## end if ($VFSType eq 'mnt') return \@usbdisks; } ## end sub get_BackupwkDest_options 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' ] ]; } ## end sub get_function_options 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"; } ## end sub get_shared_folder_to_verify 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; } ## end if ($_ =~ /\.dar/) }; # 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" ]; } ## end foreach $key (sort keys %backupfiles) $error_message .= $c->bunmount($mntdir, $VFSType); return \@list; } ## end sub get_Backupset_options 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; } ## end if ($_ =~ /\.dar/) }; # 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 () { next unless m/set/; chomp; ($bknum[$i], $setd[$i], $bkname[$i]) = split(' ', $_, 3); $i++; } ## end while () 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; } ## end sub get_Restoreset_options 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; } ## end unless (-d $mntbkdir) # Read wanted file list from selected backup if (open(RD, "-|")) { my $regex = qr/\[.*\] */; while () { chomp; $_ =~ s/$regex//; if ($filterexp) { next unless m/$rgfilter/ } push @flabels, $_; } ## end while () 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); } ## end else [ if (open(RD, "-|")) ] $error_message .= $c->bunmount($mntdir, $VFSType); return \@flabels; } ## end sub get_Restorefiles_options 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; } ## end unless ($uid) # Get current quota settings. my ($blocks) = Quota::query($dev, $uid, 0); $tarsize += $blocks; } ## end foreach my $user ($adb->users...) # 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 () { 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 () { next unless (/^\//); (undef, undef, my $s, undef) = split(/\s+/, $_); $dumpsize += $s; } ## end while () # 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 () { next unless (/^\//); (undef, undef, undef, my $s) = split(/\s+/, $_); $tmpfree += $s; } ## end while () close DF; $halffree = $tmpfree / 2; $tmpfree = showSize($tmpfree); $halffree = showSize($halffree); return ($tarsize, $dumpsize, $tmpfree, $halffree); } ## end sub CalculateSizes 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; } ## end sub showSize 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"); } ## end sub desktopBackupRecordStatus 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/$_"; } } ## end foreach (@{$valid}) } ## end if (${$valid}[0]) $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"); } } ## end sub dmount 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)); } ## end sub checkMount 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')); } } ## end else [ if (!checkMount($mntdir...))] # create the directory mount point if it does not exist my $err = createTree($mntdir); return ($c->l('bac_ERR_MOUNTING_SMBSHARE') . "\n" . $err) if $err; # mount the backup directory $err = dmount($host, $share, $mntdir, '', '', $VFSType); return ($c->l('bac_ERR_MOUNTING_SMBSHARE') . "\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')); } } ## end if (checkMount($mntdir...)) return; } ## end sub bmount 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; } ## end sub bunmount 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; } ## end foreach my $mount (@mntin) return @mntout; } ## end sub findmnt sub createTree { my $tree = shift; if (!-d "$tree") { eval { make_path("$tree") }; return ("Error while creating $tree : $@. Maybe insufficient rights directory.\n") if $@; } return; } ## end sub createTree 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)); } } ## end sub vmount 1;