1 #!/usr/local/bin/perl -w
4 use lib "__INSTALLDIR__/lib";
11 use Time::HiRes qw/time/;
13 use POSIX qw/strftime/;
15 use constant BPC_FTYPE_DIR => 5;
22 my $pidfile = new File::Pid;
24 if (my $pid = $pidfile->running ) {
25 die "$0 already running: $pid\n";
26 } elsif ($pidfile->pid ne $$) {
28 $pidfile = new File::Pid;
31 print STDERR "$0 using pid ",$pidfile->pid," file ",$pidfile->file,"\n";
33 my $t_fmt = '%Y-%m-%d %H:%M:%S';
36 my $bpc = BackupPC::Lib->new || die;
37 my %Conf = $bpc->Conf();
38 my $TopDir = $bpc->TopDir();
41 my $dsn = $Conf{SearchDSN} || die "Need SearchDSN in config.pl\n";
42 my $user = $Conf{SearchUser} || '';
44 my $dbh = DBI->connect($dsn, $user, "", { RaiseError => 1, AutoCommit => 0 });
48 if ( !getopts("cdm:v:", \%opt ) ) {
50 usage: $0 [-c|-d] [-m num] [-v|-v level]
53 -c create database on first use
54 -d delete database before import
55 -m num import just num increments for one host
56 -v num set verbosity (debug) level (default $debug)
61 ###################################create tables############################3
65 my $index = shift || return;
66 my ($table,$col,$unique) = split(/_/, $index);
69 $dbh->do(qq{ create $unique index $index on $table($col) });
72 print "creating tables...\n";
76 ID SERIAL PRIMARY KEY,
77 name VARCHAR(30) NOT NULL,
84 ID SERIAL PRIMARY KEY,
85 hostID INTEGER NOT NULL references hosts(id),
86 name VARCHAR(30) NOT NULL,
87 share VARCHAR(200) NOT NULL,
88 localpath VARCHAR(200)
93 create table backups (
94 hostID INTEGER NOT NULL references hosts(id),
96 date integer NOT NULL,
97 type CHAR(4) not null,
98 shareID integer not null references shares(id),
99 PRIMARY KEY(hostID, num, shareID)
103 #do_index('backups_hostid,num_unique');
107 ID SERIAL PRIMARY KEY,
108 num INTEGER NOT NULL,
109 name VARCHAR(255) NOT NULL,
116 ID SERIAL PRIMARY KEY,
117 shareID INTEGER NOT NULL references shares(id),
118 backupNum INTEGER NOT NULL,
119 name VARCHAR(255) NOT NULL,
120 path VARCHAR(255) NOT NULL,
121 date integer NOT NULL,
122 type INTEGER NOT NULL,
123 size INTEGER NOT NULL,
124 dvdid INTEGER references dvds(id)
128 print "creating indexes:";
130 foreach my $index (qw(
153 foreach my $table (qw(files dvds backups shares hosts)) {
155 $dbh->do(qq{ DELETE FROM $table });
163 print "Debug level at $opt{v}\n";
167 #################################INSERT VALUES#############################
170 $hosts = $bpc->HostInfoRead();
176 $sth->{insert_hosts} = $dbh->prepare(qq{
177 INSERT INTO hosts (name, IP) VALUES (?,?)
180 $sth->{hosts_by_name} = $dbh->prepare(qq{
181 SELECT ID FROM hosts WHERE name=?
184 $sth->{backups_count} = $dbh->prepare(qq{
187 WHERE hostID=? AND num=? AND shareid=?
190 $sth->{insert_backups} = $dbh->prepare(qq{
191 INSERT INTO backups (hostID, num, date, type, shareid)
195 $sth->{insert_files} = $dbh->prepare(qq{
197 (shareID, backupNum, name, path, date, type, size)
198 VALUES (?,?,?,?,?,?,?)
202 my $t = shift || return;
204 my ($ss,$mm,$hh) = gmtime($t);
205 $out .= "${hh}h" if ($hh);
206 $out .= sprintf("%02d:%02d", $mm,$ss);
210 foreach my $host_key (keys %{$hosts}) {
212 my $hostname = $hosts->{$host_key}->{'host'} || die "can't find host for $host_key";
214 $sth->{hosts_by_name}->execute($hosts->{$host_key}->{'host'});
216 unless (($hostID) = $sth->{hosts_by_name}->fetchrow_array()) {
217 $sth->{insert_hosts}->execute(
218 $hosts->{$host_key}->{'host'},
219 $hosts->{$host_key}->{'ip'}
222 $hostID = $dbh->last_insert_id(undef,undef,'hosts',undef);
225 print("host ".$hosts->{$host_key}->{'host'}.": ");
227 # get backups for a host
228 my @backups = $bpc->BackupInfoRead($hostname);
229 print scalar @backups, " increments\n";
233 foreach my $backup (@backups) {
236 last if ($opt{m} && $inc_nr > $opt{m});
238 my $backupNum = $backup->{'num'};
239 my @backupShares = ();
241 print $hosts->{$host_key}->{'host'},
242 "\t#$backupNum\t", $backup->{type} || '?', " ",
243 $backup->{nFilesNew} || '?', "/", $backup->{nFiles} || '?',
245 strftime($t_fmt,localtime($backup->{startTime})),
247 fmt_time($backup->{endTime} - $backup->{startTime}),
250 my $files = BackupPC::View->new($bpc, $hostname, \@backups, 1);
251 foreach my $share ($files->shareList($backupNum)) {
255 $shareID = getShareID($share, $hostID, $hostname);
257 $sth->{backups_count}->execute($hostID, $backupNum, $shareID);
258 my ($count) = $sth->{backups_count}->fetchrow_array();
259 # skip if allready in database!
260 next if ($count > 0);
263 print strftime($t_fmt,localtime())," ", $share;
265 my ($f, $nf, $d, $nd) = recurseDir($bpc, $hostname, $files, $backupNum, $share, "", $shareID);
267 $sth->{insert_backups}->execute(
270 $backup->{'endTime'},
278 my $dur = (time() - $t) || 1;
279 printf(" %d/%d files %d/%d dirs [%.2f/s dur: %s]\n",
292 print "total duration: ",fmt_time(time() - $start_t),"\n";
298 my ($share, $hostID, $hostname) = @_;
300 $sth->{share_id} ||= $dbh->prepare(qq{
301 SELECT ID FROM shares WHERE hostID=? AND name=?
304 $sth->{share_id}->execute($hostID,$share);
306 my ($id) = $sth->{share_id}->fetchrow_array();
308 return $id if (defined($id));
310 $sth->{insert_share} ||= $dbh->prepare(qq{
312 (hostID,name,share,localpath)
316 my $drop_down = $hostname . '/' . $share;
317 $drop_down =~ s#//+#/#g;
319 $sth->{insert_share}->execute($hostID,$share, $drop_down ,undef);
320 return $dbh->last_insert_id(undef,undef,'shares',undef);
328 my ($key, $shareID,undef,$name,$path,undef,$date,undef,$size) = @_;
330 return $beenThere->{$key} if (defined($beenThere->{$key}));
332 $sth->{file_in_db} ||= $dbh->prepare(qq{
334 WHERE shareID = ? and
341 my @param = ($shareID,$path,$name,$date,$size);
342 $sth->{file_in_db}->execute(@param);
343 my $rows = $sth->{file_in_db}->rows;
344 print STDERR "## found_in_db ",( $rows ? '+' : '-' ), join(" ",@param), "\n" if ($debug >= 3);
346 $beenThere->{$key}++;
348 $sth->{'insert_files'}->execute(@data) unless ($rows);
352 ####################################################
353 # recursing through filesystem structure and #
354 # and returning flattened files list #
355 ####################################################
356 sub recurseDir($$$$$$$$) {
358 my ($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID) = @_;
360 print STDERR "\nrecurse($hostname,$backupNum,$share,$dir,$shareID)\n" if ($debug >= 1);
362 my ($nr_files, $new_files, $nr_dirs, $new_dirs) = (0,0,0,0);
367 print STDERR "# dirAttrib($backupNum, $share, $dir)\n" if ($debug >= 2);
368 my $filesInBackup = $files->dirAttrib($backupNum, $share, $dir);
370 # first, add all the entries in current directory
371 foreach my $path_key (keys %{$filesInBackup}) {
376 $filesInBackup->{$path_key}->{'relPath'},
377 $filesInBackup->{$path_key}->{'mtime'},
378 $filesInBackup->{$path_key}->{'type'},
379 $filesInBackup->{$path_key}->{'size'}
382 my $key = join(" ", (
386 $filesInBackup->{$path_key}->{'mtime'},
387 $filesInBackup->{$path_key}->{'size'}
391 if (! defined($beenThere->{$key}) && ! found_in_db($key, @data)) {
392 print STDERR "# key: $key [", $beenThere->{$key},"]" if ($debug >= 2);
394 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
396 print STDERR " dir\n" if ($debug >= 2);
399 print STDERR " file\n" if ($debug >= 2);
403 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
406 my $full_path = $dir . '/' . $path_key;
407 push @stack, $full_path;
408 print STDERR "### store to stack: $full_path\n" if ($debug >= 3);
410 # my ($f,$nf,$d,$nd) = recurseDir($bpc, $hostname, $backups, $backupNum, $share, $path_key, $shareID) unless ($beenThere->{$key});
422 print STDERR "## STACK ",join(", ", @stack),"\n" if ($debug >= 2);
424 while ( my $dir = shift @stack ) {
425 my ($f,$nf,$d,$nd) = recurseDir($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID);
426 print STDERR "# $dir f: $f nf: $nf d: $d nd: $nd\n" if ($debug >= 1);
434 return ($nr_files, $new_files, $nr_dirs, $new_dirs);