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:i", \%opt ) ) {
50 usage: $0 [-c|-d] [-m num] [-v|-v level] [-i]
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)
57 -i update HyperEstraier full text index
63 print "Debug level at $opt{v}\n";
70 my $index_dir = '/var/tmp/casket';
72 print "updating HyperEstraier index $index_dir...";
75 my $db = HyperEstraier::Database->new();
76 $db->open($index_dir, $HyperEstraier::Database::DBWRITER | $HyperEstraier::Database::DBCREAT);
78 my $sth = $dbh->prepare(qq{
83 shares.share AS sharename,
84 files.backupNum AS backupNum,
85 files.name AS filename,
86 files.path AS filepath,
88 files.type AS filetype,
90 files.shareid AS shareid
92 INNER JOIN shares ON files.shareID=shares.ID
93 INNER JOIN hosts ON hosts.ID = shares.hostID
94 INNER JOIN backups ON backups.num = files.backupNum and backups.hostID = hosts.ID AND backups.shareID = shares.ID
99 my $dot = int($sth->rows / 15);
101 print $sth->rows, " files ($dot/#) ";
104 my $t = shift || return;
105 my $iso = BackupPC::Lib::timeStamp($t);
111 my $max = int($sth->rows / $dot);
113 while (my $row = $sth->fetchrow_hashref()) {
115 # create a document object
116 my $doc = HyperEstraier::Document->new;
118 # add attributes to the document object
119 $doc->add_attr('@uri', 'file:///' . $row->{'fid'});
121 foreach my $c (qw/fid hname sname sharename backupNum filename filepath shareid/) {
122 $doc->add_attr($c, $row->{$c}) if ($row->{$c});
125 $doc->add_attr('date', fmt_date($row->{'date'}));
127 # add the body text to the document object
128 my $path = $row->{'filepath'};
129 $doc->add_text($path);
130 $path =~ s/(.)/$1 /g;
131 $doc->add_hidden_text($path);
133 print STDERR $doc->dump_draft,"\n" if ($debug > 1);
135 # register the document object to the database
136 $db->put_doc($doc, $HyperEstraier::Database::PDCLEAN);
139 if ($i % $dot == 0) {
154 ###################################create tables############################3
158 my $index = shift || return;
159 my ($table,$col,$unique) = split(/_/, $index);
162 $dbh->do(qq{ create $unique index $index on $table($col) });
165 print "creating tables...\n";
169 ID SERIAL PRIMARY KEY,
170 name VARCHAR(30) NOT NULL,
176 create table shares (
177 ID SERIAL PRIMARY KEY,
178 hostID INTEGER NOT NULL references hosts(id),
179 name VARCHAR(30) NOT NULL,
180 share VARCHAR(200) NOT NULL,
181 localpath VARCHAR(200)
186 create table backups (
187 hostID INTEGER NOT NULL references hosts(id),
188 num INTEGER NOT NULL,
189 date integer NOT NULL,
190 type CHAR(4) not null,
191 shareID integer not null references shares(id),
192 size integer not null,
193 PRIMARY KEY(hostID, num, shareID)
197 #do_index('backups_hostid,num_unique');
201 ID SERIAL PRIMARY KEY,
202 num INTEGER NOT NULL,
203 name VARCHAR(255) NOT NULL,
210 ID SERIAL PRIMARY KEY,
211 shareID INTEGER NOT NULL references shares(id),
212 backupNum INTEGER NOT NULL,
213 name VARCHAR(255) NOT NULL,
214 path VARCHAR(255) NOT NULL,
215 date integer NOT NULL,
216 type INTEGER NOT NULL,
217 size INTEGER NOT NULL,
218 dvdid INTEGER references dvds(id)
222 print "creating indexes:";
224 foreach my $index (qw(
247 foreach my $table (qw(files dvds backups shares hosts)) {
249 $dbh->do(qq{ DELETE FROM $table });
256 #################################INSERT VALUES#############################
259 $hosts = $bpc->HostInfoRead();
265 $sth->{insert_hosts} = $dbh->prepare(qq{
266 INSERT INTO hosts (name, IP) VALUES (?,?)
269 $sth->{hosts_by_name} = $dbh->prepare(qq{
270 SELECT ID FROM hosts WHERE name=?
273 $sth->{backups_count} = $dbh->prepare(qq{
276 WHERE hostID=? AND num=? AND shareid=?
279 $sth->{insert_backups} = $dbh->prepare(qq{
280 INSERT INTO backups (hostID, num, date, type, shareid, size)
284 $sth->{insert_files} = $dbh->prepare(qq{
286 (shareID, backupNum, name, path, date, type, size)
287 VALUES (?,?,?,?,?,?,?)
291 my $t = shift || return;
293 my ($ss,$mm,$hh) = gmtime($t);
294 $out .= "${hh}h" if ($hh);
295 $out .= sprintf("%02d:%02d", $mm,$ss);
299 foreach my $host_key (keys %{$hosts}) {
301 my $hostname = $hosts->{$host_key}->{'host'} || die "can't find host for $host_key";
303 $sth->{hosts_by_name}->execute($hosts->{$host_key}->{'host'});
305 unless (($hostID) = $sth->{hosts_by_name}->fetchrow_array()) {
306 $sth->{insert_hosts}->execute(
307 $hosts->{$host_key}->{'host'},
308 $hosts->{$host_key}->{'ip'}
311 $hostID = $dbh->last_insert_id(undef,undef,'hosts',undef);
314 print "host ".$hosts->{$host_key}->{'host'}.": ";
316 # get backups for a host
317 my @backups = $bpc->BackupInfoRead($hostname);
318 my $incs = scalar @backups;
319 print "$incs increments\n";
324 foreach my $backup (@backups) {
327 last if ($opt{m} && $inc_nr > $opt{m});
329 my $backupNum = $backup->{'num'};
330 my @backupShares = ();
332 printf("%-10s %2d/%-2d #%-2d %s %5s/%5s files (date: %s dur: %s)\n",
333 $hosts->{$host_key}->{'host'},
334 $inc_nr, $incs, $backupNum,
335 $backup->{type} || '?',
336 $backup->{nFilesNew} || '?', $backup->{nFiles} || '?',
337 strftime($t_fmt,localtime($backup->{startTime})),
338 fmt_time($backup->{endTime} - $backup->{startTime})
341 my $files = BackupPC::View->new($bpc, $hostname, \@backups, 1);
342 foreach my $share ($files->shareList($backupNum)) {
346 $shareID = getShareID($share, $hostID, $hostname);
348 $sth->{backups_count}->execute($hostID, $backupNum, $shareID);
349 my ($count) = $sth->{backups_count}->fetchrow_array();
350 # skip if allready in database!
351 next if ($count > 0);
354 print strftime($t_fmt,localtime())," ", $share;
356 my ($f, $nf, $d, $nd, $size) = recurseDir($bpc, $hostname, $files, $backupNum, $share, "", $shareID);
358 $sth->{insert_backups}->execute(
361 $backup->{'endTime'},
370 my $dur = (time() - $t) || 1;
371 printf(" %d/%d files %d/%d dirs %0.2f MB [%.2f/s dur: %s]\n",
373 ($size / 1024 / 1024),
385 print "total duration: ",fmt_time(time() - $start_t),"\n";
391 my ($share, $hostID, $hostname) = @_;
393 $sth->{share_id} ||= $dbh->prepare(qq{
394 SELECT ID FROM shares WHERE hostID=? AND name=?
397 $sth->{share_id}->execute($hostID,$share);
399 my ($id) = $sth->{share_id}->fetchrow_array();
401 return $id if (defined($id));
403 $sth->{insert_share} ||= $dbh->prepare(qq{
405 (hostID,name,share,localpath)
409 my $drop_down = $hostname . '/' . $share;
410 $drop_down =~ s#//+#/#g;
412 $sth->{insert_share}->execute($hostID,$share, $drop_down ,undef);
413 return $dbh->last_insert_id(undef,undef,'shares',undef);
421 my ($key, $shareID,undef,$name,$path,$date,undef,$size) = @_;
423 return $beenThere->{$key} if (defined($beenThere->{$key}));
425 $sth->{file_in_db} ||= $dbh->prepare(qq{
427 WHERE shareID = ? and
434 my @param = ($shareID,$path,$date,$size);
435 $sth->{file_in_db}->execute(@param);
436 my $rows = $sth->{file_in_db}->rows;
437 print STDERR "## found_in_db($shareID,$path,$date,$size) ",( $rows ? '+' : '-' ), join(" ",@param), "\n" if ($debug >= 3);
439 $beenThere->{$key}++;
441 $sth->{'insert_files'}->execute(@data) unless ($rows);
445 ####################################################
446 # recursing through filesystem structure and #
447 # and returning flattened files list #
448 ####################################################
449 sub recurseDir($$$$$$$$) {
451 my ($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID) = @_;
453 print STDERR "\nrecurse($hostname,$backupNum,$share,$dir,$shareID)\n" if ($debug >= 1);
455 my ($nr_files, $new_files, $nr_dirs, $new_dirs, $size) = (0,0,0,0,0);
460 print STDERR "# dirAttrib($backupNum, $share, $dir)\n" if ($debug >= 2);
461 my $filesInBackup = $files->dirAttrib($backupNum, $share, $dir);
463 # first, add all the entries in current directory
464 foreach my $path_key (keys %{$filesInBackup}) {
465 print STDERR "# file ",Dumper($filesInBackup->{$path_key}),"\n" if ($debug >= 3);
470 $filesInBackup->{$path_key}->{'relPath'},
471 $filesInBackup->{$path_key}->{'mtime'},
472 $filesInBackup->{$path_key}->{'type'},
473 $filesInBackup->{$path_key}->{'size'}
476 my $key = join(" ", (
480 $filesInBackup->{$path_key}->{'mtime'},
481 $filesInBackup->{$path_key}->{'size'}
485 if (! defined($beenThere->{$key}) && ! ($found = found_in_db($key, @data)) ) {
486 print STDERR "# key: $key [", $beenThere->{$key},"]" if ($debug >= 2);
488 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
489 $new_dirs++ unless ($found);
490 print STDERR " dir\n" if ($debug >= 2);
492 $new_files++ unless ($found);
493 print STDERR " file\n" if ($debug >= 2);
495 $size += $filesInBackup->{$path_key}->{'size'} || 0;
498 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
501 my $full_path = $dir . '/' . $path_key;
502 push @stack, $full_path;
503 print STDERR "### store to stack: $full_path\n" if ($debug >= 3);
505 # my ($f,$nf,$d,$nd) = recurseDir($bpc, $hostname, $backups, $backupNum, $share, $path_key, $shareID) unless ($beenThere->{$key});
517 print STDERR "## STACK ",join(", ", @stack),"\n" if ($debug >= 2);
519 while ( my $dir = shift @stack ) {
520 my ($f,$nf,$d,$nd, $s) = recurseDir($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID);
521 print STDERR "# $dir f: $f nf: $nf d: $d nd: $nd\n" if ($debug >= 1);
530 return ($nr_files, $new_files, $nr_dirs, $new_dirs, $size);