1 #============================================================= -*-perl-*-
3 # BackupPC::Storage::Text package
7 # This library defines a BackupPC::Storage::Text class that implements
8 # BackupPC's persistent state storage (config, host info, backup
9 # and restore info) using text files.
12 # Craig Barratt <cbarratt@users.sourceforge.net>
15 # Copyright (C) 2004 Craig Barratt
17 # This program is free software; you can redistribute it and/or modify
18 # it under the terms of the GNU General Public License as published by
19 # the Free Software Foundation; either version 2 of the License, or
20 # (at your option) any later version.
22 # This program is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 # GNU General Public License for more details.
27 # You should have received a copy of the GNU General Public License
28 # along with this program; if not, write to the Free Software
29 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #========================================================================
33 # Version 2.1.0, released 20 Jun 2004.
35 # See http://backuppc.sourceforge.net.
37 #========================================================================
39 package BackupPC::Storage::Text;
49 my($flds, $paths) = @_;
61 local(*BK_INFO, *LOCK);
64 flock(LOCK, LOCK_EX) if open(LOCK, "$s->{TopDir}/pc/$host/LOCK");
65 if ( open(BK_INFO, "$s->{TopDir}/pc/$host/backups") ) {
69 next if ( !/^(\d+\t(incr|full|partial)[\d\t]*$)/ );
71 @{$Backups[@Backups]}{@{$s->{BackupFields}}} = split(/\t/);
81 my($s, $host, @Backups) = @_;
82 local(*BK_INFO, *LOCK);
85 flock(LOCK, LOCK_EX) if open(LOCK, "$s->{TopDir}/pc/$host/LOCK");
86 if ( -s "$s->{TopDir}/pc/$host/backups" ) {
87 unlink("$s->{TopDir}/pc/$host/backups.old")
88 if ( -f "$s->{TopDir}/pc/$host/backups.old" );
89 rename("$s->{TopDir}/pc/$host/backups",
90 "$s->{TopDir}/pc/$host/backups.old")
91 if ( -f "$s->{TopDir}/pc/$host/backups" );
93 if ( open(BK_INFO, ">$s->{TopDir}/pc/$host/backups") ) {
95 for ( $i = 0 ; $i < @Backups ; $i++ ) {
96 my %b = %{$Backups[$i]};
97 printf(BK_INFO "%s\n", join("\t", @b{@{$s->{BackupFields}}}));
107 local(*RESTORE_INFO, *LOCK);
110 flock(LOCK, LOCK_EX) if open(LOCK, "$s->{TopDir}/pc/$host/LOCK");
111 if ( open(RESTORE_INFO, "$s->{TopDir}/pc/$host/restores") ) {
112 binmode(RESTORE_INFO);
113 while ( <RESTORE_INFO> ) {
115 next if ( !/^(\d+.*)/ );
117 @{$Restores[@Restores]}{@{$s->{RestoreFields}}} = split(/\t/);
127 my($s, $host, @Restores) = @_;
128 local(*RESTORE_INFO, *LOCK);
131 flock(LOCK, LOCK_EX) if open(LOCK, "$s->{TopDir}/pc/$host/LOCK");
132 if ( -s "$s->{TopDir}/pc/$host/restores" ) {
133 unlink("$s->{TopDir}/pc/$host/restores.old")
134 if ( -f "$s->{TopDir}/pc/$host/restores.old" );
135 rename("$s->{TopDir}/pc/$host/restores",
136 "$s->{TopDir}/pc/$host/restores.old")
137 if ( -f "$s->{TopDir}/pc/$host/restores" );
139 if ( open(RESTORE_INFO, ">$s->{TopDir}/pc/$host/restores") ) {
140 binmode(RESTORE_INFO);
141 for ( $i = 0 ; $i < @Restores ; $i++ ) {
142 my %b = %{$Restores[$i]};
143 printf(RESTORE_INFO "%s\n",
144 join("\t", @b{@{$s->{RestoreFields}}}));
154 local(*ARCHIVE_INFO, *LOCK);
157 flock(LOCK, LOCK_EX) if open(LOCK, "$s->{TopDir}/pc/$host/LOCK");
158 if ( open(ARCHIVE_INFO, "$s->{TopDir}/pc/$host/archives") ) {
159 binmode(ARCHIVE_INFO);
160 while ( <ARCHIVE_INFO> ) {
162 next if ( !/^(\d+.*)/ );
164 @{$Archives[@Archives]}{@{$s->{ArchiveFields}}} = split(/\t/);
174 my($s, $host, @Archives) = @_;
175 local(*ARCHIVE_INFO, *LOCK);
178 flock(LOCK, LOCK_EX) if open(LOCK, "$s->{TopDir}/pc/$host/LOCK");
179 if ( -s "$s->{TopDir}/pc/$host/archives" ) {
180 unlink("$s->{TopDir}/pc/$host/archives.old")
181 if ( -f "$s->{TopDir}/pc/$host/archives.old" );
182 rename("$s->{TopDir}/pc/$host/archives",
183 "$s->{TopDir}/pc/$host/archives.old")
184 if ( -f "$s->{TopDir}/pc/$host/archives" );
186 if ( open(ARCHIVE_INFO, ">$s->{TopDir}/pc/$host/archives") ) {
187 binmode(ARCHIVE_INFO);
188 for ( $i = 0 ; $i < @Archives ; $i++ ) {
189 my %b = %{$Archives[$i]};
190 printf(ARCHIVE_INFO "%s\n",
191 join("\t", @b{@{$s->{ArchiveFields}}}));
201 my($ret, $mesg, $config, @configs);
208 if ( defined($host) ) {
209 push(@configs, "$s->{TopDir}/conf/$host.pl")
210 if ( $host ne "config" && -f "$s->{TopDir}/conf/$host.pl" );
211 push(@configs, "$s->{TopDir}/pc/$host/config.pl")
212 if ( -f "$s->{TopDir}/pc/$host/config.pl" );
214 push(@configs, "$s->{TopDir}/conf/config.pl");
216 foreach $config ( @configs ) {
218 if ( !defined($ret = do $config) && ($! || $@) ) {
219 $mesg = "Couldn't open $config: $!" if ( $! );
220 $mesg = "Couldn't execute $config: $@" if ( $@ );
221 $mesg =~ s/[\n\r]+//;
222 return ($mesg, $conf);
224 %$conf = ( %$conf, %Conf );
226 return (undef, $conf);
231 my($s, $host, $newConf) = @_;
233 my($confPath) = $host eq "" ? "$s->{TopDir}/conf/config.pl"
234 : "$s->{TopDir}/pc/$host/config.pl";
236 my $err = $s->ConfigFileMerge($confPath, "$confPath.new", $newConf);
238 # TODO: add lock and rename
244 my($s, $inFile, $outFile, $newConf) = @_;
246 open(C, $inFile) || return "ConfigFileMerge: can't open/read $inFile";
249 open(OUT, ">", $outFile)
250 || return "ConfigFileMerge: can't open/write $outFile";
260 if ( $comment && /^\s*#/ ) {
262 } elsif ( /^\s*\$Conf\{([^}]*)\}\s*=/ ) {
264 if ( exists($newConf->{$var}) ) {
266 my $d = Data::Dumper->new([$newConf->{$var}], [*value]);
269 my $value = $d->Dump;
270 $value =~ s/(.*)\n/$1;\n/s;
271 print OUT "\$Conf{$var} = ", $value;
274 $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<(.*);/ );
275 $endLine = $1 if ( /^\s*\$Conf\{[^}]*} *= *<<'(.*)';/ );
278 } elsif ( $skipVar ) {
279 if ( !defined($endLine) && (/^\s*[\r\n]*$/ || /^\s*#/) ) {
284 if ( defined($endLine) && /^\Q$endLine\E[\n\r]*$/ ) {
296 foreach my $var ( sort(keys(%$newConf)) ) {
297 next if ( $done->{$var} );
298 my $d = Data::Dumper->new([$newConf->{$var}], [*value]);
301 my $value = $d->Dump;
302 $value =~ s/(.*)\n/$1;\n/s;
303 print OUT "\$Conf{$var} = ", $value;
311 # Return the mtime of the config file
316 return (stat("$s->{TopDir}/conf/config.pl"))[9];
320 # Returns information from the host file in $s->{TopDir}/conf/hosts.
321 # With no argument a ref to a hash of hosts is returned. Each
322 # hash contains fields as specified in the hosts file. With an
323 # argument a ref to a single hash is returned with information
324 # for just that host.
329 my(%hosts, @hdr, @fld);
332 if ( !open(HOST_INFO, "$s->{TopDir}/conf/hosts") ) {
333 print(STDERR $s->timeStamp,
334 "Can't open $s->{TopDir}/conf/hosts\n");
338 while ( <HOST_INFO> ) {
342 next if ( /^\s*$/ || !/^([\w\.\\-]+\s+.*)/ );
344 # Split on white space, except if preceded by \
345 # using zero-width negative look-behind assertion
346 # (always wanted to use one of those).
348 @fld = split(/(?<!\\)\s+/, $1);
356 if ( defined($host) ) {
357 next if ( lc($fld[0]) ne $host );
358 @{$hosts{lc($fld[0])}}{@hdr} = @fld;
362 @{$hosts{lc($fld[0])}}{@hdr} = @fld;
373 # Return the mtime of the hosts file
378 return (stat("$s->{TopDir}/conf/hosts"))[9];