* Major changes from Ryan Kucera to add style sheets to the CGI
[BackupPC.git] / bin / BackupPC
index d18bcef..3f1fa2b 100755 (executable)
@@ -29,7 +29,7 @@
 #   Craig Barratt  <cbarratt@users.sourceforge.net>
 #
 # COPYRIGHT
-#   Copyright (C) 2001  Craig Barratt
+#   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
 #
 #========================================================================
 #
-# Version 2.0.0beta1, released 30 Mar 2003.
+# Version 2.1.0_CVS, released 3 Jul 2003.
 #
 # See http://backuppc.sourceforge.net.
 #
 #========================================================================
 
 use strict;
+no  utf8;
 use vars qw(%Status %Info $Hosts);
 use lib "/usr/local/BackupPC/lib";
 use BackupPC::Lib;
@@ -299,6 +300,7 @@ sub Main_Initialize
     $Info{ConfigModTime} = $bpc->ConfigMTime();
     $Info{pid} = $$;
     $Info{startTime} = time;
+    $Info{ConfigLTime} = time;
     $Info{Version} = $bpc->{Version};
 
     #
@@ -319,6 +321,10 @@ sub Main_Initialize
         }
         $Status{$host}{activeJob} = 0;
     }
+    foreach my $host ( sort(keys(%Status)) ) {
+        next if ( defined($Hosts->{$host}) );
+       delete($Status{$host});
+    }
 
     #
     # Write out our initial status and save our PID
@@ -472,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)
@@ -533,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";
@@ -560,9 +570,12 @@ sub Main_TryToRun_Bg_or_User_Queue
         vec($FDread, $Jobs{$host}{fn}, 1) = 1;
         $Jobs{$host}{startTime}  = time;
         $Jobs{$host}{reqTime}    = $req->{reqTime};
+        $Jobs{$host}{userReq}    = $req->{userReq};
         $Jobs{$host}{cmd}        = join(" ", $progName, @args);
         $Jobs{$host}{user}       = $user;
         $Jobs{$host}{type}       = $type;
+       $Status{$host}{userReq}  = $req->{userReq}
+                                       if ( defined($Hosts->{$host}) );
         if ( !$req->{dhcp} ) {
             $Status{$host}{state}     = "Status_".$type."_starting";
             $Status{$host}{activeJob} = 1;
@@ -589,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]} ) {
@@ -643,23 +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");
-        foreach my $host ( keys(%Jobs) ) {
-            kill(2, $Jobs{$host}{pid});
-        }
-        StatusWrite();
-        unlink("$TopDir/log/BackupPC.pid");
-        exit(1);
+        ServerShutdown("Got signal $SigName... cleaning up");
     }
     $SigName = "";
 }
@@ -838,16 +831,23 @@ sub Main_Check_Job_Messages
                 delete($Status{$host}{errorTime});
                 $Status{$host}{endTime}   = time;
             } elsif ( $mesg =~ /^nothing to do/ ) {
-                $Status{$host}{state}     = "Status_idle";
-                $Status{$host}{reason}    = "Reason_nothing_to_do";
-                $Status{$host}{startTime} = time;
+               if ( $Status{$host}{reason} ne "Reason_backup_failed"
+                       && $Status{$host}{reason} ne "Reason_restore_failed" ) {
+                   $Status{$host}{state}     = "Status_idle";
+                   $Status{$host}{reason}    = "Reason_nothing_to_do";
+                   $Status{$host}{startTime} = time;
+               }
                 $Status{$host}{dhcpCheckCnt}--
                                 if ( $Status{$host}{dhcpCheckCnt} > 0 );
             } elsif ( $mesg =~ /^no ping response/
-                            || $mesg =~ /^ping too slow/ ) {
+                            || $mesg =~ /^ping too slow/
+                            || $mesg =~ /^host not found/ ) {
                 $Status{$host}{state}     = "Status_idle";
-                if ( $Status{$host}{reason} ne "Reason_backup_failed" ) {
+                if ( $Status{$host}{userReq}
+                       || $Status{$host}{reason} ne "Reason_backup_failed"
+                       || $Status{$host}{error} =~ /^aborted by user/ ) {
                     $Status{$host}{reason}    = "Reason_no_ping";
+                   $Status{$host}{error}     = $mesg;
                     $Status{$host}{startTime} = time;
                 }
                 $Status{$host}{deadCnt}++;
@@ -860,7 +860,14 @@ sub Main_Check_Job_Messages
                 $Status{$host}{error}     = $1;
                 $Status{$host}{errorTime} = time;
                 $Status{$host}{endTime}   = time;
-                print(LOG $bpc->timeStamp, "backup failed on $host ($1)\n");
+                print(LOG $bpc->timeStamp, "Backup failed on $host ($1)\n");
+            } elsif ( $mesg =~ /^restore failed: (.*)/ ) {
+                $Status{$host}{state}     = "Status_idle";
+                $Status{$host}{reason}    = "Reason_restore_failed";
+                $Status{$host}{error}     = $1;
+                $Status{$host}{errorTime} = time;
+                $Status{$host}{endTime}   = time;
+                print(LOG $bpc->timeStamp, "Restore failed on $host ($1)\n");
             } elsif ( $mesg =~ /^log\s+(.*)/ ) {
                 print(LOG $bpc->timeStamp, "$1\n");
             } elsif ( $mesg =~ /^BackupPC_stats = (.*)/ ) {
@@ -1010,7 +1017,7 @@ sub Main_Check_Client_Messages
                 if ( $CmdJob ne $host && defined($Status{$host})
                                       && defined($Jobs{$host}) ) {
                     print(LOG $bpc->timeStamp,
-                               "Stopping current backup of $host,"
+                               "Stopping current $Jobs{$host}{type} of $host,"
                              . " request by $user (backoff=$backoff)\n");
                     kill(2, $Jobs{$host}{pid});
                    #
@@ -1022,11 +1029,20 @@ sub Main_Check_Client_Messages
                     ##close($Jobs{$host}{fh});
                     ##delete($Jobs{$host});
 
-                    $Status{$host}{state}     = "Status_idle";
-                    $Status{$host}{reason}    = "Reason_backup_canceled_by_user";
+                    $Status{$host}{state}    = "Status_idle";
+                   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";
+                   }
                     $Status{$host}{activeJob} = 0;
                     $Status{$host}{startTime} = time;
-                    $reply = "ok: backup of $host cancelled";
+                    $reply = "ok: $Jobs{$host}{type} of $host cancelled";
                 } elsif ( $BgQueueOn{$host} || $UserQueueOn{$host} ) {
                     print(LOG $bpc->timeStamp,
                                "Stopping pending backup of $host,"
@@ -1085,11 +1101,42 @@ sub Main_Check_Client_Messages
                                 user    => $user,
                                 reqTime => time,
                                 doFull  => $doFull,
+                                userReq => 1,
                                 dhcp    => $hostIP eq $host ? 0 : 1,
                         });
                     $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;
@@ -1113,6 +1160,7 @@ sub Main_Check_Client_Messages
                                 reqTime => time,
                                 dhcp    => 0,
                                 restore => 1,
+                               userReq => 1,
                         });
                     $UserQueueOn{$host} = 1;
                     if ( defined($Jobs{$host}) ) {
@@ -1174,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;
@@ -1413,8 +1470,9 @@ sub catch_signal
         print(LOG "Fatal error: unhandled signal $SigName\n");
         unlink("$TopDir/log/BackupPC.pid");
         confess("Got new signal $SigName... quitting\n");
+    } else {
+       $SigName = shift;
     }
-    $SigName = shift;
 }
 
 #
@@ -1492,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);
+}
+