2 #============================================================= -*-perl-*-
4 # BackupPC_sendEmail: send status emails to users and admins
8 # BackupPC_sendEmail: send status emails to users and admins.
9 # BackupPC_sendEmail is run by BackupPC_nightly, so it runs
13 # Craig Barratt <cbarratt@users.sourceforge.net>
16 # Copyright (C) 2001 Craig Barratt
18 # This program is free software; you can redistribute it and/or modify
19 # it under the terms of the GNU General Public License as published by
20 # the Free Software Foundation; either version 2 of the License, or
21 # (at your option) any later version.
23 # This program is distributed in the hope that it will be useful,
24 # but WITHOUT ANY WARRANTY; without even the implied warranty of
25 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 # GNU General Public License for more details.
28 # You should have received a copy of the GNU General Public License
29 # along with this program; if not, write to the Free Software
30 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #========================================================================
34 # Version 2.0.0beta2, released 13 Apr 2003.
36 # See http://backuppc.sourceforge.net.
38 #========================================================================
41 use lib "/usr/local/BackupPC/lib";
43 use BackupPC::FileZIO;
48 use vars qw($Lang $TopDir $BinDir %Conf);
50 die("BackupPC::Lib->new failed\n") if ( !(my $bpc = BackupPC::Lib->new) );
51 $TopDir = $bpc->TopDir();
52 $BinDir = $bpc->BinDir();
58 use vars qw(%UserEmailInfo);
59 do "$TopDir/log/UserEmailInfo.pl";
62 if ( !getopts("t", \%opts) || @ARGV != 0 ) {
63 print("usage: $0 [-t]\n");
67 my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort});
69 print("Can't connect to server ($err)\n");
72 my $reply = $bpc->ServerMesg("status hosts");
73 $reply = $1 if ( $reply =~ /(.*)/s );
74 my(%Status, %Info, %Jobs, @BgQueue, @UserQueue, @CmdQueue);
77 ###########################################################################
78 # Generate sysadmin warning messages
79 ###########################################################################
83 foreach my $host ( sort(keys(%Status)) ) {
84 next if ( $Status{$host}{reason} ne "backup failed"
85 || $Status{$host}{error} =~ /^lost network connection to host/ );
86 push(@badHosts, "$host ($Status{$host}{error})");
89 my $badHosts = join("\n - ", sort(@badHosts));
91 The following hosts had an error that is probably caused by a
92 misconfiguration. Please fix these hosts:
99 # Report if we skipped backups because the disk was too full
101 if ( $Info{DUDailySkipHostCntPrev} > 0 ) {
102 my $n = $Info{DUDailySkipHostCntPrev};
103 my $m = $Conf{DfMaxUsagePct};
105 Yesterday $n hosts were skipped because the file system containing
106 $TopDir was too full. The threshold in the
107 configuration file is $m%, while yesterday the file system was
108 up to $Info{DUDailyMaxPrev}% full. Please find more space on the file system,
109 or reduce the number of full or incremental backups that we keep.
115 # Check for bogus directories (probably PCs that are no longer
116 # on the backup list)
118 my $d = DirHandle->new("$TopDir/pc") or die("Can't read $TopDir/pc: $!");
120 my @files = $d->read;
122 foreach my $host ( @files ) {
123 next if ( $host eq "." || $host eq ".." || defined($Status{$host}) );
124 push(@oldDirs, "$TopDir/pc/$host");
127 my $oldDirs = join("\n - ", sort(@oldDirs));
129 The following directories are bogus and are not being used by
130 BackupPC. This typically happens when PCs are removed from the
131 backup list. If you don't need any old backups from these PCs you
132 should remove these directories. If there are machines on this
133 list that should be backed up then there is a problem with the
140 if ( $mesg ne "" && $Conf{EMailAdminUserName} ne "" ) {
142 To: $Conf{EMailAdminUserName}
143 Subject: BackupPC administrative attention needed
149 print("#" x 75, "\n");
156 ###########################################################################
157 # Generate per-host warning messages sent to each user
158 ###########################################################################
159 my $Hosts = $bpc->HostInfoRead();
161 foreach my $host ( sort(keys(%Status)) ) {
162 next if ( $Hosts->{$host}{user} eq "" );
164 # read any per-PC config settings (allowing per-PC email settings)
166 $bpc->ConfigRead($host);
167 %Conf = $bpc->Conf();
168 my $user = $Hosts->{$host}{user};
169 next if ( time - $UserEmailInfo{$user}{lastTime}
170 < $Conf{EMailNotifyMinDays} * 24*3600 );
171 my @Backups = $bpc->BackupInfoRead($host);
172 my $numBackups = @Backups;
173 if ( $numBackups == 0 ) {
174 my $subj = defined($Conf{EMailNoBackupEverSubj})
175 ? $Conf{EMailNoBackupEverSubj}
176 : $Lang->{EMailNoBackupEverSubj};
177 my $mesg = defined($Conf{EMailNoBackupEverMesg})
178 ? $Conf{EMailNoBackupEverMesg}
179 : $Lang->{EMailNoBackupEverMesg};
180 sendUserEmail($user, $host, $mesg, $subj, {
181 userName => user2name($user)
182 }) if ( !defined($Jobs{$host}) );
185 my $last = my $lastFull = my $lastIncr = 0;
186 my $lastGoodOutlook = 0;
188 my $numBadOutlook = 0;
189 for ( my $i = 0 ; $i < @Backups ; $i++ ) {
191 $lastNum = $Backups[$i]{num} if ( $lastNum < $Backups[$i]{num} );
192 if ( $Backups[$i]{type} eq "full" ) {
193 $lastFull = $Backups[$i]{startTime}
194 if ( $lastFull < $Backups[$i]{startTime} );
196 $lastIncr = $Backups[$i]{startTime}
197 if ( $lastIncr < $Backups[$i]{startTime} );
199 $last = $Backups[$i]{startTime}
200 if ( $last < $Backups[$i]{startTime} );
202 my $file = "$TopDir/pc/$host/SmbLOG.$Backups[$i]{num}";
205 $file = "$TopDir/pc/$host/XferLOG.$Backups[$i]{num}";
208 $file = "$TopDir/pc/$host/SmbLOG.$Backups[$i]{num}.z";
209 $file = "$TopDir/pc/$host/XferLOG.$Backups[$i]{num}.z"
213 next if ( !defined($fh = BackupPC::FileZIO->open($file, 0, $comp)) );
215 my $s = $fh->readLine();
216 last if ( $s eq "" );
217 if ( $s =~ /^Error reading file.*\.pst : ERRDOS - ERRlock/
218 || $s =~ /^Error reading file.*\.pst\. Got 0 bytes/ ) {
224 $numBadOutlook += $badOutlook;
225 if ( !$badOutlook ) {
226 $lastGoodOutlook = $Backups[$i]{startTime}
227 if ( $lastGoodOutlook < $Backups[$i]{startTime} );
230 if ( time - $last > $Conf{EMailNotifyOldBackupDays} * 24*3600 ) {
231 my $subj = defined($Conf{EMailNoBackupRecentSubj})
232 ? $Conf{EMailNoBackupRecentSubj}
233 : $Lang->{EMailNoBackupRecentSubj};
234 my $mesg = defined($Conf{EMailNoBackupRecentMesg})
235 ? $Conf{EMailNoBackupRecentMesg}
236 : $Lang->{EMailNoBackupRecentMesg};
237 my $firstTime = sprintf("%.1f",
238 (time - $Backups[0]{startTime}) / (24*3600));
239 my $days = sprintf("%.1f", (time - $last) / (24 * 3600));
240 sendUserEmail($user, $host, $mesg, $subj, {
241 firstTime => $firstTime,
243 userName => user2name($user),
244 numBackups => $numBackups,
245 }) if ( !defined($Jobs{$host}) );
248 if ( $numBadOutlook > 0
249 && time - $lastGoodOutlook > $Conf{EMailNotifyOldOutlookDays}
252 if ( $lastGoodOutlook == 0 ) {
253 $howLong = eval("qq{$Lang->{howLong_not_been_backed_up}}");
255 $days = sprintf("%.1f", (time - $lastGoodOutlook) / (24*3600));
256 $howLong = eval("qq{$Lang->{howLong_not_been_backed_up_for_days_days}}");
258 my $subj = defined($Conf{EMailOutlookBackupSubj})
259 ? $Conf{EMailOutlookBackupSubj}
260 : $Lang->{EMailOutlookBackupSubj};
261 my $mesg = defined($Conf{EMailOutlookBackupMesg})
262 ? $Conf{EMailOutlookBackupMesg}
263 : $Lang->{EMailOutlookBackupMesg};
264 my $firstTime = sprintf("%.1f",
265 (time - $Backups[0]{startTime}) / (24*3600));
266 my $lastTime = sprintf("%.1f",
267 (time - $Backups[$#Backups]{startTime}) / (24*3600));
268 sendUserEmail($user, $host, $mesg, $subj, {
270 firstTime => $firstTime,
271 lastTime => $lastTime,
272 numBackups => $numBackups,
273 userName => user2name($user),
275 serverHost => $Conf{ServerHost},
276 }) if ( !defined($Jobs{$host}) );
280 $Data::Dumper::Indent = 1;
281 my $dumpStr = Data::Dumper->Dump(
283 [qw(*UserEmailInfo)]);
284 if ( open(HOST, ">", "$TopDir/log/UserEmailInfo.pl") ) {
285 print(HOST $dumpStr);
293 my($name) = (getpwnam($user))[6];
295 $name = $user if ( $name eq "" );
301 my($user, $host, $mesg, $subj, $vars) = @_;
302 $vars->{user} = $user;
303 $vars->{host} = $host;
304 $vars->{domain} = $Conf{EMailUserDestDomain};
305 $vars->{CgiURL} = $Conf{CgiURL};
306 $subj =~ s/\$(\w+)/defined($vars->{$1}) ? $vars->{$1} : "\$$1"/eg;
307 $vars->{subj} = $subj;
308 $mesg =~ s/\$(\w+)/defined($vars->{$1}) ? $vars->{$1} : "\$$1"/eg;
310 print("#" x 75, "\n");
315 $UserEmailInfo{$user}{lastTime} = time;
316 $UserEmailInfo{$user}{lastSubj} = $subj;
317 $UserEmailInfo{$user}{lastHost} = $host;
323 my($from) = $Conf{EMailFromUserName};
326 $from = "-f $from" if ( $from ne "" );
327 if ( !open(MAIL, "|$Conf{SendmailPath} -t $from") ) {
328 printf("Can't run sendmail ($Conf{SendmailPath}): $!\n");