From a7e968ce327855f2ba2624ca8517069a936c9b5b Mon Sep 17 00:00:00 2001 From: cbarratt Date: Sat, 2 Aug 2003 08:01:41 +0000 Subject: [PATCH] * Major changes from Ryan Kucera to add style sheets to the CGI interface, allowing easy customization. Added new icons and BackupPC logo. Numerous navigation improvements. * Major addition of Archive feature from Josh Marshall. Special clients can be configured to be archive targets (eg: tape drives, CD-R). Any subset of the backup clients can be selected and tar archives are created, optionally compressed and split and written to the output device. Logs are maintained and are browsable. * Addition of administration options from Paul Lukins. Initial page allows server to be started/stopped/reloaded. --- ChangeLog | 13 + bin/BackupPC | 125 +++++++-- bin/BackupPC_archive | 375 +++++++++++++++++++++++++ bin/BackupPC_archivecd | 51 ++++ bin/BackupPC_archivetape | 45 +++ bin/BackupPC_dump | 24 +- cgi-bin/BackupPC_Admin | 10 + conf/config.pl | 309 +++++++++++++++++++- configure.pl | 23 +- doc-src/BackupPC.pod | 49 +++- images/icon-dir.gif | Bin 0 -> 426 bytes images/icon-file.gif | Bin 0 -> 395 bytes images/icon-symlink.gif | Bin 0 -> 458 bytes images/logo.gif | Bin 0 -> 1394 bytes init.d/src/linux-backuppc | 2 +- lib/BackupPC/CGI/AdminOptions.pm | 52 ++++ lib/BackupPC/CGI/Archive.pm | 235 ++++++++++++++++ lib/BackupPC/CGI/ArchiveInfo.pm | 87 ++++++ lib/BackupPC/CGI/Browse.pm | 71 +++-- lib/BackupPC/CGI/DirHistory.pm | 20 +- lib/BackupPC/CGI/EmailSummary.pm | 4 +- lib/BackupPC/CGI/GeneralInfo.pm | 34 +-- lib/BackupPC/CGI/HostInfo.pm | 175 +++++++----- lib/BackupPC/CGI/LOGlist.pm | 4 +- lib/BackupPC/CGI/Lib.pm | 246 +++++++++------- lib/BackupPC/CGI/Queue.pm | 6 +- lib/BackupPC/CGI/ReloadServer.pm | 51 ++++ lib/BackupPC/CGI/Restore.pm | 20 +- lib/BackupPC/CGI/RestoreInfo.pm | 5 +- lib/BackupPC/CGI/StartServer.pm | 63 +++++ lib/BackupPC/CGI/StartStopBackup.pm | 14 +- lib/BackupPC/CGI/StopServer.pm | 56 ++++ lib/BackupPC/CGI/Summary.pm | 42 +-- lib/BackupPC/CGI/View.pm | 35 ++- lib/BackupPC/Config.pm | 12 - lib/BackupPC/Lang/en.pm | 421 +++++++++++++++++++++------- lib/BackupPC/Lib.pm | 48 ++++ makeDist | 19 +- 38 files changed, 2289 insertions(+), 457 deletions(-) create mode 100644 bin/BackupPC_archive create mode 100644 bin/BackupPC_archivecd create mode 100644 bin/BackupPC_archivetape create mode 100644 images/icon-dir.gif create mode 100644 images/icon-file.gif create mode 100644 images/icon-symlink.gif create mode 100644 images/logo.gif create mode 100644 lib/BackupPC/CGI/AdminOptions.pm create mode 100644 lib/BackupPC/CGI/Archive.pm create mode 100644 lib/BackupPC/CGI/ArchiveInfo.pm create mode 100644 lib/BackupPC/CGI/ReloadServer.pm create mode 100644 lib/BackupPC/CGI/StartServer.pm create mode 100644 lib/BackupPC/CGI/StopServer.pm diff --git a/ChangeLog b/ChangeLog index de07eca..5b4a846 100644 --- a/ChangeLog +++ b/ChangeLog @@ -21,6 +21,19 @@ # Version __VERSION__, __RELEASEDATE__ #------------------------------------------------------------------------ +* Major changes from Ryan Kucera to add style sheets to the CGI + interface, allowing easy customization. Added new icons and + BackupPC logo. Numerous navigation improvements. + +* Major addition of Archive feature from Josh Marshall. Special + clients can be configured to be archive targets (eg: tape drives, + CD-R). Any subset of the backup clients can be selected and tar + archives are created, optionally compressed and split and written + to the output device. Logs are maintained and are browsable. + +* Addition of administration options from Paul Lukins. Initial + page allows server to be started/stopped/reloaded. + * Split BackupPC_Admin into a set of modules, one for each major action. Each action is now a seperate module in lib/BackupPC/CGI. diff --git a/bin/BackupPC b/bin/BackupPC index 027bbf2..3f1fa2b 100755 --- a/bin/BackupPC +++ b/bin/BackupPC @@ -300,6 +300,7 @@ sub Main_Initialize $Info{ConfigModTime} = $bpc->ConfigMTime(); $Info{pid} = $$; $Info{startTime} = time; + $Info{ConfigLTime} = time; $Info{Version} = $bpc->{Version}; # @@ -477,7 +478,7 @@ sub Main_TryToRun_Bg_or_User_Queue next; } push(@args, $req->{doFull} ? "-f" : "-i") - if ( !$req->{restore} ); + if (( !$req->{restore} ) && ( !$req->{archive} )); $UserQueueOn{$req->{host}} = 0; } elsif ( $nJobs < $Conf{MaxBackups} && (@CmdQueue + $nJobs) @@ -538,6 +539,10 @@ sub Main_TryToRun_Bg_or_User_Queue $progName = "BackupPC_restore"; $type = "restore"; push(@args, $req->{hostIP}, $req->{host}, $req->{reqFileName}); + } elsif ( $req->{archive} ) { + $progName = "BackupPC_archive"; + $type = "archive"; + push(@args, $req->{user}, $req->{host}, $req->{reqFileName}); } else { $progName = "BackupPC_dump"; $type = "backup"; @@ -597,13 +602,7 @@ sub Main_Select = localtime(time); my($currHours) = $hour + $min / 60 + $sec / 3600; if ( $bpc->ConfigMTime() != $Info{ConfigModTime} ) { - my($mesg) = $bpc->ConfigRead() - || "Re-read config file because mtime changed"; - print(LOG $bpc->timeStamp, "$mesg\n"); - %Conf = $bpc->Conf(); - $Info{ConfigModTime} = $bpc->ConfigMTime(); - umask($Conf{UmaskMode}); - ServerSocketInit(); + ServerReload("Re-read config file because mtime changed"); } my $delta = -1; foreach my $t ( @{$Conf{WakeupSchedule} || [0..23]} ) { @@ -651,30 +650,9 @@ sub Main_Process_Signal # Process signals # if ( $SigName eq "HUP" ) { - my($mesg) = $bpc->ConfigRead() - || "Re-read config file because of a SIG_HUP"; - print(LOG $bpc->timeStamp, "$mesg\n"); - $Info{ConfigModTime} = $bpc->ConfigMTime(); - %Conf = $bpc->Conf(); - umask($Conf{UmaskMode}); - ServerSocketInit(); - HostsUpdate(0); - $NextWakeup = 0; + ServerReload("Re-read config file because of a SIG_HUP"); } elsif ( $SigName ) { - print(LOG $bpc->timeStamp, "Got signal $SigName... cleaning up\n"); - if ( keys(%Jobs) ) { - foreach my $host ( keys(%Jobs) ) { - kill(2, $Jobs{$host}{pid}); - } - sleep(1); - foreach my $host ( keys(%Jobs) ) { - kill(9, $Jobs{$host}{pid}); - } - %Jobs = (); - } - StatusWrite(); - unlink("$TopDir/log/BackupPC.pid"); - exit(1); + ServerShutdown("Got signal $SigName... cleaning up"); } $SigName = ""; } @@ -1055,6 +1033,9 @@ sub Main_Check_Client_Messages if ( $Jobs{$host}{type} eq "restore" ) { $Status{$host}{reason} = "Reason_restore_canceled_by_user"; + } elsif ( $Jobs{$host}{type} eq "archive" ) { + $Status{$host}{reason} + = "Reason_archive_canceled_by_user"; } else { $Status{$host}{reason} = "Reason_backup_canceled_by_user"; @@ -1126,6 +1107,36 @@ sub Main_Check_Client_Messages $UserQueueOn{$hostIP} = 1; $reply = "ok: requested backup of $host"; } + } elsif ( $cmd =~ /^archive (\S+)\s+(\S+)\s+(\S+)/ ) { + my $user = $1; + my $archivehost = $2; + my $reqFileName = $3; + $host = $bpc->uriUnesc($archivehost); + if ( !defined($Status{$host}) ) { + print(LOG $bpc->timeStamp, + "User $user requested archive of unknown archive host" + . " $host"); + $reply = "archive error: unknown archive host $host"; + } else { + print(LOG $bpc->timeStamp, + "User $user requested archive on $host" + . " ($host)\n"); + if ( defined($Jobs{$host}) ) { + $reply = "Archive currently running on $host, please try later"; + } else { + unshift(@UserQueue, { + host => $host, + hostIP => $user, + reqFileName => $reqFileName, + reqTime => time, + dhcp => 0, + archive => 1, + userReq => 1, + }); + $UserQueueOn{$host} = 1; + $reply = "ok: requested archive on $host"; + } + } } elsif ( $cmd =~ /^restore (\S+)\s+(\S+)\s+(\S+)\s+(\S+)/ ) { my $hostIP = $1; $host = $2; @@ -1211,6 +1222,15 @@ sub Main_Check_Client_Messages QueueLink($host); } elsif ( $cmd =~ /^log\s+(.*)/ ) { print(LOG $bpc->timeStamp, "$1\n"); + } elsif ( $cmd =~ /^server\s+(\w+)/ ) { + my($type) = $1; + if ( $type eq 'reload' ) { + ServerReload("Reloading server configuration..."); + } elsif ( $type eq 'shutdown' ) { + $reply = "Shutting down...\n"; + syswrite($Clients{$client}{fh}, $reply, length($reply)); + ServerShutdown("Server shutting down..."); + } } elsif ( $cmd =~ /^quit/ || $cmd =~ /^exit/ ) { $nbytes = 0; last; @@ -1530,3 +1550,46 @@ sub ServerSocketInit $ServerInetPort = $Conf{ServerPort}; } } + +# +# Reload the server. Used by Main_Process_Signal when $SigName eq "HUP" +# or when the command "server reload" is received. +# +sub ServerReload +{ + my($mesg) = @_; + $mesg = $bpc->ConfigRead() || $mesg; + print(LOG $bpc->timeStamp, "$mesg\n"); + $Info{ConfigModTime} = $bpc->ConfigMTime(); + %Conf = $bpc->Conf(); + umask($Conf{UmaskMode}); + ServerSocketInit(); + HostsUpdate(0); + $NextWakeup = 0; + $Info{ConfigLTime} = time; +} + +# +# Gracefully shutdown the server. Used by Main_Process_Signal when +# $SigName ne "" && $SigName ne "HUP" or when the command +# "server shutdown" is received. +# +sub ServerShutdown +{ + my($mesg) = @_; + print(LOG $bpc->timeStamp, "$mesg\n"); + if ( keys(%Jobs) ) { + foreach my $host ( keys(%Jobs) ) { + kill(2, $Jobs{$host}{pid}); + } + sleep(1); + foreach my $host ( keys(%Jobs) ) { + kill(9, $Jobs{$host}{pid}); + } + %Jobs = (); + } + StatusWrite(); + unlink("$TopDir/log/BackupPC.pid"); + exit(1); +} + diff --git a/bin/BackupPC_archive b/bin/BackupPC_archive new file mode 100644 index 0000000..3a3bea4 --- /dev/null +++ b/bin/BackupPC_archive @@ -0,0 +1,375 @@ +#!/bin/perl -T +#============================================================= -*-perl-*- +# +# BackupPC_archive: Archive files for an archive client. +# +# DESCRIPTION +# +# Usage: BackupPC_archive +# +# AUTHOR +# Craig Barratt +# +# COPYRIGHT +# Copyright (C) 2001-2003 Craig Barratt +# +# 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 +# +#======================================================================== +# +# Version 2.1.0_CVS, released 3 Jul 2003. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +use strict; +no utf8; +use lib "/usr/local/BackupPC/lib"; +use BackupPC::Lib; +use BackupPC::FileZIO; +use BackupPC::Xfer::Archive; + +use vars qw( %ArchiveReq ); + +########################################################################### +# Initialize +########################################################################### + +die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) ); +my $TopDir = $bpc->TopDir(); +my $BinDir = $bpc->BinDir(); +my %Conf = $bpc->Conf(); +my $NeedPostCmd; + +my($user, $host, $client, $reqFileName, %stat); + +$bpc->ChildInit(); + +if ( @ARGV != 3 ) { + print("usage: $0 \n"); + exit(1); +} +$user = $1 if ( $ARGV[0] =~ /(.+)/ ); +$client = $1 if ( $ARGV[1] =~ /(.+)/ ); +if ( $ARGV[2] !~ /^([\w.]+)$/ ) { + print("$0: bad reqFileName (arg #3): $ARGV[2]\n"); + exit(1); +} +$reqFileName = $1; + +my $startTime = time(); + +#my $Hosts = $bpc->HostInfoRead($client); + +my $Dir = "$TopDir/pc/$client"; +my @xferPid = (); + +# +# Catch various signals +# +$SIG{INT} = \&catch_signal; +$SIG{ALRM} = \&catch_signal; +$SIG{TERM} = \&catch_signal; +$SIG{PIPE} = \&catch_signal; +$SIG{STOP} = \&catch_signal; +$SIG{TSTP} = \&catch_signal; +$SIG{TTIN} = \&catch_signal; +my $Pid = $$; + +mkpath($Dir, 0, 0777) if ( !-d $Dir ); +if ( !-f "$Dir/LOCK" ) { + open(LOCK, ">", "$Dir/LOCK") && close(LOCK); +} +open(LOG, ">>", "$Dir/LOG"); +select(LOG); $| = 1; select(STDOUT); + + +# +# Read the request file +# + +if ( !(my $ret = do "$Dir/$reqFileName") ) { + my $err; + if ( $@ ) { + $err = "couldn't parse $Dir/$reqFileName: $@"; + } elsif ( !defined($ret) ) { + $err = "couldn't do $Dir/$reqFileName: $!"; + } else { + $err = "couldn't run $Dir/$reqFileName"; + } + $stat{hostError} = $err; + exit(ArchiveCleanup($client)); +} +# +# Re-read config file, so we can include the PC-specific config +# +if ( defined(my $error = $bpc->ConfigRead($client)) ) { + $stat{hostError} = "Can't read PC's config file: $error"; + exit(ArchiveCleanup($client)); +} +%Conf = $bpc->Conf(); + +# +# Make sure we eventually timeout if there is no activity from +# the data transport program. +# +alarm($Conf{ClientTimeout}); + +# +# See if the host name is aliased +# +if ( $Conf{ClientNameAlias} ne "" ) { + $host = $Conf{ClientNameAlias}; +} else { + $host = $client; +} + +# +# Setup file extension for compression and open ArchiveLOG output file +# +$Conf{CompressLevel} = 0 if ( !BackupPC::FileZIO->compOk ); +my $fileExt = $Conf{CompressLevel} > 0 ? ".z" : ""; +my $ArchiveLOG = BackupPC::FileZIO->open("$Dir/ArchiveLOG$fileExt", 1, + $Conf{CompressLevel}); +my($logMsg, $xfer); + +$stat{xferOK} = 1; +$stat{hostAbort} = undef; +$stat{hostError} = $stat{lastOutputLine} = undef; +local(*RH, *WH); + +# +# Run an optional pre-archive command +# +UserCommandRun("ArchivePreUserCmd"); +$NeedPostCmd = 1; + +$xfer = BackupPC::Xfer::Archive->new($bpc); + +# +# Run the transport program +# + +my $xferArgs = { + client => $client, + host => $host, + user => $ArchiveReq{user}, + type => "archive", + XferLOG => $ArchiveLOG, + XferMethod => $Conf{XferMethod}, + pathHdrSrc => $ArchiveReq{pathHdrSrc}, + pathHdrDest => $ArchiveReq{pathHdrDest}, + HostList => \@{$ArchiveReq{HostList}}, + BackupList => \@{$ArchiveReq{BackupList}}, + archiveloc => $ArchiveReq{archiveloc}, + parfile => $ArchiveReq{parfile}, + compression => $ArchiveReq{compression}, + compext => $ArchiveReq{compext}, + splitsize => $ArchiveReq{splitsize}, + pidHandler => \&pidHandler, +}; + +$xfer->args($xferArgs); + +if ( !defined($logMsg = $xfer->start()) ) { + UserCommandRun("ArchivePostUserCmd") if ( $NeedPostCmd ); + $stat{hostError} = "xfer start failed: ", $xfer->errStr; + exit(ArchiveCleanup($client)); +} + +print(LOG $bpc->timeStamp, "Starting archive\n"); +$xfer->run(); +alarm(0); + +exit(ArchiveCleanup($client)); + +########################################################################### +# Subroutines +########################################################################### + +sub catch_signal +{ + my $signame = shift; + + # + # Children quit quietly on ALRM + # + exit(1) if ( $Pid != $$ && $signame eq "ALRM" ); + + # + # Ignore signals in children + # + return if ( $Pid != $$ ); + + # + # Note: needs to be tested for each kind of XferMethod + # + print(LOG $bpc->timeStamp, "cleaning up after signal $signame\n"); + $SIG{$signame} = 'IGNORE'; + $ArchiveLOG->write(\"exiting after signal $signame\n"); + $stat{xferOK} = 0; + if ( $signame eq "INT" ) { + $stat{hostError} = "aborted by user (signal=$signame)"; + } else { + $stat{hostError} = "aborted by signal=$signame"; + } + exit(ArchiveCleanup($client)); +} + +# +# Cleanup and update the archive status +# +sub ArchiveCleanup +{ + my($client) = @_; + + $stat{xferOK} = 0 if ( $stat{hostError} || $stat{hostAbort} ); + + if ( !$stat{xferOK} ) { + # + # kill off the tranfer program, first nicely then forcefully + # + if ( @xferPid ) { + kill(2, @xferPid); + sleep(1); + kill(9, @xferPid); + } + } + + my $lastNum = -1; + my @Archives; + + @Archives = $bpc->ArchiveInfoRead($client); + for ( my $i = 0 ; $i < @Archives ; $i++ ) { + $lastNum = $Archives[$i]{num} if ( $lastNum < $Archives[$i]{num} ); + } + $lastNum++; + + # + # Run an optional post-archive command + # + UserCommandRun("ArchivePostUserCmd") if ( $NeedPostCmd ); + + rename("$Dir/ArchiveLOG$fileExt", "$Dir/ArchiveLOG.$lastNum$fileExt"); + rename("$Dir/$reqFileName", "$Dir/ArchiveInfo.$lastNum"); + my $endTime = time(); + + # + # If the archive failed, clean up + # + if ( !$stat{xferOK} ) { + # + # wait a short while and see if the system is still alive + # + $stat{hostError} = $stat{lastOutputLine} if ( $stat{hostError} eq "" ); + sleep(2); + $stat{hostAbort} = 1; + $ArchiveLOG->write(\"Archive failed: $stat{hostError}\n") + if ( defined($ArchiveLOG) ); + } + + $ArchiveLOG->close() if ( defined($ArchiveLOG) ); + + # + # Add the new archive information to the archive file + # + @Archives = $bpc->ArchiveInfoRead($client); + my $i = @Archives; + $Archives[$i]{num} = $lastNum; + $Archives[$i]{startTime} = $startTime; + $Archives[$i]{endTime} = $endTime; + $Archives[$i]{result} = $stat{xferOK} ? "ok" : "failed"; + $Archives[$i]{errorMsg} = $stat{hostError}; + + while ( @Archives > $Conf{ArchiveInfoKeepCnt} ) { + my $num = $Archives[0]{num}; + unlink("$Dir/ArchiveLOG.$num.z"); + unlink("$Dir/ArchiveLOG.$num"); + unlink("$Dir/ArchiveInfo.$num"); + shift(@Archives); + } + $bpc->ArchiveInfoWrite($client, @Archives); + + if ( !$stat{xferOK} ) { + print(LOG $bpc->timeStamp, "Archive failed ($stat{hostError})\n"); + print("archive failed: $stat{hostError}\n"); + return 1; + } else { + print(LOG $bpc->timeStamp, "Archive Complete\n"); + print("Archive Complete\n"); + return; + } +} + +# +# The Xfer method might tell us from time to time about processes +# it forks. We tell BackupPC about this (for status displays) and +# keep track of the pids in case we cancel the backup +# +sub pidHandler +{ + @xferPid = @_; + @xferPid = grep(/./, @xferPid); + return if ( !@xferPid ); + my @pids = @xferPid; + my $str = join(",", @pids); + $ArchiveLOG->write(\"Xfer PIDs are now $str\n") if ( defined($ArchiveLOG) ); + print("xferPids $str\n"); +} + +# +# Run an optional pre- or post-dump command +# +sub UserCommandRun +{ + my($type) = @_; + + return if ( !defined($Conf{$type}) ); + my $vars = { + xfer => $xfer, + client => $client, + host => $host, + user => $user, + share => $ArchiveReq{shareDest}, + XferMethod => $Conf{XferMethod}, + HostList => \@{$ArchiveReq{HostList}}, + BackupList => \@{$ArchiveReq{BackupList}}, + archiveloc => $ArchiveReq{archiveloc}, + parfile => $ArchiveReq{parfile}, + compression => $ArchiveReq{compression}, + compext => $ArchiveReq{compext}, + splitsize => $ArchiveReq{splitsize}, + sshPath => $Conf{SshPath}, + LOG => *LOG, + XferLOG => $ArchiveLOG, + stat => \%stat, + xferOK => $stat{xferOK} || 0, + type => $type, + }; + my $cmd = $bpc->cmdVarSubstitute($Conf{$type}, $vars); + $ArchiveLOG->write(\"Executing $type: @$cmd\n"); + # + # Run the user's command, dumping the stdout/stderr into the + # Xfer log file. Also supply the optional $vars and %Conf in + # case the command is really perl code instead of a shell + # command. + # + $bpc->cmdSystemOrEval($cmd, + sub { + $ArchiveLOG->write(\$_[0]); + }, + $vars, \%Conf); +} diff --git a/bin/BackupPC_archivecd b/bin/BackupPC_archivecd new file mode 100644 index 0000000..d9f4c0d --- /dev/null +++ b/bin/BackupPC_archivecd @@ -0,0 +1,51 @@ +#!/bin/sh +#============================================================= +# +# BackupPC_archivecd: Archive files to a cd host +# +# DESCRIPTION +# +# Usage: BackupPC_archivecd tarCreate splitpath parpath host backupnumber compression compext splitsize archiveloc parfile share +# +# AUTHOR +# Craig Barratt +# +# COPYRIGHT +# Copyright (C) 2001-2003 Craig Barratt +# +# 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 +# +#======================================================================== +# +# Version 2.1.0_CVS, released 3 Jul 2003. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +tarCreate=$1 +splitpath=$2 +parpath=$3 +host=$4 +backupnumber=$5 +compression=$6 +compext=$7 +splitsize=$8 +archiveloc=$9 +parfile=${10} +share=${11} + +$tarCreate -h $host -n $backupnumber -s $share $share | $compression | $splitpath -b $splitsize - $archiveloc/$host.$backupnumber.tar$compext. +$parpath a -n $parfile $archiveloc/$host.$backupnumber.tar$compext.par $archiveloc/$host.$backupnumber.tar$compext.* diff --git a/bin/BackupPC_archivetape b/bin/BackupPC_archivetape new file mode 100644 index 0000000..18717ab --- /dev/null +++ b/bin/BackupPC_archivetape @@ -0,0 +1,45 @@ +#!/bin/sh +#============================================================= +# +# BackupPC_archivetape: Archive files to a tape host +# +# DESCRIPTION +# +# Usage: BackupPC_archivecd tarCreate host backupnumber compression archiveloc share +# +# AUTHOR +# Craig Barratt +# +# COPYRIGHT +# Copyright (C) 2001-2003 Craig Barratt +# +# 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 +# +#======================================================================== +# +# Version 2.1.0_CVS, released 3 Jul 2003. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +tarCreate=$1 +host=$2 +backupnumber=$3 +compression=$4 +archiveloc=$5 +share=$6 + +$tarCreate -h $host -n $backupnumber -s $share $share | $compression > $archiveloc diff --git a/bin/BackupPC_dump b/bin/BackupPC_dump index d1509b7..f59e38a 100755 --- a/bin/BackupPC_dump +++ b/bin/BackupPC_dump @@ -126,20 +126,19 @@ if ( $opts{d} ) { # $hostIP = $client; if ( $bpc->CheckHostAlive($hostIP) < 0 ) { - print("Exiting because CheckHostAlive($hostIP) failed\n") + print(STDERR "Exiting because CheckHostAlive($hostIP) failed\n") if ( $opts{v} ); exit(1); } if ( $Conf{NmbLookupCmd} eq "" ) { - print("Exiting because \$Conf{NmbLookupCmd} is empty\n") + print(STDERR "Exiting because \$Conf{NmbLookupCmd} is empty\n") if ( $opts{v} ); exit(1); } ($client, $user) = $bpc->NetBiosInfoGet($hostIP); if ( $client !~ /^([\w\.\s-]+)$/ ) { - print("Exiting because NetBiosInfoGet($hostIP) returned '$client'," - . " an invalid host name\n") - if ( $opts{v} ); + print(STDERR "Exiting because NetBiosInfoGet($hostIP) returned" + . " '$client', an invalid host name\n") if ( $opts{v} ); exit(1) } $Hosts = $bpc->HostInfoRead($client); @@ -148,8 +147,8 @@ if ( $opts{d} ) { $Hosts = $bpc->HostInfoRead($client); } if ( !defined($Hosts->{$client}) ) { - print("Exiting because host $client does not exist in the hosts file\n") - if ( $opts{v} ); + print(STDERR "Exiting because host $client does not exist in the" + . " hosts file\n") if ( $opts{v} ); exit(1) } @@ -200,6 +199,13 @@ if ( $opts{e} ) { exit(0); } +# +# For archive hosts we don't bother any further +# +if ($Conf{XferMethod} eq "archive" ) { + exit(0); +} + if ( !$opts{d} ) { # # In the non-DHCP case, make sure the host can be looked up @@ -215,7 +221,7 @@ if ( !$opts{d} ) { # Ok, NS doesn't know about it. Maybe it is a NetBios name # instead. # - print("Name server doesn't know about $host; trying NetBios\n") + print(STDERR "Name server doesn't know about $host; trying NetBios\n") if ( $opts{v} ); if ( !defined($hostIP = $bpc->NetBiosHostIPFind($host)) ) { print(LOG $bpc->timeStamp, "Can't find host $host via netbios\n"); @@ -253,7 +259,7 @@ $bpc->ServerDisconnect(); if ( $opts{d} ) { if ( $StatusHost{activeJob} ) { # oops, something is already running for this host - print("Exiting because backup is already running for $client\n") + print(STDERR "Exiting because backup is already running for $client\n") if ( $opts{v} ); exit(0); } diff --git a/cgi-bin/BackupPC_Admin b/cgi-bin/BackupPC_Admin index c99ba4c..8dc2091 100755 --- a/cgi-bin/BackupPC_Admin +++ b/cgi-bin/BackupPC_Admin @@ -61,6 +61,7 @@ my %ActionDispatch = ( $Lang->{Start_Incr_Backup} => "StartStopBackup", $Lang->{Start_Full_Backup} => "StartStopBackup", $Lang->{Stop_Dequeue_Backup} => "StartStopBackup", + $Lang->{Stop_Dequeue_Archive} => "StartStopBackup", "queue" => "Queue", "view" => "View", "LOGlist" => "LOGlist", @@ -72,12 +73,21 @@ my %ActionDispatch = ( "hostInfo" => "HostInfo", "generalInfo" => "GeneralInfo", "restoreInfo" => "RestoreInfo", + "archiveInfo" => "ArchiveInfo", + $Lang->{Start_Archive} => "Archive", + "Archive" => "Archive", + "Reload" => "ReloadServer", + "startServer" => "StartServer", + "Stop" => "StopServer", + "adminOpts" => "AdminOptions", ); # # Set default actions, then call sub handler # $In{action} ||= "hostInfo" if ( defined($In{host}) ); +## rk default non admin users to pc summary for their hosts +$In{action} = "summary" if ( !defined($ActionDispatch{$In{action}}) && !CheckPermission()); $In{action} = "generalInfo" if ( !defined($ActionDispatch{$In{action}}) ); my $action = $ActionDispatch{$In{action}}; require "BackupPC/CGI/$action.pm" diff --git a/conf/config.pl b/conf/config.pl index 2912fa3..8c58140 100644 --- a/conf/config.pl +++ b/conf/config.pl @@ -160,6 +160,16 @@ $Conf{DfPath} = '/bin/df'; # $Conf{DfCmd} = '$dfPath $topDir'; +# +# Full path to various commands for archiving +# + +$Conf{SplitPath} = '/usr/bin/split'; +$Conf{ParPath} = '/usr/bin/par'; +$Conf{CatPath} = '/bin/cat'; +$Conf{GzipPath} = '/bin/gzip'; +$Conf{Bzip2Path} = '/usr/bin/bzip2'; + # # Maximum threshold for disk utilization on the __TOPDIR__ filesystem. # If the output from $Conf{DfPath} reports a percentage larger than @@ -422,6 +432,13 @@ $Conf{IncrFill} = 0; # $Conf{RestoreInfoKeepCnt} = 10; +# +# Number of archive logs to keep. BackupPC remembers information about +# each archive request. This number per archive client will be kept around before +# the oldest ones are pruned. +# +$Conf{ArchiveInfoKeepCnt} = 10; + # # List of directories or files to backup. If this is defined, only these # directories or files will be backed up. @@ -855,6 +872,69 @@ $Conf{RsyncRestoreArgs} = [ # $Conf{RsyncLogLevel} = 1; +# +# Archive Destination +# +# The Destination of the archive +# e.g. /tmp for file archive or /dev/nst0 for device archive +# +$Conf{ArchiveDest} = '/tmp'; + +# +# Archive Compression type +# +# The valid values are: +# +# - 'none': No Compression +# +# - 'gzip': Medium Compression. Recommended. +# +# - 'bzip2': High Compression but takes longer. +# +$Conf{ArchiveComp} = 'gzip'; + +# +# Archive Parity Files +# +# The number of Parity Files to generate. +# Uses the commandline par available from +# http://parchive.sourceforge.net +# +# Only useful for file dumps. +# +# Set to 0 to disable this feature. +# +$Conf{ArchivePar} = 0; + +# +# Archive Size Split +# +# Only for file archives. Splits the output into +# the specified size * 1,000,000. +# e.g. to split into 650,000,000 bytes, specify 650 below. +# +$Conf{ArchiveSplit} = 650; + +# +# Archive Command +# +# This is the command that is called to actually run the archive process +# The following variables are substituted at run-time: +# +# $Installdir The installation directory of BackupPC +# $tarCreatePath The path to BackupPC_tarCreate +# $splitpath The path to the split program +# $parpath The path to the par program +# $host The host to archive +# $backupnumber The backup number of the host to archive +# $compression The path to the compression program +# $compext The extension assigned to the compression type +# $splitsize The number of bytes to split archives into +# $archiveloc The location to put the archive +# $parfile The number of par files to create +# +$Conf{ArchiveClientCmd} = '$Installdir/bin/BackupPC_archivecd $tarCreatePath $splitpath $parpath $host $backupnumber $compression $compext $splitsize $archiveloc $parfile /'; + # # Full path for ssh. Security caution: normal users should not # allowed to write to this file or directory. @@ -942,6 +1022,25 @@ $Conf{PingPath} = '/bin/ping'; # $Conf{PingCmd} = '$pingPath -c 1 $host'; +# +# Path to init.d script and command to use that script to start the +# server from the CGI interface. The following variables are substituted +# at run-time: +# +# $sshPath path to ssh ($Conf{SshPath}) +# $serverHost same as $Conf{ServerHost} +# $serverInitdPath path to init.d script ($Conf{ServerInitdPath}) +# +# Example: +# +# $Conf{ServerInitdPath} = '/etc/init.d/backuppc'; +# $Conf{ServerInitdStartCmd} = '$sshPath -l root $serverHost' +# . ' $serverInitdPath start' +# . ' < /dev/null >& /dev/null'; +# +$Conf{ServerInitdPath} = ''; +$Conf{ServerInitdStartCmd} = ''; + # # Compression level to use on files. 0 means no compression. Compression # levels can be from 1 (least cpu time, slightly worse compression) to @@ -1059,10 +1158,31 @@ $Conf{MaxOldPerPCLogFiles} = 12; # $pathHdrDest common starting path of destination # $fileList list of files being restored # +# The following variable substitutions are made at run time for +# $Conf{ArchivePreUserCmd} and $Conf{ArchivePostUserCmd}: +# +# $client client name being backed up +# $xferOK 1 if the archive succeeded, 0 if it didn't +# $host Name of the archive host +# $user user name from the hosts file +# $share the first share name +# $XferMethod value of $Conf{XferMethod} (eg: tar, rsync, smb) +# $HostList list of hosts being archived +# $BackupList list of backup numbers for the hosts being archived +# $archiveloc location where the archive is sent to +# $parfile number of par files being generated +# $compression compression program being used (eg: cat, gzip, bzip2) +# $compext extension used for compression type (eg: raw, gz, bz2) +# $splitsize size of the files that the archive creates +# $sshPath value of $Conf{SshPath}, +# $type set to "archive" +# $Conf{DumpPreUserCmd} = undef; $Conf{DumpPostUserCmd} = undef; $Conf{RestorePreUserCmd} = undef; $Conf{RestorePostUserCmd} = undef; +$Conf{ArchivePreUserCmd} = undef; +$Conf{ArchivePostUserCmd} = undef; # # Override the client's host name. This allows multiple clients @@ -1299,20 +1419,9 @@ $Conf{CgiDateFormatMMDD} = 1; $Conf{CgiNavBarAdminAllHosts} = 0; # -# Header font and size for CGI interface -# -$Conf{CgiHeaderFontType} = 'arial'; -$Conf{CgiHeaderFontSize} = '3'; - -# -# Color scheme for CGI interface. Default values give a very light blue -# for the background navigation color, green for the header background, -# and white for the body background. (You call tell I should stick to -# programming and not graphical design.) +# Color scheme for CGI interface. # -$Conf{CgiNavBarBgColor} = '#ddeeee'; $Conf{CgiHeaderBgColor} = '#99cc33'; -$Conf{CgiBodyBgColor} = '#ffffff'; # # Hilight colors based on status that are used in the PC summary page. @@ -1363,3 +1472,179 @@ $Conf{CgiExt2ContentType} = { }; # $Conf{CgiImageDirURL} = '/BackupPC'; # $Conf{CgiImageDirURL} = ''; + +# +# CSS stylesheet for the CGI interface. +# +$Conf{CSSstylesheet} = <<'EOF'; + +EOF diff --git a/configure.pl b/configure.pl index 0abc9a7..7fdea71 100755 --- a/configure.pl +++ b/configure.pl @@ -131,6 +131,11 @@ my %Programs = ( 'ssh/ssh2' => "SshPath", sendmail => "SendmailPath", hostname => "HostnamePath", + split => "SplitPath", + par => "ParPath", + cat => "CatPath", + gzip => "GzipPath", + bzip2 => "Bzip2Path", ); foreach my $prog ( sort(keys(%Programs)) ) { @@ -406,6 +411,7 @@ printf("Installing binaries in $Conf{InstallDir}/bin\n"); foreach my $prog ( qw(BackupPC BackupPC_dump BackupPC_link BackupPC_nightly BackupPC_sendEmail BackupPC_tarCreate BackupPC_trashClean BackupPC_tarExtract BackupPC_compressPool BackupPC_zcat + BackupPC_archive BackupPC_archivecd BackupPC_archivetape BackupPC_restore BackupPC_serverMesg BackupPC_zipCreate ) ) { InstallFile("bin/$prog", "$Conf{InstallDir}/bin/$prog", 0555); } @@ -422,6 +428,7 @@ foreach my $lib ( qw( BackupPC/Attrib.pm BackupPC/PoolWrite.pm BackupPC/View.pm + BackupPC/Xfer/Archive.pm BackupPC/Xfer/Tar.pm BackupPC/Xfer/Smb.pm BackupPC/Xfer/Rsync.pm @@ -431,6 +438,9 @@ foreach my $lib ( qw( BackupPC/Lang/fr.pm BackupPC/Lang/es.pm BackupPC/Lang/de.pm + BackupPC/CGI/AdminOptions.pm + BackupPC/CGI/Archive.pm + BackupPC/CGI/ArchiveInfo.pm BackupPC/CGI/Browse.pm BackupPC/CGI/DirHistory.pm BackupPC/CGI/EmailSummary.pm @@ -439,10 +449,13 @@ foreach my $lib ( qw( BackupPC/CGI/Lib.pm BackupPC/CGI/LOGlist.pm BackupPC/CGI/Queue.pm + BackupPC/CGI/ReloadServer.pm BackupPC/CGI/RestoreFile.pm BackupPC/CGI/RestoreInfo.pm BackupPC/CGI/Restore.pm + BackupPC/CGI/StartServer.pm BackupPC/CGI/StartStopBackup.pm + BackupPC/CGI/StopServer.pm BackupPC/CGI/Summary.pm BackupPC/CGI/View.pm ) ) { @@ -623,7 +636,9 @@ will need to do: - BackupPC should be ready to start. Don't forget to run it as user $Conf{BackupPCUser}! The installation also contains an init.d/backuppc script that can be copied to /etc/init.d - so that BackupPC can auto-start on boot. See init.d/README. + so that BackupPC can auto-start on boot. This will also enable + administrative users to start the server from the CGI interface. + See init.d/README. Enjoy! EOF @@ -716,8 +731,9 @@ sub ConfigParse my($out, @conf, $var); my $comment = 1; my $allVars = {}; + my $endLine = undef; while ( ) { - if ( /^#/ ) { + if ( /^#/ && !defined($endLine) ) { if ( $comment ) { $out .= $_; } else { @@ -745,7 +761,10 @@ sub ConfigParse $out .= $_; } $var = $1; + $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<(.*);/ ); + $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<'(.*)';/ ); } else { + $endLine = undef if ( defined($endLine) && /^\Q$endLine[\n\r]*$/ ); $out .= $_; } } diff --git a/doc-src/BackupPC.pod b/doc-src/BackupPC.pod index e8e8c0a..ef114b8 100644 --- a/doc-src/BackupPC.pod +++ b/doc-src/BackupPC.pod @@ -141,9 +141,7 @@ full support for special file types and unix attributes in v1.4.0 likely means an exact image of a linux/unix file system can be made. BackupPC saves backups onto disk. Because of pooling you can relatively -economically keep several weeks of old backups. But BackupPC does not -provide permanent storage to tape. Other Open Source applications can do -this by backing up BackupPC's pool directories to tape. +economically keep several weeks of old backups. At some sites the disk-based backup will be adequate, without a secondary tape backup. This system is robust to any single failure: if a @@ -153,8 +151,8 @@ fresh file system, and create new backups from the clients. The chance of the server disk failing can be made very small by spending more money on increasingly better RAID systems. -At other sites a secondary tape backup will be required. This tape -backup can be done perhaps weekly from the BackupPC pool file system. +At other sites a secondary tape backup or cd/dvd will be required. This +backup can be done perhaps weekly using the archive function of BackupPC. =back @@ -2032,6 +2030,47 @@ in a location different from their original location. Each of these programs reside in __INSTALLDIR__/bin. +=head1 Archive functions + +BackupPC supports archiving to removable media. For users that require +offsite backups, BackupPC can create archives that stream to tape +devices, or create files of specified sizes to fit onto cd or dvd media. + +Each archive type is specified by a BackupPC host with its XferMethod +set to 'archive'. This allows for multiple configurations at sites where +there might be a combination of tape and cd/dvd backups being made. + +=head2 Configuring an Archive Host + +To create an Archive Host, add it to the hosts file just as any other host +and call it a name that best describes the type of archive, e.g. ArchiveDLT + +To tell BackupPC that the Host is for Archives, create a config.pl file in +the Archive Hosts's pc directory, adding the following line: + +$Conf{XferMethod} = 'archive'; + +To further customise the archive's parameters you can adding the changed +parameters in the host's config.pl file. The parameters are explained in the config.pl +file. + +The example archive programs included with BackupPC are for a CD and +Tape archive. The programs are called BackupPC_archivecd and +BackupPC_archivetape. These are specified by the ArchiveClientCmd configuration +parameter. + +=head2 Starting an Archive + +In the web interface, click on the Archive Host you wish to use. You will see a +list of previous archives and a summary on each. By clicking the "Start Archive" +button you are presented with the list of hosts and the approximate backup size +(note this is raw size, not projected compressed size) Select the hosts you wish +to archive and press the "Archive Selected Hosts" button. + +The next screen allows you to adjust the parameters for this archive run. +Press the "Start the Archive" to start archiving the selected hosts with the +parameters displayed. + =head1 BackupPC Design =head2 Some design issues diff --git a/images/icon-dir.gif b/images/icon-dir.gif new file mode 100644 index 0000000000000000000000000000000000000000..dafa818460de25702dd31c08ed7d81e3ba6f315b GIT binary patch literal 426 zcmZ?wbhEHblxL7;xT?UwP&A{yzJCAy{Q?343_fY|=g(JAP%tnsV5nK@;NZYu>64I< zz#yx}pk{pj{CNg0UWWL32It84@893Qf4`uhARr*1zrUX$vxh-ga{c=C3{6o0ZXGB7AG=zweh`H6w;xWj}34;`ug6HAIt=2*?Ypb_{; zLd>SE%Qaz-oKEPHgO4qBc*I#;4t`M4n8}&JutP(RjcdEdyK1|J1{nqx-irE04nYZK zMi=(Fj>gIISyLJ~~Y~sS^h+d)jegy l6P_deayy<}bh|A0gkiC}5R*i=%Z3HknjFGfQ-oL;tO1|La&G_t literal 0 HcmV?d00001 diff --git a/images/icon-file.gif b/images/icon-file.gif new file mode 100644 index 0000000000000000000000000000000000000000..95a1145af951da1c4426a6452b727dc8cdeffdb2 GIT binary patch literal 395 zcmZ?wbhEHblxL7;xT?U=(2&;O-@kwV3M+2PHQ5x0v^=u`u9ai&`7k(JR-RXurJP){~o|xw$U0!GpQ2 zt;4;xJ6^1}VnP-ZyJv04Y&j-*Cf1o#GZ%Prh%b&?>cKj1WsIbt=h~P#A@dkmHm_U1 zbF;|$n0+2B3+Z^fkBHw2V?@s zPYi6&9i|p|=t%XSP-HopV>SOwV9-;?4xyE6%1-NgPGl8GZGJRs`z)qQO{+|#o_>~` z>XOL!W5*nCcQ>zwhU&Chch%vvJYlMVi90 zo+=EpqnH?1u3n?LZbNlIK)ma!g-cX5)DIlwKd7?PML==e!Gml(2adWkGMzfDt|or| znDeD82U*nAxYaMX-PyPOpb*ysH8z$fu8i_mnBP3$dVkAFxxHvV-vgBzZgao+Ty>{j s@iL?-Id4-|-tfaZ@I2#&H2z)@g@YRxACJGdvh{V&8w@b6@T*PEc_CWneQf3IYF> zc=f@=+&iPuC>D!h7;bwoXSG^O+9p5{)a&&EfdGOaI-Raot4Bsgcs!o4a(MCmiclzA zUio6NSQH9H*V9+^{WIy>CwYxy{S%9q?o6t?r$?sW%q}jkuC9JH{l1)og%!f^K(|tJP3k~$NaaY~>4+wN%Xs6Np{0y&t_d4SQ>h%-M&Zw$a;XVs^4uH9 zk%Hmu&jU8Ljzpk9g16o0LOPduzLcMnCsCH8ct}^dJWEj#?Mx`I2J#CqQ72TY8+I@&$kE=D*!P>`uMO#3-LaEc7o-L0^+=(# z%N;3qx?jL;7`MKayW&3LJf^UoFOcMyu}T`g%y&?MIRg~WU%0QY>e=qLNHHH5Pj{S$ zke)dlfI>=G!zy;a6Oh|`YKmdKDBx6=AtfQ+Lu`Zgp^E79tVfY@BxsdNlNkej+dOdD z;TQem2HWeO*7B+&oQ$cvc-|}oc~4<$K)H@(aKF>@~>Vl1=2_~a@h!?LgIIlB|j;wHHQSFJOfSJ)ZdofJ4ovZGcY|R zly%ET_kH>MlmiCzCqXyWkOu9z&8{BQMdv(b{o{>n3Xv6nr8X2Z!-;CuezjYQT zyo&qS9CK@dKOjc;NFxh5>1LGGpv8+OI(aLN!&1nmstZJaD0hbTw*A$v=$9X|nEY{R zFdBmm9lPOh%(Xy4Ux*P`23|{e$c8G_4O7U+^MXH*$2^HMMjlC#mSfCPo*!#q7V_$y4bw!C}1G%1U zb%C*yLj7QoPb@_Pp0lSfQ0wN?7N~@-2C5Fq2wF>;y%CMpb|%qJ_#g$X0b9)ge;3LT zX9MfCsT+1@7UdQur|KPEtS%X#aDf}<>Fd2q+JX}8@s8W1eOC<{x0!jSBzndugPR)? zb)$6)i{LLD88hZBmflkQI+$lC)9w(KUmAD5`Mg$|#5jVPqY{j0c2ZX&r9RiFp-|V5 zQn1{q^{@OI558!k5CR3Qq)j_;zau^oC0?kCrA+DW5L;zTD{HZYHe4|=ZfH6O7v*1n zG7;IA*Za?;D=y_W>z(l0C}%*xje?DJTkf*AG%`ZfVr0&qtinH%ncI7}{^ZLv6}e3Y zVYx#t-9LVFZm#IiD~6Ya>BFZZD!V=M|iKiBm>cP1PGa|k2`Lrl5_DD8ICMIy!1wNE3zdIF+ldw +# +# COPYRIGHT +# Copyright (C) 2003 Craig Barratt +# +# 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 +# +#======================================================================== +# +# Version 2.1.0_CVS, released 3 Jul 2003. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +package BackupPC::CGI::AdminOptions; + +use strict; +use BackupPC::CGI::Lib qw(:all); + +sub action +{ + unless ( CheckPermission() ) { + ErrorExit($Lang->{Only_privileged_users_can_view_admin_options}); + } + my $content = eval("qq{$Lang->{Admin_Options_Page}}"); + Header(eval("qq{$Lang->{H_Admin_Options}}"), $content); + Trailer(); +} + +1; diff --git a/lib/BackupPC/CGI/Archive.pm b/lib/BackupPC/CGI/Archive.pm new file mode 100644 index 0000000..43a9b8e --- /dev/null +++ b/lib/BackupPC/CGI/Archive.pm @@ -0,0 +1,235 @@ +#============================================================= -*-perl-*- +# +# BackupPC::CGI::Archive package +# +# DESCRIPTION +# +# This module implements the Archive action for the CGI interface. +# +# AUTHOR +# Craig Barratt +# +# COPYRIGHT +# Copyright (C) 2003 Craig Barratt +# +# 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 +# +#======================================================================== +# +# Version 2.1.0_CVS, released 3 Jul 2003. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +package BackupPC::CGI::Archive; + +use strict; +use BackupPC::CGI::Lib qw(:all); +use Data::Dumper; + +sub action +{ + if ( $In{type} == 0 ) { + my($fullTot, $fullSizeTot, $incrTot, $incrSizeTot, $str, + $strNone, $strGood, $hostCntGood, $hostCntNone, $checkBoxCnt, + $backupnumber); + + $hostCntGood = $hostCntNone = $checkBoxCnt = $fullSizeTot = 0; + GetStatusInfo("hosts"); + my $Privileged = CheckPermission(); + + if ( !$Privileged ) { + ErrorExit($Lang->{Only_privileged_users_can_archive} ); + } + foreach my $host ( sort(keys(%Status)) ) { + my($fullDur, $incrCnt, $fullSize, $fullRate); + my @Backups = $bpc->BackupInfoRead($host); + my $fullCnt = $incrCnt = 0; + for ( my $i = 0 ; $i < @Backups ; $i++ ) { + if ( $Backups[$i]{type} eq "full" ) { + $fullSize = $Backups[$i]{size} / (1024 * 1024); + $incrSizeTot = 0; + } else { + $incrSizeTot = $Backups[$i]{size} / (1024 * 1024); + } + $backupnumber = $Backups[$i]{num}; + } + $fullSizeTot += $fullSize + $incrSizeTot; + $fullSize = sprintf("%.2f", ($fullSize + $incrSizeTot) / 1000); + $str = < + ${HostLink($host)} + ${UserLink($Hosts->{$host}{user})} + $fullSize +EOF + $checkBoxCnt++; + if ( @Backups == 0 ) { + $hostCntNone++; + $strNone .= $str; + } else { + $hostCntGood++; + $strGood .= $str; + } + } + $fullSizeTot = sprintf("%.2f", $fullSizeTot / 1000); + my $now = timeStamp2(time); + my $checkAllHosts = $Lang->{checkAllHosts}; + $strGood .= < +EOF + Header($Lang->{BackupPC__Archive}); + print eval ("qq{$Lang->{BackupPC_Archive}}"); + + Trailer(); + } else { + + my(@HostList, @BackupList, $HostListStr, $hiddenStr, $pathHdr, $badFileCnt, $reply, $str); + + my $Privileged = CheckPermission(); + my $args = { + SplitPath => $bpc->{Conf}{SplitPath}, + ParPath => $bpc->{Conf}{ParPath}, + CatPath => $bpc->{Conf}{CatPath}, + GzipPath => $bpc->{Conf}{GzipPath}, + Bzip2Path => $bpc->{Conf}{Bzip2Path}, + ArchiveDest => $bpc->{Conf}{ArchiveDest}, + ArchiveComp => $bpc->{Conf}{ArchiveComp}, + ArchivePar => $bpc->{Conf}{ArchivePar}, + ArchiveSplit => $bpc->{Conf}{ArchiveSplit}, + topDir => $bpc->{TopDir}, + }; + + if ( !$Privileged ) { + ErrorExit($Lang->{Only_privileged_users_can_archive} ); + } + ServerConnect(); + + for ( my $i = 0 ; $i < $In{fcbMax} ; $i++ ) { + next if ( !defined($In{"fcb$i"}) ); + my $name = $In{"fcb$i"}; + my $backupno = $In{"backup$i"}; + push(@HostList, $name); + push(@BackupList, $backupno); + $hiddenStr .= < + +EOF + $HostListStr .= < ${EscHTML($name)} +EOF + } + $hiddenStr .= < +EOF + $hiddenStr .= "\n"; + if ( @HostList == 0 ) { + ErrorExit($Lang->{You_haven_t_selected_any_hosts}); + } + my ($ArchiveDest, $ArchiveCompNone, $ArchiveCompGzip, $ArchiveCompBzip2, $ArchivePar, $ArchiveSplit); + $ArchiveDest = $bpc->{Conf}{ArchiveDest}; + if ( $bpc->{Conf}{ArchiveComp} eq "none" ) { + $ArchiveCompNone = "checked"; + } else { + $ArchiveCompNone = ""; + } + if ( $bpc->{Conf}{ArchiveComp} eq "gzip" ) { + $ArchiveCompGzip = "checked"; + } else { + $ArchiveCompGzip = ""; + } + if ( $bpc->{Conf}{ArchiveComp} eq "bzip2" ) { + $ArchiveCompBzip2 = "checked"; + } else { + $ArchiveCompBzip2 = ""; + } + $ArchivePar = $bpc->{Conf}{ArchivePar}; + $ArchiveSplit = $bpc->{Conf}{ArchiveSplit}; + + if ( $In{type} == 1 ) { + # + # Tell the user what options they have + # + + Header($Lang->{BackupPC__Archive}); + print eval ("qq{$Lang->{BackupPC_Archive2}}"); + Trailer(); + } elsif ( $In{type} == 2 ) { + my $reqFileName; + my $archivehost = $1 if ( $In{archivehost} =~ /(.+)/ ); + for ( my $i = 0 ; ; $i++ ) { + $reqFileName = "archiveReq.$$.$i"; + last if ( !-f "$TopDir/pc/$archivehost/$reqFileName" ); + } + my $compname; + if ( $In{compression} == 2 ) { # bzip2 compression + $compname = $Conf{Bzip2Path}; + } elsif ( $In{compression} == 1 ) { # gzip compression + $compname = $Conf{GzipPath}; + } else { # No Compression + $compname = $Conf{CatPath}; + } + my $compext; + if ( $In{compression} == 2 ) { # bzip2 compression + $compext = '.bz2'; + } elsif ( $In{compression} == 1 ) { # gzip compression + $compext = '.gz'; + } else { # No Compression + $compext = '.raw'; + } + my $fullsplitsize = $In{splitsize} . '000000'; + my %ArchiveReq = ( + # parameters for the archive + archiveloc => $In{archive_device}, + archtype => $In{archive_type}, + compression => $compname, + compext => $compext, + parfile => $In{par}, + splitsize => $fullsplitsize, + host => $archivehost, + + + # list of hosts to restore + HostList => \@HostList, + BackupList => \@BackupList, + + # other info + user => $User, + reqTime => time, + ); + my($archive) = Data::Dumper->new( + [ \%ArchiveReq], + [qw(*ArchiveReq)]); + $archive->Indent(1); + if ( open(REQ, ">$TopDir/pc/$archivehost/$reqFileName") ) { + binmode(REQ); + print(REQ $archive->Dump); + close(REQ); + } else { + ErrorExit($Lang->{Can_t_open_create} ); + } + $reply = $bpc->ServerMesg("archive $User $archivehost $reqFileName"); + + $str = eval("qq{$Lang->{Archive_requested}}"); + Header($Lang->{BackupPC__Archive}); + print eval ("qq{$Lang->{BackupPC_Archive_Reply_from_server}}"); + Trailer(); + } + + } + +} + +1; diff --git a/lib/BackupPC/CGI/ArchiveInfo.pm b/lib/BackupPC/CGI/ArchiveInfo.pm new file mode 100644 index 0000000..effb362 --- /dev/null +++ b/lib/BackupPC/CGI/ArchiveInfo.pm @@ -0,0 +1,87 @@ +#============================================================= -*-perl-*- +# +# BackupPC::CGI::ArchiveInfo package +# +# DESCRIPTION +# +# This module implements the ArchiveInfo action for the CGI interface. +# +# AUTHOR +# Craig Barratt +# +# COPYRIGHT +# Copyright (C) 2003 Craig Barratt +# +# 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 +# +#======================================================================== +# +# Version 2.1.0_CVS, released 3 Jul 2003. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +package BackupPC::CGI::ArchiveInfo; + +use strict; +use BackupPC::CGI::Lib qw(:all); + +sub action +{ + my $Privileged = CheckPermission($In{host}); + my $host = $1 if ( $In{host} =~ /(.*)/ ); + my $num = $In{num}; + my $i; + + if ( !$Privileged ) { + ErrorExit($Lang->{Only_privileged_users_can_view_archive_information}); + } + # + # Find the requested archive + # + my @Archives = $bpc->ArchiveInfoRead($host); + for ( $i = 0 ; $i < @Archives ; $i++ ) { + last if ( $Archives[$i]{num} == $num ); + } + if ( $i >= @Archives ) { + ErrorExit(eval("qq{$Lang->{Archive_number__num_for_host__does_not_exist}}")); + } + + %ArchiveReq = (); + do "$TopDir/pc/$host/ArchiveInfo.$Archives[$i]{num}" + if ( -f "$TopDir/pc/$host/ArchiveInfo.$Archives[$i]{num}" ); + + my $startTime = timeStamp2($Archives[$i]{startTime}); + my $reqTime = timeStamp2($ArchiveReq{reqTime}); + my $dur = $Archives[$i]{endTime} - $Archives[$i]{startTime}; + $dur = 1 if ( $dur <= 0 ); + my $duration = sprintf("%.1f", $dur / 60); + + my $HostListStr = ""; + my $counter=0; + foreach my $f ( @{$ArchiveReq{HostList}} ) { + $HostListStr .= <$f@{$ArchiveReq{BackupList}}[$counter] +EOF + $counter++; + } + + Header(eval("qq{$Lang->{Archive___num_details_for__host}}")); + print(eval("qq{$Lang->{Archive___num_details_for__host2 }}")); + Trailer(); +} + +1; diff --git a/lib/BackupPC/CGI/Browse.pm b/lib/BackupPC/CGI/Browse.pm index aeb006b..3eb7f30 100644 --- a/lib/BackupPC/CGI/Browse.pm +++ b/lib/BackupPC/CGI/Browse.pm @@ -60,6 +60,15 @@ sub action # Find the requested backup and the previous filled backup # my @Backups = $bpc->BackupInfoRead($host); + + # + # default to the newest backup + # + if ( !defined($In{num}) && defined(@Backups) ) { + $i = @Backups - 1; + $num = $Backups[$i]{num}; + } + for ( $i = 0 ; $i < @Backups ; $i++ ) { last if ( $Backups[$i]{num} == $num ); } @@ -129,28 +138,28 @@ sub action # Display directory if it exists in current backup. # First find out if there are subdirs # - my($bold, $unbold, $BGcolor); + my $tdStyle; + my $linkStyle = "fview"; $img |= 1 << 6; $img |= 1 << 5 if ( $attr->{$f}{nlink} > 2 ); if ( $dirOpen ) { - $bold = ""; - $unbold = ""; + $linkStyle = "fviewbold"; $img |= 1 << 2; $img |= 1 << 3 if ( $attr->{$f}{nlink} > 2 ); } my $imgFileName = sprintf("%07b.gif", $img); $imgStr = ""; if ( "$relDir/$f" eq $dir ) { - $BGcolor = " bgcolor=\"$Conf{CgiHeaderBgColor}\""; + $tdStyle = "fviewon"; } else { - $BGcolor = ""; + $tdStyle = "fviewoff"; } my $dirName = $f; $dirName =~ s/ / /g; push(@DirStr, {needTick => 1, - tdArgs => $BGcolor, + tdArgs => " class=\"$tdStyle\"", link => <$imgStr $bold$dirName$unbold +$imgStr $dirName EOF $fileCnt++; $gotDir = 1; @@ -184,34 +193,49 @@ EOF # # This is the selected directory, so display all the files # - my $attrStr; + my ($attrStr, $iconStr); if ( defined($a = $attr->{$f}) ) { my $mtimeStr = $bpc->timeStamp($a->{mtime}); # UGH -> fix this my $typeStr = BackupPC::Attrib::fileType2Text(undef, $a->{type}); my $modeStr = sprintf("0%o", $a->{mode} & 07777); + $iconStr = < +EOF $attrStr .= <$typeStr - $modeStr - $a->{backupNum} - $a->{size} - $mtimeStr + $typeStr + $modeStr + $a->{backupNum} + $a->{size} + $mtimeStr EOF } else { - $attrStr .= " \n"; + $attrStr .= " \n"; } (my $fDisp = "${EscHTML($f)}") =~ s/ / /g; if ( $gotDir ) { $fileStr .= < $fDisp + +
+  $iconStr + +  $fDisp +
+ $attrStr EOF } else { $fileStr .= < $fDisp + +
+  $iconStr + +  $fDisp +
+ $attrStr EOF @@ -246,7 +270,6 @@ EOF my $numF = join(", #", @mergeNums); $filledBackup = eval("qq{$Lang->{This_display_is_merged_with_backup}}"); } - Header(eval("qq{$Lang->{Browse_backup__num_for__host}}")); foreach my $d ( @DirStrPrev ) { $dirStr .= "{tdArgs}>$d->{link}\n"; @@ -273,14 +296,22 @@ EOF $pathURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; $shareURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; foreach my $i ( $view->backupList($share, $dir) ) { - push(@otherDirs, "$i"); + push(@otherDirs, $i); } if ( @otherDirs ) { my $otherDirs = join(",\n", @otherDirs); + my $inc = 0; + foreach my $value (@otherDirs) { + my $selected = undef; + my $showDate = timeStamp2($Backups[$inc]{startTime}); + $selected = " selected" if ($value == $num); + $otherDirs .= "\n"; + $inc++; + } $filledBackup .= eval("qq{$Lang->{Visit_this_directory_in_backup}}"); } - print (eval("qq{$Lang->{Backup_browse_for__host}}")); + my $content = eval("qq{$Lang->{Backup_browse_for__host}}"); + Header(eval("qq{$Lang->{Browse_backup__num_for__host}}"), $content); Trailer(); } diff --git a/lib/BackupPC/CGI/DirHistory.pm b/lib/BackupPC/CGI/DirHistory.pm index a1b2d2d..728068f 100644 --- a/lib/BackupPC/CGI/DirHistory.pm +++ b/lib/BackupPC/CGI/DirHistory.pm @@ -84,8 +84,9 @@ sub action my %inode2name; my $nameCnt = 0; (my $fDisp = "${EscHTML($f)}") =~ s/ / /g; - $fileStr .= "$fDisp"; - my($colSpan, $url, $inode, $type, $tdColor); + $fileStr .= "$fDisp"; + my($colSpan, $url, $inode, $type); + my $tdClass = ' class="histView"'; for ( $i = 0 ; $i < @Backups ; $i++ ) { my($path); if ( $colSpan > 0 ) { @@ -109,16 +110,16 @@ sub action $colSpan++; next; } - $fileStr .= "" + $fileStr .= "" . "$url"; $colSpan = 0; - $tdColor = ""; + $tdClass = ' class="histView"'; } if ( !defined($hist->{$f}[$i]) ) { $colSpan = 1; $url = " "; $inode = -3; # special value for missing - $tdColor = ' bgcolor="#ffffaa"'; + $tdClass = ' class="histViewMis"'; next; } if ( $dir eq "" ) { @@ -154,7 +155,7 @@ EOF $colSpan = 1; } if ( $colSpan > 0 ) { - $fileStr .= "$url"; + $fileStr .= "$url"; $colSpan = 0; } $fileStr .= "\n"; @@ -164,11 +165,8 @@ EOF $dirDisplay =~ s{//+}{/}g; $dirDisplay =~ s{/+$}{}g; $dirDisplay = "/" if ( $dirDisplay eq "" ); - - Header(eval("qq{$Lang->{DirHistory_backup_for__host}}")); - - print (eval("qq{$Lang->{DirHistory_for__host}}")); - + my $content = eval("qq{$Lang->{DirHistory_for__host}}"); + Header(eval("qq{$Lang->{DirHistory_backup_for__host}}"), $content); Trailer(); } diff --git a/lib/BackupPC/CGI/EmailSummary.pm b/lib/BackupPC/CGI/EmailSummary.pm index 1b9ec70..68bd498 100644 --- a/lib/BackupPC/CGI/EmailSummary.pm +++ b/lib/BackupPC/CGI/EmailSummary.pm @@ -62,8 +62,8 @@ EOF foreach my $t ( sort({$b <=> $a} keys(%EmailStr)) ) { $str .= $EmailStr{$t}; } - Header($Lang->{Email_Summary}); - print (eval("qq{$Lang->{Recent_Email_Summary}}")); + my $content = eval("qq{$Lang->{Recent_Email_Summary}}"); + Header($Lang->{Email_Summary}, $content); Trailer(); } diff --git a/lib/BackupPC/CGI/GeneralInfo.pm b/lib/BackupPC/CGI/GeneralInfo.pm index bb71e51..1418ace 100644 --- a/lib/BackupPC/CGI/GeneralInfo.pm +++ b/lib/BackupPC/CGI/GeneralInfo.pm @@ -54,14 +54,14 @@ sub action (my $cmd = $Jobs{$host}{cmd}) =~ s/$BinDir\///g; (my $xferPid = $Jobs{$host}{xferPid}) =~ s/,/, /g; $jobStr .= < ${HostLink($host)} - $Jobs{$host}{type} - ${UserLink(defined($Hosts->{$host}) + ${HostLink($host)} + $Jobs{$host}{type} + ${UserLink(defined($Hosts->{$host}) ? $Hosts->{$host}{user} : "")} - $startTime - $cmd - $Jobs{$host}{pid} - $xferPid + $startTime + $cmd + $Jobs{$host}{pid} + $xferPid EOF $jobStr .= "\n"; } @@ -89,14 +89,14 @@ EOF } (my $shortErr = $Status{$host}{error}) =~ s/(.{48}).*/$1.../; $statusStr .= < ${HostLink($host)} - $Status{$host}{type} - ${UserLink(defined($Hosts->{$host}) + ${HostLink($host)} + $Status{$host}{type} + ${UserLink(defined($Hosts->{$host}) ? $Hosts->{$host}{user} : "")} - $startTime - $XferViewStr - $errorTime - ${EscHTML($shortErr)} + $startTime + $XferViewStr + $errorTime + ${EscHTML($shortErr)} EOF } my $now = timeStamp2(time); @@ -107,6 +107,7 @@ EOF my $numUserQueue = $QueueLen{UserQueue}; my $numCmdQueue = $QueueLen{CmdQueue}; my $serverStartTime = timeStamp2($Info{startTime}); + my $configLoadTime = timeStamp2($Info{ConfigLTime}); my $poolInfo = genPoolInfo("pool", \%Info); my $cpoolInfo = genPoolInfo("cpool", \%Info); if ( $Info{poolFileCnt} > 0 && $Info{cpoolFileCnt} > 0 ) { @@ -123,9 +124,8 @@ EOF } elsif ( $Info{cpoolFileCnt} > 0 ) { $poolInfo = $cpoolInfo; } - - Header($Lang->{H_BackupPC_Server_Status}); - print (eval ("qq{$Lang->{BackupPC_Server_Status}}")); + my $content = eval ("qq{$Lang->{BackupPC_Server_Status}}"); + Header($Lang->{H_BackupPC_Server_Status}, $content); Trailer(); } diff --git a/lib/BackupPC/CGI/HostInfo.pm b/lib/BackupPC/CGI/HostInfo.pm index 81d75a3..a49eb79 100644 --- a/lib/BackupPC/CGI/HostInfo.pm +++ b/lib/BackupPC/CGI/HostInfo.pm @@ -76,6 +76,50 @@ sub action } ReadUserEmailInfo(); + if ($Conf{XferMethod} eq "archive" ) { + my @Archives = $bpc->ArchiveInfoRead($host); + my ($ArchiveStr,$warnStr); + + for ( my $i = 0 ; $i < @Archives ; $i++ ) { + my $startTime = timeStamp2($Archives[$i]{startTime}); + my $dur = $Archives[$i]{endTime} - $Archives[$i]{startTime}; + $dur = 1 if ( $dur <= 0 ); + my $duration = sprintf("%.1f", $dur / 60); + my $Archives_Result = $Lang->{failed}; + if ($Archives[$i]{result} ne "failed") { $Archives_Result = $Lang->{success}; } + $ArchiveStr .= <$Archives[$i]{num} + $Archives_Result + $startTime + $duration + +EOF + } + if ( $ArchiveStr ne "" ) { + $ArchiveStr = eval("qq{$Lang->{Archive_Summary}}"); + } + if ( @Archives == 0 ) { + $warnStr = $Lang->{There_have_been_no_archives}; + } + if ( $StatusHost{BgQueueOn} ) { + $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon}}"); + } + if ( $StatusHost{UserQueueOn} ) { + $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon}}"); + } + if ( $StatusHost{CmdQueueOn} ) { + $statusStr .= eval("qq{$Lang->{A_command_for_host_is_on_the_command_queue_will_run_soon}}"); + } + + my $content = eval("qq{$Lang->{Host__host_Archive_Summary2}}"); + Header(eval("qq{$Lang->{Host__host_Archive_Summary}}"), $content); + Trailer(); + return; + } + + # + # Normal, non-archive case + # my @Backups = $bpc->BackupInfoRead($host); my($str, $sizeStr, $compStr, $errStr, $warnStr); for ( my $i = 0 ; $i < @Backups ; $i++ ) { @@ -105,52 +149,52 @@ sub action my $filled = $Backups[$i]{noFill} ? $Lang->{No} : $Lang->{Yes}; $filled .= " ($Backups[$i]{fillFromNum}) " if ( $Backups[$i]{fillFromNum} ne "" ); - my $ltype = $Lang->{"backupType_$Backups[$i]{type}"}; + my $ltype = $Lang->{"backupType_$Backups[$i]{type}"}; $str .= < $Backups[$i]{num} - $ltype - $filled - $startTime - $duration - $age - $TopDir/pc/$host/$Backups[$i]{num} + $Backups[$i]{num} + $ltype + $filled + $startTime + $duration + $age + $TopDir/pc/$host/$Backups[$i]{num} EOF $sizeStr .= < $Backups[$i]{num} - $ltype - $Backups[$i]{nFiles} - $MB - $MBperSec - $Backups[$i]{nFilesExist} - $MBExist - $Backups[$i]{nFilesNew} - $MBNew + $Backups[$i]{num} + $ltype + $Backups[$i]{nFiles} + $MB + $MBperSec + $Backups[$i]{nFilesExist} + $MBExist + $Backups[$i]{nFilesNew} + $MBNew EOF - my $is_compress = $Backups[$i]{compress} || $Lang->{off}; - if (! $ExistComp) { $ExistComp = " "; } - if (! $MBExistComp) { $MBExistComp = " "; } + my $is_compress = $Backups[$i]{compress} || $Lang->{off}; + if (! $ExistComp) { $ExistComp = " "; } + if (! $MBExistComp) { $MBExistComp = " "; } $compStr .= < $Backups[$i]{num} - $ltype - $is_compress - $MBExist - $MBExistComp - $ExistComp - $MBNew - $MBNewComp - $NewComp + $Backups[$i]{num} + $ltype + $is_compress + $MBExist + $MBExistComp + $ExistComp + $MBNew + $MBNewComp + $NewComp EOF $errStr .= < $Backups[$i]{num} - $ltype - $Lang->{XferLOG}, + $Backups[$i]{num} + $ltype + $Lang->{XferLOG}, $Lang->{Errors} - $Backups[$i]{xferErrs} - $Backups[$i]{xferBadFile} - $Backups[$i]{xferBadShare} - $Backups[$i]{tarErrs} + $Backups[$i]{xferErrs} + $Backups[$i]{xferBadFile} + $Backups[$i]{xferBadShare} + $Backups[$i]{tarErrs} EOF } @@ -164,39 +208,39 @@ EOF my $duration = sprintf("%.1f", $dur / 60); my $MB = sprintf("%.1f", $Restores[$i]{size} / (1024*1024)); my $MBperSec = sprintf("%.2f", $Restores[$i]{size} / (1024*1024*$dur)); - my $Restores_Result = $Lang->{failed}; - if ($Restores[$i]{result} ne "failed") { $Restores_Result = $Lang->{success}; } - $restoreStr .= <$Restores[$i]{num} - $Restores_Result - $startTime - $duration - $Restores[$i]{nFiles} - $MB - $Restores[$i]{tarCreateErrs} - $Restores[$i]{xferErrs} + my $Restores_Result = $Lang->{failed}; + if ($Restores[$i]{result} ne "failed") { $Restores_Result = $Lang->{success}; } + $restoreStr .= <$Restores[$i]{num} + $Restores_Result + $startTime + $duration + $Restores[$i]{nFiles} + $MB + $Restores[$i]{tarCreateErrs} + $Restores[$i]{xferErrs} EOF } if ( $restoreStr ne "" ) { - $restoreStr = eval("qq{$Lang->{Restore_Summary}}"); + $restoreStr = eval("qq{$Lang->{Restore_Summary}}"); } if ( @Backups == 0 ) { $warnStr = $Lang->{This_PC_has_never_been_backed_up}; } if ( defined($Hosts->{$host}) ) { my $user = $Hosts->{$host}{user}; - my @moreUsers = sort(keys(%{$Hosts->{$host}{moreUsers}})); - my $moreUserStr; - foreach my $u ( sort(keys(%{$Hosts->{$host}{moreUsers}})) ) { - $moreUserStr .= ", " if ( $moreUserStr ne "" ); - $moreUserStr .= "${UserLink($u)}"; - } - if ( $moreUserStr ne "" ) { - $moreUserStr = " ($Lang->{and} $moreUserStr).\n"; - } else { - $moreUserStr = ".\n"; - } + my @moreUsers = sort(keys(%{$Hosts->{$host}{moreUsers}})); + my $moreUserStr; + foreach my $u ( sort(keys(%{$Hosts->{$host}{moreUsers}})) ) { + $moreUserStr .= ", " if ( $moreUserStr ne "" ); + $moreUserStr .= "${UserLink($u)}"; + } + if ( $moreUserStr ne "" ) { + $moreUserStr = " ($Lang->{and} $moreUserStr).\n"; + } else { + $moreUserStr = ".\n"; + } if ( $user ne "" ) { $statusStr .= eval("qq{$Lang->{This_PC_is_used_by}$moreUserStr}"); } @@ -230,8 +274,8 @@ EOF $statusStr .= eval("qq{$Lang->{Last_status_is_state_StatusHost_state_reason_as_of_startTime}}"); if ( $StatusHost{state} ne "Status_backup_in_progress" - && $StatusHost{state} ne "Status_restore_in_progress" - && $StatusHost{error} ne "" ) { + && $StatusHost{state} ne "Status_restore_in_progress" + && $StatusHost{error} ne "" ) { $statusStr .= eval("qq{$Lang->{Last_error_is____EscHTML_StatusHost_error}}"); } my $priorStr = "Pings"; @@ -243,8 +287,8 @@ EOF $statusStr .= eval("qq{$Lang->{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times}}"); if ( $StatusHost{aliveCnt} >= $Conf{BlackoutGoodCnt} - && $Conf{BlackoutGoodCnt} >= 0 && $Conf{BlackoutHourBegin} >= 0 - && $Conf{BlackoutHourEnd} >= 0 ) { + && $Conf{BlackoutGoodCnt} >= 0 && $Conf{BlackoutHourBegin} >= 0 + && $Conf{BlackoutHourEnd} >= 0 ) { my(@days) = qw(Sun Mon Tue Wed Thu Fri Sat); my($days) = join(", ", @days[@{$Conf{BlackoutWeekDays}}]); my($t0) = sprintf("%d:%02d", $Conf{BlackoutHourBegin}, @@ -269,9 +313,8 @@ EOF } $startIncrStr = eval ("qq{$startIncrStr}"); - - Header(eval("qq{$Lang->{Host__host_Backup_Summary}}")); - print(eval("qq{$Lang->{Host__host_Backup_Summary2}}")); + my $content = eval("qq{$Lang->{Host__host_Backup_Summary2}}"); + Header(eval("qq{$Lang->{Host__host_Backup_Summary}}"), $content); Trailer(); } diff --git a/lib/BackupPC/CGI/LOGlist.pm b/lib/BackupPC/CGI/LOGlist.pm index 8e55b46..71b7d81 100644 --- a/lib/BackupPC/CGI/LOGlist.pm +++ b/lib/BackupPC/CGI/LOGlist.pm @@ -74,8 +74,8 @@ sub action $mtimeStr EOF } - Header($Lang->{BackupPC__Log_File_History}); - print (eval("qq{$Lang->{Log_File_History__hdr}}")); + my $content = eval("qq{$Lang->{Log_File_History__hdr}}"); + Header($Lang->{BackupPC__Log_File_History}, $content); Trailer(); } diff --git a/lib/BackupPC/CGI/Lib.pm b/lib/BackupPC/CGI/Lib.pm index 078f3fd..a9dad66 100644 --- a/lib/BackupPC/CGI/Lib.pm +++ b/lib/BackupPC/CGI/Lib.pm @@ -48,7 +48,7 @@ use vars qw($Cgi %In $MyURL $User %Conf $TopDir $BinDir $bpc); use vars qw(%Status %Info %Jobs @BgQueue @UserQueue @CmdQueue %QueueLen %StatusHost); use vars qw($Hosts $HostsMTime $ConfigMTime $PrivAdmin); -use vars qw(%UserEmailInfo $UserEmailInfoMTime %RestoreReq); +use vars qw(%UserEmailInfo $UserEmailInfoMTime %RestoreReq %ArchiveReq); use vars qw($Lang); @ISA = qw(Exporter); @@ -80,7 +80,7 @@ use vars qw($Lang); %Status %Info %Jobs @BgQueue @UserQueue @CmdQueue %QueueLen %StatusHost $Hosts $HostsMTime $ConfigMTime $PrivAdmin - %UserEmailInfo $UserEmailInfoMTime %RestoreReq + %UserEmailInfo $UserEmailInfoMTime %RestoreReq %ArchiveReq $Lang ); @@ -117,10 +117,8 @@ sub NewRequest $Lang = $bpc->Lang(); $ConfigMTime = $bpc->ConfigMTime(); } elsif ( $bpc->ConfigMTime() != $ConfigMTime ) { - $bpc->ConfigRead(); - %Conf = $bpc->Conf(); - $ConfigMTime = $bpc->ConfigMTime(); - $Lang = $bpc->Lang(); + $bpc->ServerMesg("log Re-read config file because mtime changed"); + $bpc->ServerMesg("server reload"); } # @@ -223,10 +221,6 @@ sub ErrorExit my(@mesg) = @_; my($head) = shift(@mesg); my($mesg) = join("

\n

", @mesg); - $Conf{CgiHeaderFontType} ||= "arial"; - $Conf{CgiHeaderFontSize} ||= "3"; - $Conf{CgiNavBarBgColor} ||= "#ddeeee"; - $Conf{CgiHeaderBgColor} ||= "#99cc33"; if ( !defined($ENV{REMOTE_USER}) ) { $mesg .= <ServerMesg("log User $User (host=$In{host}) got CGI error: $head") if ( defined($bpc) ); if ( !defined($Lang->{Error}) ) { - Header("BackupPC: Error"); $mesg = <$mesg

EOF + Header("BackupPC: Error", $content); Trailer(); } else { - Header(eval("qq{$Lang->{Error}}")); - print (eval("qq{$Lang->{Error____head}}")); + my $content = eval("qq{$Lang->{Error____head}}"); + Header(eval("qq{$Lang->{Error}}"), $content); Trailer(); } exit(1); @@ -267,7 +261,16 @@ sub ServerConnect return if ( $bpc->ServerOK() ); $bpc->ServerDisconnect(); if ( my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}) ) { - ErrorExit(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}")); + if ( CheckPermission() + && -f $Conf{ServerInitdPath} + && $Conf{ServerInitdStartCmd} ne "" ) { + Header(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}")); + print (eval("qq{$Lang->{Admin_Start_Server}}")); + Trailer(); + exit(1); + } else { + ErrorExit(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}")); + } } } @@ -332,12 +335,20 @@ sub CheckPermission # sub GetUserHosts { - if ( $Conf{CgiNavBarAdminAllHosts} && CheckPermission() ) { - return sort keys %$Hosts; - } + my($host) = @_; + my @hosts; - return sort grep { $Hosts->{$_}{user} eq $User || + if ( $Conf{CgiNavBarAdminAllHosts} && CheckPermission() ) { + @hosts = sort keys %$Hosts; + } else { + @hosts = sort grep { $Hosts->{$_}{user} eq $User || defined($Hosts->{$_}{moreUsers}{$User}) } keys(%$Hosts); + } + # + # return the selected host first (if present) + # + return @hosts if ( !defined($host) || !grep(/^$host$/, @hosts) ); + return ($host, grep(!/^$host$/, @hosts)); } # @@ -381,94 +392,127 @@ sub ConfirmIPAddress sub Header { - my($title) = @_; + my($title, $content) = @_; my @adminLinks = ( - { link => "", name => $Lang->{Status}, - priv => 1}, - { link => "?action=summary", name => $Lang->{PC_Summary} }, - { link => "?action=view&type=LOG", name => $Lang->{LOG_file} }, - { link => "?action=LOGlist", name => $Lang->{Old_LOGs} }, - { link => "?action=emailSummary", name => $Lang->{Email_summary} }, - { link => "?action=view&type=config", name => $Lang->{Config_file} }, - { link => "?action=view&type=hosts", name => $Lang->{Hosts_file} }, - { link => "?action=queue", name => $Lang->{Current_queues} }, - { link => "?action=view&type=docs", name => $Lang->{Documentation}, - priv => 1}, + { link => "", name => $Lang->{Status}, + priv => 1}, + { link => "?action=adminOpts", name => $Lang->{Admin_Options} }, + { link => "?action=summary", name => $Lang->{PC_Summary} }, + { link => "?action=view&type=LOG", name => $Lang->{LOG_file} }, + { link => "?action=LOGlist", name => $Lang->{Old_LOGs} }, + { link => "?action=emailSummary", name => $Lang->{Email_summary} }, + { link => "?action=view&type=config", name => $Lang->{Config_file} }, + { link => "?action=view&type=hosts", name => $Lang->{Hosts_file} }, + { link => "?action=queue", name => $Lang->{Current_queues} }, + { link => "?action=view&type=docs", name => $Lang->{Documentation}, + priv => 1}, { link => "http://backuppc.sourceforge.net/faq", name => "FAQ", - priv => 1}, + priv => 1}, { link => "http://backuppc.sourceforge.net", name => "SourceForge", - priv => 1}, + priv => 1}, ); print $Cgi->header(); print < $title +$Conf{CSSstylesheet} $Conf{CgiHeaders} - - - - -
+ +
EOF - NavSectionTitle("BackupPC"); - print " \n"; - if ( defined($In{host}) && defined($Hosts->{$In{host}}) ) { - my $host = $In{host}; - NavSectionTitle( eval("qq{$Lang->{Host_Inhost}}") ); - NavSectionStart(); - NavLink("?host=${EscURI($host)}", $Lang->{Home}); - NavLink("?action=view&type=LOG&host=${EscURI($host)}", $Lang->{LOG_file}); - NavLink("?action=LOGlist&host=${EscURI($host)}", $Lang->{Old_LOGs}); - if ( -f "$TopDir/pc/$host/SmbLOG.bad" - || -f "$TopDir/pc/$host/SmbLOG.bad.z" - || -f "$TopDir/pc/$host/XferLOG.bad" - || -f "$TopDir/pc/$host/XferLOG.bad.z" ) { - NavLink("?action=view&type=XferLOGbad&host=${EscURI($host)}", - $Lang->{Last_bad_XferLOG}); - NavLink("?action=view&type=XferErrbad&host=${EscURI($host)}", - $Lang->{Last_bad_XferLOG_errors_only}); - } - if ( -f "$TopDir/pc/$host/config.pl" ) { - NavLink("?action=view&type=config&host=${EscURI($host)}", $Lang->{Config_file}); - } - NavSectionEnd(); + if (!defined($In{host})) { + print < +$content +


+ +
$Lang->{Host_or_User_name}
+ -
+ EOF - if ( defined($Hosts) && %$Hosts > 0 ) { - NavSectionStart(1); - foreach my $host ( GetUserHosts() ) { - NavLink("?host=${EscURI($host)}", $host); - } - NavSectionEnd(); + NavSectionTitle($Lang->{NavSectionTitle_}); + foreach my $l ( @adminLinks ) { + if ( $PrivAdmin || $l->{priv} ) { + NavLink($l->{link}, $l->{name}); } - print <   - +} + +print <

+ EOF } sub Trailer { print < EOF } @@ -478,41 +522,35 @@ sub NavSectionTitle { my($head) = @_; print < -$head - - + EOF } sub NavSectionStart { - my($padding) = @_; - - $padding = 1 if ( !defined($padding) ); - print < -EOF } sub NavSectionEnd { - print "\n"; } sub NavLink { my($link, $text) = @_; - print "·"; if ( defined($link) ) { + my($class); + $class = " class=\"NavCurrent\"" + if ( length($link) && $ENV{REQUEST_URI} =~ /\Q$link\E$/ + || $link eq "" && $ENV{REQUEST_URI} !~ /\?/ ); $link = "$MyURL$link" if ( $link eq "" || $link =~ /^\?/ ); print <$text +$text EOF } else { print <$text +$text
EOF } } @@ -521,12 +559,7 @@ sub h1 { my($str) = @_; return \< - - $str - - +
$str
EOF } @@ -534,11 +567,6 @@ sub h2 { my($str) = @_; return \< - - $str - - +
$str
EOF } diff --git a/lib/BackupPC/CGI/Queue.pm b/lib/BackupPC/CGI/Queue.pm index 116ea0f..82eeed4 100644 --- a/lib/BackupPC/CGI/Queue.pm +++ b/lib/BackupPC/CGI/Queue.pm @@ -79,10 +79,8 @@ EOF $cmd $req->{cmd}[0] EOF } - Header($Lang->{BackupPC__Queue_Summary}); - - print ( eval ( "qq{$Lang->{Backup_Queue_Summary}}") ); - + my $content = eval ( "qq{$Lang->{Backup_Queue_Summary}}"); + Header($Lang->{BackupPC__Queue_Summary}, $content); Trailer(); } diff --git a/lib/BackupPC/CGI/ReloadServer.pm b/lib/BackupPC/CGI/ReloadServer.pm new file mode 100644 index 0000000..13b66b2 --- /dev/null +++ b/lib/BackupPC/CGI/ReloadServer.pm @@ -0,0 +1,51 @@ +#============================================================= -*-perl-*- +# +# BackupPC::CGI::ReloadServer package +# +# DESCRIPTION +# +# This module implements the ReloadServer action for the CGI interface. +# +# AUTHOR +# Craig Barratt +# +# COPYRIGHT +# Copyright (C) 2003 Craig Barratt +# +# 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 +# +#======================================================================== +# +# Version 2.1.0_CVS, released 3 Jul 2003. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +package BackupPC::CGI::ReloadServer; + +use strict; +use BackupPC::CGI::Lib qw(:all); + +sub action +{ + if ( defined($bpc) ) { + $bpc->ServerMesg("log User $User requested server configuration reload"); + $bpc->ServerMesg("server reload"); + } + print $Cgi->redirect($MyURL); +} + +1; diff --git a/lib/BackupPC/CGI/Restore.pm b/lib/BackupPC/CGI/Restore.pm index f236e1e..2a6d683 100644 --- a/lib/BackupPC/CGI/Restore.pm +++ b/lib/BackupPC/CGI/Restore.pm @@ -42,7 +42,7 @@ use Data::Dumper; sub action { - my($str, $reply); + my($str, $reply, $content); my $Privileged = CheckPermission($In{host}); if ( !$Privileged ) { ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_restore_backup_files}}")); @@ -101,19 +101,19 @@ EOF # # Tell the user what options they have # - Header(eval("qq{$Lang->{Restore_Options_for__host}}")); - print(eval("qq{$Lang->{Restore_Options_for__host2}}")); + $content .= eval("qq{$Lang->{Restore_Options_for__host2}}"); # # Verify that Archive::Zip is available before showing the # zip restore option # if ( eval { require Archive::Zip } ) { - print (eval("qq{$Lang->{Option_2__Download_Zip_archive}}")); + $content .= eval("qq{$Lang->{Option_2__Download_Zip_archive}}"); } else { - print (eval("qq{$Lang->{Option_2__Download_Zip_archive2}}")); + $content .= eval("qq{$Lang->{Option_2__Download_Zip_archive2}}"); } - print (eval("qq{$Lang->{Option_3__Download_Zip_archive}}")); + $content .= eval("qq{$Lang->{Option_3__Download_Zip_archive}}"); + Header(eval("qq{$Lang->{Restore_Options_for__host}}"), $content); Trailer(); } elsif ( $In{type} == 1 ) { # @@ -209,8 +209,8 @@ EOF $host:/$strippedShare$f$In{hostDest}:/$strippedShareDest$targetFile EOF } - Header(eval("qq{$Lang->{Restore_Confirm_on__host}}")); - print(eval("qq{$Lang->{Are_you_sure}}")); + my $content = eval("qq{$Lang->{Are_you_sure}}"); + Header(eval("qq{$Lang->{Restore_Confirm_on__host}}"), $content); Trailer(); } elsif ( $In{type} == 4 ) { if ( !defined($Hosts->{$In{hostDest}}) ) { @@ -265,8 +265,8 @@ EOF $reply = $bpc->ServerMesg("restore ${EscURI($ipAddr)}" . " ${EscURI($hostDest)} $User $reqFileName"); $str = eval("qq{$Lang->{Restore_requested_to_host__hostDest__backup___num}}"); - Header(eval("qq{$Lang->{Restore_Requested_on__hostDest}}")); - print (eval("qq{$Lang->{Reply_from_server_was___reply}}")); + my $content = eval("qq{$Lang->{Reply_from_server_was___reply}}"); + Header(eval("qq{$Lang->{Restore_Requested_on__hostDest}}"), $content); Trailer(); } } diff --git a/lib/BackupPC/CGI/RestoreInfo.pm b/lib/BackupPC/CGI/RestoreInfo.pm index 83583e8..b018bf5 100644 --- a/lib/BackupPC/CGI/RestoreInfo.pm +++ b/lib/BackupPC/CGI/RestoreInfo.pm @@ -83,9 +83,8 @@ sub action $RestoreReq{hostSrc}:/$strippedShareSrc$f$RestoreReq{hostDest}:/$strippedShareDest$targetFile EOF } - - Header(eval("qq{$Lang->{Restore___num_details_for__host}}")); - print(eval("qq{$Lang->{Restore___num_details_for__host2 }}")); + my $content = eval("qq{$Lang->{Restore___num_details_for__host2 }}"); + Header(eval("qq{$Lang->{Restore___num_details_for__host}}"),$content); Trailer(); } diff --git a/lib/BackupPC/CGI/StartServer.pm b/lib/BackupPC/CGI/StartServer.pm new file mode 100644 index 0000000..d0cb973 --- /dev/null +++ b/lib/BackupPC/CGI/StartServer.pm @@ -0,0 +1,63 @@ +#============================================================= -*-perl-*- +# +# BackupPC::CGI::StartServer package +# +# DESCRIPTION +# +# This module implements the StartServer action for the CGI interface. +# +# AUTHOR +# Craig Barratt +# +# COPYRIGHT +# Copyright (C) 2003 Craig Barratt +# +# 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 +# +#======================================================================== +# +# Version 2.1.0_CVS, released 3 Jul 2003. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +package BackupPC::CGI::StartServer; + +use strict; +use BackupPC::CGI::Lib qw(:all); + +sub action +{ + if ( -f $Conf{ServerInitdPath} + && $bpc->{Conf}{ServerInitdStartCmd} ne "" + && !$bpc->ServerOK() ) { + my $args = { + serverInitdPath => $bpc->{Conf}{ServerInitdPath}, + sshPath => $bpc->{Conf}{SshPath}, + serverHost => $bpc->{Conf}{ServerHost}, + }; + my $serverInitdStartCmd = $bpc->cmdVarSubstitute($bpc->{Conf}{ServerInitdStartCmd}, $args); + $bpc->cmdSystemOrEval($serverInitdStartCmd, undef, $args); + for ( my $i = 0; $i < 10; $i++ ) { + last unless ( $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}) ); + sleep(1); + } + $bpc->ServerDisconnect(); + } + print $Cgi->redirect($MyURL); +} + +1; diff --git a/lib/BackupPC/CGI/StartStopBackup.pm b/lib/BackupPC/CGI/StartStopBackup.pm index 38b7f6f..e62ced2 100644 --- a/lib/BackupPC/CGI/StartStopBackup.pm +++ b/lib/BackupPC/CGI/StartStopBackup.pm @@ -70,17 +70,15 @@ sub action $reply = $bpc->ServerMesg("stop ${EscURI($host)} $User $In{backoff}"); $str = eval("qq{$Lang->{Backup_stopped_dequeued_on__host_by__User}}"); } - - Header(eval ("qq{$Lang->{BackupPC__Backup_Requested_on__host}}") ); - print (eval ("qq{$Lang->{REPLY_FROM_SERVER}}")); + my $content = eval ("qq{$Lang->{REPLY_FROM_SERVER}}"); + Header(eval ("qq{$Lang->{BackupPC__Backup_Requested_on__host}}"),$content); Trailer(); } else { if ( $start ) { my $ipAddr = ConfirmIPAddress($host); - - Header(eval("qq{$Lang->{BackupPC__Start_Backup_Confirm_on__host}}")); - print (eval("qq{$Lang->{Are_you_sure_start}}")); + my $content = eval("qq{$Lang->{Are_you_sure_start}}"); + Header(eval("qq{$Lang->{BackupPC__Start_Backup_Confirm_on__host}}"),$content); } else { my $backoff = ""; GetStatusInfo("host(${EscURI($host)})"); @@ -88,8 +86,8 @@ sub action $backoff = sprintf("%.1f", ($StatusHost{backoffTime} - time) / 3600); } - Header($Lang->{BackupPC__Stop_Backup_Confirm_on__host}); - print (eval ("qq{$Lang->{Are_you_sure_stop}}")); + my $content = eval ("qq{$Lang->{Are_you_sure_stop}}"); + Header($Lang->{BackupPC__Stop_Backup_Confirm_on__host},$content); } Trailer(); } diff --git a/lib/BackupPC/CGI/StopServer.pm b/lib/BackupPC/CGI/StopServer.pm new file mode 100644 index 0000000..f73f4b8 --- /dev/null +++ b/lib/BackupPC/CGI/StopServer.pm @@ -0,0 +1,56 @@ +#============================================================= -*-perl-*- +# +# BackupPC::CGI::StopServer package +# +# DESCRIPTION +# +# This module implements the StopServer action for the CGI interface. +# +# AUTHOR +# Craig Barratt +# +# COPYRIGHT +# Copyright (C) 2003 Craig Barratt +# +# 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 +# +#======================================================================== +# +# Version 2.1.0_CVS, released 3 Jul 2003. +# +# See http://backuppc.sourceforge.net. +# +#======================================================================== + +package BackupPC::CGI::StopServer; + +use strict; +use BackupPC::CGI::Lib qw(:all); + +sub action +{ + if ( defined($bpc) && $bpc->ServerOK() ) { + $bpc->ServerMesg("log User $User requested server shutdown"); + $bpc->ServerMesg("server shutdown"); + for ( my $i = 0; $i < 10; $i++ ) { + last unless $bpc->ServerOK(); + sleep(1); + } + sleep(2); + } + print $Cgi->redirect($MyURL); +} + +1; diff --git a/lib/BackupPC/CGI/Summary.pm b/lib/BackupPC/CGI/Summary.pm index 0706b62..50e730b 100644 --- a/lib/BackupPC/CGI/Summary.pm +++ b/lib/BackupPC/CGI/Summary.pm @@ -48,15 +48,24 @@ sub action GetStatusInfo("hosts"); my $Privileged = CheckPermission(); - if ( !$Privileged ) { - ErrorExit($Lang->{Only_privileged_users_can_view_PC_summaries} ); - } - foreach my $host ( sort(keys(%Status)) ) { + foreach my $host ( GetUserHosts() ) { ## give access to users hosts only + ## foreach my $host ( sort(keys(%Status)) ) { my($fullDur, $incrCnt, $incrAge, $fullSize, $fullRate, $reasonHilite); my($shortErr); my @Backups = $bpc->BackupInfoRead($host); my $fullCnt = $incrCnt = 0; my $fullAge = $incrAge = -1; + + if ( defined(my $error = $bpc->ConfigRead($host)) ) { + print("dump failed: Can't read PC's config file: $error\n"); + exit(1); + } + %Conf = $bpc->Conf(); + + if ($Conf{XferMethod} eq "archive" ) { + next; + } + for ( my $i = 0 ; $i < @Backups ; $i++ ) { if ( $Backups[$i]{type} eq "full" ) { $fullCnt++; @@ -102,17 +111,17 @@ sub action } $str = < ${HostLink($host)} - ${UserLink(defined($Hosts->{$host}) + ${HostLink($host)} + ${UserLink(defined($Hosts->{$host}) ? $Hosts->{$host}{user} : "")} - $fullCnt - $fullAge - $fullSize - $fullRate - $incrCnt - $incrAge - $Lang->{$Status{$host}{state}} - $Lang->{$Status{$host}{reason}}$shortErr + $fullCnt + $fullAge + $fullSize + $fullRate + $incrCnt + $incrAge + $Lang->{$Status{$host}{state}} + $Lang->{$Status{$host}{reason}}$shortErr EOF if ( @Backups == 0 ) { $hostCntNone++; @@ -126,9 +135,8 @@ EOF $incrSizeTot = sprintf("%.2f", $incrSizeTot / 1000); my $now = timeStamp2(time); - Header($Lang->{BackupPC__Server_Summary}); - print eval ("qq{$Lang->{BackupPC_Summary}}"); - + my $content = eval ("qq{$Lang->{BackupPC_Summary}}"); + Header($Lang->{BackupPC__Server_Summary}, $content); Trailer(); } diff --git a/lib/BackupPC/CGI/View.pm b/lib/BackupPC/CGI/View.pm index 2a4239b..4f3070a 100644 --- a/lib/BackupPC/CGI/View.pm +++ b/lib/BackupPC/CGI/View.pm @@ -72,6 +72,11 @@ sub action } elsif ( $type eq "RestoreErr" ) { $file = "$TopDir/pc/$host/RestoreLOG$ext"; $comment = $Lang->{Extracting_only_Errors}; + } elsif ( $type eq "ArchiveLOG" ) { + $file = "$TopDir/pc/$host/ArchiveLOG$ext"; + } elsif ( $type eq "ArchiveErr" ) { + $file = "$TopDir/pc/$host/ArchiveLOG$ext"; + $comment = $Lang->{Extracting_only_Errors}; } elsif ( $host ne "" && $type eq "config" ) { $file = "$TopDir/pc/$host/config.pl"; $file = "$TopDir/conf/$host.pl" @@ -81,9 +86,10 @@ sub action $file = "$BinDir/../doc/BackupPC.html"; if ( open(LOG, $file) ) { binmode(LOG); - Header($Lang->{BackupPC__Documentation}); - print while ( ); + my $content; + $content .= $_ while ( ); close(LOG); + Header($Lang->{BackupPC__Documentation}, $content); Trailer(); } else { ErrorExit(eval("qq{$Lang->{Unable_to_open__file__configuration_problem}}")); @@ -106,21 +112,21 @@ sub action $file .= ".z"; $compress = 1; } - Header(eval("qq{$Lang->{Backup_PC__Log_File__file}}") ); - print( eval ("qq{$Lang->{Log_File__file__comment}}")); + my $content; + $content .= eval ("qq{$Lang->{Log_File__file__comment}}"); if ( defined($fh = BackupPC::FileZIO->open($file, 0, $compress)) ) { my $mtimeStr = $bpc->timeStamp((stat($file))[9], 1); - print ( eval ("qq{$Lang->{Contents_of_log_file}}")); + $content .= ( eval ("qq{$Lang->{Contents_of_log_file}}")); - print "
";
+        $content .= "
";
         if ( $type eq "XferErr" || $type eq "XferErrbad"
 				|| $type eq "RestoreErr" ) {
 	    my $skipped;
             while ( 1 ) {
                 $_ = $fh->readLine();
                 if ( $_ eq "" ) {
-		    print(eval ("qq{$Lang->{skipped__skipped_lines}}"))
+		    $content .= (eval ("qq{$Lang->{skipped__skipped_lines}}"))
 						    if ( $skipped );
 		    last;
 		}
@@ -144,10 +150,10 @@ sub action
 		    $skipped++;
 		    next;
 		}
-		print(eval("qq{$Lang->{skipped__skipped_lines}}"))
+		$content .= (eval("qq{$Lang->{skipped__skipped_lines}}"))
 						     if ( $skipped );
 		$skipped = 0;
-                print ${EscHTML($_)};
+                $content .= ${EscHTML($_)};
             }
         } elsif ( $linkHosts ) {
             while ( 1 ) {
@@ -156,7 +162,7 @@ sub action
                 my $s = ${EscHTML($_)};
                 $s =~ s/\b([\w-]+)\b/defined($Hosts->{$1})
                                         ? ${HostLink($1)} : $1/eg;
-                print $s;
+                $content .= $s;
             }
         } elsif ( $type eq "config" ) {
             while ( 1 ) {
@@ -167,22 +173,23 @@ sub action
                 s/(SmbShareUserName.*=.*['"]).*(['"])/$1$2/ig;
                 s/(RsyncdPasswd.*=.*['"]).*(['"])/$1$2/ig;
                 s/(ServerMesgSecret.*=.*['"]).*(['"])/$1$2/ig;
-                print ${EscHTML($_)};
+                $content .= ${EscHTML($_)};
             }
         } else {
             while ( 1 ) {
                 $_ = $fh->readLine();
                 last if ( $_ eq "" );
-                print ${EscHTML($_)};
+                $content .= ${EscHTML($_)};
             }
         }
         $fh->close();
     } else {
-	printf( eval("qq{$Lang->{_pre___Can_t_open_log_file__file}}"));
+	$content .= ( eval("qq{$Lang->{_pre___Can_t_open_log_file__file}}"));
     }
-    print <
 EOF
+    Header(eval("qq{$Lang->{Backup_PC__Log_File__file}}"), $content);
     Trailer();
 }
 
diff --git a/lib/BackupPC/Config.pm b/lib/BackupPC/Config.pm
index b8ef8e8..aa7b217 100644
--- a/lib/BackupPC/Config.pm
+++ b/lib/BackupPC/Config.pm
@@ -332,18 +332,6 @@ sub ConnectData
     CgiNavBarAdminAllHosts       => {struct => 'SCALAR',
                                      type   => 'BOOLEAN', },
 
-    CgiHeaderFontType            => {struct => 'SCALAR',
-                                     type   => 'STRING', },
-
-    CgiHeaderFontSize            => {struct => 'SCALAR',
-                                     type   => 'INT', },
-
-    CgiNavBarBgColor             => {struct => 'SCALAR',
-                                     type   => 'STRING', },
-
-    CgiHeaderBgColor             => {struct => 'SCALAR',
-                                     type   => 'STRING', },
-
     CgiHeaders                   => {struct => 'SCALAR',
                                      type   => 'STRING', },
 
diff --git a/lib/BackupPC/Lang/en.pm b/lib/BackupPC/Lang/en.pm
index 4c4554e..2a417bc 100644
--- a/lib/BackupPC/Lang/en.pm
+++ b/lib/BackupPC/Lang/en.pm
@@ -1,11 +1,12 @@
 #!/bin/perl -T
 
 #my %lang;
-
 #use strict;
 
 # --------------------------------
 
+$Lang{Start_Archive} = "Start Archive";
+$Lang{Stop_Dequeue_Archive} = "Stop/Dequeue Archive";
 $Lang{Start_Full_Backup} = "Start Full Backup";
 $Lang{Start_Incr_Backup} = "Start Incr Backup";
 $Lang{Stop_Dequeue_Backup} = "Stop/Dequeue Backup";
@@ -13,6 +14,44 @@ $Lang{Restore} = "Restore";
 
 # -----
 
+$Lang{Only_privileged_users_can_view_admin_options} = "Only privileged users can view admin options.";
+$Lang{H_Admin_Options} = "BackupPC Server: Admin Options";
+$Lang{Admin_Options} = "Admin Options";
+$Lang{Admin_Options_Page} = <
+\${h1("Server Control")}
+
+ +
Stop the server: +
Reload the server configuration: +
+
+\${h1("Server Configuration")} +
    +
  • Other options can go here... e.g., +
  • Edit server configuration +
+EOF +$Lang{Unable_to_connect_to_BackupPC_server} = "Unable to connect to BackupPC server", + "This CGI script (\$MyURL) is unable to connect to the BackupPC" + . " server on \$Conf{ServerHost} port \$Conf{ServerPort}. The error" + . " was: \$err.", + "Perhaps the BackupPC server is not running or there is a " + . " configuration error. Please report this to your Sys Admin."; +$Lang{Admin_Start_Server} = < +The BackupPC server at \$Conf{ServerHost} port \$Conf{ServerPort} +is not currently running (maybe you just stopped it, or haven't yet started it).
+Do you want to start it? + + + +EOF + +# ----- + $Lang{H_BackupPC_Server_Status} = "BackupPC Server Status"; $Lang{BackupPC_Server_Status}= < The servers PID is \$Info{pid}, on host \$Conf{ServerHost}, version \$Info{Version}, started at \$serverStartTime.
  • This status was generated at \$now. +
  • The configuration was last loaded at \$configLoadTime.
  • PCs will be next queued at \$nextWakeupTime.
  • Other info:
      @@ -40,8 +80,8 @@ $Lang{BackupPC_Server_Status}= < - - +
      Host
      + @@ -55,8 +95,8 @@ $Lang{BackupPC_Server_Status}= < -
      Host Type User Start Time
      - +
      Host
      + @@ -69,12 +109,13 @@ EOF # -------------------------------- $Lang{BackupPC__Server_Summary} = "BackupPC: Server Summary"; +$Lang{BackupPC__Archive} = "BackupPC: Archive"; $Lang{BackupPC_Summary}=< This status was generated at \$now. -

      +

      \${h2("Hosts with good Backups")}

      @@ -85,8 +126,9 @@ There are \$hostCntGood hosts that have been backed up, for a total of:

    • \$incrTot incr backups of total size \${incrSizeTot}GB (prior to pooling and compression). -
    • Host Type User Last Try
      - +

      +
      Host
      + @@ -98,14 +140,13 @@ There are \$hostCntGood hosts that have been backed up, for a total of: \$strGood
      Host User #Full Full Age/days Last attempt
      -

      - +

      \${h2("Hosts with no Backups")}

      There are \$hostCntNone hosts with no backups.

      - - +
      Host
      + @@ -119,6 +160,91 @@ There are \$hostCntNone hosts with no backups.
      Host User #Full Full Age/days
      EOF +$Lang{BackupPC_Archive}=< + + + +There are \$hostCntGood hosts that have been backed up for a total size of \${fullSizeTot}GB +

      +

      + + + +
      + + + + +\$strGood +\$checkAllHosts +
      Host User Backup Size
      +
      +

      + + +EOF + +$Lang{BackupPC_Archive2}=< +\$HostListStr +

    + +\$hiddenStr + + + + + + + + + + + + + + + + + + + + + + +
    Archive Location/Device
    CompressionNone
    + gzip
    + bzip2
    Number of Parity Files
    Split output into:Megabytes
    +EOF + # ----------------------------------- $Lang{Pool_Stat} = <Pool is \${poolSize}GB comprising \$info->{"\${name}FileCnt"} files @@ -181,38 +307,42 @@ EOF # -------------------------------- $Lang{Only_privileged_users_can_view_queues_} = "Only privileged users can view queues."; # -------------------------------- +$Lang{Only_privileged_users_can_archive} = "Only privileged users can Archive."; +# -------------------------------- $Lang{BackupPC__Queue_Summary} = "BackupPC: Queue Summary"; # -------------------------------- $Lang{Backup_Queue_Summary} = < +

    \${h2("User Queue Summary")}

    The following user requests are currently queued: - - +

    +
    Host
    + \$strUser
    Host Req Time User
    -

    +

    \${h2("Background Queue Summary")}

    The following background requests are currently queued: - - +

    +
    Host
    + \$strBg
    Host Req Time User
    -

    - +

    \${h2("Command Queue Summary")}

    The following command requests are currently queued: - - +

    +
    Host
    + @@ -241,8 +371,8 @@ $Lang{BackupPC__Log_File_History} = "BackupPC: Log File History"; $Lang{Log_File_History__hdr} = < -
    Host Req Time User Command
    - +
    File
    + \$str @@ -253,8 +383,8 @@ EOF $Lang{Recent_Email_Summary} = < -
    File Size Modification time
    - +
    Recipient
    + @@ -269,25 +399,26 @@ $Lang{Browse_backup__num_for__host} = "BackupPC: Browse backup \$num for \$host" # ------------------------------ $Lang{Restore_Options_for__host} = "BackupPC: Restore Options for \$host"; $Lang{Restore_Options_for__host2} = < You have selected the following files/directories from share \$share, backup number #\$num:
      \$fileListStr
    -

    +

    You have three choices for restoring these files/directories. Please select one of the following options. -

    +

    \${h2("Option 1: Direct Restore")}

    You can start a restore that will restore these files directly onto \$host. -

    +

    Warning: any existing files that match the ones you have selected will be overwritten! - - +

    + @@ -296,8 +427,17 @@ selected will be overwritten!
    Recipient Host Time Subject
    - +
    Restore the files to host + + + Search for available shares
    Restore the files to share +

    Warning: depending upon which files/directories you have selected, this archive might be very very large. It might take many minutes to create and transfer the archive, and you will need enough local disk space to store it. -

    +

    @@ -352,7 +492,7 @@ Archive::Zip is not installed so you will not be able to download a zip archive. Please ask your system adminstrator to install Archive::Zip from www.cpan.org. -

    +

    EOF @@ -363,12 +503,12 @@ $Lang{Option_3__Download_Zip_archive} = < +

    Warning: depending upon which files/directories you have selected, this archive might be very very large. It might take many minutes to create and transfer the archive, and you will need enough local disk space to store it. -

    +

    @@ -424,6 +564,13 @@ Reply from server was: \$reply Go back to \$hostDest home page. EOF +$Lang{BackupPC_Archive_Reply_from_server} = < +Reply from server was: \$reply +EOF + + # ------------------------- $Lang{Host__host_Backup_Summary} = "BackupPC: Host \$host Backup Summary"; @@ -434,7 +581,7 @@ $Lang{Host__host_Backup_Summary2} = < \$statusStr - +

    \${h2("User Actions")}

    @@ -443,12 +590,13 @@ $Lang{Host__host_Backup_Summary2} = < - +

    \${h2("Backup Summary")}

    Click on the backup number to browse and restore backup files. - - +

    +
    Backup#
    + @@ -461,11 +609,12 @@ Click on the backup number to browse and restore backup files.

    \$restoreStr - +

    +

    \${h2("Xfer Error Summary")} -

    -

    Backup# Type Filled Start Date
    - +

    +
    Backup#
    + @@ -475,20 +624,21 @@ Click on the backup number to browse and restore backup files. \$errStr
    Backup# Type View #Xfer errs
    -

    +

    \${h2("File Size/Count Reuse Summary")}

    Existing files are those already in the pool; new files are those added to the pool. Empty files and SMB errors aren\'t counted in the reuse and new counts. - - +

    +
    + - + @@ -501,18 +651,19 @@ Empty files and SMB errors aren\'t counted in the reuse and new counts. \$sizeStr
    Totals Existing Files New Files
    Backup# Type #Files
    -

    +

    \${h2("Compression Summary")}

    Compression performance for files already in the pool and newly compressed files. - - +

    +
    + - + @@ -524,7 +675,30 @@ compressed files. \$compStr
    Existing Files New Files
    Backup#
    Backup# Type Comp Level Size/MB
    +

    +EOF + +$Lang{Host__host_Archive_Summary} = "BackupPC: Host \$host Archive Summary"; +$Lang{Host__host_Archive_Summary2} = < +\$warnStr +

      +\$statusStr +
    + +\${h2("User Actions")} +

    +

    + + + + +
    + + +\$ArchiveStr + EOF # ------------------------- @@ -577,10 +751,10 @@ $Lang{Backup_browse_for__host} = < You are browsing backup #\$num, which started around \$backupTime (\$backupAge days ago), \$filledBackup +
  • Enter directory:
  • Click on a directory below to navigate into that directory,
  • Click on a file below to restore that file,
  • You can view the backup history of the current directory. -
  • Enter directory: @@ -592,18 +766,15 @@ $Lang{Backup_browse_for__host} = <
    - +
    -
    \$dirStr
    -
    - EOF +$Lang{checkAllHosts} = < +EOF + $Lang{fileHeader} = < + - + EOF @@ -887,15 +1093,15 @@ $Lang{This_display_is_merged_with_backup} = < Visit this directory in backup #\$otherDirs. +
  • Select the backup you wish to view: EOF $Lang{Restore_Summary} = < Click on the restore number for more details. -
  • - +
    \$fileHeader \$topCheckAll \$fileStr @@ -615,7 +786,6 @@ $Lang{Backup_browse_for__host} = < --> -
    EOF @@ -651,9 +821,9 @@ the backups: \${h2("History of \${EscHTML(\$dirDisplay)}")}
    - -\$backupNumStr -\$backupTimeStr +
    Backup number
    Backup time
    +\$backupNumStr +\$backupTimeStr \$fileStr
    Backup number
    Backup time
    EOF @@ -664,35 +834,63 @@ $Lang{Restore___num_details_for__host} = "BackupPC: Restore #\$num details for \ $Lang{Restore___num_details_for__host2 } = < + + + + + + + + + + + + + + + + + + + +
    Number \$Restores[\$i]{num}
    Requested by \$RestoreReq{user}
    Request time \$reqTime
    Result \$Restores[\$i]{result}
    Error Message \$Restores[\$i]{errorMsg}
    Source host \$RestoreReq{hostSrc}
    Source backup num \$RestoreReq{num}
    Source share \$RestoreReq{shareSrc}
    Destination host \$RestoreReq{hostDest}
    Destination share \$RestoreReq{shareDest}
    Start time \$startTime
    Duration \$duration min
    Number of files \$Restores[\$i]{nFiles}
    Total size \${MB} MB
    Transfer rate \$MBperSec MB/sec
    TarCreate errors \$Restores[\$i]{tarCreateErrs}
    Xfer errors \$Restores[\$i]{xferErrs}
    Xfer log file +View, +Errors +
    +

    +\${h1("File/Directory list")} +

    + + +\$fileListStr +
    Original file/dirRestored to
    +EOF + +# ------------------------------ +$Lang{Archive___num_details_for__host} = "BackupPC: Archive #\$num details for \$host"; + +$Lang{Archive___num_details_for__host2 } = < - - + + - - - - - - - + + - - - - -
    Number \$Restores[\$i]{num}
    Requested by \$RestoreReq{user}
    Number \$Archives[\$i]{num}
    Requested by \$ArchiveReq{user}
    Request time \$reqTime
    Result \$Restores[\$i]{result}
    Error Message \$Restores[\$i]{errorMsg}
    Source host \$RestoreReq{hostSrc}
    Source backup num \$RestoreReq{num}
    Source share \$RestoreReq{shareSrc}
    Destination host \$RestoreReq{hostDest}
    Destination share \$RestoreReq{shareDest}
    Result \$Archives[\$i]{result}
    Error Message \$Archives[\$i]{errorMsg}
    Start time \$startTime
    Duration \$duration min
    Number of files \$Restores[\$i]{nFiles}
    Total size \${MB} MB
    Transfer rate \$MBperSec MB/sec
    TarCreate errors \$Restores[\$i]{tarCreateErrs}
    Xfer errors \$Restores[\$i]{xferErrs}
    Xfer log file -View, -Errors +View, +Errors

    -\${h1("File/Directory list")} +\${h1("Host list")}

    - -\$fileListStr + +\$HostListStr
    Original file/dirRestored to
    HostBackup Number
    EOF @@ -706,7 +904,7 @@ $Lang{BackupPC__Lib__new_failed__check_apache_error_log} = "BackupPC::Lib->new f $Lang{Wrong_user__my_userid_is___} = "Wrong user: my userid is \$>, instead of \$uid" . "(\$Conf{BackupPCUser})\n"; -$Lang{Only_privileged_users_can_view_PC_summaries} = "Only privileged users can view PC summaries."; +# $Lang{Only_privileged_users_can_view_PC_summaries} = "Only privileged users can view PC summaries."; $Lang{Only_privileged_users_can_stop_or_start_backups} = "Only privileged users can stop or start backups on" . " \${EscHTML(\$host)}."; @@ -727,6 +925,8 @@ $Lang{Only_privileged_users_can_restore_backup_files} = "Only privileged users c $Lang{Bad_host_name} = "Bad host name \${EscHTML(\$host)}"; $Lang{You_haven_t_selected_any_files__please_go_Back_to} = "You haven\'t selected any files; please go Back to" . " select some files."; +$Lang{You_haven_t_selected_any_hosts} = "You haven\'t selected any hosts; please go Back to" + . " select some hosts."; $Lang{Nice_try__but_you_can_t_put} = "Nice try, but you can\'t put \'..\' in any of the file names"; $Lang{Host__doesn_t_exist} = "Host \${EscHTML(\$In{hostDest})} doesn\'t exist"; $Lang{You_don_t_have_permission_to_restore_onto_host} = "You don\'t have permission to restore onto host" @@ -739,17 +939,12 @@ $Lang{Empty_host_name} = "Empty host name"; $Lang{Unknown_host_or_user} = "Unknown host or user \${EscHTML(\$host)}"; $Lang{Only_privileged_users_can_view_information_about} = "Only privileged users can view information about" . " host \${EscHTML(\$host)}." ; +$Lang{Only_privileged_users_can_view_archive_information} = "Only privileged users can view archive information."; $Lang{Only_privileged_users_can_view_restore_information} = "Only privileged users can view restore information."; $Lang{Restore_number__num_for_host__does_not_exist} = "Restore number \$num for host \${EscHTML(\$host)} does" . " not exist."; - -$Lang{Unable_to_connect_to_BackupPC_server} = "Unable to connect to BackupPC server", - "This CGI script (\$MyURL) is unable to connect to the BackupPC" - . " server on \$Conf{ServerHost} port \$Conf{ServerPort}. The error" - . " was: \$err.", - "Perhaps the BackupPC server is not running or there is a " - . " configuration error. Please report this to your Sys Admin."; - +$Lang{Archive_number__num_for_host__does_not_exist} = "Archive number \$num for host \${EscHTML(\$host)} does" + . " not exist."; $Lang{Can_t_find_IP_address_for} = "Can\'t find IP address for \${EscHTML(\$host)}"; $Lang{host_is_a_DHCP_host} = <

    +
     Select all - +
    + Select all + + +
    Name
    Name Type Mode # Size Mod time Date modified
    - +
    Restore#
    + @@ -909,6 +1115,21 @@ Click on the restore number for more details.

    EOF +$Lang{Archive_Summary} = < +Click on the archive number for more details. +

    Restore# Result Start Date Dur/mins
    + + + + + +\$ArchiveStr +
    Archive# Result Start Date Dur/mins
    +

    +EOF + $Lang{BackupPC__Documentation} = "BackupPC: Documentation"; $Lang{No} = "no"; diff --git a/lib/BackupPC/Lib.pm b/lib/BackupPC/Lib.pm index cf99f4b..cef6d9c 100644 --- a/lib/BackupPC/Lib.pm +++ b/lib/BackupPC/Lib.pm @@ -70,6 +70,9 @@ sub new num startTime endTime result errorMsg nFiles size tarCreateErrs xferErrs )], + ArchiveFields => [qw( + num startTime endTime result errorMsg + )], }, $class; $bpc->{BinDir} .= "/bin"; $bpc->{LibDir} .= "/lib"; @@ -255,6 +258,51 @@ sub RestoreInfoWrite close(LOCK); } +sub ArchiveInfoRead +{ + my($bpc, $host) = @_; + local(*ARCHIVE_INFO, *LOCK); + my(@Archives); + + flock(LOCK, LOCK_EX) if open(LOCK, "$bpc->{TopDir}/pc/$host/LOCK"); + if ( open(ARCHIVE_INFO, "$bpc->{TopDir}/pc/$host/archives") ) { + binmode(ARCHIVE_INFO); + while ( ) { + s/[\n\r]+//; + next if ( !/^(\d+.*)/ ); + $_ = $1; + @{$Archives[@Archives]}{@{$bpc->{ArchiveFields}}} = split(/\t/); + } + close(ARCHIVE_INFO); + } + close(LOCK); + return @Archives; +} + +sub ArchiveInfoWrite +{ + my($bpc, $host, @Archives) = @_; + local(*ARCHIVE_INFO, *LOCK); + my($i); + + flock(LOCK, LOCK_EX) if open(LOCK, "$bpc->{TopDir}/pc/$host/LOCK"); + unlink("$bpc->{TopDir}/pc/$host/archives.old") + if ( -f "$bpc->{TopDir}/pc/$host/archives.old" ); + rename("$bpc->{TopDir}/pc/$host/archives", + "$bpc->{TopDir}/pc/$host/archives.old") + if ( -f "$bpc->{TopDir}/pc/$host/archives" ); + if ( open(ARCHIVE_INFO, ">$bpc->{TopDir}/pc/$host/archives") ) { + binmode(ARCHIVE_INFO); + for ( $i = 0 ; $i < @Archives ; $i++ ) { + my %b = %{$Archives[$i]}; + printf(ARCHIVE_INFO "%s\n", + join("\t", @b{@{$bpc->{ArchiveFields}}})); + } + close(ARCHIVE_INFO); + } + close(LOCK); +} + sub ConfigRead { my($bpc, $host) = @_; diff --git a/makeDist b/makeDist index 712ca05..8c547a4 100755 --- a/makeDist +++ b/makeDist @@ -47,6 +47,9 @@ my $DistDir = "dist/BackupPC-$Version"; my @PerlSrc = qw( bin/BackupPC + bin/BackupPC_archive + bin/BackupPC_archivecd + bin/BackupPC_archivetape bin/BackupPC_dump bin/BackupPC_link bin/BackupPC_nightly @@ -64,6 +67,9 @@ my @PerlSrc = qw( lib/BackupPC/Lib.pm lib/BackupPC/PoolWrite.pm lib/BackupPC/View.pm + lib/BackupPC/CGI/AdminOptions.pm + lib/BackupPC/CGI/Archive.pm + lib/BackupPC/CGI/ArchiveInfo.pm lib/BackupPC/CGI/Browse.pm lib/BackupPC/CGI/DirHistory.pm lib/BackupPC/CGI/EmailSummary.pm @@ -72,16 +78,20 @@ my @PerlSrc = qw( lib/BackupPC/CGI/Lib.pm lib/BackupPC/CGI/LOGlist.pm lib/BackupPC/CGI/Queue.pm + lib/BackupPC/CGI/ReloadServer.pm lib/BackupPC/CGI/RestoreFile.pm lib/BackupPC/CGI/RestoreInfo.pm lib/BackupPC/CGI/Restore.pm + lib/BackupPC/CGI/StartServer.pm lib/BackupPC/CGI/StartStopBackup.pm + lib/BackupPC/CGI/StopServer.pm lib/BackupPC/CGI/Summary.pm lib/BackupPC/CGI/View.pm lib/BackupPC/Lang/de.pm lib/BackupPC/Lang/en.pm lib/BackupPC/Lang/es.pm lib/BackupPC/Lang/fr.pm + lib/BackupPC/Xfer/Archive.pm lib/BackupPC/Xfer/Smb.pm lib/BackupPC/Xfer/Tar.pm lib/BackupPC/Xfer/Rsync.pm @@ -107,6 +117,7 @@ foreach my $file ( @PerlSrc ) { $errCnt += CheckLangUsage(); exit(1) if ( $errCnt ); +$errCnt = 0; foreach my $var ( sort(keys(%$ConfVars) ) ) { next if ( $ConfVars->{$var} >= 2 || $var =~ /^\$/ ); printf("Unused config parameter $var\n"); @@ -335,7 +346,11 @@ sub CheckLangUsage } close(F); } - foreach my $f ( ) { + # CB: disable other lang checks for now + # + # foreach my $f ( ) { + # + foreach my $f ( ) { my $done = {}; open(F, $f) || die("can't open $f\n"); binmode(F); @@ -343,7 +358,7 @@ sub CheckLangUsage s/#.*//g; s/\$Lang{([^}]*)}/ my $var = $1; - next if ( $var =~ m{^(Reason_|Status_)} ); + next if ( $var =~ m{^(Reason_|Status_|backupType_)} ); if ( !defined($vars->{$var}) ) { print("Unexpected Lang var $var in $f\n"); $errors++; -- 2.20.1