* Another minor change to BackupPC_Admin
[BackupPC.git] / cgi-bin / BackupPC_Admin
index a2eefad..d61bdb1 100755 (executable)
@@ -39,7 +39,7 @@
 #
 #========================================================================
 #
-# Version 2.0.0beta1, released 30 Mar 2003.
+# Version 2.0.0beta2, released 11 May 2003.
 #
 # See http://backuppc.sourceforge.net.
 #
@@ -76,7 +76,7 @@ $User   = $ENV{REMOTE_USER};
 
 if ( !defined($bpc) ) {
     ErrorExit($Lang->{BackupPC__Lib__new_failed__check_apache_error_log})
-       if ( !($bpc = BackupPC::Lib->new) );
+       if ( !($bpc = BackupPC::Lib->new(undef, undef, 1)) );
     $TopDir = $bpc->TopDir();
     $BinDir = $bpc->BinDir();
     %Conf   = $bpc->Conf();
@@ -100,14 +100,23 @@ $ENV{PATH} = $Conf{MyPath};
 #
 if ( $Conf{BackupPCUserVerify}
         && $> != (my $uid = (getpwnam($Conf{BackupPCUser}))[2]) ) {
-    ErrorExit(eval("qq{$Lang->{Wrong_user__my_userid_is___}}"));
+    ErrorExit(eval("qq{$Lang->{Wrong_user__my_userid_is___}}"), <<EOF);
+This script needs to run as the user specified in \$Conf{BackupPCUser},
+which is set to $Conf{BackupPCUser}.
+<p>
+This is an installation problem.  If you are using mod_perl then
+it appears that Apache is not running as user $Conf{BackupPCUser}.
+If you are not using mod_perl, then most like setuid is not working
+properly on BackupPC_Admin.  Check the permissions on
+$Conf{CgiDir}/BackupPC_Admin and look at the documentation.
+EOF
 }
 
 if ( !defined($Hosts) || $bpc->HostsMTime() != $HostsMTime ) {
     $HostsMTime = $bpc->HostsMTime();
     $Hosts = $bpc->HostInfoRead();
 
-    # turn operators list into a hash for quick lookups
+    # turn moreUsers list into a hash for quick lookups
     foreach my $host (keys %$Hosts) {
        $Hosts->{$host}{moreUsers} =
            {map {$_, 1} split(",", $Hosts->{$host}{moreUsers}) }
@@ -156,7 +165,8 @@ sub Action_Summary
         ErrorExit($Lang->{Only_privileged_users_can_view_PC_summaries} );
     }
     foreach my $host ( sort(keys(%Status)) ) {
-        my($fullDur, $incrCnt, $incrAge, $fullSize, $fullRate);
+        my($fullDur, $incrCnt, $incrAge, $fullSize, $fullRate, $reasonHilite);
+       my($shortErr);
         my @Backups = $bpc->BackupInfoRead($host);
         my $fullCnt = $incrCnt = 0;
         my $fullAge = $incrAge = -1;
@@ -193,10 +203,20 @@ sub Action_Summary
         $fullTot += $fullCnt;
         $incrTot += $incrCnt;
         $fullSize = sprintf("%.2f", $fullSize / 1000);
-       if (! $incrAge) { $incrAge = "&nbsp;"; }
+       $incrAge = "&nbsp;" if ( $incrAge eq "" );
+       $reasonHilite = $Conf{CgiStatusHilightColor}{$Status{$host}{reason}};
+       $reasonHilite = " bgcolor=\"$reasonHilite\"" if ( $reasonHilite ne "" );
+        if ( $Status{$host}{state} ne "Status_backup_in_progress"
+               && $Status{$host}{state} ne "Status_restore_in_progress"
+               && $Status{$host}{error} ne "" ) {
+           ($shortErr = $Status{$host}{error}) =~ s/(.{48}).*/$1.../;
+           $shortErr = " ($shortErr)";
+       }
+
         $str = <<EOF;
-<tr><td> ${HostLink($host)} </td>
-    <td align="center"> ${UserLink($Hosts->{$host}{user})} </td>
+<tr$reasonHilite><td> ${HostLink($host)} </td>
+    <td align="center"> ${UserLink(defined($Hosts->{$host})
+                                   ? $Hosts->{$host}{user} : "")} </td>
     <td align="center"> $fullCnt </td>
     <td align="center"> $fullAge </td>
     <td align="center"> $fullSize </td>
@@ -204,7 +224,7 @@ sub Action_Summary
     <td align="center"> $incrCnt </td>
     <td align="center"> $incrAge </td>
     <td align="center"> $Lang->{$Status{$host}{state}} </td>
-    <td> $Lang->{$Status{$host}{reason}} </td></tr>
+    <td> $Lang->{$Status{$host}{reason}}$shortErr </td></tr>
 EOF
         if ( @Backups == 0 ) {
             $hostCntNone++;
@@ -312,12 +332,12 @@ EOF
     while ( @CmdQueue ) {
         my $req = pop(@CmdQueue);
         my $reqTime = timeStamp2($req->{reqTime});
-        (my $cmd = $req->{cmd}) =~ s/$BinDir\///;
+        (my $cmd = $req->{cmd}[0]) =~ s/$BinDir\///;
         $strCmd .= <<EOF;
 <tr><td> ${HostLink($req->{host})} </td>
     <td align="center"> $reqTime </td>
     <td align="center"> $req->{user} </td>
-    <td> $cmd </td></tr>
+    <td> $cmd $req->{cmd}[0] </td></tr>
 EOF
     }
     Header($Lang->{BackupPC__Queue_Summary});
@@ -349,16 +369,16 @@ sub Action_View
     } elsif ( $type eq "XferErrbad" ) {
         $file = "$TopDir/pc/$host/SmbLOG.bad";
         $file = "$TopDir/pc/$host/XferLOG.bad" if ( !-f $file && !-f "$file.z");
-        $comment = "(Extracting only Errors)";
+        $comment = $Lang->{Extracting_only_Errors};
     } elsif ( $type eq "XferErr" ) {
         $file = "$TopDir/pc/$host/SmbLOG$ext";
         $file = "$TopDir/pc/$host/XferLOG$ext" if ( !-f $file && !-f "$file.z");
-        $comment = "(Extracting only Errors)";
+        $comment = $Lang->{Extracting_only_Errors};
     } elsif ( $type eq "RestoreLOG" ) {
         $file = "$TopDir/pc/$host/RestoreLOG$ext";
     } elsif ( $type eq "RestoreErr" ) {
         $file = "$TopDir/pc/$host/RestoreLOG$ext";
-        $comment = "(Extracting only Errors)";
+        $comment = $Lang->{Extracting_only_Errors};
     } elsif ( $host ne "" && $type eq "config" ) {
         $file = "$TopDir/pc/$host/config.pl";
         $file = "$TopDir/conf/$host.pl"
@@ -859,10 +879,6 @@ EOF
         #
         # Provide the selected files via a tar archive.
        #
-       # We no longer use fork/exec (as in v1.5.0) since some mod_perls
-       # do not correctly preserve the stdout connection to the client
-       # browser, so we execute BackupPC_tarCreate in-line.
-        #
        my @fileListTrim = @fileList;
        if ( @fileListTrim > 10 ) {
            @fileListTrim = (@fileListTrim[0..9], '...');
@@ -873,33 +889,30 @@ EOF
         if ( $In{relative} ) {
             @pathOpts = ("-r", $pathHdr, "-p", "");
         }
-       #
-       # We use syswrite since BackupPC_tarCreate uses syswrite too.
-       # Need to test this with mod_perl: PaulL says it doesn't work.
-       #
-       syswrite(STDOUT, <<EOF);
+       print(STDOUT <<EOF);
 Content-Type: application/x-gtar
 Content-Transfer-Encoding: binary
 Content-Disposition: attachment; filename=\"restore.tar\"
 
 EOF
-       local(@ARGV);
-       @ARGV = (
-             "-h", $host,
-             "-n", $num,
-             "-s", $share,
-             @pathOpts,
-             @fileList
+       #
+       # Fork the child off and manually copy the output to our stdout.
+       # This is necessary to ensure the output gets to the correct place
+       # under mod_perl.
+       #
+       $bpc->cmdSystemOrEval(["$BinDir/BackupPC_tarCreate",
+                "-h", $host,
+                "-n", $num,
+                "-s", $share,
+                @pathOpts,
+                @fileList
+           ],
+           sub { print(@_); }
        );
-       do "$BinDir/BackupPC_tarCreate";
     } elsif ( $In{type} == 2 ) {
         #
         # Provide the selected files via a zip archive.
        #
-       # We no longer use fork/exec (as in v1.5.0) since some mod_perls
-       # do not correctly preserve the stdout connection to the client
-       # browser, so we execute BackupPC_tarCreate in-line.
-        #
        my @fileListTrim = @fileList;
        if ( @fileListTrim > 10 ) {
            @fileListTrim = (@fileListTrim[0..9], '...');
@@ -910,27 +923,28 @@ EOF
         if ( $In{relative} ) {
             @pathOpts = ("-r", $pathHdr, "-p", "");
         }
-       #
-       # We use syswrite since BackupPC_tarCreate uses syswrite too.
-       # Need to test this with mod_perl: PaulL says it doesn't work.
-       #
-       syswrite(STDOUT, <<EOF);
+       print(STDOUT <<EOF);
 Content-Type: application/zip
 Content-Transfer-Encoding: binary
 Content-Disposition: attachment; filename=\"restore.zip\"
 
 EOF
        $In{compressLevel} = 5 if ( $In{compressLevel} !~ /^\d+$/ );
-       local(@ARGV);
-       @ARGV = (
-             "-h", $host,
-             "-n", $num,
-             "-c", $In{compressLevel},
-             "-s", $share,
-             @pathOpts,
-             @fileList
-        );
-        do "$BinDir/BackupPC_zipCreate";
+       #
+       # Fork the child off and manually copy the output to our stdout.
+       # This is necessary to ensure the output gets to the correct place
+       # under mod_perl.
+       #
+       $bpc->cmdSystemOrEval(["$BinDir/BackupPC_zipCreate",
+                "-h", $host,
+                "-n", $num,
+                "-c", $In{compressLevel},
+                "-s", $share,
+                @pathOpts,
+                @fileList
+           ],
+           sub { print(@_); }
+       );
     } elsif ( $In{type} == 3 ) {
         #
         # Do restore directly onto host
@@ -1262,8 +1276,8 @@ 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)}">XferLOG</a>,
-                      <a href="$MyURL?action=view&type=XferErr&num=$Backups[$i]{num}&host=${EscURI($host)}">Errors</a> </td>
+    <td align="center"> <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>
@@ -1346,7 +1360,9 @@ EOF
     }
     $statusStr .= eval("qq{$Lang->{Last_status_is_state_StatusHost_state_reason_as_of_startTime}}");
 
-    if ( $StatusHost{error} ne "" ) {
+    if ( $StatusHost{state} ne "Status_backup_in_progress"
+           && $StatusHost{state} ne "Status_restore_in_progress"
+           && $StatusHost{error} ne "" ) {
         $statusStr .= eval("qq{$Lang->{Last_error_is____EscHTML_StatusHost_error}}");
     }
     my $priorStr = "Pings";
@@ -1395,7 +1411,7 @@ sub Action_GeneralInfo
     GetStatusInfo("info jobs hosts queueLen");
     my $Privileged = CheckPermission();
 
-    my($jobStr, $statusStr, $tarPidHdr);
+    my($jobStr, $statusStr);
     foreach my $host ( sort(keys(%Jobs)) ) {
         my $startTime = timeStamp2($Jobs{$host}{startTime});
         next if ( $host eq $bpc->trashJob
@@ -1403,24 +1419,23 @@ sub Action_GeneralInfo
         $Jobs{$host}{type} = $Status{$host}{type}
                     if ( $Jobs{$host}{type} eq "" && defined($Status{$host}));
         (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($Hosts->{$host}{user})} </td>
+    <td align="center"> ${UserLink(defined($Hosts->{$host})
+                                       ? $Hosts->{$host}{user} : "")} </td>
     <td> $startTime </td>
     <td> $cmd </td>
     <td align="center"> $Jobs{$host}{pid} </td>
-    <td align="center"> $Jobs{$host}{xferPid} </td>
+    <td align="center"> $xferPid </td>
 EOF
-        if ( $Jobs{$host}{tarPid} > 0 ) {
-            $jobStr .= "    <td align=\"center\"> $Jobs{$host}{tarPid} </td>\n";
-            $tarPidHdr ||= "<td align=\"center\"> tar PID </td>\n";
-        }
         $jobStr .= "</tr>\n";
     }
     foreach my $host ( sort(keys(%Status)) ) {
         next if ( $Status{$host}{reason} ne "Reason_backup_failed"
-              || $Status{$host}{error} =~ /^Can't find host \Q$host/ );
+                   && (!$Status{$host}{userReq}
+                       || $Status{$host}{reason} ne "Reason_no_ping") );
         my $startTime = timeStamp2($Status{$host}{startTime});
         my($errorTime, $XferViewStr);
         if ( $Status{$host}{errorTime} > 0 ) {
@@ -1432,8 +1447,8 @@ EOF
                 || -f "$TopDir/pc/$host/XferLOG.bad.z"
                 ) {
             $XferViewStr = <<EOF;
-<a href="$MyURL?action=view&type=XferLOGbad&host=${EscURI($host)}">XferLOG</a>,
-<a href="$MyURL?action=view&type=XferErrbad&host=${EscURI($host)}">XferErr</a>
+<a href="$MyURL?action=view&type=XferLOGbad&host=${EscURI($host)}">$Lang->{XferLOG}</a>,
+<a href="$MyURL?action=view&type=XferErrbad&host=${EscURI($host)}">$Lang->{Errors}</a>
 EOF
         } else {
             $XferViewStr = "";
@@ -1442,7 +1457,8 @@ EOF
         $statusStr .= <<EOF;
 <tr><td> ${HostLink($host)} </td>
     <td align="center"> $Status{$host}{type} </td>
-    <td align="center"> ${UserLink($Hosts->{$host}{user})} </td>
+    <td align="center"> ${UserLink(defined($Hosts->{$host})
+                                       ? $Hosts->{$host}{user} : "")} </td>
     <td align="right"> $startTime </td>
     <td> $XferViewStr </td>
     <td align="right"> $errorTime </td>
@@ -1604,6 +1620,16 @@ sub ErrorExit
     $Conf{CgiNavBarBgColor}  ||= "#ddeeee";
     $Conf{CgiHeaderBgColor}  ||= "#99cc33";
 
+    if ( !defined($ENV{REMOTE_USER}) ) {
+       $mesg .= <<EOF;
+<p>
+Note: \$ENV{REMOTE_USER} is not set, which could mean there is an
+installation problem.  BackupPC_Admin expects Apache to authenticate
+the user and pass their user name into this script as the REMOTE_USER
+environment variable.  See the documentation.
+EOF
+    }
+
     $bpc->ServerMesg("log User $User (host=$In{host}) got CGI error: $head")
                             if ( defined($bpc) );
     if ( !defined($Lang->{Error}) ) {
@@ -1683,7 +1709,7 @@ sub CheckPermission
     }
     $PrivAdmin = $Privileged;
     $Privileged ||= $User eq $Hosts->{$host}{user};
-    $Privileged ||= defined($Hosts->{$host}{operators}{$User});
+    $Privileged ||= defined($Hosts->{$host}{moreUsers}{$User});
 
     return $Privileged;
 }
@@ -1717,7 +1743,7 @@ sub ConfirmIPAddress
     my($host) = @_;
     my $ipAddr = $host;
 
-    if ( $Hosts->{$host}{dhcp}
+    if ( defined($Hosts->{$host}) && $Hosts->{$host}{dhcp}
               && $ENV{REMOTE_ADDR} =~ /^(\d+[\.\d]*)$/ ) {
        $ipAddr = $1;
        my($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($ipAddr);