#!/usr/bin/perl -wT #---------------------------------------------------------------------- # heading : Administration # description : Backup or restore # navigation : 4000 4200 # Copyright (C) 2002 Mitel Networks Corporation # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Technical support for this program is available from Mitel Networks # Please visit our web site www.mitel.com/sme/ for details. #---------------------------------------------------------------------- use strict; use esmith::FormMagick; use esmith::ConfigDB; use esmith::Backup; use esmith::BackupHistoryDB; use esmith::BlockDevices; use esmith::AccountsDB; use esmith::cgi; use esmith::util; use esmith::lockfile; use File::Find; use File::Path qw(make_path remove_tree); $File::Find::dont_use_nlink = 1; # fix for Windows shares my $fm = esmith::FormMagick->new(); # These statements _must_ come _after_ the FormMagick constructor. It sets # POST_MAX and DISABLE_UPLOADS to values that will cause this script to fail # on restores and verification of files. $CGI::POST_MAX = -1; # allow any size POST $CGI::DISABLE_UPLOADS = 0; # need to upload to restore from desktop $fm->parse_xml(); my $conf = esmith::ConfigDB->open() || die $fm->localise('CANNOT_OPEN').'configuration db'; my $restore = esmith::ConfigDB->open('/etc/e-smith/restore') || die $fm->localise('CANNOT_OPEN').'/etc/e-smith/restore'; 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"); #------------------------------------------------------------ # examine state parameter and display the appropriate form #------------------------------------------------------------ my $q = $fm->{cgi}; if (! grep (/^state$/, $q->param)) { showInitial ($q, ''); } elsif ($q->param ('state') eq "perform") { performAndShowResult ($q); } elsif ($q->param ('state') eq "tape-configure") { updateTapeBackupConfig($q); } elsif ($q->param ('state') eq "tape-restore") { performTapeRestore ($q); } elsif ($q->param ('state') eq "workstn-configure") { WorkstnBackupConfig1($q); } elsif ($q->param ('state') eq "workstn-configure1") { updateWorkstnBackupConfig($q); } elsif ($q->param ('state') eq "workstn-verify") { performWorkstnVerify($q); } elsif ($q->param ('state') eq "workstn-restore") { performWorkstnRestore ($q); } elsif ($q->param ('state') eq "workstn-sel-restore") { performWorkstnSelRestore ($q); } elsif ($q->param ('state') eq "workstn-sel-restore2") { performWorkstnSelRestore2 ($q); } else { esmith::cgi::genStateError ($q, undef); } exit (0); #------------------------------------------------------------ # subroutine to display initial form #------------------------------------------------------------ sub showInitial { my ($q, $msg) = @_; my $rec = $restore->get('restore'); if($msg eq '') { if($rec) { $msg = $rec->prop('errmsg') || ''; $rec->delete_prop('errmsg'); } } if ($msg eq '') { esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('BACKUP_TITLE')); } else { esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('OPERATION_STATUS_REPORT')); print $q->div ({-class => "sme-error"}, $msg); print $q->hr; } print $q->div({-class => 'error'}, $q->h5('Warning: a reboot is required before proceeding! Failure to reboot now may leave your system in an unknown state!')) if $conf->get_prop('bootstrap-console', 'Run') eq 'yes'; my ($tarsize, $dumpsize, undef, undef) = &CalculateSizes(); my $module = $conf->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"; } # Hack to evaluate embedded vars print eval 'return "'.$fm->localise('BACKUP_DESC_DAR').'\n";'; if ($tarsize =~ /Tb/ or $tarsize =~ /(\d+)Gb/ and $1 >= 2) { print $fm->localise("BACKUP_DESKTOP_TOO_BIG"), "\n"; } print $q->h2($fm->localise("BACKUP_CONFIG_STATUS")),"\n"; my $backup_status = $conf->get('backup'); if ($backup_status) { $backup_status = $backup_status->prop('status'); } if (defined $backup_status && $backup_status eq "enabled") { my $backupTime = $conf->get('backup')->prop('backupTime'); my $reminderTime = $conf->get('backup')->prop('reminderTime'); print $q->p ($fm->localise('TAPE_BACKUPS_ENABLED'), $fm->localise('BACKUPS_RUN_AT'),$q->b($backupTime), $fm->localise('REMINDER_MESSAGE_AT'),$q->b($reminderTime)), "\n"; } else { print $q->p($fm->localise('TAPE_BACKUPS_DISABLED')),"\n"; } my $backupwk_status = $conf->get('backupwk'); if ($backupwk_status) { $backupwk_status = $backupwk_status->prop('status'); } if (defined $backupwk_status && $backupwk_status eq "enabled") { my $backupwkTime = $conf->get('backupwk')->prop('BackupTime'); print $q->p ($fm->localise('WORKSTN_BACKUPS_ENABLED'), $fm->localise('WKBACKUPS_RUN_AT'),$q->b($backupwkTime)); } else { print $q->p($fm->localise('WORKSTN_BACKUPS_DISABLED')); } my %labels = ( "desktop-backup" => $fm->localise('DESKTOP_BACKUP'), "tape-configure" => $fm->localise('TAPE_CONFIGURE'), "tape-restore" => $fm->localise('TAPE_RESTORE'), "workstn-configure" => $fm->localise('WORKSTN_CONFIGURE'), "workstn-verify" => $fm->localise('WORKSTN_VERIFY'), "workstn-restore" => $fm->localise('WORKSTN_RESTORE'), "workstn-sel-restore" => $fm->localise('WORKSTN_SEL_RESTORE'), ); my @labels = ( 'desktop-backup', 'tape-configure', 'tape-restore', 'workstn-configure', 'workstn-verify', 'workstn-restore', 'workstn-sel-restore', ); my $default_action = 'desktop-backup'; my $restore_state; if($rec) { $restore_state = $rec->prop('state'); } unless (defined $restore_state) { # Undefined, set it to idle if ($rec) { $rec->reset_props(type=>'status', state=>'idle', finish=>0, start=>0); } else { $rec = $restore->new_record('restore', {type=>'status', state=>'idle', finish=>0, start=>0}); } $restore_state = 'idle'; } elsif ($restore_state eq 'running') { my $start = $rec->prop('start'); $start = scalar localtime($start); print $q->p ($fm->localise('RESTORE_IN_PROGRESS_BEGAN_AT') . "$start.\n" ); %labels = ( "refresh" => $fm->localise('REFRESH_THIS_DISPLAY'), "tape-configure" => $fm->localise('CONFIGURE_TAPE_BACKUP'), "workstn-configure" => $fm->localise('CONFIGURE_WORKSTN_BACKUP'), ); @labels = ( 'refresh', 'tape-configure', 'workstn-configure', ); $default_action = 'refresh'; } elsif ($restore_state eq 'complete') { my $start = $rec->prop('start'); $start = scalar localtime($start); my $finish = $rec->prop('finish'); $finish = scalar localtime($finish); print $q->p ($fm->localise('RESTORE_COMPLETED'),$q->br(), $fm->localise('STARTED_AT'),$q->b($start),$q->br(), $fm->localise('FINISHED_AT'),$q->b($finish)),"\n"; print $q->p ( $q->b ($fm->localise('YOU_MUST_REBOOT') ) ),"\n"; $default_action = 'reboot'; } else { # Unknown state. Reset it to idle $rec->set_prop('state', 'idle'); $restore_state = 'idle'; } print $q->start_form( -method => 'POST', -action => $q->url (-absolute => 1) ),"\n"; print $q->start_table ({-class => "sme-noborders"}),"\n"; if ($default_action eq 'reboot') { print $q->start_table ({width => "100%", -class => "sme-noborders"}),"\n"; print esmith::cgi::genButtonRow( $q, $q->submit (-name => 'action', -value => $fm->localise('REBOOT')) ); # Put in a hidden widget to store the reboot value. print $q->hidden( -name => 'function', -value => 'reboot' ),"\n"; print $q->end_table,"\n"; } else { print $q->Tr( $q->td({-class => "sme-noborders-label"}, $fm->localise("SELECT_AN_ACTION")), $q->td({-class => "sme-noborders-content"}, $q->popup_menu ( -name => 'function', -values => [ @labels ], -default => $default_action, -labels => \%labels ) ) ),"\n"; } print $q->end_table,"\n"; if ($default_action ne 'reboot') { print $q->start_table ({width => "100%", -class => "sme-noborders"}), "\n"; print esmith::cgi::genButtonRow( $q, $q->submit (-name => 'action', -value => $fm->localise('PERFORM')) ); print $q->end_table,"\n"; } print $q->hidden ( -name => 'state', -override => 1, -default => 'perform' ),"\n"; print $q->end_form,"\n"; esmith::cgi::genFooter($fm); return; } sub performAndShowResult { my ($q) = @_; my $function = $q->param ('function'); if ($function eq 'refresh') { showInitial ($q, ''); } elsif ($function eq 'reboot') { performReboot(); } elsif ($function eq 'desktop-backup') { desktopBackup(); } elsif ($function eq 'tape-configure') { TapeBackupConfig ($q); } elsif ($function eq 'tape-restore') { tapeRestore(); } elsif ($function eq 'workstn-configure') { WorkstnBackupConfig ($q); } elsif ($function eq 'workstn-verify') { workstnVerify(); } elsif ($function eq 'workstn-restore') { workstnRestore(); } elsif ($function eq 'workstn-sel-restore') { workstnSelRestore(); } else { # Unknown function - refresh the screen anyway showInitial ($q, 'unknown'); } return; esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise("X_BACKUP_OR_RESTORE")); print $q->p ( $function ); esmith::cgi::genFooter($fm); } 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 desktopBackup () { # Generate a header that will trigger a download and send data as # an octet stream. my $backups = esmith::BackupHistoryDB->open; my $now = time(); my $backup_rec = $backups->new_record($now, { type => 'backup_record', BackupType => 'desktop', StartEpochTime => $now, }); my $CompressionLevel = $conf->get_prop("backupconsole", "CompressionLevel") || "-6"; my @exclude = map (" --exclude=$_",@backup_excludes); # Dump the current mysql tables so that they are part of the image. # The events handle cases where mysqld is not enabled, and/or is # not running. my $status = system("/sbin/e-smith/signal-event", "pre-backup", "desktop"); if ($status) { desktopBackupRecordStatus($backup_rec, 'pre-backup', $status); esmith::cgi::genHeaderNonCacheable( $fm->{cgi}, undef, $fm->localise('OPERATION_STATUS_REPORT')); esmith::cgi::genResult( $fm->{cgi}, $fm->localise('ERR_PRE_BACKUP')); return; } print "Expires: 0\n"; print "Content-type: application/octet-stream\n"; print "Content-disposition: attachment; filename=smeserver.tgz\n"; print "\n"; setpgrp; my $ourpgrp = getpgrp; local $SIG{PIPE} = sub { local $SIG{HUP} = 'IGNORE'; warn "Got sigpipe - sending HUP to $ourpgrp\n"; kill HUP => -$ourpgrp; desktopBackupRecordStatus($backup_rec, 'send2browser', 'Incomplete'); exit 1; }; open(RD, "/bin/tar --directory / --create @directories --file=-" . "@exclude | /usr/bin/gzip $CompressionLevel |" ); while () { print; } close RD; # Remove the dumped tables. $status = system("/sbin/e-smith/signal-event", "post-backup", "desktop"); if ($status) { desktopBackupRecordStatus($backup_rec, 'post-backup', $status); die ($fm->localise('ERR_POST_BACKUP'),"\n"); } $now = time(); $backup_rec->set_prop('EndEpochTime', "$now"); $backup_rec->set_prop('Result', "0"); } sub TapeBackupConfig { my ($q) = @_; my $enabledChk = ""; my $backupAMPM = 'AM'; my $backupMin; my $backupHour; my $reminderAMPM = 'AM'; my $reminderMin; my $reminderHour; esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('ENABLE_DISABLE_TAPE')); print $q->p ($fm->localise('TAPE_CONFIG_DESC')); # Obtain time for backup from the backup cron template my $rec = $conf->get('backup'); my $backupTime = "2:00"; if ($rec) { $backupTime = $rec->prop('backupTime') || "2:00"; } ($backupHour, $backupMin) = split (":", $backupTime, -1); if ($backupHour > 11) { if ($backupHour > 12) { $backupHour -= 12; } $backupAMPM = 'PM'; } # Obtain time for reminder notice from the backup cron template my $reminderTime = "14:00"; if ($rec) { $reminderTime = $rec->prop('reminderTime') || "14:00"; } ($reminderHour, $reminderMin) = split (":", $reminderTime, -1); if ($reminderHour > 12) { $reminderHour -= 12; $reminderAMPM = 'PM'; } print $q->start_form( -method => 'POST', -action => $q->url (-absolute => 1) ); print $q->start_table ({-class => "sme-noborders"}); my $backup_status; if ($rec) { $backup_status = $rec->prop('status'); } if (defined $backup_status && $backup_status eq "enabled") { $enabledChk = "checked"; } print $q->Tr( $q->td( $fm->localise('ENABLE_TAPE_BACKUP') . " " ) ); print $q->Tr( esmith::cgi::genCell( $q,$fm->localise('TAPE_BACKUP_TIME') ), esmith::cgi::genCell ($q, $q->textfield (-name => 'backupHour', -override => 1, -default => $backupHour, -size => 2)), esmith::cgi::genCell ($q, $q->textfield (-name => 'backupMin', -override => 1, -default => $backupMin, -size => 2)), esmith::cgi::genCell ($q, "AM/PM:"), esmith::cgi::genCell ($q, $q->popup_menu (-name => 'backupAMPM', -values => ['AM', 'PM'], -default => $backupAMPM))); my %timelabels=('AM' => $fm->localise('AM'),'PM' => $fm->localise('PM')); print $q->Tr( esmith::cgi::genCell( $q, $fm->localise('LOAD_TAPE_REMINDER_TIME') ), esmith::cgi::genCell( $q, $q->textfield( -name => 'reminderHour', -override => 1, -default => $reminderHour, -size => 2 ) ), esmith::cgi::genCell( $q, $q->textfield( -name => 'reminderMin', -override => 1, -default => $reminderMin, -size => 2 ) ), esmith::cgi::genCell($q, $fm->localise("AM/PM").":"), esmith::cgi::genCell( $q, $q->popup_menu( -name => 'reminderAMPM', -values => ['AM', 'PM'], -labels => \%timelabels, -default => $reminderAMPM ) ) ); print "\n"; print $q->start_table ({width => "100%", -class => "sme-noborders"}); print $q->Tr($q->th({-class => "sme-layout"}, $q->submit( -name => 'action', -value => $fm->localise('UPDATE_CONF') ) ) ); print $q->hidden( -name => 'state', -override => 1, -default => 'tape-configure' ); print $q->end_form; print ''; esmith::cgi::genFooter($fm); return; } sub updateTapeBackupConfig { my ($q) = @_; my $status = $q->param ('tapebackup'); my $ampm; esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('UPDATING_TAPE_CONF') ); if (defined $status && $status eq "on") { #-------------------------------------------------- # Untaint parameters and check for validity #-------------------------------------------------- my $backupHour = $q->param ('backupHour'); if ($backupHour =~ /^(.*)$/) { $backupHour = $1; } else { $backupHour = "12"; } if (($backupHour < 1) || ($backupHour > 12)) { esmith::cgi::genResult( $fm->{cgi}, $fm->localise('ERR_INVALID_HOUR').$backupHour. $fm->localise('BETWEEN_1_AND_12') ); return; } my $backupMin = $q->param ('backupMin'); if ($backupMin =~ /^(.*)$/) { $backupMin = $1; } else { $backupMin = "0"; } if (($backupMin < 0) || ($backupMin > 59)) { esmith::cgi::genResult( $fm->{cgi}, $fm->localise('ERR_INVALID_MINUTE').$backupMin. $fm->localise('BETWEEN_0_AND_59') ); return; } $backupMin = sprintf("%02d", $backupMin); $ampm = $q->param ('backupAMPM'); if ($ampm =~ /^(.*)$/) { $ampm = $1; } else { $ampm = "AM"; } # convert to 24 hour time $backupHour = $backupHour % 12; if ($ampm eq "PM") { $backupHour = $backupHour + 12; } my $reminderHour = $q->param ('reminderHour'); if ($reminderHour =~ /^(.*)$/) { $reminderHour = $1; } else { $reminderHour = "12"; } if (($reminderHour < 1) || ($reminderHour > 12)) { esmith::cgi::genResult( $fm->{cgi}, $fm->localise('ERR_INVALID_REMINDER_HOUR').$reminderHour .$fm->localise('BETWEEN_1_AND_12') ); return; } my $reminderMin = $q->param ('reminderMin'); if ($reminderMin =~ /^(.*)$/) { $reminderMin = $1; } else { $reminderMin = "0"; } if (($reminderMin < 0) || ($reminderMin > 59)) { esmith::cgi::genResult( $fm->{cgi}, $fm->localise('ERR_INVALID_REMINDER_MINUTE'). $reminderMin.$fm->localise('BETWEEN_0_AND_59') ); return; } $reminderMin = sprintf("%02d", $reminderMin); $ampm = $q->param ('reminderAMPM'); if ($ampm =~ /^(.*)$/) { $ampm = $1; } else { $ampm = "AM"; } # convert to 24 hour time $reminderHour = $reminderHour % 12; if ($ampm eq "PM") { $reminderHour = $reminderHour + 12; } # variables passed validity checks, set configuration database values my $old = $conf->get('UnsavedChanges')->value; my $rec = $conf->get('backup'); unless (defined $rec) { $rec = $conf->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"); $conf->get('UnsavedChanges')->set_value($old); system("/sbin/e-smith/signal-event", "conf-backup") == 0 or die($fm->localise('ERR_CONF_BACKUP'),"\n"); esmith::cgi::genResult( $fm->{cgi}, $fm->localise('SUCCESSFULLY_ENABLED_TAPE').$q->br(). $fm->localise('WITH_BACKUP_TIME')."$backupHour:$backupMin". $q->br().$fm->localise('WITH_REMINDER_TIME'). "$reminderHour:$reminderMin"); } else { # set service to disabled my $old = $conf->get('UnsavedChanges')->value; my $rec = $conf->get('backup'); unless ($rec) { $rec = $conf->new_record('backup', {type=>'service'}); } $rec->set_prop('status', 'disabled'); $conf->get('UnsavedChanges')->set_value($old); system("/sbin/e-smith/signal-event", "conf-backup") == 0 or die($fm->localise('ERR_CONF_BACKUP')."\n"); esmith::cgi::genResult( $fm->{cgi}, $fm->localise('SUCCESSFULLY_DISABLED') ); } return; } sub tapeRestore () { esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('RESTORE_CONF_FROM_TAPE')); print $fm->localise('RESTORE_CONF_FROM_TAPE_DESC'); print $q->start_multipart_form( -method => 'POST', -action => $q->url (-absolute => 1) ); print $q->start_table ({width => "100%", -class => "sme-noborders"}); print esmith::cgi::genButtonRow( $q, $q->submit (-name => 'action', -value => $fm->localise('RESTORE_FROM_TAPE')) ); print "\n"; print $q->hidden( -name => 'state', -override => 1, -default => 'tape-restore' ); print $q->end_form; esmith::cgi::genFooter($fm); } sub performTapeRestore { my ($q) = @_; #---------------------------------------- # restore system from backup tape #---------------------------------------- my $lock_file = "/var/lock/subsys/e-smith-restore"; my $file_handle = &esmith::lockfile::LockFileOrReturn($lock_file); unless ($file_handle) { esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('UNABLE_TO_RESTORE_CONF') ); print $q->p ( $q->b ($fm->localise('ANOTHER_RESTORE_IN_PROGRESS') ) ); esmith::cgi::genFooter($fm); return; } my $rec = $restore->get('restore'); $rec->set_prop('state', 'running'); $rec->set_prop('start', time); my $sec = 10; print "Refresh: $sec; URL=/server-manager/cgi-bin/backup\n"; esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise() ); print $q->p($fm->localise('NOW_RESTORING_FROM_TAPE') ); print $q->p ( $q->b ($fm->localise('MUST_REBOOT_AFTER_RESTORE')) ); print $q->p($fm->localise('PAGE_REFRESH_IN', {sec=>$sec})); my $child; if ($child = fork) { # Parent $SIG{'CHLD'} = 'IGNORE'; &esmith::lockfile::UnlockFile($file_handle); esmith::cgi::genFooter($fm); return; } 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', $fm->localise('ERR_PRE_RESTORE')); $rec->delete_prop('state'); die ($fm->localise('ERR_PRE_RESTORE'),"\n"); } unless(system("/sbin/e-smith/signal-event", "restore-tape") == 0) { $rec->set_prop('errmsg', $fm->localise('ERR_RESTORING_FROM_TAPE')); $rec->delete_prop('state'); die ($fm->localise('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').'
'. $fm->localise('ERR_RESTORING_GID')); warn ($fm->localise('ERR_RESTORING_GID')."\n"); } unless(system("/usr/sbin/usermod", "-g", "$www_gid", "www") == 0) { $rec->set_prop('errmsg', $rec->prop('errmsg').'
'. $fm->localise('ERR_RESTORING_INITIAL_GRP')); warn ($fm->localise('ERR_RESTORING_INITIAL_GRP')."\n"); } unless(system("/sbin/e-smith/signal-event", "post-upgrade") == 0) { $rec->set_prop('errmsg', $rec->prop('errmsg').'
'. $fm->localise('ERR_UPDATING_CONF_AFTER_TAPE_RESTORE')); $rec->delete_prop('state'); die ($fm->localise('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', $fm->localise('COULD_NOT_FORK')); die ($fm->localise("COULD_NOT_FORK")."$!\n"); } } sub WorkstnBackupConfig { my ($q) = @_; my $backupwk_status; my $enabledIncOnlyTimeout = ""; my $smbv1 = ""; 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 $compression; my $VFSType; my $dof; my @dlabels = split(' ', $fm->localise('DOW')); my @VFST = ('cifs', 'nfs', 'usb','mnt'); my %VFST = ('cifs', $fm->localise('cifs'), 'nfs', $fm->localise('nfs'), 'usb', $fm->localise('local removable disk'), 'mnt', $fm->localise('Mounted disk')); # Obtain backup informations from configuration my $rec = $conf->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'; $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'; $smbv1 = ( ($rec->prop('SmbV1') || 'disabled') eq "enabled" ) ? "enabled" : "disabled" ; $VFSType = $rec->prop('VFSType') || 'cifs'; $backupwk_status = $rec->prop('status'); } esmith::cgi::genHeaderNonCacheable($q, undef, $fm->localise('CONFIGURE_WORKSTN_BACKUP')); if ($rec) { print $fm->localise('WORKSTN_BACKUP_DESC'); print $fm->localise('WORKSTN_BACKUP_ENABLED'), $q->b(' '.$fm->localise(uc($backupwk_status))), '.
'; if ($VFSType eq 'usb') { print $fm->localise('WORKSTN_BACKUP_USB'), ' ', $backupwkFolder, '
'; } elsif ($VFSType eq 'mnt') { print $fm->localise('WORKSTN_BACKUP_MNT'), ' ', $backupwkMount, '
'; } else { print $fm->localise('WORKSTN_BACKUP_HOST'), ' ', $backupwkStation; print ' ', $fm->localise('WORKSTN_BACKUP_VFSTYPE'), ' ', $VFSType, '
'; print $fm->localise('WORKSTN_BACKUP_SHARE'), ' ', $backupwkFolder, '
'; } if ($VFSType eq 'cifs') { print $fm->localise('LOGIN'), ' ', $backupwkLogin, '
'; print $fm->localise('PASSWORD'), ' ********
'; print $fm->localise('SMBV1'), ' ', $smbv1, '
'; } print $fm->localise('WORKSTN_BACKUP_SETSNUM'), ' ', $setsNumber, '
'; print $fm->localise('WORKSTN_BACKUP_DAYSINSET'), ' ', $filesinset, '
'; print $fm->localise('WORKSTN_BACKUP_COMPRESSION'), ' ', $compression, '
'; print $fm->localise('WORKSTN_BACKUP_TOD'), ' ', $backupwkTime, '
'; print $fm->localise('WORKSTN_BACKUP_TIMEOUT'), ' ', $backupwkTimeout, ' ', $fm->localise('HOURS'); if ( $backupwkIncOnlyTimeout eq 'yes' ) { print $fm->localise('WORKSTN_BACKUP_INCONLY_TIMEOUT') } print '
'; if ( $dof eq '7' ) { print $fm->localise('WORKSTN_FULL_BACKUP_EVERYDAY', '
'); } else { print $fm->localise('WORKSTN_FULL_BACKUP_DAY'), ' ', $dlabels[$dof], '
'; } } else { print $fm->localise('WORKSTN_BACKUP_NOT_CONFIGURED'), '
' } print $q->start_form( -method => 'POST', -action => $q->url (-absolute => 1) ); print $q->start_table ({-class => "sme-noborders"}); print $q->Tr($q->td($q->h3 ($fm->localise('WORKSTATION_BACKUP_SETCONF')))); print $q->Tr( esmith::cgi::genCell( $q, $fm->localise('SELECT_VFS_TYPE') ), esmith::cgi::genCell ($q, $q->popup_menu (-name => 'VFSType', -values => [ @VFST ], -labels => \%VFST, -default => $VFSType))); print "\n"; print $q->start_table ({width => "100%", -class => "sme-noborders"}); print $q->Tr($q->th({-class => "sme-layout"}, $q->submit( -name => 'action', -value => $fm->localise('NEXT') ) ) ); print $q->hidden( -name => 'state', -override => 1, -default => 'workstn-configure' ); print ''; print $q->end_form; esmith::cgi::genFooter ($q); return; } sub WorkstnBackupConfig1 { my ($q) = @_; my $enabledChk = ""; my $enabledIncOnlyTimeout = ""; my $backupwkAMPM = 'AM'; my $backupwkMin; my $backupwkHour; my $backupwkLogin = 'backup'; my $backupwkPassword = 'backup'; my $backupwkStation = 'host'; my $backupwkFolder = 'share'; my $backupwkMount = ''; my $setsNumber; my $filesinset; my $backupwkTimeout; my $backupwkIncOnlyTimeout; my $compression; my $VFSType = $q->param ('VFSType'); my $error=""; my $dof; my $smbv1 = ''; my @usbdisks; my %dlabels = (); my @dlabels = split(' ', $fm->localise('DOW')); my $i = 0; foreach (@dlabels) { $dlabels{$i} = $_; $i++; } # Obtain backup informations from configuration my $rec = $conf->get('backupwk'); my $backupwkTime = '2:00'; 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') || ''; $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'; $smbv1 = ( ($rec->prop('SmbV1') || 'disabled') eq "enabled" ) ? "checked" : "" ; } ($backupwkHour, $backupwkMin) = split (':', $backupwkTime, -1); if ($backupwkHour > 12) { $backupwkHour -= 12; $backupwkAMPM = 'PM'; } my $backupwk_status; if ($rec) { $backupwk_status = $rec->prop('status'); } if (defined $backupwk_status && $backupwk_status eq 'enabled') { $enabledChk = 'checked'; } if (defined $backupwkIncOnlyTimeout && $backupwkIncOnlyTimeout eq 'yes') { $enabledIncOnlyTimeout = 'checked'; } esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('CONFIGURE_WORKSTN_BACKUP')); 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($_); } } else { push @usbdisks, $fm->localise('ERR_NO_USB_DISK'); $error='nousb'; } $devices->destroy; } if ( $VFSType eq 'mnt' ) { @usbdisks = findmnt (); unless ($usbdisks[0]){ push @usbdisks, $fm->localise('ERR_NO_MOUNTED_DISK'); $error='nomnt'; } } print $q->start_form( -method => 'POST', -action => $q->url (-absolute => 1) ); print $fm->localise('CONFIGURE_WORKSTN_BACKUP_DESC'); print $q->start_table ({-class => "sme-noborders"}); print $q->Tr( $q->td( $fm->localise('ENABLE_WORKSTN_BACKUP') . " " ) ); print $q->Tr($q->td($q->h3 ($fm->localise('WORKSTATION_BACKUP_DEST')))); if ( $VFSType =~ m/cifs|nfs/s ) { print $q->Tr(esmith::cgi::genCell($q,$fm->localise('WORKSTN_NAME')), esmith::cgi::genCell ($q, $q->textfield (-name => 'backupwkStation', -override => 1, -default => $backupwkStation, -size => 20)), ); } if ( $VFSType eq 'usb' ) { print $q->Tr(esmith::cgi::genCell($q,$fm->localise('local removable disk')), esmith::cgi::genCell ($q, $q->popup_menu (-name => 'backupwkFolder', -values => [ @usbdisks ], -default => $backupwkFolder,)), ); } elsif ($VFSType eq 'mnt') { print $q->Tr(esmith::cgi::genCell($q,$fm->localise('Mounted disk')), esmith::cgi::genCell ($q, $q->popup_menu (-name => 'backupwkFolder', -values => [ @usbdisks ], -default => $backupwkMount,)), ); } else { print $q->Tr(esmith::cgi::genCell($q,$fm->localise('SHARED_FOLDER_NAME')), esmith::cgi::genCell ($q, $q->textfield (-name => 'backupwkFolder', -override => 1, -default => $backupwkFolder, -size => 20)), ); } if ( $VFSType eq 'cifs' ) { print $q->Tr( esmith::cgi::genCell( $q, $fm->localise('WORKSTN_LOGIN') ), esmith::cgi::genCell ($q, $q->textfield (-name => 'backupwkLogin', -override => 1, -default => $backupwkLogin, -size => 12)), ); print $q->Tr( esmith::cgi::genCell( $q, $fm->localise('PASSWORD') ), esmith::cgi::genCell ($q, $q->password_field (-name => 'backupwkPassword', -override => 1, -default => $backupwkPassword, -size => 20)), ); #smbv1 print $q->Tr( esmith::cgi::genCell( $q, $fm->localise('SMBV1') ), esmith::cgi::genCell ( $q, " " ), ); } print ''; print $q->table ({border => 0, cellspacing => 1, cellpadding => 4}); print $q->Tr($q->td({-colspan=>4},$q->h3 ($fm->localise('WORKSTN_BACKUP_SETTINGS')))); print $q->Tr( esmith::cgi::genCell( $q, $fm->localise('NUMBER_OF_SETS') ), esmith::cgi::genCell ($q, $q->textfield (-name => 'setsNumber', -override => 1, -default => $setsNumber, -size => 3)), esmith::cgi::genCell ($q, $fm->localise('NUMBER_OF_FILES_IN_SET')), esmith::cgi::genCell ($q, $q->textfield (-name => 'filesinset', -override => 1, -default => $filesinset, -size => 3)) ); print $q->Tr( esmith::cgi::genCell( $q, $fm->localise('WORKSTN_BACKUP_TIME') ), esmith::cgi::genCell ($q, $q->textfield (-name => 'backupwkHour', -override => 1, -default => $backupwkHour, -size => 2)), esmith::cgi::genCell ($q, $q->textfield (-name => 'backupwkMin', -override => 1, -default => $backupwkMin, -size => 2)), esmith::cgi::genCell ($q, $q->popup_menu (-name => 'backupwkAMPM', -values => ['AM', 'PM'], -default => $backupwkAMPM))); print $q->Tr( esmith::cgi::genCell( $q, $fm->localise('WORKSTN_TIMEOUT') ), esmith::cgi::genCell ($q, $q->textfield (-name => 'backupwkTimeout', -override => 1, -default => $backupwkTimeout, -size => 2)), esmith::cgi::genCell( $q, $fm->localise('INC_ONLY_TIMEOUT') ), esmith::cgi::genCell ( $q, " " ), ); print $q->Tr( esmith::cgi::genCell( $q, $fm->localise('COMPRESSION_LEVEL') ), esmith::cgi::genCell ($q, $q->textfield (-name => 'compression', -override => 1, -default => $compression, -size => 1)), esmith::cgi::genCell( $q, $fm->localise('FULL_ONLY_ON') ), esmith::cgi::genCell ( $q, $q->popup_menu ( -name => 'dof', -values => [ '7', '0', '1', '2', '3', '4', '5', '6' ], -labels => \%dlabels, -default => $dof)) ); print "\n"; if ($error =~ m/nousb|nomnt/s) {$VFSType = $error;} print $q->start_table ({width => "100%", -class => "sme-noborders"}); print $q->Tr($q->th({-class => "sme-layout"}, $q->submit( -name => 'action', -value => $fm->localise('UPDATE_CONF') ) ) ); print $q->hidden( -name => 'state', -override => 1, -default => 'workstn-configure1' ); print $q->hidden( -name => 'VFSType', -override => 1, -default => $VFSType ); print ''; print $q->end_form; esmith::cgi::genFooter ($q); return; } sub updateWorkstnBackupConfig { my ($q) = @_; my $status = $q->param ('workstnbackup') || ""; my $inconly = $q->param ('incOnlyTimeout'); my $smbv1 = $q->param ('smbv1'); my $dof = $q->param('dof'); my $ampm; my $incOnlyTimeout; esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('UPDATING_WORKSTN_CONF') ); my $rec = $conf->get('backupwk'); unless (defined $rec) { $rec = $conf->new_record('backupwk', {type=>'service'}); } my $backupwkMount = $rec->prop('Mount') || '/mnt/smb'; unless ( $status eq 'on') { # set service to disabled my $old = $conf->get('UnsavedChanges')->value; $rec->set_prop('status', 'disabled'); $conf->get('UnsavedChanges')->set_value($old); system("/sbin/e-smith/signal-event", "conf-backup") == 0 or die($fm->localise('ERR_CONF_BACKUP')."\n"); esmith::cgi::genResult($q, $fm->localise('SUCCESSFULLY_DISABLED_WORKSTN')); return; } #-------------------------------------------------- # Untaint parameters and check for validity #-------------------------------------------------- my $VFSType = $q->param ('VFSType'); if ( $VFSType eq 'nousb') { esmith::cgi::genResult($q, $fm->localise('ERR_NO_USB_DISK')); return; } if ( $VFSType eq 'nomnt') { esmith::cgi::genResult($q, $fm->localise('ERR_NO_MOUNTED_DISK')); return; } my $backupwkStation = $q->param ('backupwkStation'); if ( $VFSType =~ m/usb|mnt/s) { $backupwkStation = 'localhost' } if ($backupwkStation =~ /^\s*(\S+)\s*$/) { $backupwkStation = $1; } else { $backupwkStation = ""; } if ( $backupwkStation eq "" ) { esmith::cgi::genResult( $q, $fm->localise('ERR_INVALID_WORKSTN') ); return; } my $backupwkFolder = $q->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 '') { esmith::cgi::genResult($q, $fm->localise('ERR_INVALID_FOLDER')); return; } my $backupwkLogin = $q->param ('backupwkLogin') || ''; if ($backupwkLogin =~ /^(.*)$/) { $backupwkLogin = $1; } else { $backupwkLogin = ""; } if ( ( $backupwkLogin eq "" ) && ( $VFSType eq 'cifs' ) ) { esmith::cgi::genResult( $q, $fm->localise('ERR_INVALID_LOGIN') ); return; } my $backupwkPassword = $q->param ('backupwkPassword') || ''; if ($backupwkPassword =~ /^(.*)$/) { $backupwkPassword = $1; } else { $backupwkPassword = ""; } if ( ( $backupwkPassword eq "" ) && ( $VFSType eq 'cifs' ) ) { esmith::cgi::genResult( $q, $fm->localise('ERR_INVALID_PASSWORD') ); return; } my $setsNumber = $q->param ('setsNumber'); unless ( $setsNumber > 0 ) { esmith::cgi::genResult( $q, $fm->localise('ERR_INVALID_SETS_NUMBER') ); return; } my $filesinset = $q->param ('filesinset'); unless ( $filesinset > 0 ) { esmith::cgi::genResult( $q, $fm->localise('ERR_INVALID_FILES_IN_SET_NUMBER') ); return; } my $timeout = $q->param ('backupwkTimeout'); if (( $timeout eq '') || ( $timeout == 0 )) {$timeout = 24 } if (( $timeout < 1 ) || ( $timeout > 24 )) { esmith::cgi::genResult( $q, $fm->localise('ERR_INVALID_TIMEOUT') ); return; } if (defined $inconly && $inconly eq 'on') { $incOnlyTimeout = 'yes'; } else { $incOnlyTimeout = 'no'; } if (defined $smbv1 && $smbv1 eq 'on') { $smbv1 = 'enabled'; } else { $smbv1 = 'disabled'; } my $compression = $q->param ('compression'); if (( $compression < 0 ) || ( $compression > 9 )) { esmith::cgi::genResult( $q, $fm->localise('ERR_INVALID_COMPRESSION') ); return; } $rec->set_prop('SmbHost', $backupwkStation); $rec->set_prop('SmbShare', $backupwkFolder); $rec->set_prop('SmbV1', $smbv1); $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 = $q->param ('backupwkHour'); if ($backupwkHour =~ /^(.*)$/) { $backupwkHour = $1; } else { $backupwkHour = '12'; } if (($backupwkHour < 0) || ($backupwkHour > 12)) { esmith::cgi::genResult( $q, $fm->localise('ERR_INVALID_HOUR').$backupwkHour. $fm->localise('BETWEEN_0_AND_12') ); return; } my $backupwkMin = $q->param ('backupwkMin'); if ($backupwkMin =~ /^(.*)$/) { $backupwkMin = $1; } else { $backupwkMin = '0'; } if (($backupwkMin < 0) || ($backupwkMin > 59)) { esmith::cgi::genResult( $q, $fm->localise('ERR_INVALID_MINUTE').$backupwkMin. $fm->localise('BETWEEN_0_AND_59') ); return; } $backupwkMin = sprintf("%02d", $backupwkMin); $ampm = $q->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 = $conf->get('UnsavedChanges')->value; $rec->set_prop('status', 'enabled'); $rec->set_prop('BackupTime', "$backupwkHour:$backupwkMin"); $conf->get('UnsavedChanges')->set_value($old); system("/sbin/e-smith/signal-event", "conf-backup") == 0 or die($fm->localise('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,$smbv1); if (! $error_message) { bunmount($backupwkMount,$VFSType); } elsif ($error_message) { esmith::cgi::genResult($q, $fm->localise('ERROR_WHEN_TESTING_REMOTE_SERVER').$q->br() . "$error_message"); return; } } esmith::cgi::genResult( $q, $fm->localise('SUCCESSFULLY_ENABLED_WORKSTN').$q->br(). $fm->localise('WITH_BACKUP_TIME')."$backupwkHour:$backupwkMin"); return; } sub workstnVerify () { my $rec = $conf->get('backupwk'); esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('VERIFY_WORKSTN_BACKUP_FILE')); unless ($rec) { esmith::cgi::genResult( $q, $fm->localise('CONFIGURATION_TO_BE_DONE')); return; } my %backupfiles = (); my $mntdir = $rec->prop('Mount') || '/mnt/smb'; my $mntbkdir; my $key; my $id = $rec->prop('Id') || $conf->get('SystemName')->value . "." . $conf->get('DomainName')->value; my $smbhost = $rec->prop('SmbHost'); my $smbshare = $rec->prop('SmbShare'); my $smbv1 = $rec->prop('SmbV1') || 'disabled'; 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 = bmount($mntdir,$smbhost,$smbshare,$VFSType,$smbv1); if ($error_message) { esmith::cgi::genResult($q, $error_message, $id); return; } # Test if backup subdirectory for our server $mntbkdir = $mntdir . "/$id"; unless (-d $mntbkdir) { $error_message = $fm->localise('ERR_NO_HOST_DIR')."\n"; $error_message .= bunmount($mntdir,$VFSType); esmith::cgi::genResult($q, $error_message, $id); return; } # Finding existing backups find { wanted => \&$setbackuplist, untaint => 1, untaint_pattern => qr|^([-+@\w\s./]+)$|}, $mntbkdir ; my %blabels = (); my @blabels; my $backups = 0; foreach $key (sort keys %backupfiles) { my $labkey = $mntbkdir . '/' . $backupfiles{$key}[0] . '/' . $backupfiles{$key}[1]; $blabels{$labkey} = $backupfiles{$key}[1] . " (" . $backupfiles{$key}[0] . ")"; $backups = push @blabels, $labkey; } $error_message = bunmount($mntdir,$VFSType); die($error_message) if $error_message; # Stops here if no backups if ( $backups == 0 ) { esmith::cgi::genResult( $q, $fm->localise('NO_BACKUPS_TO_RESTORE')); return; } print $q->p ($fm->localise('VERIFY_WORKSTN_BACKUP_DESC') . ' ' . "$smbhost/$smbshare/$id"); print $q->p; print $q->start_multipart_form( -method => 'POST', -action => $q->url (-absolute => 1) ); print $q->table ({border => 0, cellspacing => 0, cellpadding => 4}, esmith::cgi::genWidgetRow( $q, $fm->localise('SELECT_BACKUP_FILE'), $q->popup_menu ( -name => 'backupset', -values => [ @blabels ], -labels => \%blabels ) ) ); print $q->table ({border => 0, cellspacing => 0, cellpadding => 4}, esmith::cgi::genWidgetRow( $q, $fm->localise('CHECK_TO_VERIFY_FULL_RESTORE'), $q->checkbox ( -name => 'verifyall', -checked=>0, -label=>'' ) ), $q->Tr( esmith::cgi::genTextRow( $q, $fm->localise('CHECK_INTEGRITY_WARNING') ) ) ),"\n"; print $q->table ({width => "100%", -class => "sme-noborders"}, esmith::cgi::genButtonRow( $q, $q->submit( -name => 'action', -value => $fm->localise('VERIFY') ) ) ),"\n"; print $q->hidden( -name => 'state', -override => 1, -default => 'workstn-verify' ); print $q->end_form; esmith::cgi::genFooter ($q); } sub performWorkstnVerify { my ($q) = @_; my $backupwkrec = $conf->get('backupwk'); my $smbhost = $backupwkrec->prop('SmbHost'); my $smbshare = $backupwkrec->prop('SmbShare'); my $smbv1 = $backupwkrec->prop('SmbV1') || 'disabled'; my $mntdir = $backupwkrec->prop('Mount') || '/mnt/smb'; my $key; my $error_message; my $id = $backupwkrec->prop('Id') || $conf->get('SystemName')->value . "." . $conf->get('DomainName')->value; my $err; my $VFSType = $backupwkrec->prop('VFSType') || 'cifs'; my $verifyref = $q->param ('backupset'); $mntdir = "/$smbshare" if ( $VFSType eq 'usb' ); # Mounting backup shared folder $error_message = bmount($mntdir,$smbhost,$smbshare,$VFSType,$smbv1); if ($error_message) { esmith::cgi::genResult($q, $error_message, $id); return; } # Test if backup subdirectory for our server my $mntbkdir = $mntdir . "/$id"; unless (-d $mntbkdir) { $error_message = $fm->localise('ERR_NO_HOST_DIR')."\n"; $error_message .= bunmount($mntdir,$VFSType); esmith::cgi::genResult($q, $error_message, $id); return; } my $fullverify = $q->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-/ ) { esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('VERIFY_WORKSTN_BACKUP_FILE') ); esmith::cgi::genResult( $q, $fm->localise('ERR_NO_FULL_BACKUP') ); return; } } else { my $numf = sprintf("%03d", $num); unless ( $backupsetfiles{$key} =~ /\/inc-$numf-/ ) { esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('VERIFY_WORKSTN_BACKUP_FILE') ); esmith::cgi::genResult( $q, $fm->localise('ERR_NO_INC_BACKUP') . " " . $numf ); return; } } $num++; last if ( $backupsetfiles{$key} eq $verifyref ); } # and test them $| = 1; if (open(RD, "-|")) { esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('VERIFY_WORKSTN_BACKUP_FILE')); print $q->p ( $q->b ($fm->localise('TESTING_NEEDED_BACKUPS_FOR_RESTORE') ) ); print ''; my $message; if (!close RD) { print $q->p ($q->b ( $fm->localise('RESTORE_VERIFY_FAILED') )); } else { print $q->p ($q->b ( $fm->localise('VERIFY_COMPLETE') )); } esmith::cgi::genFooter ($q); } else { select(STDOUT); $| = 1; my $file; foreach $file (@restorefiles) { if ($file =~ /^(.*)$/) { $file = $1; } else { $error_message = "Unsecure data : $file\n"; $error_message .= bunmount($mntdir,$VFSType); die ($error_message); } print $q->p($fm->localise('TESTED_BACKUP') . " " . $file); system ("/usr/bin/dar", "-Q", "--test", "$file", "--noconf"); } $error_message = 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 .= bunmount($mntdir,$VFSType); die ($error_message); } if (open(RD, "-|")) { esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('VERIFY_WORKSTN_BACKUP_FILE')); print $q->p($fm->localise('FILES_IN_BACKUP')); print ''; my $status = close RD ? ($complete ? $fm->localise('VERIFY_COMPLETE') : $fm->localise('BACKUP_FILE_INCOMPLETE')) : ($fm->localise('ERROR_READING_FILE').' : '.$backupkey); print $q->p ($q->b ($status)); esmith::cgi::genFooter ($q); } else { select(STDOUT); $| = 1; system ("/usr/bin/dar", "-Q", "--list", "$backupkey", "--noconf") == 0 or die ($fm->localise('ERR_EXTRACT')." : ".$!); $error_message = bunmount($mntdir,$VFSType); die($error_message) if $error_message; exit(0); } } return; } sub workstnRestore () { my $rec = $conf->get('backupwk'); esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('RESTORE_CONF_FROM_WORKSTN')); unless ($rec) { esmith::cgi::genResult( $q, $fm->localise('CONFIGURATION_TO_BE_DONE')); return; } my $mntdir = $rec->prop('Mount') || '/mnt/smb'; my $mntbkdir; my %backupfiles = (); my $key; my $id = $rec->prop('Id') || $conf->get('SystemName')->value . "." . $conf->get('DomainName')->value; my $VFSType = $rec->prop('VFSType') || 'cifs'; my $smbhost = $rec->prop('SmbHost'); my $smbshare = $rec->prop('SmbShare'); my $smbv1 = $rec->prop('SmbV1') || 'disabled'; my $err; $mntdir = "/$smbshare" if ( $VFSType eq 'usb' ); my $setbackupflist = 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 = bmount($mntdir,$smbhost,$smbshare,$VFSType,$smbv1); if ($error_message) { esmith::cgi::genResult($q, $error_message, $id); return; } # Test if backup subdirectory for our server $mntbkdir = $mntdir . "/$id"; unless (-d $mntbkdir) { $error_message = $fm->localise('ERR_NO_HOST_DIR')."\n"; $error_message .= bunmount($mntdir,$VFSType); esmith::cgi::genResult($q, $error_message, $id); return; } # Finding existing backups find { wanted => \&$setbackupflist, untaint => 1, untaint_pattern => qr|^([-+@\w\s./]+)$| }, $mntbkdir ; my %blabels = (); my @blabels; my $backups = 0; foreach $key (sort keys %backupfiles) { my $labkey = $mntbkdir . '/' . $backupfiles{$key}[0] . '/' . $backupfiles{$key}[1]; $blabels{$labkey} = $backupfiles{$key}[1] . ' (' . $backupfiles{$key}[0] . ')'; $backups = push @blabels, $labkey; } $error_message = bunmount($mntdir,$VFSType); die($error_message) if $error_message; if ( $backups == 0 ) { esmith::cgi::genResult( $q, $fm->localise('NO_BACKUPS_TO_RESTORE')); return; } print $q->p ($fm->localise('RESTORE_CONF_FROM_WORKSTN_DESC') . ' ' . "$smbhost/$smbshare/$id"); print $q->p; print $q->start_multipart_form( -method => 'POST', -action => $q->url (-absolute => 1) ); print $q->table ( {border => 0, cellspacing => 0, cellpadding => 4}, esmith::cgi::genTextRow( $q, $q->b($fm->localise('DO_NOT_RESTORE_BIN_SBIN_LIB_LIB64_FROM_SME9')), ), esmith::cgi::genWidgetRow( $q, $fm->localise('SELECT_BACKUP_FILE'), $q->popup_menu ( -name => 'backuptorestore', -values => [ @blabels ], -labels => \%blabels ) ) ); print $q->table ( {width => "100%", -class => "sme-noborders"}, esmith::cgi::genButtonRow( $q, $q->submit( -name => 'action', -value => $fm->localise('RESTORE_FROM_WORKSTN') ) ) ); print $q->hidden( -name => 'state', -override => 1, -default => 'workstn-restore' ); print $q->end_form; esmith::cgi::genFooter ($q); } sub performWorkstnRestore { my ($q) = @_; my $restoreref = $q->param ('backuptorestore'); 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) { esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('RESTORE_CANNOT_PROCEED') ); print $q->p ( $q->b ($fm->localise('ANOTHER_RESTORE_IN_PROGRESS') ) ); esmith::cgi::genFooter ($q); return; } my $backupwkrec = $conf->get('backupwk'); my $id = $backupwkrec->prop('Id') || $conf->get('SystemName')->value . "." . $conf->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 $smbv1 = $backupwkrec->prop('SmbV1') || 'disabled'; $mntdir = "/$smbshare" if ( $VFSType eq 'usb' ); my $err; my $error_message; # Mounting backup shared folder $error_message = bmount($mntdir,$smbhost,$smbshare,$VFSType,$smbv1); if ($error_message) { esmith::cgi::genHeaderNonCacheable($q,undef, $fm->localise('RESTORE_CANNOT_PROCEED')); esmith::cgi::genResult($q, $error_message, $id); return; } # Test if backup subdirectory for our server my $mntbkdir = $mntdir . "/$id"; unless (-d $mntbkdir) { $error_message = $fm->localise('ERR_NO_HOST_DIR')."\n"; $error_message .= bunmount($mntdir,$VFSType); esmith::cgi::genHeaderNonCacheable($q,undef, $fm->localise('RESTORE_CANNOT_PROCEED')); esmith::cgi::genResult($q, $error_message, $id); return; } # 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-/ ) { esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('RESTORE_CANNOT_PROCEED') ); esmith::cgi::genResult( $q, $fm->localise('ERR_NO_FULL_BACKUP') ); return; } } else { my $numf = sprintf("%03d", $num); unless ( $backupsetfiles{$key} =~ /\/inc-$numf-/ ) { esmith::cgi::genHeaderNonCacheable( $q, undef, $fm->localise('RESTORE_CANNOT_PROCEED') ); esmith::cgi::genResult( $q, $fm->localise('ERR_NO_INC_BACKUP') . $numf ); return; } } $num++; last if ( $backupsetfiles{$key} eq $restoreref ); } # backup is online, restoring now my $rec = $restore->get('restore'); $rec->set_prop('state','running'); $rec->set_prop('start', time); $conf->get('bootstrap-console')->set_prop('Run', 'yes'); unless (system("/sbin/e-smith/signal-event", "pre-restore") == 0) { esmith::cgi::genHeaderNonCacheable( $fm->{cgi}, undef, $fm->localise('OPERATION_STATUS_REPORT')); esmith::cgi::genResult( $fm->{cgi}, $fm->localise('ERR_PRE_RESTORE')); return; } $| = 1; if (open(RD, "-|")) { #----------------------------------------------------- # restore system from uploaded workstation backup file #----------------------------------------------------- esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('RESTORE_IN_PROGRESS')); print $q->p ( $q->b ($fm->localise('RESTORE_IN_PROGRESS_DESC') ) ); print $q->p($fm->localise('FILES_HAVE_BEEN_RESTORED')); print ''; my $message; if (!close RD) { $message = $fm->localise('RESTORE_FAILED_MSG'); } else { #----------------------------------------------------- # if restore completed, regenerate configuration files #----------------------------------------------------- if ($complete) { $message = $fm->localise('RESTORE_COMPLETE'); system("/usr/sbin/groupmod", "-g", "$www_gid", "www") == 0 or warn ($fm->localise('ERR_RESTORING_GID')."\n"); system("/usr/sbin/usermod", "-g", "$www_gid", "www") == 0 or warn ($fm->localise('ERR_RESTORING_INITIAL_GRP')."\n"); system("/sbin/e-smith/signal-event", "post-upgrade") == 0 or die ($fm->localise('ERROR_UPDATING_CONFIGURATION')."\n"); } else { $message = $fm->localise('RESTORE_FAILED'); } } $rec->set_prop('state', 'complete'); $rec->set_prop('finish', time); &esmith::lockfile::UnlockFile($file_handle); print $q->p ($q->b ($message)); print $q->start_form( -method => 'POST', -action => $q->url (-absolute => 1) ); print $q->p($q->b ($fm->localise('YOU_MUST_REBOOT'))),"\n"; print $q->start_table ({width => "100%", -class => "sme-noborders"}),"\n"; print esmith::cgi::genButtonRow( $q, $q->submit (-name => 'action', -value => $fm->localise('REBOOT')) ); # Put in a hidden widget to store the reboot value. print $q->hidden( -name => 'function', -value => 'reboot' ),"\n"; print $q->hidden ( -name => 'state', -override => 1, -default => 'perform' ),"\n"; print $q->end_table,"\n"; print $q->end_form; esmith::cgi::genFooter ($q); } else { select(STDOUT); $| = 1; my $file; foreach $file (@restorefiles) { if ($file =~ /^(.*)$/) { $file = $1; } else { $error_message = "Unsecure data : $file\n"; $error_message .= bunmount($mntdir,$VFSType); die ($error_message); } # because CentOS 7/SME 10 has now links in place of folder for bin lib lib64 and sbin, we need to exclude them to avoid to trash the server # if restoring from a previous SME version. An alternative or complement to exclud directories would be to add this rule : '-/{!T&~D}[Pp];Oo' : do not replace symlink by dir, otherwise replace. system ("/usr/bin/dar", "-Q", "-x", "$file", "-v", "-N", "-R", "/", "-wa", '-P', 'bin', '-P', 'lib', '-P', 'lib64', '-P', 'sbin', '-P', 'var/run', '-P', 'var/lock'); # However if one has added element to backup to this location; we need to resore them to their new location # TODO condition to execute or not the following .... # something like dar -l full-20210309124352 -g bin -g lib -g lib64 -g sbin -/'{T}[Pp]'|grep -E '(sbin|bin|lib)'|wc -l ; if result > to 4 # or with a loop accross the 4 locations system ("/usr/bin/dar", "-Q", "-x", "$file", "-v", "-N", "-R", "/usr/", "-wa", '-g', 'bin', '-g', 'lib', '-g', 'lib64', '-g', 'sbin', '-D'); } $error_message = bunmount($mntdir,$VFSType); die($error_message) if $error_message; exit(0); } return; } sub workstnSelRestore() { my $rec = $conf->get('backupwk'); esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('WORKSTN_SELECTIVE_RESTORE')); unless ($rec) { esmith::cgi::genResult( $q, $fm->localise('CONFIGURATION_TO_BE_DONE')); return; } my %backupfiles = (); my $mntdir = $rec->prop('Mount') || '/mnt/smb'; my $mntbkdir; my $key; my $id = $rec->prop('Id') || $conf->get('SystemName')->value . '.' . $conf->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'); my $smbv1 = $rec->prop('SmbV1') || 'disabled'; $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 = bmount($mntdir,$smbhost,$smbshare,$VFSType,$smbv1); if ($error_message) { esmith::cgi::genResult($q, $error_message, $id); return; } # Test if backup subdirectory for our server $mntbkdir = $mntdir . "/$id"; unless (-d $mntbkdir) { $error_message = $fm->localise('ERR_NO_HOST_DIR')."\n"; $error_message .= bunmount($mntdir,$VFSType); esmith::cgi::genResult($q, $error_message, $id); 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++; } close (DAR_LIST); # set drop down list of backups push @blabels, "0"; $blabels{"0"} = $fm->localise('ALL_BACKUPS'); $j = 0; while ($j < $i) { push @blabels, $bknum[$j]; $blabels{$bknum[$j]} = $bkname[$j]; $j++ } print $q->p ($fm->localise('WORKSTN_SEL_REST_DESC') . " $smbhost/$smbshare/$id"); print $q->h2 ($fm->localise('BACKUP_CHOICE')); print $q->start_multipart_form( -method => 'POST', -action => $q->url (-absolute => 1) ); print $q->table ({border => 0, cellspacing => 0, cellpadding => 4}, esmith::cgi::genWidgetRow( $q, $q->b($fm->localise('SELECT_BACKUP_FILE')), $q->popup_menu ( -name => 'backupset', -values => [ @blabels ], -labels => \%blabels) ), esmith::cgi::genNameValueRow( $q, $fm->localise('FILTER_EXPRESSION'), 'filterexp', $filterexp ) ); print $q->table ({width => "100%", -class => "sme-noborders"}, esmith::cgi::genButtonRow( $q, $q->submit( -name => 'action', -value => $fm->localise('PERFORM') ) ) ),"\n"; print $q->hidden( -name => 'state', -override => 1, -default => 'workstn-sel-restore' ); print $q->end_form; esmith::cgi::genFooter ($q); $error_message = bunmount($mntdir,$VFSType); die($error_message) if $error_message; } sub performWorkstnSelRestore { my ($q) = @_; my $rgfilter; my $filterexp = $q->param ('filterexp'); if ($filterexp =~ /^(.*)$/) { $filterexp = $1; $rgfilter = qr/$filterexp/; } else { $filterexp = ""; } my $seldatebf; esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('WORKSTN_SELECTIVE_RESTORE')); my $backupwkrec = $conf->get('backupwk'); my $smbhost = $backupwkrec->prop('SmbHost'); my $smbshare = $backupwkrec->prop('SmbShare'); my $smbv1 = $backupwkrec->prop('SmbV1') || 'disabled'; my $mntdir = $backupwkrec->prop('Mount') || '/mnt/smb'; my $key; my $id = $backupwkrec->prop('Id') || $conf->get('SystemName')->value . "." . $conf->get('DomainName')->value; my @flabels; my %flabels = (); my $VFSType = $backupwkrec->prop('VFSType') || 'cifs'; my $err; my $error_message; $mntdir = "/$smbshare" if ( $VFSType eq 'usb' ); my $backupkey = $q->param ('backupset'); if ($backupkey =~ /^(.*)$/) { $backupkey = $1; } else { die('Unsecure data : ' . $backupkey); } # Mounting backup shared folder $error_message = bmount($mntdir,$smbhost,$smbshare,$VFSType,$smbv1); if ($error_message) { esmith::cgi::genResult($q, $error_message, $id); return; } # Test if backup subdirectory for our server my $mntbkdir = $mntdir . "/$id"; unless (-d $mntbkdir) { $error_message = $fm->localise('ERR_NO_HOST_DIR')."\n"; $error_message .= bunmount($mntdir,$VFSType); esmith::cgi::genResult($q, $error_message, $id); return; } # Read wanted file list from selected backup if (open(RD, "-|")) { my $regex = qr/\[.*\] */; while () { chomp; $_ =~ s/$regex//; next if m/^(sbin|bin|lib|lib64|var\/run|var\/lock)$/; if ($filterexp) {next unless m/$rgfilter/}; push @flabels, $_; } my $status = close RD ? $fm->localise('READ_COMPLETE') : ($fm->localise('ERROR_READING_FILE').' : '.$backupkey); print $q->p ($status); } else { select(STDOUT); $| = 1; system ("/usr/bin/dar_manager", "-B", "$mntbkdir/dar-catalog", "-u", "$backupkey") == 0 or die ($fm->localise('ERR_EXTRACT')." : ".$!); $error_message = bunmount($mntdir,$VFSType); die($error_message) if $error_message; exit(0); } print $q->start_multipart_form( -method => 'POST', -action => $q->url (-absolute => 1) ); print $q->table ({border => 0, cellspacing => 0, cellpadding => 4}, esmith::cgi::genTextRow( $q, $q->b($fm->localise('DO_NOT_RESTORE_BIN_SBIN_LIB_LIB64_FROM_SME9')), ), esmith::cgi::genWidgetRow( $q, $q->b($fm->localise('SELECT_FILES_TO_RESTORE')), $q->scrolling_list ( -name => 'restorefiles', -values => [ @flabels ], -size => 15, -multiple => 'true') ), esmith::cgi::genNameValueRow( $q, $fm->localise('SELECT_DATE_BEFORE'), 'seldatebefore', $seldatebf ) ); print $q->table ({width => "100%", -class => "sme-noborders"}, esmith::cgi::genButtonRow( $q, $q->submit( -name => 'action', -value => $fm->localise('PERFORM') ) ) ),"\n"; print $q->hidden( -name => 'state', -override => 1, -default => 'workstn-sel-restore2' ); print $q->hidden( -name => 'when', -override => 1, -value => $seldatebf ); print $q->end_form; esmith::cgi::genFooter ($q); } sub performWorkstnSelRestore2 { my ($q) = @_; esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('RESTORE_IN_PROGRESS')); my @restorelist; my $when = $q->param ('seldatebefore'); if ($when =~ /^(.*)$/) { $when = $1; } else { die('Unsecure data : ' . $when); } 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])?/; unless (($when =~ m/^$tymd$thms$/) || ($when eq "")) { esmith::cgi::genResult( $q, "$when : " . $fm->localise('ERR_INVALID_SELDATE') ); return; } my @restorefiles = $q->param ('restorefiles'); my $f; foreach $f (@restorefiles) { if ($f =~ /^(.*)$/) { push @restorelist, "\"".$1."\""; } } my $backupwkrec = $conf->get('backupwk'); my $id = $backupwkrec->prop('Id') || $conf->get('SystemName')->value . "." . $conf->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 $smbv1 = $backupwkrec->prop('SmbV1') || 'disabled'; my $err; my $error_message; $mntdir = "/$smbshare" if ( $VFSType eq 'usb' ); # Mounting backup shared folder $error_message = bmount($mntdir,$smbhost,$smbshare,$VFSType,$smbv1); if ($error_message) { esmith::cgi::genResult($q, $error_message, $id); return; } # Test if backup subdirectory for our server my $mntbkdir = $mntdir . "/$id"; unless (-d $mntbkdir) { $error_message = $fm->localise('ERR_NO_HOST_DIR')."\n"; $error_message .= bunmount($mntdir,$VFSType); esmith::cgi::genResult($q, $error_message, $id); return; } # backup is online, restoring now $| = 1; my $restorerr; if (open(RD, "-|")) { #----------------------------------------------------- # restore system from uploaded workstation backup file #----------------------------------------------------- print $q->p($fm->localise('FILES_HAVE_BEEN_RESTORED')); print '
    '; while () { print "
  • $_
  • \n"; } print '
'; my $message; if (!close RD) { $message = $fm->localise('RESTORE_FAILED_MSG'); } else { if ($restorerr) { $message = $fm->localise('RESTORE_FAILED'); } else { $message = $fm->localise('RESTORE_COMPLETE'); } } print $q->p ($q->b ($message)); esmith::cgi::genFooter ($q); } else { select(STDOUT); $| = 1; # we could add this filter here -/{!T&~D}[Pp];Oo to prevent symlinks to be transformed as folder, but it would lead to $?=4 or 5 or 1024.... thus marking as failed if ($when) { $restorerr = system ("/usr/bin/dar_manager -B \"$mntbkdir/dar-catalog\" -Q -w $when -e '-v -N -R / -w -P sbin -P bin -P lib64 -P lib -P var/run -P var/lock' -r @restorelist"); } else { $restorerr = system ("/usr/bin/dar_manager -B \"$mntbkdir/dar-catalog\" -Q -k -e '-v -N -R / -w -P sbin -P bin -P lib64 -P lib -P var/run -P var/lock' -r @restorelist"); } $error_message = bunmount($mntdir,$VFSType); die($error_message) if $error_message; exit(0); } return; } sub performReboot () { esmith::cgi::genHeaderNonCacheable ($q, undef, $fm->localise('SERVER_REBOOT')); print $q->p ( $q->b ($fm->localise('SERVER_WILL_REBOOT')) ); esmith::cgi::genFooter($fm); esmith::util::backgroundCommand( 5, "/sbin/e-smith/signal-event", "reboot" ); } sub CalculateSizes () { #------------------------------------------------------------ # 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. use Quota; use esmith::AccountsDB; my $accounts = esmith::AccountsDB->open; # Get a $dev value appropriate for use in Quota::query call. my $dev = Quota::getqcarg("/home/e-smith/files"); foreach my $user ($accounts->users()) { my $name = $user->key; my $uid = getpwnam($name); unless ($uid) { warn ($fm->localise('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 () { 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', 'ext3', '-t', 'ext4', '-t', 'xfs'; while () { 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', 'ext3', '-t', 'ext4', '-t', 'xfs', '/tmp'; while () { 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 dmount { # mount dar unit according to dar-workstation configuration # return nothing if mount successfull my ($host,$share,$mountdir,$login,$password,$VFSType,$smbv1) = @_; if ($VFSType eq 'cifs') { my $opt= ($smbv1 eq "enabled")? ",vers=1.0": ""; return ( qx(/bin/mount -t cifs "//$host/$share" $mountdir -o credentials=/etc/dar/CIFScredentials,nounix$opt 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) ); } else { return ("Error while mounting $host/$share : $VFSType not supported.\n"); } } sub checkMount { # check if $mountdir is mounted my $mountdir = shift; $|=1; # Auto-flush my @res = qx( findmnt $mountdir ); return ( !@res ); } sub bmount { my ($mntdir,$host,$share,$VFSType,$smbv1) = @_; # verify backup directory not already mounted if (!checkMount ($mntdir)) { return if ($VFSType eq 'mnt'); return ($fm->localise('ERR_ALREADY_MOUNTED')); } else { if ($VFSType eq 'mnt') { return ($fm->localise('ERR_NOT_MOUNTED')); } } # create the directory mount point if it does not exist my $err = createTree ($mntdir); return ($fm->localise('ERR_MOUNTING_SMBSHARE') . "\n" . $err) if $err; # mount the backup directory $err = dmount($host,$share,$mntdir,'','',$VFSType,$smbv1); return ($fm->localise('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 ($fm->localise('ERR_NOT_MOUNTED')); } } return; } sub bunmount { my ($mount,$type) = @_; return if ($type eq 'mnt'); # Don't unmount for type 'mnt' if (!checkMount ($mount)) { system('/bin/umount', '-f', $mount) == 0 or return ($fm->localise('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; } #Used to test if the remote share is mountable when you save settings in database sub vmount { # 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,$smbv1) = @_; if ($VFSType eq 'cifs') { my $opt= ($smbv1 eq "enabled")? ",vers=1.0": ""; return ( qx(/bin/mount -t cifs "//$host/$share" $mountdir -o credentials=/etc/dar/CIFScredentials,nounix$opt 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) ); } } __DATA__