546beefceeeef70e2f4f036c61d3049f4efc2aac
[BackupPC.git] / lib / BackupPC / CGI / HostInfo.pm
1 #============================================================= -*-perl-*-
2 #
3 # BackupPC::CGI::HostInfo package
4 #
5 # DESCRIPTION
6 #
7 #   This module implements the HostInfo action for the CGI interface.
8 #
9 # AUTHOR
10 #   Craig Barratt  <cbarratt@users.sourceforge.net>
11 #
12 # COPYRIGHT
13 #   Copyright (C) 2003-2007  Craig Barratt
14 #
15 #   This program is free software; you can redistribute it and/or modify
16 #   it under the terms of the GNU General Public License as published by
17 #   the Free Software Foundation; either version 2 of the License, or
18 #   (at your option) any later version.
19 #
20 #   This program is distributed in the hope that it will be useful,
21 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
22 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 #   GNU General Public License for more details.
24 #
25 #   You should have received a copy of the GNU General Public License
26 #   along with this program; if not, write to the Free Software
27 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 #
29 #========================================================================
30 #
31 # Version 3.1.0, released 25 Nov 2007.
32 #
33 # See http://backuppc.sourceforge.net.
34 #
35 #========================================================================
36
37 package BackupPC::CGI::HostInfo;
38
39 use strict;
40 use BackupPC::CGI::Lib qw(:all);
41
42 sub action
43 {
44     my $host = $1 if ( $In{host} =~ /(.*)/ );
45     my($statusStr, $startIncrStr);
46
47     $host =~ s/^\s+//;
48     $host =~ s/\s+$//;
49     if ( $host eq "" ) {
50         ErrorExit(eval("qq{$Lang->{Unknown_host_or_user}}"));
51     }
52     $host = lc($host)
53                if ( !-d "$TopDir/pc/$host" && -d "$TopDir/pc/" . lc($host) );
54     if ( $host =~ /\.\./ || !-d "$TopDir/pc/$host" ) {
55         #
56         # try to lookup by user name
57         #
58         if ( $host eq "" || !defined($Hosts->{$host}) ) {
59             foreach my $h ( keys(%$Hosts) ) {
60                 if ( $Hosts->{$h}{user} eq $host
61                         || lc($Hosts->{$h}{user}) eq lc($host) ) {
62                     $host = $h;
63                     last;
64                 }
65             }
66             CheckPermission();
67             ErrorExit(eval("qq{$Lang->{Unknown_host_or_user}}"))
68                                if ( !defined($Hosts->{$host}) );
69         }
70         $In{host} = $host;
71     }
72     GetStatusInfo("host(${EscURI($host)})");
73     $bpc->ConfigRead($host);
74     %Conf = $bpc->Conf();
75     my $Privileged = CheckPermission($host);
76     if ( !$Privileged ) {
77         ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_view_information_about}}"));
78     }
79     ReadUserEmailInfo();
80
81     if ( $Conf{XferMethod} eq "archive" ) {
82         my @Archives = $bpc->ArchiveInfoRead($host);
83         my ($ArchiveStr,$warnStr);
84
85         for ( my $i = 0 ; $i < @Archives ; $i++ ) {
86             my $startTime = timeStamp2($Archives[$i]{startTime});
87             my $dur       = $Archives[$i]{endTime} - $Archives[$i]{startTime};
88             $dur          = 1 if ( $dur <= 0 );
89             my $duration  = sprintf("%.1f", $dur / 60);
90             my $Archives_Result = $Lang->{failed};
91             if ($Archives[$i]{result} ne "failed") { $Archives_Result = $Lang->{success}; }
92             $ArchiveStr  .= <<EOF;
93 <tr><td align="center"><a href="$MyURL?action=archiveInfo&num=$Archives[$i]{num}&host=${EscURI($host)}">$Archives[$i]{num}</a> </td>
94     <td align="center"> $Archives_Result </td>
95     <td align="right"> $startTime </td>
96     <td align="right"> $duration </td>
97 </tr>
98 EOF
99         }
100         if ( $ArchiveStr ne "" ) {
101             $ArchiveStr = eval("qq{$Lang->{Archive_Summary}}");
102         }
103         if ( @Archives == 0 ) {
104             $warnStr = $Lang->{There_have_been_no_archives};
105         }
106         if ( $StatusHost{BgQueueOn} ) {
107             $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon}}");
108         }
109         if ( $StatusHost{UserQueueOn} ) {
110             $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon}}");
111         }
112         if ( $StatusHost{CmdQueueOn} ) {
113             $statusStr .= eval("qq{$Lang->{A_command_for_host_is_on_the_command_queue_will_run_soon}}");
114         }
115
116         my $content = eval("qq{$Lang->{Host__host_Archive_Summary2}}");
117         Header(eval("qq{$Lang->{Host__host_Archive_Summary}}"), $content, 1);
118         Trailer();
119         return;
120     }
121
122     #
123     # Normal, non-archive case
124     #
125     my @Backups = $bpc->BackupInfoRead($host);
126     my($str, $sizeStr, $compStr, $errStr, $warnStr);
127     for ( my $i = 0 ; $i < @Backups ; $i++ ) {
128         my $startTime = timeStamp2($Backups[$i]{startTime});
129         my $dur       = $Backups[$i]{endTime} - $Backups[$i]{startTime};
130         $dur          = 1 if ( $dur <= 0 );
131         my $duration  = sprintf("%.1f", $dur / 60);
132         my $MB        = sprintf("%.1f", $Backups[$i]{size} / (1024*1024));
133         my $MBperSec  = sprintf("%.2f", $Backups[$i]{size} / (1024*1024*$dur));
134         my $MBExist   = sprintf("%.1f", $Backups[$i]{sizeExist} / (1024*1024));
135         my $MBNew     = sprintf("%.1f", $Backups[$i]{sizeNew} / (1024*1024));
136         my($MBExistComp, $ExistComp, $MBNewComp, $NewComp);
137         if ( $Backups[$i]{sizeExist} && $Backups[$i]{sizeExistComp} ) {
138             $MBExistComp = sprintf("%.1f", $Backups[$i]{sizeExistComp}
139                                                 / (1024 * 1024));
140             $ExistComp = sprintf("%.1f%%", 100 *
141                   (1 - $Backups[$i]{sizeExistComp} / $Backups[$i]{sizeExist}));
142         }
143         if ( $Backups[$i]{sizeNew} && $Backups[$i]{sizeNewComp} ) {
144             $MBNewComp = sprintf("%.1f", $Backups[$i]{sizeNewComp}
145                                                 / (1024 * 1024));
146             $NewComp = sprintf("%.1f%%", 100 *
147                   (1 - $Backups[$i]{sizeNewComp} / $Backups[$i]{sizeNew}));
148         }
149         my $age = sprintf("%.1f", (time - $Backups[$i]{startTime}) / (24*3600));
150         my $browseURL = "$MyURL?action=browse&host=${EscURI($host)}&num=$Backups[$i]{num}";
151         my $level  = $Backups[$i]{level};
152         my $filled = $Backups[$i]{noFill} ? $Lang->{No} : $Lang->{Yes};
153         $filled .= " ($Backups[$i]{fillFromNum}) "
154                             if ( $Backups[$i]{fillFromNum} ne "" );
155         my $ltype = $Lang->{"backupType_$Backups[$i]{type}"};
156         $str .= <<EOF;
157 <tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
158     <td align="center" class="border"> $ltype </td>
159     <td align="center" class="border"> $filled </td>
160     <td align="center" class="border"> $level </td>
161     <td align="right" class="border">  $startTime </td>
162     <td align="right" class="border">  $duration </td>
163     <td align="right" class="border">  $age </td>
164     <td align="left" class="border">   <tt>$TopDir/pc/$host/$Backups[$i]{num}</tt> </td></tr>
165 EOF
166         $sizeStr .= <<EOF;
167 <tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
168     <td align="center" class="border"> $ltype </td>
169     <td align="right" class="border">  $Backups[$i]{nFiles} </td>
170     <td align="right" class="border">  $MB </td>
171     <td align="right" class="border">  $MBperSec </td>
172     <td align="right" class="border">  $Backups[$i]{nFilesExist} </td>
173     <td align="right" class="border">  $MBExist </td>
174     <td align="right" class="border">  $Backups[$i]{nFilesNew} </td>
175     <td align="right" class="border">  $MBNew </td>
176 </tr>
177 EOF
178         my $is_compress = $Backups[$i]{compress} || $Lang->{off};
179         if (! $ExistComp) { $ExistComp = "&nbsp;"; }
180         if (! $MBExistComp) { $MBExistComp = "&nbsp;"; }
181         $compStr .= <<EOF;
182 <tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
183     <td align="center" class="border"> $ltype </td>
184     <td align="center" class="border"> $is_compress </td>
185     <td align="right" class="border">  $MBExist </td>
186     <td align="right" class="border">  $MBExistComp </td>
187     <td align="right" class="border">  $ExistComp </td>
188     <td align="right" class="border">  $MBNew </td>
189     <td align="right" class="border">  $MBNewComp </td>
190     <td align="right" class="border">  $NewComp </td>
191 </tr>
192 EOF
193         $errStr .= <<EOF;
194 <tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td>
195     <td align="center" class="border"> $ltype </td>
196     <td align="center" class="border"> <a href="$MyURL?action=view&type=XferLOG&num=$Backups[$i]{num}&host=${EscURI($host)}">$Lang->{XferLOG}</a>,
197                       <a href="$MyURL?action=view&type=XferErr&num=$Backups[$i]{num}&host=${EscURI($host)}">$Lang->{Errors}</a> </td>
198     <td align="right" class="border">  $Backups[$i]{xferErrs} </td>
199     <td align="right" class="border">  $Backups[$i]{xferBadFile} </td>
200     <td align="right" class="border">  $Backups[$i]{xferBadShare} </td>
201     <td align="right" class="border">  $Backups[$i]{tarErrs} </td></tr>
202 EOF
203     }
204
205     my @Restores = $bpc->RestoreInfoRead($host);
206     my $restoreStr;
207
208     for ( my $i = 0 ; $i < @Restores ; $i++ ) {
209         my $startTime = timeStamp2($Restores[$i]{startTime});
210         my $dur       = $Restores[$i]{endTime} - $Restores[$i]{startTime};
211         $dur          = 1 if ( $dur <= 0 );
212         my $duration  = sprintf("%.1f", $dur / 60);
213         my $MB        = sprintf("%.1f", $Restores[$i]{size} / (1024*1024));
214         my $MBperSec  = sprintf("%.2f", $Restores[$i]{size} / (1024*1024*$dur));
215         my $Restores_Result = $Lang->{failed};
216         if ($Restores[$i]{result} ne "failed") { $Restores_Result = $Lang->{success}; }
217         $restoreStr  .= <<EOF;
218 <tr><td align="center" class="border"><a href="$MyURL?action=restoreInfo&num=$Restores[$i]{num}&host=${EscURI($host)}">$Restores[$i]{num}</a> </td>
219     <td align="center" class="border"> $Restores_Result </td>
220     <td align="right" class="border"> $startTime </td>
221     <td align="right" class="border"> $duration </td>
222     <td align="right" class="border"> $Restores[$i]{nFiles} </td>
223     <td align="right" class="border"> $MB </td>
224     <td align="right" class="border"> $Restores[$i]{tarCreateErrs} </td>
225     <td align="right" class="border"> $Restores[$i]{xferErrs} </td>
226 </tr>
227 EOF
228     }
229     if ( $restoreStr ne "" ) {
230         $restoreStr = eval("qq{$Lang->{Restore_Summary}}");
231     }
232     if ( @Backups == 0 ) {
233         $warnStr = $Lang->{This_PC_has_never_been_backed_up};
234     }
235     if ( defined($Hosts->{$host}) ) {
236         my $user = $Hosts->{$host}{user};
237         my @moreUsers = sort(keys(%{$Hosts->{$host}{moreUsers}}));
238         my $moreUserStr;
239         foreach my $u ( sort(keys(%{$Hosts->{$host}{moreUsers}})) ) {
240             $moreUserStr .= ", " if ( $moreUserStr ne "" );
241             $moreUserStr .= "${UserLink($u)}";
242         }
243         if ( $moreUserStr ne "" ) {
244             $moreUserStr = " ($Lang->{and} $moreUserStr).\n";
245         } else {
246             $moreUserStr = ".\n";
247         }
248         if ( $user ne "" ) {
249             $statusStr .= eval("qq{$Lang->{This_PC_is_used_by}$moreUserStr}");
250         }
251         if ( defined($UserEmailInfo{$user})
252                 && $UserEmailInfo{$user}{lastHost} eq $host ) {
253             my $mailTime = timeStamp2($UserEmailInfo{$user}{lastTime});
254             my $subj     = $UserEmailInfo{$user}{lastSubj};
255             $statusStr  .= eval("qq{$Lang->{Last_email_sent_to__was_at___subject}}");
256         }
257     }
258     if ( defined($Jobs{$host}) ) {
259         my $startTime = timeStamp2($Jobs{$host}{startTime});
260         (my $cmd = $Jobs{$host}{cmd}) =~ s/$BinDir\///g;
261         $statusStr .= eval("qq{$Lang->{The_command_cmd_is_currently_running_for_started}}");
262     }
263     if ( $StatusHost{BgQueueOn} ) {
264         $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon}}");
265     }
266     if ( $StatusHost{UserQueueOn} ) {
267         $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon}}");
268     }
269     if ( $StatusHost{CmdQueueOn} ) {
270         $statusStr .= eval("qq{$Lang->{A_command_for_host_is_on_the_command_queue_will_run_soon}}");
271     }
272     my $startTime = timeStamp2($StatusHost{endTime} == 0 ?
273                 $StatusHost{startTime} : $StatusHost{endTime});
274     my $reason = "";
275     if ( $StatusHost{reason} ne "" ) {
276         $reason = " ($Lang->{$StatusHost{reason}})";
277     }
278     $statusStr .= eval("qq{$Lang->{Last_status_is_state_StatusHost_state_reason_as_of_startTime}}");
279
280     if ( $StatusHost{state} ne "Status_backup_in_progress"
281             && $StatusHost{state} ne "Status_restore_in_progress"
282             && $StatusHost{error} ne "" ) {
283         $statusStr .= eval("qq{$Lang->{Last_error_is____EscHTML_StatusHost_error}}");
284     }
285     my $priorStr = "Pings";
286     if ( $StatusHost{deadCnt} > 0 ) {
287         $statusStr .= eval("qq{$Lang->{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times}}");
288         $priorStr = $Lang->{Prior_to_that__pings};
289     }
290     if ( $StatusHost{aliveCnt} > 0 ) {
291         $statusStr .= eval("qq{$Lang->{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times}}");
292
293         if ( (@{$Conf{BlackoutPeriods}} || defined($Conf{BlackoutHourBegin}))
294                 && $StatusHost{aliveCnt} >= $Conf{BlackoutGoodCnt}
295                 && $Conf{BlackoutGoodCnt} >= 0 ) {
296             #
297             # Handle backward compatibility with original separate scalar
298             # blackout parameters.
299             #
300             if ( defined($Conf{BlackoutHourBegin}) ) {
301                 push(@{$Conf{BlackoutPeriods}},
302                      {
303                          hourBegin => $Conf{BlackoutHourBegin},
304                          hourEnd   => $Conf{BlackoutHourEnd},
305                          weekDays  => $Conf{BlackoutWeekDays},
306                      }
307                 );
308             }
309
310             #
311             # TODO: this string needs i18n.  Also, comma-separated
312             # list with "and" for the last element might not translate
313             # correctly.
314             #
315             my(@days) = qw(Sun Mon Tue Wed Thu Fri Sat);
316             my $blackoutStr;
317             my $periodCnt = 0;
318             foreach my $p ( @{$Conf{BlackoutPeriods}} ) {
319                 next if ( ref($p->{weekDays}) ne "ARRAY"
320                             || !defined($p->{hourBegin})
321                             || !defined($p->{hourEnd})
322                         );
323                 my $days = join(", ", @days[@{$p->{weekDays}}]);
324                 my $t0   = sprintf("%d:%02d", $p->{hourBegin},
325                               60 * ($p->{hourBegin} - int($p->{hourBegin})));
326                 my $t1   = sprintf("%d:%02d", $p->{hourEnd},
327                               60 * ($p->{hourEnd} - int($p->{hourEnd})));
328                 if ( $periodCnt ) {
329                     $blackoutStr .= ", ";
330                     if ( $periodCnt == @{$Conf{BlackoutPeriods}} - 1 ) {
331                         $blackoutStr .= eval("qq{$Lang->{and}}");
332                         $blackoutStr .= " ";
333                     }
334                 }
335                 $blackoutStr
336                         .= eval("qq{$Lang->{__time0_to__time1_on__days}}");
337                 $periodCnt++;
338             }
339             $statusStr .= eval("qq{$Lang->{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___}}");
340         }
341     }
342     if ( $StatusHost{backoffTime} > time ) {
343         my $hours = sprintf("%.1f", ($StatusHost{backoffTime} - time) / 3600);
344         $statusStr .= eval("qq{$Lang->{Backups_are_deferred_for_hours_hours_change_this_number}}");
345
346     }
347     if ( @Backups ) {
348         # only allow incremental if there are already some backups
349         $startIncrStr = <<EOF;
350 <input type="button" value="$Lang->{Start_Incr_Backup}"
351  onClick="document.StartStopForm.action.value='Start_Incr_Backup';
352           document.StartStopForm.submit();">
353 EOF
354     }
355
356     $startIncrStr = eval("qq{$startIncrStr}");
357     my $content = eval("qq{$Lang->{Host__host_Backup_Summary2}}");
358     Header(eval("qq{$Lang->{Host__host_Backup_Summary}}"), $content);
359     Trailer();
360 }
361
362 1;