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 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 2.1.0, released 20 Jun 2004. |
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 $filled = $Backups[$i]{noFill} ? $Lang->{No} : $Lang->{Yes}; |
152 |
$filled .= " ($Backups[$i]{fillFromNum}) " |
153 |
if ( $Backups[$i]{fillFromNum} ne "" ); |
154 |
my $ltype = $Lang->{"backupType_$Backups[$i]{type}"}; |
155 |
$str .= <<EOF; |
156 |
<tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td> |
157 |
<td align="center" class="border"> $ltype </td> |
158 |
<td align="center" class="border"> $filled </td> |
159 |
<td align="right" class="border"> $startTime </td> |
160 |
<td align="right" class="border"> $duration </td> |
161 |
<td align="right" class="border"> $age </td> |
162 |
<td align="left" class="border"> <tt>$TopDir/pc/$host/$Backups[$i]{num}</tt> </td></tr> |
163 |
EOF |
164 |
$sizeStr .= <<EOF; |
165 |
<tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td> |
166 |
<td align="center" class="border"> $ltype </td> |
167 |
<td align="right" class="border"> $Backups[$i]{nFiles} </td> |
168 |
<td align="right" class="border"> $MB </td> |
169 |
<td align="right" class="border"> $MBperSec </td> |
170 |
<td align="right" class="border"> $Backups[$i]{nFilesExist} </td> |
171 |
<td align="right" class="border"> $MBExist </td> |
172 |
<td align="right" class="border"> $Backups[$i]{nFilesNew} </td> |
173 |
<td align="right" class="border"> $MBNew </td> |
174 |
</tr> |
175 |
EOF |
176 |
my $is_compress = $Backups[$i]{compress} || $Lang->{off}; |
177 |
if (! $ExistComp) { $ExistComp = " "; } |
178 |
if (! $MBExistComp) { $MBExistComp = " "; } |
179 |
$compStr .= <<EOF; |
180 |
<tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td> |
181 |
<td align="center" class="border"> $ltype </td> |
182 |
<td align="center" class="border"> $is_compress </td> |
183 |
<td align="right" class="border"> $MBExist </td> |
184 |
<td align="right" class="border"> $MBExistComp </td> |
185 |
<td align="right" class="border"> $ExistComp </td> |
186 |
<td align="right" class="border"> $MBNew </td> |
187 |
<td align="right" class="border"> $MBNewComp </td> |
188 |
<td align="right" class="border"> $NewComp </td> |
189 |
</tr> |
190 |
EOF |
191 |
$errStr .= <<EOF; |
192 |
<tr><td align="center" class="border"> <a href="$browseURL">$Backups[$i]{num}</a> </td> |
193 |
<td align="center" class="border"> $ltype </td> |
194 |
<td align="center" class="border"> <a href="$MyURL?action=view&type=XferLOG&num=$Backups[$i]{num}&host=${EscURI($host)}">$Lang->{XferLOG}</a>, |
195 |
<a href="$MyURL?action=view&type=XferErr&num=$Backups[$i]{num}&host=${EscURI($host)}">$Lang->{Errors}</a> </td> |
196 |
<td align="right" class="border"> $Backups[$i]{xferErrs} </td> |
197 |
<td align="right" class="border"> $Backups[$i]{xferBadFile} </td> |
198 |
<td align="right" class="border"> $Backups[$i]{xferBadShare} </td> |
199 |
<td align="right" class="border"> $Backups[$i]{tarErrs} </td></tr> |
200 |
EOF |
201 |
} |
202 |
|
203 |
my @Restores = $bpc->RestoreInfoRead($host); |
204 |
my $restoreStr; |
205 |
|
206 |
for ( my $i = 0 ; $i < @Restores ; $i++ ) { |
207 |
my $startTime = timeStamp2($Restores[$i]{startTime}); |
208 |
my $dur = $Restores[$i]{endTime} - $Restores[$i]{startTime}; |
209 |
$dur = 1 if ( $dur <= 0 ); |
210 |
my $duration = sprintf("%.1f", $dur / 60); |
211 |
my $MB = sprintf("%.1f", $Restores[$i]{size} / (1024*1024)); |
212 |
my $MBperSec = sprintf("%.2f", $Restores[$i]{size} / (1024*1024*$dur)); |
213 |
my $Restores_Result = $Lang->{failed}; |
214 |
if ($Restores[$i]{result} ne "failed") { $Restores_Result = $Lang->{success}; } |
215 |
$restoreStr .= <<EOF; |
216 |
<tr><td align="center" class="border"><a href="$MyURL?action=restoreInfo&num=$Restores[$i]{num}&host=${EscURI($host)}">$Restores[$i]{num}</a> </td> |
217 |
<td align="center" class="border"> $Restores_Result </td> |
218 |
<td align="right" class="border"> $startTime </td> |
219 |
<td align="right" class="border"> $duration </td> |
220 |
<td align="right" class="border"> $Restores[$i]{nFiles} </td> |
221 |
<td align="right" class="border"> $MB </td> |
222 |
<td align="right" class="border"> $Restores[$i]{tarCreateErrs} </td> |
223 |
<td align="right" class="border"> $Restores[$i]{xferErrs} </td> |
224 |
</tr> |
225 |
EOF |
226 |
} |
227 |
if ( $restoreStr ne "" ) { |
228 |
$restoreStr = eval("qq{$Lang->{Restore_Summary}}"); |
229 |
} |
230 |
if ( @Backups == 0 ) { |
231 |
$warnStr = $Lang->{This_PC_has_never_been_backed_up}; |
232 |
} |
233 |
if ( defined($Hosts->{$host}) ) { |
234 |
my $user = $Hosts->{$host}{user}; |
235 |
my @moreUsers = sort(keys(%{$Hosts->{$host}{moreUsers}})); |
236 |
my $moreUserStr; |
237 |
foreach my $u ( sort(keys(%{$Hosts->{$host}{moreUsers}})) ) { |
238 |
$moreUserStr .= ", " if ( $moreUserStr ne "" ); |
239 |
$moreUserStr .= "${UserLink($u)}"; |
240 |
} |
241 |
if ( $moreUserStr ne "" ) { |
242 |
$moreUserStr = " ($Lang->{and} $moreUserStr).\n"; |
243 |
} else { |
244 |
$moreUserStr = ".\n"; |
245 |
} |
246 |
if ( $user ne "" ) { |
247 |
$statusStr .= eval("qq{$Lang->{This_PC_is_used_by}$moreUserStr}"); |
248 |
} |
249 |
if ( defined($UserEmailInfo{$user}) |
250 |
&& $UserEmailInfo{$user}{lastHost} eq $host ) { |
251 |
my $mailTime = timeStamp2($UserEmailInfo{$user}{lastTime}); |
252 |
my $subj = $UserEmailInfo{$user}{lastSubj}; |
253 |
$statusStr .= eval("qq{$Lang->{Last_email_sent_to__was_at___subject}}"); |
254 |
} |
255 |
} |
256 |
if ( defined($Jobs{$host}) ) { |
257 |
my $startTime = timeStamp2($Jobs{$host}{startTime}); |
258 |
(my $cmd = $Jobs{$host}{cmd}) =~ s/$BinDir\///g; |
259 |
$statusStr .= eval("qq{$Lang->{The_command_cmd_is_currently_running_for_started}}"); |
260 |
} |
261 |
if ( $StatusHost{BgQueueOn} ) { |
262 |
$statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon}}"); |
263 |
} |
264 |
if ( $StatusHost{UserQueueOn} ) { |
265 |
$statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon}}"); |
266 |
} |
267 |
if ( $StatusHost{CmdQueueOn} ) { |
268 |
$statusStr .= eval("qq{$Lang->{A_command_for_host_is_on_the_command_queue_will_run_soon}}"); |
269 |
} |
270 |
my $startTime = timeStamp2($StatusHost{endTime} == 0 ? |
271 |
$StatusHost{startTime} : $StatusHost{endTime}); |
272 |
my $reason = ""; |
273 |
if ( $StatusHost{reason} ne "" ) { |
274 |
$reason = " ($Lang->{$StatusHost{reason}})"; |
275 |
} |
276 |
$statusStr .= eval("qq{$Lang->{Last_status_is_state_StatusHost_state_reason_as_of_startTime}}"); |
277 |
|
278 |
if ( $StatusHost{state} ne "Status_backup_in_progress" |
279 |
&& $StatusHost{state} ne "Status_restore_in_progress" |
280 |
&& $StatusHost{error} ne "" ) { |
281 |
$statusStr .= eval("qq{$Lang->{Last_error_is____EscHTML_StatusHost_error}}"); |
282 |
} |
283 |
my $priorStr = "Pings"; |
284 |
if ( $StatusHost{deadCnt} > 0 ) { |
285 |
$statusStr .= eval("qq{$Lang->{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times}}"); |
286 |
$priorStr = $Lang->{Prior_to_that__pings}; |
287 |
} |
288 |
if ( $StatusHost{aliveCnt} > 0 ) { |
289 |
$statusStr .= eval("qq{$Lang->{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times}}"); |
290 |
|
291 |
if ( (@{$Conf{BlackoutPeriods}} || defined($Conf{BlackoutHourBegin})) |
292 |
&& $StatusHost{aliveCnt} >= $Conf{BlackoutGoodCnt} |
293 |
&& $Conf{BlackoutGoodCnt} >= 0 ) { |
294 |
# |
295 |
# Handle backward compatibility with original separate scalar |
296 |
# blackout parameters. |
297 |
# |
298 |
if ( defined($Conf{BlackoutHourBegin}) ) { |
299 |
push(@{$Conf{BlackoutPeriods}}, |
300 |
{ |
301 |
hourBegin => $Conf{BlackoutHourBegin}, |
302 |
hourEnd => $Conf{BlackoutHourEnd}, |
303 |
weekDays => $Conf{BlackoutWeekDays}, |
304 |
} |
305 |
); |
306 |
} |
307 |
|
308 |
# |
309 |
# TODO: this string needs i18n. Also, comma-separated |
310 |
# list with "and" for the last element might not translate |
311 |
# correctly. |
312 |
# |
313 |
my(@days) = qw(Sun Mon Tue Wed Thu Fri Sat); |
314 |
my $blackoutStr; |
315 |
my $periodCnt = 0; |
316 |
foreach my $p ( @{$Conf{BlackoutPeriods}} ) { |
317 |
next if ( ref($p->{weekDays}) ne "ARRAY" |
318 |
|| !defined($p->{hourBegin}) |
319 |
|| !defined($p->{hourEnd}) |
320 |
); |
321 |
my $days = join(", ", @days[@{$p->{weekDays}}]); |
322 |
my $t0 = sprintf("%d:%02d", $p->{hourBegin}, |
323 |
60 * ($p->{hourBegin} - int($p->{hourBegin}))); |
324 |
my $t1 = sprintf("%d:%02d", $p->{hourEnd}, |
325 |
60 * ($p->{hourEnd} - int($p->{hourEnd}))); |
326 |
if ( $periodCnt ) { |
327 |
$blackoutStr .= ", "; |
328 |
if ( $periodCnt == @{$Conf{BlackoutPeriods}} - 1 ) { |
329 |
$blackoutStr .= eval("qq{$Lang->{and}}"); |
330 |
$blackoutStr .= " "; |
331 |
} |
332 |
} |
333 |
$blackoutStr |
334 |
.= eval("qq{$Lang->{__time0_to__time1_on__days}}"); |
335 |
$periodCnt++; |
336 |
} |
337 |
$statusStr .= eval("qq{$Lang->{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___}}"); |
338 |
} |
339 |
} |
340 |
if ( $StatusHost{backoffTime} > time ) { |
341 |
my $hours = sprintf("%.1f", ($StatusHost{backoffTime} - time) / 3600); |
342 |
$statusStr .= eval("qq{$Lang->{Backups_are_deferred_for_hours_hours_change_this_number}}"); |
343 |
|
344 |
} |
345 |
if ( @Backups ) { |
346 |
# only allow incremental if there are already some backups |
347 |
$startIncrStr = <<EOF; |
348 |
<input type="submit" value="\$Lang->{Start_Incr_Backup}" name="action"> |
349 |
EOF |
350 |
} |
351 |
|
352 |
$startIncrStr = eval ("qq{$startIncrStr}"); |
353 |
my $content = eval("qq{$Lang->{Host__host_Backup_Summary2}}"); |
354 |
Header(eval("qq{$Lang->{Host__host_Backup_Summary}}"), $content); |
355 |
Trailer(); |
356 |
} |
357 |
|
358 |
1; |