fix backup selection
[BackupPC.git] / lib / BackupPC / CGI / Archive.pm
1 #============================================================= -*-perl-*-
2 #
3 # BackupPC::CGI::Archive package
4 #
5 # DESCRIPTION
6 #
7 #   This module implements the Archive action for the CGI interface.
8 #
9 # AUTHOR
10 #   Craig Barratt  <cbarratt@users.sourceforge.net>
11 #
12 # COPYRIGHT
13 #   Copyright (C) 2003-2009  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.2.0, released 31 Jul 2010.
32 #
33 # See http://backuppc.sourceforge.net.
34 #
35 #========================================================================
36
37 package BackupPC::CGI::Archive;
38
39 use strict;
40 use BackupPC::CGI::Lib qw(:all);
41 use Data::Dumper;
42 use DBI;
43
44 sub action
45 {
46     my $archHost = $In{host};
47     my $Privileged = CheckPermission();
48
49     if ( !$Privileged ) {
50         ErrorExit($Lang->{Only_privileged_users_can_archive} );
51     }
52     if ( $In{type} == 0 ) {
53         my($fullTot, $fullSizeTot, $incrTot, $incrSizeTot, $str,
54            $strNone, $strGood, $hostCntGood, $hostCntNone, $checkBoxCnt,
55            $backupnumber);
56
57         $hostCntGood = $hostCntNone = $checkBoxCnt = $fullSizeTot = 0;
58         GetStatusInfo("hosts");
59
60         foreach my $host ( sort(keys(%Status)) ) {
61             my($fullDur, $incrCnt, $fullSize, $fullRate);
62             my @Backups = $bpc->BackupInfoRead($host);
63             my $fullCnt = $incrCnt = 0;
64             my $all_backup_numbers;
65
66             for ( my $i = 0 ; $i < @Backups ; $i++ ) {
67                 if ( $Backups[$i]{type} eq "full" ) {
68                     $fullSize = $Backups[$i]{size} / (1024 * 1024);
69                     $incrSizeTot = 0;
70                 } else {
71                     $incrSizeTot = $Backups[$i]{size} / (1024 * 1024);
72                 }
73                 $backupnumber = $Backups[$i]{num};
74                 $all_backup_numbers->{$backupnumber}++;
75             }
76             $fullSizeTot += $fullSize + $incrSizeTot;
77             $fullSize = sprintf("%.2f", ($fullSize + $incrSizeTot) / 1000);
78
79                 $bpc->ConfigRead($archHost);
80                 %Conf = $bpc->Conf();
81
82 use Data::Dump qw(dump);
83
84                 my $dbh = DBI->connect($Conf{SearchDSN}, $Conf{SearchUser}, "", { RaiseError => 1, AutoCommit => 0 });
85                 my $sth = $dbh->prepare(qq{
86                         select
87                                 hosts.name as host, -- FIXME for debug
88                                 backups.num as num,
89                                 inc_size,
90                                 size,
91                                 inc_deleted
92                         from backups
93                         join hosts on hosts.id = hostid
94                         where hosts.name = ?
95                 });
96                 $sth->execute($host);
97                 # and inc_size < 0 and size > 0 and not inc_deleted
98                 while( my $row = $sth->fetchrow_hashref ) {
99 warn "# row ",dump $row;
100                         $all_backup_numbers->{ $row->{num} } =
101                         $row->{inc_deleted}  ? 0 :
102                         $row->{size}    == 0 ? 0 :
103                         $row->{inc_size} > 0 ? 0 :
104                         $row->{size}     > 0 ? 1 :
105                         0;
106                 }
107
108 warn "# host $host all_backup_numbers = ",dump($all_backup_numbers);
109                 my @backup_nums = 
110                         sort
111                         grep { $all_backup_numbers->{$_} }
112                         keys %$all_backup_numbers;
113
114                 my $checkboxes;
115                 foreach my $backupnumber ( @backup_nums ) {
116                         $checkboxes .= qq|
117 <input type="hidden" name="fcb$checkBoxCnt" value="$host">
118 <input type="checkbox" name="backup$checkBoxCnt" value="$backupnumber">$backupnumber
119                         |;
120                         $checkBoxCnt++;
121                 }
122
123 =for ASA extension removed this
124             $str = <<EOF;
125 <tr>
126 <td class="border"><input type="hidden" name="backup$checkBoxCnt" value="$backupnumber"><input type="checkbox" name="fcb$checkBoxCnt" value="$host">&nbsp;${HostLink($host)} </td>
127 <td align="center" class="border"> ${UserLink($Hosts->{$host}{user})} </td>
128 <td align="center" class="border"> $fullSize </td>
129 EOF
130             $checkBoxCnt++;
131 =cut
132
133             $str = <<EOF;
134 <tr>
135 <td class="border">
136 ${HostLink($host)}
137 $checkboxes
138 </td>
139 <td align="center" class="border">
140 ${UserLink($Hosts->{$host}{user})}
141 </td>
142 <td align="center" class="border"> $fullSize </td>
143 EOF
144
145             if ( @Backups == 0 ) {
146                 $hostCntNone++;
147                 $strNone .= $str;
148             } else {
149                 $hostCntGood++;
150                 $strGood .= $str;
151             }
152         }
153         $fullSizeTot = sprintf("%.2f", $fullSizeTot / 1000);
154         my $now      = timeStamp2(time);
155         my $checkAllHosts = $Lang->{checkAllHosts};
156         $strGood .= <<EOF;
157 <input type="hidden" name="archivehost" value="$In{'archivehost'}">
158 EOF
159         my $content = eval("qq{$Lang->{BackupPC_Archive}}");
160         Header(eval("qq{$Lang->{BackupPC__Archive}}"), $content, 1);
161         Trailer();
162     } else {
163         my(@HostList, @BackupList, $HostListStr, $hiddenStr, $pathHdr,
164            $badFileCnt, $reply, $str);
165
166         #
167         # Pick up the archive host's config file
168         #
169         $bpc->ConfigRead($archHost);
170         %Conf = $bpc->Conf();
171
172         my $args = {
173             SplitPath    => $Conf{SplitPath},
174             ParPath      => $Conf{ParPath},
175             CatPath      => $Conf{CatPath},
176             GzipPath     => $Conf{GzipPath},
177             Bzip2Path    => $Conf{Bzip2Path},
178             ArchiveDest  => $Conf{ArchiveDest},
179             ArchiveComp  => $Conf{ArchiveComp},
180             ArchivePar   => $Conf{ArchivePar},
181             ArchiveSplit => $Conf{ArchiveSplit},
182             topDir       => $bpc->{TopDir},
183         };
184
185         ServerConnect();
186
187         for ( my $i = 0 ; $i < $In{fcbMax} ; $i++ ) {
188             next if ( !defined($In{"fcb$i"}) );
189             my $name = $In{"fcb$i"};
190             my $backupno = $In{"backup$i"};
191             next unless defined $backupno; # ASA - skip hosts without backups checked
192             push(@HostList, $name);
193             push(@BackupList, $backupno);
194             $hiddenStr .= <<EOF;
195 <input type="hidden" name="fcb$i" value="$In{'fcb' . $i}">
196 <input type="hidden" name="backup$i" value="$In{'backup' . $i}">
197 EOF
198             $HostListStr .= <<EOF;
199 <li> ${EscHTML($name)} $i
200 EOF
201         }
202         $hiddenStr .= <<EOF;
203 <input type="hidden" name="archivehost" value="$In{'archivehost'}">
204 EOF
205         $hiddenStr .= "<input type=\"hidden\" name=\"fcbMax\" value=\"$In{fcbMax}\">\n";
206         if ( @HostList == 0 ) {
207             ErrorExit($Lang->{You_haven_t_selected_any_hosts});
208         }
209         my ($ArchiveDest, $ArchiveCompNone, $ArchiveCompGzip,
210             $ArchiveCompBzip2, $ArchivePar, $ArchiveSplit);
211         $ArchiveDest = $Conf{ArchiveDest};
212         if ( $Conf{ArchiveComp} eq "none" ) {
213             $ArchiveCompNone   = "checked";
214         } else {
215             $ArchiveCompNone   = "";
216         }
217         if ( $Conf{ArchiveComp} eq "gzip" ) {
218             $ArchiveCompGzip   = "checked";
219         } else {
220             $ArchiveCompGzip   = "";
221         }
222         if ( $Conf{ArchiveComp} eq "bzip2" ) {
223             $ArchiveCompBzip2  = "checked";
224         } else {
225             $ArchiveCompBzip2  = "";
226         }
227         $ArchivePar   = $Conf{ArchivePar};
228         $ArchiveSplit = $Conf{ArchiveSplit};
229
230         if ( $In{type} == 1 ) {
231             #
232             # Tell the user what options they have
233             #
234             my $paramStr = "";
235             if ( $Conf{ArchiveClientCmd} =~ /\$archiveloc\b/ ) {
236                 $paramStr .= eval("qq{$Lang->{BackupPC_Archive2_location}}");
237             }
238             if ( $Conf{ArchiveClientCmd} =~ /\$compression\b/ ) {
239                 $paramStr .= eval("qq{$Lang->{BackupPC_Archive2_compression}}");
240             }
241             if ( $Conf{ArchiveClientCmd} =~ /\$parfile\b/
242                     && -x $Conf{ParPath} ) {
243                 $paramStr .= eval("qq{$Lang->{BackupPC_Archive2_parity}}");
244             }
245             if ( $Conf{ArchiveClientCmd} =~ /\$splitsize\b/
246                     && -x $Conf{SplitPath} ) {
247                 $paramStr .= eval("qq{$Lang->{BackupPC_Archive2_split}}");
248             }
249             my $content = eval("qq{$Lang->{BackupPC_Archive2}}");
250             Header(eval("qq{$Lang->{BackupPC__Archive}}"), $content, 1);
251             Trailer();
252         } elsif ( $In{type} == 2 ) {
253             my $reqFileName;
254             my $archivehost = $1 if ( $In{archivehost} =~ /(.+)/ );
255             for ( my $i = 0 ; ; $i++ ) {
256                 $reqFileName = "archiveReq.$$.$i";
257                 last if ( !-f "$TopDir/pc/$archivehost/$reqFileName" );
258             }
259             my($compname, $compext);
260             if ( $In{compression} == 2 ) {          # bzip2 compression
261                 $compname = $Conf{Bzip2Path};
262                 $compext = '.bz2';
263             } elsif ( $In{compression} == 1 ) {     # gzip compression
264                 $compname = $Conf{GzipPath};
265                 $compext = '.gz';
266             } else { # No Compression
267                 $compname = $Conf{CatPath};
268                 $compext = '.raw';
269             }
270             my $fullsplitsize = $In{splitsize} . '000000'; # mb -> bytes
271             my %ArchiveReq = (
272                 # parameters for the archive
273                 archiveloc  => $In{archive_device},
274                 archtype    => $In{archive_type},
275                 compression => $compname,
276                 compext     => $compext,
277                 parfile     => $In{par},
278                 splitsize   => $fullsplitsize,
279                 host        => $archivehost,
280
281                 # list of hosts to restore
282                 HostList    => \@HostList,
283                 BackupList  => \@BackupList,
284
285                 # other info
286                 user        => $User,
287                 reqTime     => time,
288             );
289             my($archive) = Data::Dumper->new(
290                             [  \%ArchiveReq],
291                             [qw(*ArchiveReq)]);
292             $archive->Indent(1);
293             my $openPath = "$TopDir/pc/$archivehost/$reqFileName";
294             if ( open(REQ, ">", $openPath) ) {
295                 binmode(REQ);
296                 print(REQ $archive->Dump);
297                 close(REQ);
298             } else {
299                 ErrorExit(eval("qq{$Lang->{Can_t_open_create__openPath}}"));
300             }
301             $reply = $bpc->ServerMesg("archive $User $archivehost $reqFileName");
302             $str = eval("qq{$Lang->{Archive_requested}}");
303
304             my $content = eval("qq{$Lang->{BackupPC_Archive_Reply_from_server}}");
305             Header(eval("qq{$Lang->{BackupPC__Archive}}"), $content, 1);
306             Trailer();
307         }
308     }
309 }
310
311 1;