* Major changes from Ryan Kucera to add style sheets to the CGI
authorcbarratt <cbarratt>
Sat, 2 Aug 2003 08:01:41 +0000 (08:01 +0000)
committercbarratt <cbarratt>
Sat, 2 Aug 2003 08:01:41 +0000 (08:01 +0000)
  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.

38 files changed:
ChangeLog
bin/BackupPC
bin/BackupPC_archive [new file with mode: 0644]
bin/BackupPC_archivecd [new file with mode: 0644]
bin/BackupPC_archivetape [new file with mode: 0644]
bin/BackupPC_dump
cgi-bin/BackupPC_Admin
conf/config.pl
configure.pl
doc-src/BackupPC.pod
images/icon-dir.gif [new file with mode: 0644]
images/icon-file.gif [new file with mode: 0644]
images/icon-symlink.gif [new file with mode: 0644]
images/logo.gif [new file with mode: 0644]
init.d/src/linux-backuppc
lib/BackupPC/CGI/AdminOptions.pm [new file with mode: 0644]
lib/BackupPC/CGI/Archive.pm [new file with mode: 0644]
lib/BackupPC/CGI/ArchiveInfo.pm [new file with mode: 0644]
lib/BackupPC/CGI/Browse.pm
lib/BackupPC/CGI/DirHistory.pm
lib/BackupPC/CGI/EmailSummary.pm
lib/BackupPC/CGI/GeneralInfo.pm
lib/BackupPC/CGI/HostInfo.pm
lib/BackupPC/CGI/LOGlist.pm
lib/BackupPC/CGI/Lib.pm
lib/BackupPC/CGI/Queue.pm
lib/BackupPC/CGI/ReloadServer.pm [new file with mode: 0644]
lib/BackupPC/CGI/Restore.pm
lib/BackupPC/CGI/RestoreInfo.pm
lib/BackupPC/CGI/StartServer.pm [new file with mode: 0644]
lib/BackupPC/CGI/StartStopBackup.pm
lib/BackupPC/CGI/StopServer.pm [new file with mode: 0644]
lib/BackupPC/CGI/Summary.pm
lib/BackupPC/CGI/View.pm
lib/BackupPC/Config.pm
lib/BackupPC/Lang/en.pm
lib/BackupPC/Lib.pm
makeDist

index de07eca..5b4a846 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 # 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.
 
index 027bbf2..3f1fa2b 100755 (executable)
@@ -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 (file)
index 0000000..3a3bea4
--- /dev/null
@@ -0,0 +1,375 @@
+#!/bin/perl -T
+#============================================================= -*-perl-*-
+#
+# BackupPC_archive: Archive files for an archive client.
+#
+# DESCRIPTION
+#
+#   Usage: BackupPC_archive <user> <archiveclient> <reqFileName>
+#
+# AUTHOR
+#   Craig Barratt  <cbarratt@users.sourceforge.net>
+#
+# 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 <user> <archiveclient> <reqFileName>\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 (file)
index 0000000..d9f4c0d
--- /dev/null
@@ -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  <cbarratt@users.sourceforge.net>
+#
+# 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 (file)
index 0000000..18717ab
--- /dev/null
@@ -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  <cbarratt@users.sourceforge.net>
+#
+# 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
index d1509b7..f59e38a 100755 (executable)
@@ -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);
     }
index c99ba4c..8dc2091 100755 (executable)
@@ -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"
index 2912fa3..8c58140 100644 (file)
@@ -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';
+<style type="text/css">
+body {
+    font-family:arial,sans-serif;
+    font-size:1em;
+    background-color:#ffffff;
+    margin:2px 5px 0px 2px;
+    height:100%
+}
+
+h1 {
+    font-family:arial,sans-serif;
+    font-size:1.5em;
+    color:#000000
+}
+
+h2 {
+    font-family:arial,sans-serif;
+    font-size:1em;
+    color:#000000
+}
+        
+p {
+    font-family:arial,sans-serif;
+    font-size:.8em
+}
+        
+a {
+    font-family:arial,sans-serif;
+    font-size:1em;
+    color:#3333ff
+}
+        
+a:hover {
+    color:#cc0000;
+    text-decoration:none
+}
+        
+a.NavCurrent {
+    font-weight:bold;
+    padding-left:5px;
+    padding-right:5px;
+}
+        
+a.navbar {
+    padding-left:5px;
+    padding-right:5px;
+}
+
+.h1 {
+    font-family:arial,sans-serif;
+    font-size:1.5em;
+    color:#000000;
+    font-weight:bold;
+    background-color:#99cc33;
+    padding:3px;
+    padding-left:10px
+}
+        
+.h2 {
+    font-family:arial,sans-serif;
+    font-size:1em;
+    color:#000000;
+    font-weight:bold;
+    background-color:#ddeeee;
+    padding:3px;
+    padding-left:10px
+}
+    
+.border {
+    border-bottom:1px solid #000000;
+    border-left:1px dotted #666666
+}
+
+.tableheader {
+    font-weight:bold;
+    background-color:#cccccc
+}
+    
+.fviewheader {
+    font-weight:bold;
+    color:#ffffff;
+    background-color:#999999
+}
+    
+.fviewborder {
+    border-bottom:1px solid #000000;
+    border-left:1px dotted #666666;
+    background-color:#dddddd
+}
+    
+.fviewon {
+    background-color:#cccccc
+}
+
+.fviewoff {
+    background-color:#ffffff
+}
+
+.fview {
+    font-size:13px;
+    font-family:arial,sans-serif;
+    text-decoration:none;
+    line-height:15px
+}
+
+.fviewbold {
+    font-size:13px;
+    font-family:arial,sans-serif;
+    text-decoration:none;
+    line-height:15px;
+    font-weight:bold
+}
+
+.histView {
+    border-bottom:1px solid #000000;
+    border-left:2px solid #ffffff;
+    background-color:#dddddd
+}
+    
+.histViewMis {
+    border-bottom:1px solid #000000;
+    background-color:#ffdddd
+}
+    
+div.NavMenu {
+    width:18%;
+    margin:0px;
+    background-color:#ddeeee;
+}
+             
+div.NavMenu a {
+    font-size:.8em;
+    display:block;
+    margin-left:8px;
+    padding:3px;
+}
+             
+div.NavTitle {
+    padding-left:10px;
+    background-color:#99cc33;
+    font-family:arial,sans-serif;
+    color:#000000;
+    font-weight:bold
+}
+             
+div.HostOn {
+    width:18%;
+    background-color:#ddeeee;
+    padding:3px;
+    padding-left:10px;
+}
+
+div.HostOnContent {
+    background-color:#ddeeee;
+    padding:5px;
+}
+
+div.HostOnContent a {
+    font-size:.8em;
+    display:block;
+}
+
+#Content {
+    float:right;
+    width:80%;
+    left:20%;
+    top:10px;
+    position:absolute;
+}
+</style>
+EOF
index 0abc9a7..7fdea71 100755 (executable)
@@ -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 ( <C> ) {
-        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 .= $_;
         }
     }
index e8e8c0a..ef114b8 100644 (file)
@@ -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 (file)
index 0000000..dafa818
Binary files /dev/null and b/images/icon-dir.gif differ
diff --git a/images/icon-file.gif b/images/icon-file.gif
new file mode 100644 (file)
index 0000000..95a1145
Binary files /dev/null and b/images/icon-file.gif differ
diff --git a/images/icon-symlink.gif b/images/icon-symlink.gif
new file mode 100644 (file)
index 0000000..02562c4
Binary files /dev/null and b/images/icon-symlink.gif differ
diff --git a/images/logo.gif b/images/logo.gif
new file mode 100644 (file)
index 0000000..a1c7b0d
Binary files /dev/null and b/images/logo.gif differ
index 87c7e2b..1532722 100755 (executable)
@@ -45,7 +45,7 @@ stop() {
     echo -n "Shutting down BackupPC: "
     killproc __INSTALLDIR__/bin/BackupPC
     RETVAL=$?
-    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/backupcpc
+    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/backuppc
     echo ""
     return $RETVAL
 }      
diff --git a/lib/BackupPC/CGI/AdminOptions.pm b/lib/BackupPC/CGI/AdminOptions.pm
new file mode 100644 (file)
index 0000000..d9e58b3
--- /dev/null
@@ -0,0 +1,52 @@
+#============================================================= -*-perl-*-
+#
+# BackupPC::CGI::AdminOptions package
+#
+# DESCRIPTION
+#
+#   This module implements the AdminOptions action for the CGI interface.
+#
+# AUTHOR
+#   Craig Barratt  <cbarratt@users.sourceforge.net>
+#
+# 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 (file)
index 0000000..43a9b8e
--- /dev/null
@@ -0,0 +1,235 @@
+#============================================================= -*-perl-*-
+#
+# BackupPC::CGI::Archive package
+#
+# DESCRIPTION
+#
+#   This module implements the Archive action for the CGI interface.
+#
+# AUTHOR
+#   Craig Barratt  <cbarratt@users.sourceforge.net>
+#
+# 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 = <<EOF;
+<tr bgcolor="#ffffcc">
+<td><input type="hidden" name="backup$checkBoxCnt" value="$backupnumber"><input type="checkbox" name="fcb$checkBoxCnt" value="$host">&nbsp;${HostLink($host)} </td>
+<td align="center"> ${UserLink($Hosts->{$host}{user})} </td>
+<td align="center"> $fullSize </td>
+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;
+<input type="hidden" name="archivehost" value="$In{'archivehost'}">
+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;
+<input type="hidden" name="fcb$i" value="$In{'fcb' . $i}">
+<input type="hidden" name="backup$i" value="$In{'backup' . $i}">
+EOF
+            $HostListStr .= <<EOF;
+<li> ${EscHTML($name)}
+EOF
+        }
+        $hiddenStr .= <<EOF;
+<input type="hidden" name="archivehost" value="$In{'archivehost'}">
+EOF
+        $hiddenStr .= "<input type=\"hidden\" name=\"fcbMax\" value=\"$In{fcbMax}\">\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 (file)
index 0000000..effb362
--- /dev/null
@@ -0,0 +1,87 @@
+#============================================================= -*-perl-*-
+#
+# BackupPC::CGI::ArchiveInfo package
+#
+# DESCRIPTION
+#
+#   This module implements the ArchiveInfo action for the CGI interface.
+#
+# AUTHOR
+#   Craig Barratt  <cbarratt@users.sourceforge.net>
+#
+# 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 .= <<EOF;
+<tr><td>$f</td><td>@{$ArchiveReq{BackupList}}[$counter]</td></tr>
+EOF
+        $counter++;
+    }
+
+    Header(eval("qq{$Lang->{Archive___num_details_for__host}}"));
+    print(eval("qq{$Lang->{Archive___num_details_for__host2 }}"));
+    Trailer();
+}
+
+1;
index aeb006b..3eb7f30 100644 (file)
@@ -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 = "<b>";
-                   $unbold = "</b>";
+                    $linkStyle = "fviewbold";
                    $img |= 1 << 2;
                    $img |= 1 << 3 if ( $attr->{$f}{nlink} > 2 );
                }
                my $imgFileName = sprintf("%07b.gif", $img);
                $imgStr = "<img src=\"$Conf{CgiImageDirURL}/$imgFileName\" align=\"absmiddle\" width=\"9\" height=\"19\" border=\"0\">";
                if ( "$relDir/$f" eq $dir ) {
-                   $BGcolor = " bgcolor=\"$Conf{CgiHeaderBgColor}\"";
+                    $tdStyle = "fviewon";
                } else {
-                   $BGcolor = "";
+                    $tdStyle = "fviewoff";
                }
                my $dirName = $f;
                $dirName =~ s/ /&nbsp;/g;
                push(@DirStr, {needTick => 1,
-                              tdArgs   => $BGcolor,
+                               tdArgs   => " class=\"$tdStyle\"",
                               link     => <<EOF});
-<a href="$MyURL?action=browse&host=${EscURI($host)}&num=$num&share=$shareURI&dir=$path">$imgStr</a><a href="$MyURL?action=browse&host=${EscURI($host)}&num=$num&share=$shareURI&dir=$path" style="font-size:13px;font-family:arial;text-decoration:none;line-height:15px">&nbsp;$bold$dirName$unbold</a></td></tr>
+<a href="$MyURL?action=browse&host=${EscURI($host)}&num=$num&share=$shareURI&dir=$path">$imgStr</a><a href="$MyURL?action=browse&host=${EscURI($host)}&num=$num&share=$shareURI&dir=$path" class="$linkStyle">&nbsp;$dirName</a></td></tr>
 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;
+<img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" align="center">
+EOF
                     $attrStr .= <<EOF;
-    <td align="center">$typeStr</td>
-    <td align="center">$modeStr</td>
-    <td align="center">$a->{backupNum}</td>
-    <td align="right">$a->{size}</td>
-    <td align="right">$mtimeStr</td>
+    <td align="center" class="fviewborder">$typeStr</td>
+    <td align="center" class="fviewborder">$modeStr</td>
+    <td align="center" class="fviewborder">$a->{backupNum}</td>
+    <td align="right" class="fviewborder">$a->{size}</td>
+    <td align="right" class="fviewborder">$mtimeStr</td>
 </tr>
 EOF
                 } else {
-                    $attrStr .= "<td colspan=\"5\" align=\"center\"> </td>\n";
+                    $attrStr .= "<td colspan=\"5\" align=\"center\" class=\"fviewborder\"> </td>\n";
                 }
                (my $fDisp = "${EscHTML($f)}") =~ s/ /&nbsp;/g;
                 if ( $gotDir ) {
                     $fileStr .= <<EOF;
-<tr bgcolor="#ffffcc"><td><input type="checkbox" name="fcb$checkBoxCnt" value="$path">&nbsp;<a href="$MyURL?action=browse&host=${EscURI($host)}&num=$num&share=$shareURI&dir=$path">$fDisp</a></td>
+<tr><td class="fviewborder">
+    <table cellpadding="0" cellspacing="0" border="0"><tr><td>
+    <input type="checkbox" name="fcb$checkBoxCnt" value="$path">&nbsp;$iconStr
+    </td><td>
+        &nbsp;<a href="$MyURL?action=browse&host=${EscURI($host)}&num=$num&share=$shareURI&dir=$path">$fDisp</a>
+    </td></tr></table>
+</td>
 $attrStr
 </tr>
 EOF
                 } else {
                     $fileStr .= <<EOF;
-<tr bgcolor="#ffffcc"><td><input type="checkbox" name="fcb$checkBoxCnt" value="$path">&nbsp;<a href="$MyURL?action=RestoreFile&host=${EscURI($host)}&num=$num&share=$shareURI&dir=$path">$fDisp</a></td>
+<tr><td class="fviewborder">
+    <table cellpadding="0" cellspacing="0" border="0"><tr><td>
+    <input type="checkbox" name="fcb$checkBoxCnt" value="$path">&nbsp;$iconStr
+    </td><td>
+        &nbsp;<a href="$MyURL?action=RestoreFile&host=${EscURI($host)}&num=$num&share=$shareURI&dir=$path">$fDisp</a>
+    </td></tr></table>
+</td>
 $attrStr
 </tr>
 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 .= "<tr><td$d->{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, "<a href=\"$MyURL?action=browse&host=${EscURI($host)}"
-                       . "&num=$i&share=$shareURI&dir=$pathURI\">$i</a>");
+        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 .= "<option value=\"$MyURL?action=browse&host=${EscURI($host)}&num=$value\"$selected>#$value - ($showDate)</option>\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();
 }
 
index a1b2d2d..728068f 100644 (file)
@@ -84,8 +84,9 @@ sub action
        my %inode2name;
        my $nameCnt = 0;
        (my $fDisp  = "${EscHTML($f)}") =~ s/ /&nbsp;/g;
-       $fileStr   .= "<tr><td align=left>$fDisp</td>";
-       my($colSpan, $url, $inode, $type, $tdColor);
+       $fileStr   .= "<tr><td align=\"left\"  class=\"histView\">$fDisp</td>";\r
+       my($colSpan, $url, $inode, $type);\r
+       my $tdClass = ' class="histView"';\r
        for ( $i = 0 ; $i < @Backups ; $i++ ) {
            my($path);
            if ( $colSpan > 0 ) {
@@ -109,16 +110,16 @@ sub action
                    $colSpan++;
                    next;
                }
-               $fileStr .= "<td align=center colspan=$colSpan$tdColor>"
+               $fileStr .= "<td align=center colspan=$colSpan$tdClass>"\r
                          . "$url</td>";
                $colSpan = 0;
-               $tdColor = "";
+               $tdClass = ' class="histView"';\r
            }
            if ( !defined($hist->{$f}[$i]) ) {
                $colSpan = 1;
                $url     = "&nbsp;";
                $inode   = -3;                  # special value for missing
-               $tdColor = ' bgcolor="#ffffaa"';
+               $tdClass = ' class="histViewMis"';\r
                next;
            }
             if ( $dir eq "" ) {
@@ -154,7 +155,7 @@ EOF
            $colSpan = 1;
        }
        if ( $colSpan > 0 ) {
-           $fileStr .= "<td align=center colspan=$colSpan$tdColor>$url</td>";
+           $fileStr .= "<td align=center colspan=$colSpan$tdClass>$url</td>";\r
            $colSpan = 0;
        }
        $fileStr .= "</tr>\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}}");\r
+    Header(eval("qq{$Lang->{DirHistory_backup_for__host}}"), $content);\r
     Trailer();
 }
 
index 1b9ec70..68bd498 100644 (file)
@@ -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();
 }
 
index bb71e51..1418ace 100644 (file)
@@ -54,14 +54,14 @@ sub action
         (my $cmd = $Jobs{$host}{cmd}) =~ s/$BinDir\///g;
         (my $xferPid = $Jobs{$host}{xferPid}) =~ s/,/, /g;
         $jobStr .= <<EOF;
-<tr><td> ${HostLink($host)} </td>
-    <td align="center"> $Jobs{$host}{type} </td>
-    <td align="center"> ${UserLink(defined($Hosts->{$host})
+<tr><td class="border"> ${HostLink($host)} </td>
+    <td align="center" class="border"> $Jobs{$host}{type} </td>
+    <td align="center" class="border"> ${UserLink(defined($Hosts->{$host})
                                        ? $Hosts->{$host}{user} : "")} </td>
-    <td> $startTime </td>
-    <td> $cmd </td>
-    <td align="center"> $Jobs{$host}{pid} </td>
-    <td align="center"> $xferPid </td>
+    <td class="border"> $startTime </td>
+    <td class="border"> $cmd </td>
+    <td align="center" class="border"> $Jobs{$host}{pid} </td>
+    <td align="center" class="border"> $xferPid </td>
 EOF
         $jobStr .= "</tr>\n";
     }
@@ -89,14 +89,14 @@ EOF
         }
         (my $shortErr = $Status{$host}{error}) =~ s/(.{48}).*/$1.../;   
         $statusStr .= <<EOF;
-<tr><td> ${HostLink($host)} </td>
-    <td align="center"> $Status{$host}{type} </td>
-    <td align="center"> ${UserLink(defined($Hosts->{$host})
+<tr><td class="border"> ${HostLink($host)} </td>
+    <td align="center" class="border"> $Status{$host}{type} </td>
+    <td align="center" class="border"> ${UserLink(defined($Hosts->{$host})
                                        ? $Hosts->{$host}{user} : "")} </td>
-    <td align="right"> $startTime </td>
-    <td> $XferViewStr </td>
-    <td align="right"> $errorTime </td>
-    <td> ${EscHTML($shortErr)} </td></tr>
+    <td align="right" class="border"> $startTime </td>
+    <td class="border"> $XferViewStr </td>
+    <td align="right" class="border"> $errorTime </td>
+    <td class="border"> ${EscHTML($shortErr)} </td></tr>
 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();
 }
 
index 81d75a3..a49eb79 100644 (file)
@@ -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  .= <<EOF;
+<tr><td align="center"><a href="$MyURL?action=archiveInfo&num=$Archives[$i]{num}&host=${EscURI($host)}">$Archives[$i]{num}</a> </td>
+    <td align="center"> $Archives_Result </td>
+    <td align="right"> $startTime </td>
+    <td align="right"> $duration </td>
+</tr>
+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 .= <<EOF;
-<tr><td align="center"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
-    <td align="center"> $ltype </td>
-    <td align="center"> $filled </td>
-    <td align="right">  $startTime </td>
-    <td align="right">  $duration </td>
-    <td align="right">  $age </td>
-    <td align="left">   <tt>$TopDir/pc/$host/$Backups[$i]{num}</tt> </td></tr>
+<tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
+    <td align="center" class="border"> $ltype </td>
+    <td align="center" class="border"> $filled </td>
+    <td align="right" class="border">  $startTime </td>
+    <td align="right" class="border">  $duration </td>
+    <td align="right" class="border">  $age </td>
+    <td align="left" class="border">   <tt>$TopDir/pc/$host/$Backups[$i]{num}</tt> </td></tr>
 EOF
         $sizeStr .= <<EOF;
-<tr><td align="center"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
-    <td align="center"> $ltype </td>
-    <td align="right">  $Backups[$i]{nFiles} </td>
-    <td align="right">  $MB </td>
-    <td align="right">  $MBperSec </td>
-    <td align="right">  $Backups[$i]{nFilesExist} </td>
-    <td align="right">  $MBExist </td>
-    <td align="right">  $Backups[$i]{nFilesNew} </td>
-    <td align="right">  $MBNew </td>
+<tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
+    <td align="center" class="border"> $ltype </td>
+    <td align="right" class="border">  $Backups[$i]{nFiles} </td>
+    <td align="right" class="border">  $MB </td>
+    <td align="right" class="border">  $MBperSec </td>
+    <td align="right" class="border">  $Backups[$i]{nFilesExist} </td>
+    <td align="right" class="border">  $MBExist </td>
+    <td align="right" class="border">  $Backups[$i]{nFilesNew} </td>
+    <td align="right" class="border">  $MBNew </td>
 </tr>
 EOF
-       my $is_compress = $Backups[$i]{compress} || $Lang->{off};
-       if (! $ExistComp) { $ExistComp = "&nbsp;"; }
-       if (! $MBExistComp) { $MBExistComp = "&nbsp;"; }
+        my $is_compress = $Backups[$i]{compress} || $Lang->{off};
+        if (! $ExistComp) { $ExistComp = "&nbsp;"; }
+        if (! $MBExistComp) { $MBExistComp = "&nbsp;"; }
         $compStr .= <<EOF;
-<tr><td align="center"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
-    <td align="center"> $ltype </td>
-    <td align="center"> $is_compress </td> 
-    <td align="right">  $MBExist </td>
-    <td align="right">  $MBExistComp </td> 
-    <td align="right">  $ExistComp </td>   
-    <td align="right">  $MBNew </td>
-    <td align="right">  $MBNewComp </td>
-    <td align="right">  $NewComp </td>
+<tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
+    <td align="center" class="border"> $ltype </td>
+    <td align="center" class="border"> $is_compress </td>
+    <td align="right" class="border">  $MBExist </td>
+    <td align="right" class="border">  $MBExistComp </td>
+    <td align="right" class="border">  $ExistComp </td>
+    <td align="right" class="border">  $MBNew </td>
+    <td align="right" class="border">  $MBNewComp </td>
+    <td align="right" class="border">  $NewComp </td>
 </tr>
 EOF
         $errStr .= <<EOF;
-<tr><td align="center"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
-    <td align="center"> $ltype </td>
-    <td align="center"> <a href="$MyURL?action=view&type=XferLOG&num=$Backups[$i]{num}&host=${EscURI($host)}">$Lang->{XferLOG}</a>,
+<tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
+    <td align="center" class="border"> $ltype </td>
+    <td align="center" class="border"> <a href="$MyURL?action=view&type=XferLOG&num=$Backups[$i]{num}&host=${EscURI($host)}">$Lang->{XferLOG}</a>,
                       <a href="$MyURL?action=view&type=XferErr&num=$Backups[$i]{num}&host=${EscURI($host)}">$Lang->{Errors}</a> </td>
-    <td align="right">  $Backups[$i]{xferErrs} </td>
-    <td align="right">  $Backups[$i]{xferBadFile} </td>
-    <td align="right">  $Backups[$i]{xferBadShare} </td>
-    <td align="right">  $Backups[$i]{tarErrs} </td></tr>
+    <td align="right" class="border">  $Backups[$i]{xferErrs} </td>
+    <td align="right" class="border">  $Backups[$i]{xferBadFile} </td>
+    <td align="right" class="border">  $Backups[$i]{xferBadShare} </td>
+    <td align="right" class="border">  $Backups[$i]{tarErrs} </td></tr>
 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  .= <<EOF;
-<tr><td align="center"><a href="$MyURL?action=restoreInfo&num=$Restores[$i]{num}&host=${EscURI($host)}">$Restores[$i]{num}</a> </td>
-    <td align="center"> $Restores_Result </td>
-    <td align="right"> $startTime </td>
-    <td align="right"> $duration </td>
-    <td align="right"> $Restores[$i]{nFiles} </td>
-    <td align="right"> $MB </td>
-    <td align="right"> $Restores[$i]{tarCreateErrs} </td>
-    <td align="right"> $Restores[$i]{xferErrs} </td>
+        my $Restores_Result = $Lang->{failed};
+        if ($Restores[$i]{result} ne "failed") { $Restores_Result = $Lang->{success}; }
+        $restoreStr  .= <<EOF;
+<tr><td align="center" class="border"><a href="$MyURL?action=restoreInfo&num=$Restores[$i]{num}&host=${EscURI($host)}">$Restores[$i]{num}</a> </td>
+    <td align="center" class="border"> $Restores_Result </td>
+    <td align="right" class="border"> $startTime </td>
+    <td align="right" class="border"> $duration </td>
+    <td align="right" class="border"> $Restores[$i]{nFiles} </td>
+    <td align="right" class="border"> $MB </td>
+    <td align="right" class="border"> $Restores[$i]{tarCreateErrs} </td>
+    <td align="right" class="border"> $Restores[$i]{xferErrs} </td>
 </tr>
 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();
 }
 
index 8e55b46..71b7d81 100644 (file)
@@ -74,8 +74,8 @@ sub action
     <td> $mtimeStr </td></tr>
 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();
 }
 
index 078f3fd..a9dad66 100644 (file)
@@ -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("</p>\n<p>", @mesg);
-    $Conf{CgiHeaderFontType} ||= "arial"; 
-    $Conf{CgiHeaderFontSize} ||= "3";  
-    $Conf{CgiNavBarBgColor}  ||= "#ddeeee";
-    $Conf{CgiHeaderBgColor}  ||= "#99cc33";
 
     if ( !defined($ENV{REMOTE_USER}) ) {
        $mesg .= <<EOF;
@@ -241,19 +235,19 @@ EOF
     $bpc->ServerMesg("log User $User (host=$In{host}) got CGI error: $head")
                             if ( defined($bpc) );
     if ( !defined($Lang->{Error}) ) {
-       Header("BackupPC: Error");
         $mesg = <<EOF if ( !defined($mesg) );
 There is some problem with the BackupPC installation.
 Please check the permissions on BackupPC_Admin.
 EOF
-       print <<EOF;
+        my $content = <<EOF;
 ${h1("Error: Unable to read config.pl or language strings!!")}
 <p>$mesg</p>
 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 <<EOF;
 <!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html><head>
 <title>$title</title>
+$Conf{CSSstylesheet}
 $Conf{CgiHeaders}
-</head><body bgcolor="$Conf{CgiBodyBgColor}">
-<table cellpadding="0" cellspacing="0" border="0">
-<tr valign="top"><td valign="top" bgcolor="$Conf{CgiNavBarBgColor}" width="10%">
+</head><body onLoad="document.getElementById('NavMenu').style.height=document.body.scrollHeight">
+<img src="$Conf{CgiImageDirURL}/logo.gif" hspace="5" vspace="7"><br>
 EOF
-    NavSectionTitle("BackupPC");
-    print "&nbsp;\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 <<EOF;
+<div id="Content">
+$content
+<br><br><br>
+</div>
+<div class="NavMenu" id="NavMenu" style="height:100%">
+EOF
+    } else {
+        print "<div class=\"NavMenu\">";
     }
-    NavSectionTitle($Lang->{NavSectionTitle_});
-    NavSectionStart();
-    foreach my $l ( @adminLinks ) {
-        if ( $PrivAdmin || $l->{priv} ) {
-            NavLink($l->{link}, $l->{name});
-        } else {
-            NavLink(undef, $l->{name});
+    NavSectionTitle($Lang->{Hosts});
+    my $hostSelectbox = "<option value=\"#\">Select a host...</option>";
+    my @hosts;
+    if ( defined($Hosts) && %$Hosts > 0 ) {
+        @hosts = GetUserHosts($In{host});
+        foreach my $host ( @hosts ) {
+            if ($In{host} eq $host) {
+                print <<EOF;
+</div>
+<div class="HostOn"> <a href="?host=${EscURI($host)}">$host</a> <br>
+<div class="HostOnContent">
+EOF
+                NavLink("?host=${EscURI($host)}",
+                        "$host $Lang->{Home}", " class=\"navbar\"");
+                NavLink("?action=browse&host=${EscURI($host)}",
+                        "Browse backups", " class=\"navbar\"");
+                NavLink("?action=view&type=LOG&host=${EscURI($host)}",
+                        $Lang->{LOG_file}, " class=\"navbar\"");
+                NavLink("?action=LOGlist&host=${EscURI($host)}",
+                        $Lang->{LOG_files}, " class=\"navbar\"");
+                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}, " class=\"navbar\"");
+                   NavLink("?action=view&type=XferErrbad&host=${EscURI($host)}",
+                            $Lang->{Last_bad_XferLOG_errors_only},
+                            " class=\"navbar\"");
+                }
+                if ( -f "$TopDir/pc/$host/config.pl" ) {
+                    NavLink("?action=view&type=config&host=${EscURI($host)}",
+                            $Lang->{Config_file}, " class=\"navbar\"");
+                }
+                print <<EOF;
+</div></div>
+<div id="Content">
+$content
+<br><br><br>
+</div>
+<div class="NavMenu" style="height:100%" id="NavMenu">
+EOF
+            } else {
+                NavLink("?host=${EscURI($host)}", $host) if ( @hosts < 6 );
+                $hostSelectbox .= "<option value=\"?host=${EscURI($host)}\">"
+                                . "$host</option>";
+            }
         }
     }
-    NavSectionEnd();
-    NavSectionTitle($Lang->{Hosts});
+    if ( @hosts >= 6 ) {
+        print <<EOF;
+<br>
+<select onChange="document.location=this.value">
+$hostSelectbox
+</select>
+<br><br>
+EOF
+    }
+    NavSectionTitle($Lang->{Host_or_User_name});
     print <<EOF;
-<table cellpadding="2" cellspacing="0" border="0" width="100%">
-    <tr><td>$Lang->{Host_or_User_name}</td>
-    <tr><td><form action="$MyURL" method="get"><small>
+<form action="$MyURL" method="get">
     <input type="text" name="host" size="10" maxlength="64">
     <input type="hidden" name="action" value="hostInfo"><input type="submit" value="$Lang->{Go}" name="ignore">
-    </small></form></td></tr>
-</table>
+    </form>
 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 <<EOF;
-</td><td valign="top" width="5">&nbsp;&nbsp;</td>
-<td valign="top" width="90%">
+}
+
+print <<EOF;
+<br><br><br>
+</div>
 EOF
 }
 
 sub Trailer
 {
     print <<EOF;
-</td></table>
 </body></html>
 EOF
 }
@@ -478,41 +522,35 @@ sub NavSectionTitle
 {
     my($head) = @_;
     print <<EOF;
-<table cellpadding="2" cellspacing="0" border="0" width="100%">
-<tr><td bgcolor="$Conf{CgiHeaderBgColor}"><font face="$Conf{CgiHeaderFontType}"
-size="$Conf{CgiHeaderFontSize}"><b>$head</b>
-</font></td></tr>
-</table>
+<div class="NavTitle">
+$head
+</div>
 EOF
 }
 
 sub NavSectionStart
 {
-    my($padding) = @_;
-
-    $padding = 1 if ( !defined($padding) );
-    print <<EOF;
-<table cellpadding="$padding" cellspacing="0" border="0" width="100%">
-EOF
 }
 
 sub NavSectionEnd
 {
-    print "</table>\n";
 }
 
 sub NavLink
 {
     my($link, $text) = @_;
-    print "<tr><td width=\"2%\" valign=\"top\"><b>&middot;</b></td>";
     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 <<EOF;
-<td width="98%"><a href="$link"><small>$text</small></a></td></tr>
+<a href="$link"$class>$text</a>
 EOF
     } else {
         print <<EOF;
-<td width="98%"><small>$text</small></td></tr>
+$text<br>
 EOF
     }
 }
@@ -521,12 +559,7 @@ sub h1
 {
     my($str) = @_;
     return \<<EOF;
-<table cellpadding="2" cellspacing="0" border="0" width="100%">
-<tr>
-<td bgcolor="$Conf{CgiHeaderBgColor}">&nbsp;<font face="$Conf{CgiHeaderFontType}"
-    size="$Conf{CgiHeaderFontSize}"><b>$str</b></font>
-</td></tr>
-</table>
+<div class="h1">$str</div>
 EOF
 }
 
@@ -534,11 +567,6 @@ sub h2
 {
     my($str) = @_;
     return \<<EOF;
-<table cellpadding="2" cellspacing="0" border="0" width="100%">
-<tr>
-<td bgcolor="$Conf{CgiHeaderBgColor}">&nbsp;<font face="$Conf{CgiHeaderFontType}"
-    size="$Conf{CgiHeaderFontSize}"><b>$str</b></font>
-</td></tr>
-</table>
+<div class="h2">$str</div>
 EOF
 }
index 116ea0f..82eeed4 100644 (file)
@@ -79,10 +79,8 @@ EOF
     <td> $cmd $req->{cmd}[0] </td></tr>
 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 (file)
index 0000000..13b66b2
--- /dev/null
@@ -0,0 +1,51 @@
+#============================================================= -*-perl-*-
+#
+# BackupPC::CGI::ReloadServer package
+#
+# DESCRIPTION
+#
+#   This module implements the ReloadServer action for the CGI interface.
+#
+# AUTHOR
+#   Craig Barratt  <cbarratt@users.sourceforge.net>
+#
+# 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;
index f236e1e..2a6d683 100644 (file)
@@ -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
 <tr><td>$host:/$strippedShare$f</td><td>$In{hostDest}:/$strippedShareDest$targetFile</td></tr>
 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();
     }
 }
index 83583e8..b018bf5 100644 (file)
@@ -83,9 +83,8 @@ sub action
 <tr><td>$RestoreReq{hostSrc}:/$strippedShareSrc$f</td><td>$RestoreReq{hostDest}:/$strippedShareDest$targetFile</td></tr>
 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 (file)
index 0000000..d0cb973
--- /dev/null
@@ -0,0 +1,63 @@
+#============================================================= -*-perl-*-
+#
+# BackupPC::CGI::StartServer package
+#
+# DESCRIPTION
+#
+#   This module implements the StartServer action for the CGI interface.
+#
+# AUTHOR
+#   Craig Barratt  <cbarratt@users.sourceforge.net>
+#
+# 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;
index 38b7f6f..e62ced2 100644 (file)
@@ -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 (file)
index 0000000..f73f4b8
--- /dev/null
@@ -0,0 +1,56 @@
+#============================================================= -*-perl-*-
+#
+# BackupPC::CGI::StopServer package
+#
+# DESCRIPTION
+#
+#   This module implements the StopServer action for the CGI interface.
+#
+# AUTHOR
+#   Craig Barratt  <cbarratt@users.sourceforge.net>
+#
+# 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;
index 0706b62..50e730b 100644 (file)
@@ -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 = <<EOF;
-<tr$reasonHilite><td> ${HostLink($host)} </td>
-    <td align="center"> ${UserLink(defined($Hosts->{$host})
+<tr$reasonHilite><td class="border"> ${HostLink($host)} </td>
+    <td align="center" class="border"> ${UserLink(defined($Hosts->{$host})
                                    ? $Hosts->{$host}{user} : "")} </td>
-    <td align="center"> $fullCnt </td>
-    <td align="center"> $fullAge </td>
-    <td align="center"> $fullSize </td>
-    <td align="center"> $fullRate </td>
-    <td align="center"> $incrCnt </td>
-    <td align="center"> $incrAge </td>
-    <td align="center"> $Lang->{$Status{$host}{state}} </td>
-    <td> $Lang->{$Status{$host}{reason}}$shortErr </td></tr>
+    <td align="center" class="border"> $fullCnt </td>
+    <td align="center" class="border"> $fullAge </td>
+    <td align="center" class="border"> $fullSize </td>
+    <td align="center" class="border"> $fullRate </td>
+    <td align="center" class="border"> $incrCnt </td>
+    <td align="center" class="border"> $incrAge </td>
+    <td align="center" class="border"> $Lang->{$Status{$host}{state}} </td>
+    <td class="border"> $Lang->{$Status{$host}{reason}}$shortErr </td></tr>
 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();
 }
 
index 2a4239b..4f3070a 100644 (file)
@@ -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 ( <LOG> );
+            my $content;
+            $content .= $_ while ( <LOG> );
             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 "<pre>";
+        $content .= "<pre>";
         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;
+    $content .= <<EOF;
 </pre>
 EOF
+    Header(eval("qq{$Lang->{Backup_PC__Log_File__file}}"), $content);
     Trailer();
 }
 
index b8ef8e8..aa7b217 100644 (file)
@@ -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', },
 
index 4c4554e..2a417bc 100644 (file)
@@ -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} = <<EOF;
+\${h1(qq{$Lang{Admin_Options}})}
+<br>
+\${h1("Server Control")}
+<form action="\$MyURL" method="get">
+<table>
+  <tr><td>Stop the server:<td><input type="submit" name="action" value="Stop">
+  <tr><td>Reload the server configuration:<td><input type="submit" name="action" value="Reload">
+</table>
+</form>
+\${h1("Server Configuration")}
+<ul>
+  <li><i>Other options can go here... e.g.,</i>
+  <li>Edit server configuration
+</ul>
+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} = <<EOF;
+\${h1(qq{$Lang{Unable_to_connect_to_BackupPC_server}})}
+<form action="\$MyURL" method="get">
+The BackupPC server at <tt>\$Conf{ServerHost}</tt> port <tt>\$Conf{ServerPort}</tt>
+is not currently running (maybe you just stopped it, or haven't yet started it).<br>
+Do you want to start it?
+<input type="hidden" name="action" value="startServer">
+<input type="submit" value="Start Server" name="ignore">
+</form>
+EOF
+
+# -----
+
 $Lang{H_BackupPC_Server_Status} = "BackupPC Server Status";
 
 $Lang{BackupPC_Server_Status}= <<EOF;
@@ -25,6 +64,7 @@ $Lang{BackupPC_Server_Status}= <<EOF;
 <li> The servers PID is \$Info{pid},  on host \$Conf{ServerHost},
      version \$Info{Version}, started at \$serverStartTime.
 <li> This status was generated at \$now.
+<li> The configuration was last loaded at \$configLoadTime.
 <li> PCs will be next queued at \$nextWakeupTime.
 <li> Other info:
     <ul>
@@ -40,8 +80,8 @@ $Lang{BackupPC_Server_Status}= <<EOF;
 
 \${h2("Currently Running Jobs")}
 <p>
-<table border>
-<tr><td> Host </td>
+<table cellspacing="1" cellpadding="3">
+<tr class="tableheader"><td> Host </td>
     <td> Type </td>
     <td> User </td>
     <td> Start Time </td>
@@ -55,8 +95,8 @@ $Lang{BackupPC_Server_Status}= <<EOF;
 
 \${h2("Failures that need attention")}
 <p>
-<table border>
-<tr><td align="center"> Host </td>
+<table cellspacing="1" cellpadding="3">
+<tr class="tableheader"><td align="center"> Host </td>
     <td align="center"> Type </td>
     <td align="center"> User </td>
     <td align="center"> Last Try </td>
@@ -69,12 +109,13 @@ EOF
 
 # --------------------------------
 $Lang{BackupPC__Server_Summary} = "BackupPC: Server Summary";
+$Lang{BackupPC__Archive} = "BackupPC: Archive";
 $Lang{BackupPC_Summary}=<<EOF;
 
 \${h1(qq{$Lang{BackupPC__Server_Summary}})}
 <p>
 This status was generated at \$now.
-<p>
+</p>
 
 \${h2("Hosts with good Backups")}
 <p>
@@ -85,8 +126,9 @@ There are \$hostCntGood hosts that have been backed up, for a total of:
 <li> \$incrTot incr backups of total size \${incrSizeTot}GB
      (prior to pooling and compression).
 </ul>
-<table border>
-<tr><td> Host </td>
+</p>
+<table cellpadding="3" cellspacing="1">
+<tr class="tableheader"><td> Host </td>
     <td align="center"> User </td>
     <td align="center"> #Full </td>
     <td align="center"> Full Age/days </td>
@@ -98,14 +140,13 @@ There are \$hostCntGood hosts that have been backed up, for a total of:
     <td align="center"> Last attempt </td></tr>
 \$strGood
 </table>
-<p>
-
+<br><br>
 \${h2("Hosts with no Backups")}
 <p>
 There are \$hostCntNone hosts with no backups.
 <p>
-<table border>
-<tr><td> Host </td>
+<table cellpadding="3" cellspacing="1">
+<tr class="tableheader"><td> Host </td>
     <td align="center"> User </td>
     <td align="center"> #Full </td>
     <td align="center"> Full Age/days </td>
@@ -119,6 +160,91 @@ There are \$hostCntNone hosts with no backups.
 </table>
 EOF
 
+$Lang{BackupPC_Archive}=<<EOF;
+
+\${h1(qq{$Lang{BackupPC__Archive}})}
+<script language="javascript" type="text/javascript">
+<!--
+
+    function checkAll(location)
+    {
+      for (var i=0;i<document.form1.elements.length;i++)
+      {
+        var e = document.form1.elements[i];
+        if ((e.checked || !e.checked) && e.name != \'all\') {
+            if (eval("document.form1."+location+".checked")) {
+                e.checked = true;
+            } else {
+                e.checked = false;
+            }
+        }
+      }
+    }
+
+    function toggleThis(checkbox)
+    {
+       var cb = eval("document.form1."+checkbox);
+       cb.checked = !cb.checked;
+    }
+
+//-->
+</script>
+
+There are \$hostCntGood hosts that have been backed up for a total size of \${fullSizeTot}GB
+<p>
+<form name="form1" method="post" action="\$MyURL">
+<input type="hidden" name="fcbMax" value="\$checkBoxCnt">
+<input type="hidden" name="type" value="1">
+<input type="hidden" name="action" value="Archive">
+<table cellpadding="0" cellspacing="0" bgcolor="#333333"><tr><td>
+<table border="0" width="100%" align="left" cellpadding="2" cellspacing="1">
+<tr bgcolor="#99cc33"><td align=center> Host</td>
+    <td align="center"> User </td>
+    <td align="center"> Backup Size </td>
+\$strGood
+\$checkAllHosts
+</table>
+</td></tr></table>
+<p>
+
+</table>
+EOF
+
+$Lang{BackupPC_Archive2}=<<EOF;
+\${h1(qq{$Lang{BackupPC__Archive}})}
+About to archive the following hosts
+<ul>
+\$HostListStr
+</ul>
+<form action="\$MyURL" method="post">
+\$hiddenStr
+<input type="hidden" name="action" value="Archive">
+<input type="hidden" name="type" value="2">
+<input type="hidden" value="0" name="archive_type">
+<table border="1">
+<tr>
+    <td>Archive Location/Device</td>
+    <td><input type="text" value="\$ArchiveDest" name="archive_device"></td>
+<tr>
+    <td>Compression</td>
+    <td><input type="radio" value="0" name="compression" \$ArchiveCompNone>None<br>
+    <input type="radio" value="1" name="compression" \$ArchiveCompGzip>gzip<br>
+    <input type="radio" value="2" name="compression" \$ArchiveCompBzip2>bzip2</tr>
+</tr>
+<tr>
+    <td>Number of Parity Files</td>
+    <td><input type="numeric" value="\$ArchivePar" name="par"></td>
+</tr>
+<tr>
+    <td>Split output into:</td>
+    <td><input type="numeric" value="\$ArchiveSplit" name="splitsize">Megabytes</td>
+</tr>
+<tr>
+    <td colspan=2><input type="submit" value="Start the Archive" name=""></td>
+</tr>
+</table>
+EOF
+
 # -----------------------------------
 $Lang{Pool_Stat} = <<EOF;
         <li>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} = <<EOF;
 \${h1("Backup Queue Summary")}
-<p>
+<br><br>
 \${h2("User Queue Summary")}
 <p>
 The following user requests are currently queued:
-<table border>
-<tr><td> Host </td>
+</p>
+<table cellspacing="1" cellpadding="3" width="80%">
+<tr class="tableheader"><td> Host </td>
     <td> Req Time </td>
     <td> User </td></tr>
 \$strUser
 </table>
-<p>
+<br><br>
 
 \${h2("Background Queue Summary")}
 <p>
 The following background requests are currently queued:
-<table border>
-<tr><td> Host </td>
+</p>
+<table cellspacing="1" cellpadding="3" width="80%">
+<tr class="tableheader"><td> Host </td>
     <td> Req Time </td>
     <td> User </td></tr>
 \$strBg
 </table>
-<p>
-
+<br><br>
 \${h2("Command Queue Summary")}
 <p>
 The following command requests are currently queued:
-<table border>
-<tr><td> Host </td>
+</p>
+<table cellspacing="1" cellpadding="3" width="80%">
+<tr class="tableheader"><td> Host </td>
     <td> Req Time </td>
     <td> User </td>
     <td> Command </td></tr>
@@ -241,8 +371,8 @@ $Lang{BackupPC__Log_File_History} = "BackupPC: Log File History";
 $Lang{Log_File_History__hdr} = <<EOF;
 \${h1("Log File History \$hdr")}
 <p>
-<table border>
-<tr><td align="center"> File </td>
+<table cellspacing="1" cellpadding="3" width="100%">
+<tr class="tableheader"><td align="center"> File </td>
     <td align="center"> Size </td>
     <td align="center"> Modification time </td></tr>
 \$str
@@ -253,8 +383,8 @@ EOF
 $Lang{Recent_Email_Summary} = <<EOF;
 \${h1("Recent Email Summary (Reverse time order)")}
 <p>
-<table border>
-<tr><td align="center"> Recipient </td>
+<table cellspacing="1" cellpadding="3" width="100%">
+<tr class="tableheader"><td align="center"> Recipient </td>
     <td align="center"> Host </td>
     <td align="center"> Time </td>
     <td align="center"> Subject </td></tr>
@@ -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} = <<EOF;
+\${h1("Restore Options for \$host")}
 <p>
 You have selected the following files/directories from
 share \$share, backup number #\$num:
 <ul>
 \$fileListStr
 </ul>
-<p>
+</p><p>
 You have three choices for restoring these files/directories.
 Please select one of the following options.
-<p>
+</p>
 \${h2("Option 1: Direct Restore")}
 <p>
 You can start a restore that will restore these files directly onto
 \$host.
-<p>
+</p><p>
 <b>Warning:</b> any existing files that match the ones you have
 selected will be overwritten!
-
-<form action="\$MyURL" method="post">
+</p>
+<form action="\$MyURL" method="post" name="direct">
 <input type="hidden" name="host" value="\${EscHTML(\$host)}">
 <input type="hidden" name="num" value="\$num">
 <input type="hidden" name="type" value="3">
@@ -296,8 +427,17 @@ selected will be overwritten!
 <table border="0">
 <tr>
     <td>Restore the files to host</td>
-    <td><input type="text" size="40" value="\${EscHTML(\$host)}"
-        name="hostDest"></td>
+    <td><!--<input type="text" size="40" value="\${EscHTML(\$host)}"
+        name="hostDest">-->
+        <select name="hostDest" onChange="document.direct.shareDest.value=''">
+        <!-- hostOpt -->
+        </select>
+        <script language="Javascript">
+        function myOpen(URL) {
+               window.open(URL,'','width=200,height=200');
+       }
+        </script>
+        <a href="javascript:myOpen('\$MyURL?action=findShares&host='+document.direct.hostDest.options.value)">Search for available shares</a></td>
 </tr><tr>
     <td>Restore the files to share</td>
     <td><input type="text" size="40" value="\${EscHTML(\$share)}"
@@ -320,12 +460,12 @@ $Lang{Option_2__Download_Zip_archive} = <<EOF;
 You can download a Zip archive containing all the files/directories you have
 selected.  You can then use a local application, such as WinZip,
 to view or extract any of the files.
-<p>
+</p><p>
 <b>Warning:</b> 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.
-<p>
+</p>
 <form action="\$MyURL" method="post">
 <input type="hidden" name="host" value="\${EscHTML(\$host)}">
 <input type="hidden" name="num" value="\$num">
@@ -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
 <a href="http://www.cpan.org">www.cpan.org</a>.
-<p>
+</p>
 EOF
 
 
@@ -363,12 +503,12 @@ $Lang{Option_3__Download_Zip_archive} = <<EOF;
 You can download a Tar archive containing all the files/directories you
 have selected.  You can then use a local application, such as tar or
 WinZip to view or extract any of the files.
-<p>
+</p><p>
 <b>Warning:</b> 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.
-<p>
+</p>
 <form action="\$MyURL" method="post">
 <input type="hidden" name="host" value="\${EscHTML(\$host)}">
 <input type="hidden" name="num" value="\$num">
@@ -424,6 +564,13 @@ Reply from server was: \$reply
 Go back to <a href="\$MyURL?host=\$hostDest">\$hostDest home page</a>.
 EOF
 
+$Lang{BackupPC_Archive_Reply_from_server} = <<EOF;
+\${h1(\$str)}
+<p>
+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} = <<EOF;
 <ul>
 \$statusStr
 </ul>
-
+</p>
 \${h2("User Actions")}
 <p>
 <form action="\$MyURL" method="get">
@@ -443,12 +590,13 @@ $Lang{Host__host_Backup_Summary2} = <<EOF;
 <input type="submit" value="$Lang{Start_Full_Backup}" name="action">
 <input type="submit" value="$Lang{Stop_Dequeue_Backup}" name="action">
 </form>
-
+</p>
 \${h2("Backup Summary")}
 <p>
 Click on the backup number to browse and restore backup files.
-<table border>
-<tr><td align="center"> Backup# </td>
+</p>
+<table cellspacing="1" cellpadding="3" width="100%">
+<tr class="tableheader"><td align="center"> Backup# </td>
     <td align="center"> Type </td>
     <td align="center"> Filled </td>
     <td align="center"> Start Date </td>
@@ -461,11 +609,12 @@ Click on the backup number to browse and restore backup files.
 <p>
 
 \$restoreStr
-
+</p>
+<br><br>
 \${h2("Xfer Error Summary")}
-<p>
-<table border>
-<tr><td align="center"> Backup# </td>
+<br><br>
+<table cellspacing="1" cellpadding="3" width="100%">
+<tr class="tableheader"><td align="center"> Backup# </td>
     <td align="center"> Type </td>
     <td align="center"> View </td>
     <td align="center"> #Xfer errs </td>
@@ -475,20 +624,21 @@ Click on the backup number to browse and restore backup files.
 </tr>
 \$errStr
 </table>
-<p>
+<br><br>
 
 \${h2("File Size/Count Reuse Summary")}
 <p>
 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.
-<table border>
-<tr><td colspan="2"></td>
+</p>
+<table cellspacing="1" cellpadding="3" width="100%">
+<tr class="tableheader"><td colspan="2" bgcolor="#ffffff"></td>
     <td align="center" colspan="3"> Totals </td>
     <td align="center" colspan="2"> Existing Files </td>
     <td align="center" colspan="2"> New Files </td>
 </tr>
-<tr>
+<tr class="tableheader">
     <td align="center"> Backup# </td>
     <td align="center"> Type </td>
     <td align="center"> #Files </td>
@@ -501,18 +651,19 @@ Empty files and SMB errors aren\'t counted in the reuse and new counts.
 </tr>
 \$sizeStr
 </table>
-<p>
+<br><br>
 
 \${h2("Compression Summary")}
 <p>
 Compression performance for files already in the pool and newly
 compressed files.
-<table border>
-<tr><td colspan="3"></td>
+</p>
+<table cellspacing="1" cellpadding="3" width="100%">
+<tr class="tableheader"><td colspan="3" bgcolor="#ffffff"></td>
     <td align="center" colspan="3"> Existing Files </td>
     <td align="center" colspan="3"> New Files </td>
 </tr>
-<tr><td align="center"> Backup# </td>
+<tr class="tableheader"><td align="center"> Backup# </td>
     <td align="center"> Type </td>
     <td align="center"> Comp Level </td>
     <td align="center"> Size/MB </td>
@@ -524,7 +675,30 @@ compressed files.
 </tr>
 \$compStr
 </table>
+<br><br>
+EOF
+
+$Lang{Host__host_Archive_Summary} = "BackupPC: Host \$host Archive Summary";
+$Lang{Host__host_Archive_Summary2} = <<EOF;
+\${h1("Host \$host Archive Summary")}
 <p>
+\$warnStr
+<ul>
+\$statusStr
+</ul>
+
+\${h2("User Actions")}
+<p>
+<form action="\$MyURL" method="get">
+<input type="hidden" name="archivehost" value="\$host">
+<input type="hidden" name="host" value="\$host">
+<input type="submit" value="$Lang{Start_Archive}" name="action">
+<input type="submit" value="$Lang{Stop_Dequeue_Archive}" name="action">
+</form>
+
+
+\$ArchiveStr
+
 EOF
 
 # -------------------------
@@ -577,10 +751,10 @@ $Lang{Backup_browse_for__host} = <<EOF;
 <li> You are browsing backup #\$num, which started around \$backupTime
         (\$backupAge days ago),
 \$filledBackup
+<li> Enter directory: <input type="text" name="dir" size="50" maxlength="4096" value="\${EscHTML(\$dir)}"> <input type="submit" value="\$Lang->{Go}" name="Submit">
 <li> Click on a directory below to navigate into that directory,
 <li> Click on a file below to restore that file,
 <li> You can view the backup <a href="\$MyURL?action=dirHistory&host=\${EscURI(\$host)}&share=\$shareURI&dir=\$pathURI">history</a> of the current directory.
-<li> Enter directory: <input type="text" name="dir" size="50" maxlength="4096" value="\${EscHTML(\$dir)}"> <input type="submit" value="\$Lang->{Go}" name="Submit">
 </ul>
 </form>
 
@@ -592,18 +766,15 @@ $Lang{Backup_browse_for__host} = <<EOF;
 <input type="hidden" name="fcbMax" value="\$checkBoxCnt">
 <input type="hidden" name="action" value="$Lang{Restore}">
 <br>
-<table>
+<table width="100%">
 <tr><td valign="top">
-    <!--Navigate here:-->
     <br><table align="center" border="0" cellpadding="0" cellspacing="0" bgcolor="#ffffff">
     \$dirStr
     </table>
 </td><td width="3%">
 </td><td valign="top">
-    <!--Restore files here:-->
     <br>
-    <table cellpadding="0" cellspacing="0" bgcolor="#333333"><tr><td>
-        <table border="0" width="100%" align="left" cellpadding="2" cellspacing="1">
+        <table border="0" width="100%" align="left" cellpadding="3" cellspacing="1">
         \$fileHeader
         \$topCheckAll
         \$fileStr
@@ -615,7 +786,6 @@ $Lang{Backup_browse_for__host} = <<EOF;
 This is now in the checkAll row
 <input type="submit" name="Submit" value="Restore selected files">
 -->
-</td></tr></table>
 </form>
 EOF
 
@@ -651,9 +821,9 @@ the backups:
 \${h2("History of \${EscHTML(\$dirDisplay)}")}
 
 <br>
-<table border bgcolor="#ffffcc">
-<tr bgcolor="\$Conf{CgiHeaderBgColor}"><td>Backup number</td>\$backupNumStr</tr>
-<tr bgcolor="\$Conf{CgiHeaderBgColor}"><td>Backup time</td>\$backupTimeStr</tr>
+<table cellspacing="2" cellpadding="3">
+<tr class="fviewheader"><td>Backup number</td>\$backupNumStr</tr>
+<tr class="fviewheader"><td>Backup time</td>\$backupTimeStr</tr>
 \$fileStr
 </table>
 EOF
@@ -664,35 +834,63 @@ $Lang{Restore___num_details_for__host} = "BackupPC: Restore #\$num details for \
 $Lang{Restore___num_details_for__host2 } = <<EOF;
 \${h1("Restore #\$num Details for \$host")}
 <p>
+<table cellspacing="1" cellpadding="3" width="50%">
+<tr><td class="tableheader"> Number </td><td class="border"> \$Restores[\$i]{num} </td></tr>
+<tr><td class="tableheader"> Requested by </td><td class="border"> \$RestoreReq{user} </td></tr>
+<tr><td class="tableheader"> Request time </td><td class="border"> \$reqTime </td></tr>
+<tr><td class="tableheader"> Result </td><td class="border"> \$Restores[\$i]{result} </td></tr>
+<tr><td class="tableheader"> Error Message </td><td class="border"> \$Restores[\$i]{errorMsg} </td></tr>
+<tr><td class="tableheader"> Source host </td><td class="border"> \$RestoreReq{hostSrc} </td></tr>
+<tr><td class="tableheader"> Source backup num </td><td class="border"> \$RestoreReq{num} </td></tr>
+<tr><td class="tableheader"> Source share </td><td class="border"> \$RestoreReq{shareSrc} </td></tr>
+<tr><td class="tableheader"> Destination host </td><td class="border"> \$RestoreReq{hostDest} </td></tr>
+<tr><td class="tableheader"> Destination share </td><td class="border"> \$RestoreReq{shareDest} </td></tr>
+<tr><td class="tableheader"> Start time </td><td class="border"> \$startTime </td></tr>
+<tr><td class="tableheader"> Duration </td><td class="border"> \$duration min </td></tr>
+<tr><td class="tableheader"> Number of files </td><td class="border"> \$Restores[\$i]{nFiles} </td></tr>
+<tr><td class="tableheader"> Total size </td><td class="border"> \${MB} MB </td></tr>
+<tr><td class="tableheader"> Transfer rate </td><td class="border"> \$MBperSec MB/sec </td></tr>
+<tr><td class="tableheader"> TarCreate errors </td><td class="border"> \$Restores[\$i]{tarCreateErrs} </td></tr>
+<tr><td class="tableheader"> Xfer errors </td><td class="border"> \$Restores[\$i]{xferErrs} </td></tr>
+<tr><td class="tableheader"> Xfer log file </td><td class="border">
+<a href="\$MyURL?action=view&type=RestoreLOG&num=\$Restores[\$i]{num}&host=\$host">View</a>,
+<a href="\$MyURL?action=view&type=RestoreErr&num=\$Restores[\$i]{num}&host=\$host">Errors</a>
+</tr></tr>
+</table>
+<p>
+\${h1("File/Directory list")}
+<p>
+<table cellspacing="1" cellpadding="3" width="100%">
+<tr class="tableheader"><td>Original file/dir</td><td>Restored to</td></tr>
+\$fileListStr
+</table>
+EOF
+
+# ------------------------------
+$Lang{Archive___num_details_for__host} = "BackupPC: Archive #\$num details for \$host";
+
+$Lang{Archive___num_details_for__host2 } = <<EOF;
+\${h1("Archive #\$num Details for \$host")}
+<p>
 <table border>
-<tr><td> Number </td><td> \$Restores[\$i]{num} </td></tr>
-<tr><td> Requested by </td><td> \$RestoreReq{user} </td></tr>
+<tr><td> Number </td><td> \$Archives[\$i]{num} </td></tr>
+<tr><td> Requested by </td><td> \$ArchiveReq{user} </td></tr>
 <tr><td> Request time </td><td> \$reqTime </td></tr>
-<tr><td> Result </td><td> \$Restores[\$i]{result} </td></tr>
-<tr><td> Error Message </td><td> \$Restores[\$i]{errorMsg} </td></tr>
-<tr><td> Source host </td><td> \$RestoreReq{hostSrc} </td></tr>
-<tr><td> Source backup num </td><td> \$RestoreReq{num} </td></tr>
-<tr><td> Source share </td><td> \$RestoreReq{shareSrc} </td></tr>
-<tr><td> Destination host </td><td> \$RestoreReq{hostDest} </td></tr>
-<tr><td> Destination share </td><td> \$RestoreReq{shareDest} </td></tr>
+<tr><td> Result </td><td> \$Archives[\$i]{result} </td></tr>
+<tr><td> Error Message </td><td> \$Archives[\$i]{errorMsg} </td></tr>
 <tr><td> Start time </td><td> \$startTime </td></tr>
 <tr><td> Duration </td><td> \$duration min </td></tr>
-<tr><td> Number of files </td><td> \$Restores[\$i]{nFiles} </td></tr>
-<tr><td> Total size </td><td> \${MB} MB </td></tr>
-<tr><td> Transfer rate </td><td> \$MBperSec MB/sec </td></tr>
-<tr><td> TarCreate errors </td><td> \$Restores[\$i]{tarCreateErrs} </td></tr>
-<tr><td> Xfer errors </td><td> \$Restores[\$i]{xferErrs} </td></tr>
 <tr><td> Xfer log file </td><td>
-<a href="\$MyURL?action=view&type=RestoreLOG&num=\$Restores[\$i]{num}&host=\$host">View</a>,
-<a href="\$MyURL?action=view&type=RestoreErr&num=\$Restores[\$i]{num}&host=\$host">Errors</a>
+<a href="\$MyURL?action=view&type=ArchiveLOG&num=\$Archives[\$i]{num}&host=\$host">View</a>,
+<a href="\$MyURL?action=view&type=ArchiveErr&num=\$Archives[\$i]{num}&host=\$host">Errors</a>
 </tr></tr>
 </table>
 <p>
-\${h1("File/Directory list")}
+\${h1("Host list")}
 <p>
 <table border>
-<tr><td>Original file/dir</td><td>Restored to</td></tr>
-\$fileListStr
+<tr><td>Host</td><td>Backup Number</td></tr>
+\$HostListStr
 </table>
 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} = <<EOF;
 \$host is a DHCP host, and I don\'t know its IP address.  I checked the
@@ -772,6 +967,7 @@ $Lang{Backup_stopped_dequeued_on__host_by__User} = "Backup stopped/dequeued on \
 
 $Lang{Restore_requested_to_host__hostDest__backup___num} = "Restore requested to host \$hostDest, backup #\$num,"
             . " by \$User from \$ENV{REMOTE_ADDR}";
+$Lang{Archive_requested} = "Archive requested by \$User from \$ENV{REMOTE_ADDR}";
 
 # -------------------------------------------------
 # ------- Stuff that was forgotten ----------------
@@ -780,6 +976,7 @@ $Lang{Restore_requested_to_host__hostDest__backup___num} = "Restore requested to
 $Lang{Status} = "Status";
 $Lang{PC_Summary} = "PC Summary";
 $Lang{LOG_file} = "LOG file";
+$Lang{LOG_files} = "LOG files";
 $Lang{Old_LOGs} = "Old LOGs";
 $Lang{Email_summary} = "Email summary";
 $Lang{Config_file} = "Config file";
@@ -791,6 +988,7 @@ $Lang{Host_or_User_name} = "<small>Host or User name:</small>";
 $Lang{Go} = "Go";
 $Lang{Hosts} = "Hosts";
 
+$Lang{There_have_been_no_archives} = "<h2> There have been no archives </h2>\n";
 $Lang{This_PC_has_never_been_backed_up} = "<h2> This PC has never been backed up!! </h2>\n";
 $Lang{This_PC_is_used_by} = "<li>This PC is used by \${UserLink(\$user)}";
 
@@ -858,23 +1056,31 @@ EOF
 
 $Lang{tryIP} = " and \$StatusHost{dhcpHostIP}";
 
-$Lang{Host_Inhost} = "Host \$In{host}";
+$Lang{Host_Inhost} = "Host \$In{host}";
 
 $Lang{checkAll} = <<EOF;
-<tr bgcolor="#ffffcc"><td>
+<tr><td class="fviewborder">
 <input type="checkbox" name="allFiles" onClick="return checkAll('allFiles');">&nbsp;Select all
-</td><td colspan="5" align="center">
+</td><td colspan="5" align="center" class="fviewborder">
 <input type="submit" name="Submit" value="Restore selected files">
 </td></tr>
 EOF
 
+$Lang{checkAllHosts} = <<EOF;
+<tr bgcolor="#ffffcc"><td>
+<input type="checkbox" name="allFiles" onClick="return checkAll('allFiles');">&nbsp;Select all
+</td><td colspan="2" align="center">
+<input type="submit" name="Submit" value="Archive selected hosts">
+</td></tr>
+EOF
+
 $Lang{fileHeader} = <<EOF;
-    <tr bgcolor="\$Conf{CgiHeaderBgColor}"><td align=center> Name</td>
+    <tr class="fviewheader"><td align=center> Name</td>
        <td align="center"> Type</td>
        <td align="center"> Mode</td>
        <td align="center"> #</td>
        <td align="center"> Size</td>
-       <td align="center"> Mod time</td>
+       <td align="center"> Date modified</td>
     </tr>
 EOF
 
@@ -887,15 +1093,15 @@ $Lang{This_display_is_merged_with_backup} = <<EOF;
 EOF
 
 $Lang{Visit_this_directory_in_backup} = <<EOF;
-<li> Visit this directory in backup #\$otherDirs.
+<li> Select the backup you wish to view: <select onChange="window.location=this.value">\$otherDirs </select>
 EOF
 
 $Lang{Restore_Summary} = <<EOF;
 \${h2("Restore Summary")}
 <p>
 Click on the restore number for more details.
-<table border>
-<tr><td align="center"> Restore# </td>
+<table cellspacing="1" cellpadding="3" width="100%">
+<tr class="tableheader"><td align="center"> Restore# </td>
     <td align="center"> Result </td>
     <td align="right"> Start Date</td>
     <td align="right"> Dur/mins</td>
@@ -909,6 +1115,21 @@ Click on the restore number for more details.
 <p>
 EOF
 
+$Lang{Archive_Summary} = <<EOF;
+\${h2("Archive Summary")}
+<p>
+Click on the archive number for more details.
+<table border>
+<tr><td align="center"> Archive# </td>
+    <td align="center"> Result </td>
+    <td align="right"> Start Date</td>
+    <td align="right"> Dur/mins</td>
+</tr>
+\$ArchiveStr
+</table>
+<p>
+EOF
+
 $Lang{BackupPC__Documentation} = "BackupPC: Documentation";
 
 $Lang{No} = "no";
index cf99f4b..cef6d9c 100644 (file)
@@ -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 ( <ARCHIVE_INFO> ) {
+            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) = @_;
index 712ca05..8c547a4 100755 (executable)
--- 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 ( <lib/BackupPC/Lang/*.pm> ) {
+    # CB: disable other lang checks for now
+    #
+    # foreach my $f ( <lib/BackupPC/Lang/*.pm> ) {
+    #
+    foreach my $f ( <lib/BackupPC/Lang/en.pm> ) {
         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++;