#!/usr/local/bin/perl -w use strict; use lib "__INSTALLDIR__/lib"; use DBI; use BackupPC::Lib; use BackupPC::View; use Data::Dumper; use Getopt::Std; use Time::HiRes qw/time/; use File::Pid; use POSIX qw/strftime/; use constant BPC_FTYPE_DIR => 5; my $debug = 0; $|=1; my $start_t = time(); my $pidfile = new File::Pid; if (my $pid = $pidfile->running ) { die "$0 already running: $pid\n"; } elsif ($pidfile->pid ne $$) { $pidfile->remove; $pidfile = new File::Pid; } $pidfile->write; print STDERR "$0 using pid ",$pidfile->pid," file ",$pidfile->file,"\n"; my $t_fmt = '%Y-%m-%d %H:%M:%S'; my $hosts; my $bpc = BackupPC::Lib->new || die; my %Conf = $bpc->Conf(); my $TopDir = $bpc->TopDir(); my $beenThere = {}; my $dsn = $Conf{SearchDSN} || die "Need SearchDSN in config.pl\n"; my $user = $Conf{SearchUser} || ''; my $dbh = DBI->connect($dsn, $user, "", { RaiseError => 1, AutoCommit => 0 }); my %opt; if ( !getopts("cdm:v:", \%opt ) ) { print STDERR <do(qq{ create $unique index $index on $table($col) }); } print "creating tables...\n"; $dbh->do(qq{ create table hosts ( ID SERIAL PRIMARY KEY, name VARCHAR(30) NOT NULL, IP VARCHAR(15) ); }); $dbh->do(qq{ create table shares ( ID SERIAL PRIMARY KEY, hostID INTEGER NOT NULL references hosts(id), name VARCHAR(30) NOT NULL, share VARCHAR(200) NOT NULL, localpath VARCHAR(200) ); }); $dbh->do(qq{ create table backups ( hostID INTEGER NOT NULL references hosts(id), num INTEGER NOT NULL, date integer NOT NULL, type CHAR(4) not null, PRIMARY KEY(hostID, num) ); }); do_index('backups_hostid,num_unique'); $dbh->do(qq{ create table dvds ( ID SERIAL PRIMARY KEY, num INTEGER NOT NULL, name VARCHAR(255) NOT NULL, mjesto VARCHAR(255) ); }); $dbh->do(qq{ create table files ( ID SERIAL PRIMARY KEY, shareID INTEGER NOT NULL references shares(id), backupNum INTEGER NOT NULL, name VARCHAR(255) NOT NULL, path VARCHAR(255) NOT NULL, date integer NOT NULL, type INTEGER NOT NULL, size INTEGER NOT NULL, dvdid INTEGER references dvds(id) ); }); print "creating indexes:"; foreach my $index (qw( hosts_name backups_hostID backups_num shares_hostID shares_name files_shareID files_path files_name files_date files_size )) { print " $index"; do_index($index); } print "...\n"; $dbh->commit; } if ($opt{d}) { print "deleting "; foreach my $table (qw(files dvds backups shares hosts)) { print "$table "; $dbh->do(qq{ DELETE FROM $table }); } print " done...\n"; $dbh->commit; } if ($opt{v}) { print "Debug level at $opt{v}\n"; $debug = $opt{v}; } #################################INSERT VALUES############################# # get hosts $hosts = $bpc->HostInfoRead(); my $hostID; my $shareID; my $sth; $sth->{insert_hosts} = $dbh->prepare(qq{ INSERT INTO hosts (name, IP) VALUES (?,?) }); $sth->{hosts_by_name} = $dbh->prepare(qq{ SELECT ID FROM hosts WHERE name=? }); $sth->{backups_broj} = $dbh->prepare(qq{ SELECT COUNT(*) FROM backups WHERE hostID=? AND num=? }); $sth->{insert_backups} = $dbh->prepare(qq{ INSERT INTO backups (hostID, num, date, type) VALUES (?,?,?,?) }); $sth->{insert_files} = $dbh->prepare(qq{ INSERT INTO files (shareID, backupNum, name, path, date, type, size) VALUES (?,?,?,?,?,?,?) }); sub fmt_time { my $t = shift || return; my $out = ""; my ($ss,$mm,$hh) = gmtime($t); $out .= "${hh}h" if ($hh); $out .= sprintf("%02d:%02d", $mm,$ss); return $out; } foreach my $host_key (keys %{$hosts}) { my $hostname = $hosts->{$host_key}->{'host'} || die "can't find host for $host_key"; $sth->{hosts_by_name}->execute($hosts->{$host_key}->{'host'}); unless (($hostID) = $sth->{hosts_by_name}->fetchrow_array()) { $sth->{insert_hosts}->execute( $hosts->{$host_key}->{'host'}, $hosts->{$host_key}->{'ip'} ); $hostID = $dbh->last_insert_id(undef,undef,'hosts',undef); } print("host ".$hosts->{$host_key}->{'host'}.": "); # get backups for a host my @backups = $bpc->BackupInfoRead($hostname); print scalar @backups, " increments\n"; my $inc_nr = 0; foreach my $backup (@backups) { $inc_nr++; last if ($opt{m} && $inc_nr > $opt{m}); my $backupNum = $backup->{'num'}; my @backupShares = (); print $hosts->{$host_key}->{'host'}, "\t#$backupNum\t", $backup->{type} || '?', " ", $backup->{nFilesNew} || '?', "/", $backup->{nFiles} || '?', " files (date: ", strftime($t_fmt,localtime($backup->{startTime})), " dur: ", fmt_time($backup->{endTime} - $backup->{startTime}), ")\n"; $sth->{backups_broj}->execute($hostID, $backupNum); my ($broj) = $sth->{backups_broj}->fetchrow_array(); next if ($broj > 0); $sth->{insert_backups}->execute( $hostID, $backupNum, $backup->{'endTime'}, $backup->{'type'} ); $dbh->commit(); my $files = BackupPC::View->new($bpc, $hostname, \@backups, 1); foreach my $share ($files->shareList($backupNum)) { my $t = time(); print strftime($t_fmt,localtime())," ", $share; $shareID = getShareID($share, $hostID, $hostname); my ($f, $nf, $d, $nd) = recurseDir($bpc, $hostname, $files, $backupNum, $share, "", $shareID); my $dur = (time() - $t) || 1; printf(" %d/%d files %d/%d dirs [%.2f/s dur: %s]\n", $nf, $f, $nd, $d, ( ($f+$d) / $dur ), fmt_time($dur) ); $dbh->commit(); } } } undef $sth; $dbh->commit(); $dbh->disconnect(); print "total duration: ",fmt_time(time() - $start_t),"\n"; $pidfile->remove; sub getShareID() { my ($share, $hostID, $hostname) = @_; $sth->{share_id} ||= $dbh->prepare(qq{ SELECT ID FROM shares WHERE hostID=? AND name=? }); $sth->{share_id}->execute($hostID,$share); my ($id) = $sth->{share_id}->fetchrow_array(); return $id if (defined($id)); $sth->{insert_share} ||= $dbh->prepare(qq{ INSERT INTO shares (hostID,name,share,localpath) VALUES (?,?,?,?) }); my $drop_down = $hostname . '/' . $share; $drop_down =~ s#//+#/#g; $sth->{insert_share}->execute($hostID,$share, $drop_down ,undef); return $dbh->last_insert_id(undef,undef,'shares',undef); } sub found_in_db { my @data = @_; shift @data; my ($key, $shareID,undef,$name,$path,undef,$date,undef,$size) = @_; return $beenThere->{$key} if (defined($beenThere->{$key})); $sth->{file_in_db} ||= $dbh->prepare(qq{ SELECT 1 FROM files WHERE shareID = ? and path = ? and name = ? and date = ? and size = ? }); my @param = ($shareID,$path,$name,$date,$size); $sth->{file_in_db}->execute(@param); my $rows = $sth->{file_in_db}->rows; print STDERR "## found_in_db ",( $rows ? '+' : '-' ), join(" ",@param), "\n" if ($debug >= 3); $beenThere->{$key}++; $sth->{'insert_files'}->execute(@data) unless ($rows); return $rows; } #################################################### # recursing through filesystem structure and # # and returning flattened files list # #################################################### sub recurseDir($$$$$$$$) { my ($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID) = @_; print STDERR "\nrecurse($hostname,$backupNum,$share,$dir,$shareID)\n" if ($debug >= 1); my ($nr_files, $new_files, $nr_dirs, $new_dirs) = (0,0,0,0); { # scope my @stack; print STDERR "# dirAttrib($backupNum, $share, $dir)\n" if ($debug >= 2); my $filesInBackup = $files->dirAttrib($backupNum, $share, $dir); # first, add all the entries in current directory foreach my $path_key (keys %{$filesInBackup}) { my @data = ( $shareID, $backupNum, $path_key, $filesInBackup->{$path_key}->{'relPath'}, $filesInBackup->{$path_key}->{'mtime'}, $filesInBackup->{$path_key}->{'type'}, $filesInBackup->{$path_key}->{'size'} ); my $key = join(" ", ( $shareID, $dir, $path_key, $filesInBackup->{$path_key}->{'mtime'}, $filesInBackup->{$path_key}->{'size'} )); if (! defined($beenThere->{$key}) && ! found_in_db($key, @data)) { print STDERR "# key: $key [", $beenThere->{$key},"]" if ($debug >= 2); if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) { $new_dirs++; print STDERR " dir\n" if ($debug >= 2); } else { $new_files++; print STDERR " file\n" if ($debug >= 2); } } if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) { $nr_dirs++; my $full_path = $dir . '/' . $path_key; push @stack, $full_path; print STDERR "### store to stack: $full_path\n" if ($debug >= 3); # my ($f,$nf,$d,$nd) = recurseDir($bpc, $hostname, $backups, $backupNum, $share, $path_key, $shareID) unless ($beenThere->{$key}); # # $nr_files += $f; # $new_files += $nf; # $nr_dirs += $d; # $new_dirs += $nd; } else { $nr_files++; } } print STDERR "## STACK ",join(", ", @stack),"\n" if ($debug >= 2); while ( my $dir = shift @stack ) { my ($f,$nf,$d,$nd) = recurseDir($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID); print STDERR "# $dir f: $f nf: $nf d: $d nd: $nd\n" if ($debug >= 1); $nr_files += $f; $new_files += $nf; $nr_dirs += $d; $new_dirs += $nd; } } return ($nr_files, $new_files, $nr_dirs, $new_dirs); }