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;
16 use constant EST_SYNC_EVERY => 10000;
23 my $pidfile = new File::Pid;
25 if (my $pid = $pidfile->running ) {
26 die "$0 already running: $pid\n";
27 } elsif ($pidfile->pid ne $$) {
29 $pidfile = new File::Pid;
32 print STDERR "$0 using pid ",$pidfile->pid," file ",$pidfile->file,"\n";
34 my $t_fmt = '%Y-%m-%d %H:%M:%S';
37 my $bpc = BackupPC::Lib->new || die;
38 my %Conf = $bpc->Conf();
39 my $TopDir = $bpc->TopDir();
42 my $dsn = $Conf{SearchDSN} || die "Need SearchDSN in config.pl\n";
43 my $user = $Conf{SearchUser} || '';
44 my $index_path = $Conf{HyperEstraierIndex};
45 $index_path = $TopDir . '/' . $index_path;
46 $index_path =~ s#//#/#g;
49 my $dbh = DBI->connect($dsn, $user, "", { RaiseError => 1, AutoCommit => 0 });
53 if ( !getopts("cdm:v:i", \%opt ) ) {
55 usage: $0 [-c|-d] [-m num] [-v|-v level] [-i]
58 -c create database on first use
59 -d delete database before import
60 -m num import just num increments for one host
61 -v num set verbosity (debug) level (default $debug)
62 -i update HyperEstraier full text index
68 print "Debug level at $opt{v}\n";
75 my $t = shift || return;
77 my ($ss,$mm,$hh) = gmtime($t);
78 $out .= "${hh}h" if ($hh);
79 $out .= sprintf("%02d:%02d", $mm,$ss);
84 return strftime($t_fmt,localtime());
92 print "\nCaught a SIG$sig--syncing database and shutting down\n";
99 $SIG{'INT'} = \&signal;
100 $SIG{'QUIT'} = \&signal;
104 my ($host_id, $share_id, $num) = @_;
106 print curr_time," updating HyperEstraier: select files";
111 if ($host_id && $share_id && $num) {
120 my $sth = $dbh->prepare(qq{
124 shares.name AS sname,
125 -- shares.share AS sharename,
126 files.backupnum AS backupnum,
127 -- files.name AS filename,
128 files.path AS filepath,
132 files.shareid AS shareid,
133 backups.date AS backup_date
135 INNER JOIN shares ON files.shareID=shares.ID
136 INNER JOIN hosts ON hosts.ID = shares.hostID
137 INNER JOIN backups ON backups.num = files.backupNum and backups.hostID = hosts.ID AND backups.shareID = shares.ID
142 my $results = $sth->rows;
145 print " - no files, skipping\n";
149 my $dot = int($results / 15) || 1;
151 print " $results ($dot/#)";
154 my $t = shift || return;
155 my $iso = BackupPC::Lib::timeStamp($t);
160 my $max = int($results / $dot);
162 print ", opening index $index_path...";
164 my $db = HyperEstraier::Database->new();
166 # unless ($hest_db) {
167 # print " open reader";
168 # $hest_db = HyperEstraier::Database->new();
173 $db->open($index_path, $HyperEstraier::Database::DBWRITER | $HyperEstraier::Database::DBCREAT);
177 while (my $row = $sth->fetchrow_hashref()) {
179 my $fid = $row->{'fid'} || die "no fid?";
180 my $uri = 'file:///' . $fid;
182 my $id = $db->uri_to_id($uri);
183 next unless ($id == -1);
185 # create a document object
186 my $doc = HyperEstraier::Document->new;
188 # add attributes to the document object
189 $doc->add_attr('@uri', $uri);
191 foreach my $c (@{ $sth->{NAME} }) {
192 $doc->add_attr($c, $row->{$c}) if ($row->{$c});
195 #$doc->add_attr('@cdate', fmt_date($row->{'date'}));
197 # add the body text to the document object
198 my $path = $row->{'filepath'};
199 $doc->add_text($path);
200 $path =~ s/(.)/$1 /g;
201 $doc->add_hidden_text($path);
203 print STDERR $doc->dump_draft,"\n" if ($debug > 1);
205 # register the document object to the database
206 $db->put_doc($doc, $HyperEstraier::Database::PDCLEAN);
209 if ($added % $dot == 0) {
214 if ($added % EST_SYNC_EVERY == 0) {
221 print "sync $added new files";
226 my $dur = (time() - $t) || 1;
227 printf(" [%.2f/s new %.2f/s dur: %s]\n",
238 if (($opt{i} || ($index_path && ! -e $index_path)) && !$opt{c}) {
240 print "force update of HyperEstraier index ";
241 print "importing existing data" unless (-e $index_path);
242 print "by -i flag" if ($opt{i});
250 my $index = shift || return;
251 my ($table,$col,$unique) = split(/_/, $index);
254 $dbh->do(qq{ create $unique index $index on $table($col) });
257 print "creating tables...\n";
261 ID SERIAL PRIMARY KEY,
262 name VARCHAR(30) NOT NULL,
268 create table shares (
269 ID SERIAL PRIMARY KEY,
270 hostID INTEGER NOT NULL references hosts(id),
271 name VARCHAR(30) NOT NULL,
272 share VARCHAR(200) NOT NULL,
273 localpath VARCHAR(200)
278 create table backups (
279 hostID INTEGER NOT NULL references hosts(id),
280 num INTEGER NOT NULL,
281 date integer NOT NULL,
282 type CHAR(4) not null,
283 shareID integer not null references shares(id),
284 size integer not null,
285 PRIMARY KEY(hostID, num, shareID)
289 #do_index('backups_hostid,num_unique');
293 ID SERIAL PRIMARY KEY,
294 num INTEGER NOT NULL,
295 name VARCHAR(255) NOT NULL,
302 ID SERIAL PRIMARY KEY,
303 shareID INTEGER NOT NULL references shares(id),
304 backupNum INTEGER NOT NULL,
305 name VARCHAR(255) NOT NULL,
306 path VARCHAR(255) NOT NULL,
307 date integer NOT NULL,
308 type INTEGER NOT NULL,
309 size INTEGER NOT NULL,
310 dvdid INTEGER references dvds(id)
314 print "creating indexes:";
316 foreach my $index (qw(
337 ## delete data before inseting ##
340 foreach my $table (qw(files dvds backups shares hosts)) {
342 $dbh->do(qq{ DELETE FROM $table });
349 ## insert new values ##
352 $hosts = $bpc->HostInfoRead();
358 $sth->{insert_hosts} = $dbh->prepare(qq{
359 INSERT INTO hosts (name, IP) VALUES (?,?)
362 $sth->{hosts_by_name} = $dbh->prepare(qq{
363 SELECT ID FROM hosts WHERE name=?
366 $sth->{backups_count} = $dbh->prepare(qq{
369 WHERE hostID=? AND num=? AND shareid=?
372 $sth->{insert_backups} = $dbh->prepare(qq{
373 INSERT INTO backups (hostID, num, date, type, shareid, size)
377 $sth->{insert_files} = $dbh->prepare(qq{
379 (shareID, backupNum, name, path, date, type, size)
380 VALUES (?,?,?,?,?,?,?)
383 foreach my $host_key (keys %{$hosts}) {
385 my $hostname = $hosts->{$host_key}->{'host'} || die "can't find host for $host_key";
387 $sth->{hosts_by_name}->execute($hosts->{$host_key}->{'host'});
389 unless (($hostID) = $sth->{hosts_by_name}->fetchrow_array()) {
390 $sth->{insert_hosts}->execute(
391 $hosts->{$host_key}->{'host'},
392 $hosts->{$host_key}->{'ip'}
395 $hostID = $dbh->last_insert_id(undef,undef,'hosts',undef);
398 print "host ".$hosts->{$host_key}->{'host'}.": ";
400 # get backups for a host
401 my @backups = $bpc->BackupInfoRead($hostname);
402 my $incs = scalar @backups;
403 print "$incs increments\n";
408 foreach my $backup (@backups) {
411 last if ($opt{m} && $inc_nr > $opt{m});
413 my $backupNum = $backup->{'num'};
414 my @backupShares = ();
416 printf("%-10s %2d/%-2d #%-2d %s %5s/%5s files (date: %s dur: %s)\n",
417 $hosts->{$host_key}->{'host'},
418 $inc_nr, $incs, $backupNum,
419 $backup->{type} || '?',
420 $backup->{nFilesNew} || '?', $backup->{nFiles} || '?',
421 strftime($t_fmt,localtime($backup->{startTime})),
422 fmt_time($backup->{endTime} - $backup->{startTime})
425 my $files = BackupPC::View->new($bpc, $hostname, \@backups, 1);
426 foreach my $share ($files->shareList($backupNum)) {
430 $shareID = getShareID($share, $hostID, $hostname);
432 $sth->{backups_count}->execute($hostID, $backupNum, $shareID);
433 my ($count) = $sth->{backups_count}->fetchrow_array();
434 # skip if allready in database!
435 next if ($count > 0);
438 print curr_time," ", $share;
440 my ($f, $nf, $d, $nd, $size) = recurseDir($bpc, $hostname, $files, $backupNum, $share, "", $shareID);
442 $sth->{insert_backups}->execute(
445 $backup->{'endTime'},
454 my $dur = (time() - $t) || 1;
455 printf(" %d/%d files %d/%d dirs %0.2f MB [%.2f/s dur: %s]\n",
457 ($size / 1024 / 1024),
462 hest_update($hostID, $shareID, $backupNum);
471 print "total duration: ",fmt_time(time() - $start_t),"\n";
477 my ($share, $hostID, $hostname) = @_;
479 $sth->{share_id} ||= $dbh->prepare(qq{
480 SELECT ID FROM shares WHERE hostID=? AND name=?
483 $sth->{share_id}->execute($hostID,$share);
485 my ($id) = $sth->{share_id}->fetchrow_array();
487 return $id if (defined($id));
489 $sth->{insert_share} ||= $dbh->prepare(qq{
491 (hostID,name,share,localpath)
495 my $drop_down = $hostname . '/' . $share;
496 $drop_down =~ s#//+#/#g;
498 $sth->{insert_share}->execute($hostID,$share, $drop_down ,undef);
499 return $dbh->last_insert_id(undef,undef,'shares',undef);
507 my ($key, $shareID,undef,$name,$path,$date,undef,$size) = @_;
509 return $beenThere->{$key} if (defined($beenThere->{$key}));
511 $sth->{file_in_db} ||= $dbh->prepare(qq{
513 WHERE shareID = ? and
520 my @param = ($shareID,$path,$date,$size);
521 $sth->{file_in_db}->execute(@param);
522 my $rows = $sth->{file_in_db}->rows;
523 print STDERR "## found_in_db($shareID,$path,$date,$size) ",( $rows ? '+' : '-' ), join(" ",@param), "\n" if ($debug >= 3);
525 $beenThere->{$key}++;
527 $sth->{'insert_files'}->execute(@data) unless ($rows);
531 ####################################################
532 # recursing through filesystem structure and #
533 # and returning flattened files list #
534 ####################################################
535 sub recurseDir($$$$$$$$) {
537 my ($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID) = @_;
539 print STDERR "\nrecurse($hostname,$backupNum,$share,$dir,$shareID)\n" if ($debug >= 1);
541 my ($nr_files, $new_files, $nr_dirs, $new_dirs, $size) = (0,0,0,0,0);
546 print STDERR "# dirAttrib($backupNum, $share, $dir)\n" if ($debug >= 2);
547 my $filesInBackup = $files->dirAttrib($backupNum, $share, $dir);
549 # first, add all the entries in current directory
550 foreach my $path_key (keys %{$filesInBackup}) {
551 print STDERR "# file ",Dumper($filesInBackup->{$path_key}),"\n" if ($debug >= 3);
556 $filesInBackup->{$path_key}->{'relPath'},
557 $filesInBackup->{$path_key}->{'mtime'},
558 $filesInBackup->{$path_key}->{'type'},
559 $filesInBackup->{$path_key}->{'size'}
562 my $key = join(" ", (
566 $filesInBackup->{$path_key}->{'mtime'},
567 $filesInBackup->{$path_key}->{'size'}
571 if (! defined($beenThere->{$key}) && ! ($found = found_in_db($key, @data)) ) {
572 print STDERR "# key: $key [", $beenThere->{$key},"]" if ($debug >= 2);
574 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
575 $new_dirs++ unless ($found);
576 print STDERR " dir\n" if ($debug >= 2);
578 $new_files++ unless ($found);
579 print STDERR " file\n" if ($debug >= 2);
581 $size += $filesInBackup->{$path_key}->{'size'} || 0;
584 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
587 my $full_path = $dir . '/' . $path_key;
588 push @stack, $full_path;
589 print STDERR "### store to stack: $full_path\n" if ($debug >= 3);
591 # my ($f,$nf,$d,$nd) = recurseDir($bpc, $hostname, $backups, $backupNum, $share, $path_key, $shareID) unless ($beenThere->{$key});
603 print STDERR "## STACK ",join(", ", @stack),"\n" if ($debug >= 2);
605 while ( my $dir = shift @stack ) {
606 my ($f,$nf,$d,$nd, $s) = recurseDir($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID);
607 print STDERR "# $dir f: $f nf: $nf d: $d nd: $nd\n" if ($debug >= 1);
616 return ($nr_files, $new_files, $nr_dirs, $new_dirs, $size);