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} || '';
43 my $index_path = $Conf{HyperEstraierIndex};
44 $index_path = $TopDir . '/' . $index_path;
45 $index_path =~ s#//#/#g;
48 my $dbh = DBI->connect($dsn, $user, "", { RaiseError => 1, AutoCommit => 0 });
52 if ( !getopts("cdm:v:i", \%opt ) ) {
54 usage: $0 [-c|-d] [-m num] [-v|-v level] [-i]
57 -c create database on first use
58 -d delete database before import
59 -m num import just num increments for one host
60 -v num set verbosity (debug) level (default $debug)
61 -i update HyperEstraier full text index
67 print "Debug level at $opt{v}\n";
74 my $t = shift || return;
76 my ($ss,$mm,$hh) = gmtime($t);
77 $out .= "${hh}h" if ($hh);
78 $out .= sprintf("%02d:%02d", $mm,$ss);
83 return strftime($t_fmt,localtime());
90 my ($host_id, $share_id, $num) = @_;
92 print curr_time," updating HyperEstraier: select files";
97 if ($host_id && $share_id && $num) {
106 my $sth = $dbh->prepare(qq{
110 shares.name AS sname,
111 -- shares.share AS sharename,
112 files.backupnum AS backupnum,
113 -- files.name AS filename,
114 files.path AS filepath,
118 files.shareid AS shareid,
119 backups.date AS backup_date
121 INNER JOIN shares ON files.shareID=shares.ID
122 INNER JOIN hosts ON hosts.ID = shares.hostID
123 INNER JOIN backups ON backups.num = files.backupNum and backups.hostID = hosts.ID AND backups.shareID = shares.ID
128 my $results = $sth->rows;
131 print " - no files, skipping\n";
135 my $dot = int($results / 15) || 1;
137 print " $results ($dot/#)";
140 my $t = shift || return;
141 my $iso = BackupPC::Lib::timeStamp($t);
146 my $max = int($results / $dot);
148 print ", opening index $index_path...";
150 my $db = HyperEstraier::Database->new();
152 # unless ($hest_db) {
153 # print " open reader";
154 # $hest_db = HyperEstraier::Database->new();
159 $db->open($index_path, $HyperEstraier::Database::DBWRITER | $HyperEstraier::Database::DBCREAT);
163 while (my $row = $sth->fetchrow_hashref()) {
165 my $fid = $row->{'fid'} || die "no fid?";
166 my $uri = 'file:///' . $fid;
168 my $id = $db->uri_to_id($uri);
169 next unless ($id == -1);
171 # create a document object
172 my $doc = HyperEstraier::Document->new;
174 # add attributes to the document object
175 $doc->add_attr('@uri', $uri);
177 foreach my $c (@{ $sth->{NAME} }) {
178 $doc->add_attr($c, $row->{$c}) if ($row->{$c});
181 #$doc->add_attr('@cdate', fmt_date($row->{'date'}));
183 # add the body text to the document object
184 my $path = $row->{'filepath'};
185 $doc->add_text($path);
186 $path =~ s/(.)/$1 /g;
187 $doc->add_hidden_text($path);
189 print STDERR $doc->dump_draft,"\n" if ($debug > 1);
191 # register the document object to the database
192 $db->put_doc($doc, $HyperEstraier::Database::PDCLEAN);
195 if ($added % $dot == 0) {
202 print "sync $added new files";
207 my $dur = (time() - $t) || 1;
208 printf(" [%.2f/s new %.2f/s dur: %s]\n",
219 if (($opt{i} || ($index_path && ! -e $index_path)) && !$opt{c}) {
221 print "force update of HyperEstraier index ";
222 print "importing existing data" unless (-e $index_path);
223 print "by -i flag" if ($opt{i});
231 my $index = shift || return;
232 my ($table,$col,$unique) = split(/_/, $index);
235 $dbh->do(qq{ create $unique index $index on $table($col) });
238 print "creating tables...\n";
242 ID SERIAL PRIMARY KEY,
243 name VARCHAR(30) NOT NULL,
249 create table shares (
250 ID SERIAL PRIMARY KEY,
251 hostID INTEGER NOT NULL references hosts(id),
252 name VARCHAR(30) NOT NULL,
253 share VARCHAR(200) NOT NULL,
254 localpath VARCHAR(200)
259 create table backups (
260 hostID INTEGER NOT NULL references hosts(id),
261 num INTEGER NOT NULL,
262 date integer NOT NULL,
263 type CHAR(4) not null,
264 shareID integer not null references shares(id),
265 size integer not null,
266 PRIMARY KEY(hostID, num, shareID)
270 #do_index('backups_hostid,num_unique');
274 ID SERIAL PRIMARY KEY,
275 num INTEGER NOT NULL,
276 name VARCHAR(255) NOT NULL,
283 ID SERIAL PRIMARY KEY,
284 shareID INTEGER NOT NULL references shares(id),
285 backupNum INTEGER NOT NULL,
286 name VARCHAR(255) NOT NULL,
287 path VARCHAR(255) NOT NULL,
288 date integer NOT NULL,
289 type INTEGER NOT NULL,
290 size INTEGER NOT NULL,
291 dvdid INTEGER references dvds(id)
295 print "creating indexes:";
297 foreach my $index (qw(
318 ## delete data before inseting ##
321 foreach my $table (qw(files dvds backups shares hosts)) {
323 $dbh->do(qq{ DELETE FROM $table });
330 ## insert new values ##
333 $hosts = $bpc->HostInfoRead();
339 $sth->{insert_hosts} = $dbh->prepare(qq{
340 INSERT INTO hosts (name, IP) VALUES (?,?)
343 $sth->{hosts_by_name} = $dbh->prepare(qq{
344 SELECT ID FROM hosts WHERE name=?
347 $sth->{backups_count} = $dbh->prepare(qq{
350 WHERE hostID=? AND num=? AND shareid=?
353 $sth->{insert_backups} = $dbh->prepare(qq{
354 INSERT INTO backups (hostID, num, date, type, shareid, size)
358 $sth->{insert_files} = $dbh->prepare(qq{
360 (shareID, backupNum, name, path, date, type, size)
361 VALUES (?,?,?,?,?,?,?)
364 foreach my $host_key (keys %{$hosts}) {
366 my $hostname = $hosts->{$host_key}->{'host'} || die "can't find host for $host_key";
368 $sth->{hosts_by_name}->execute($hosts->{$host_key}->{'host'});
370 unless (($hostID) = $sth->{hosts_by_name}->fetchrow_array()) {
371 $sth->{insert_hosts}->execute(
372 $hosts->{$host_key}->{'host'},
373 $hosts->{$host_key}->{'ip'}
376 $hostID = $dbh->last_insert_id(undef,undef,'hosts',undef);
379 print "host ".$hosts->{$host_key}->{'host'}.": ";
381 # get backups for a host
382 my @backups = $bpc->BackupInfoRead($hostname);
383 my $incs = scalar @backups;
384 print "$incs increments\n";
389 foreach my $backup (@backups) {
392 last if ($opt{m} && $inc_nr > $opt{m});
394 my $backupNum = $backup->{'num'};
395 my @backupShares = ();
397 printf("%-10s %2d/%-2d #%-2d %s %5s/%5s files (date: %s dur: %s)\n",
398 $hosts->{$host_key}->{'host'},
399 $inc_nr, $incs, $backupNum,
400 $backup->{type} || '?',
401 $backup->{nFilesNew} || '?', $backup->{nFiles} || '?',
402 strftime($t_fmt,localtime($backup->{startTime})),
403 fmt_time($backup->{endTime} - $backup->{startTime})
406 my $files = BackupPC::View->new($bpc, $hostname, \@backups, 1);
407 foreach my $share ($files->shareList($backupNum)) {
411 $shareID = getShareID($share, $hostID, $hostname);
413 $sth->{backups_count}->execute($hostID, $backupNum, $shareID);
414 my ($count) = $sth->{backups_count}->fetchrow_array();
415 # skip if allready in database!
416 next if ($count > 0);
419 print curr_time," ", $share;
421 my ($f, $nf, $d, $nd, $size) = recurseDir($bpc, $hostname, $files, $backupNum, $share, "", $shareID);
423 $sth->{insert_backups}->execute(
426 $backup->{'endTime'},
435 my $dur = (time() - $t) || 1;
436 printf(" %d/%d files %d/%d dirs %0.2f MB [%.2f/s dur: %s]\n",
438 ($size / 1024 / 1024),
443 hest_update($hostID, $shareID, $backupNum);
452 print "total duration: ",fmt_time(time() - $start_t),"\n";
458 my ($share, $hostID, $hostname) = @_;
460 $sth->{share_id} ||= $dbh->prepare(qq{
461 SELECT ID FROM shares WHERE hostID=? AND name=?
464 $sth->{share_id}->execute($hostID,$share);
466 my ($id) = $sth->{share_id}->fetchrow_array();
468 return $id if (defined($id));
470 $sth->{insert_share} ||= $dbh->prepare(qq{
472 (hostID,name,share,localpath)
476 my $drop_down = $hostname . '/' . $share;
477 $drop_down =~ s#//+#/#g;
479 $sth->{insert_share}->execute($hostID,$share, $drop_down ,undef);
480 return $dbh->last_insert_id(undef,undef,'shares',undef);
488 my ($key, $shareID,undef,$name,$path,$date,undef,$size) = @_;
490 return $beenThere->{$key} if (defined($beenThere->{$key}));
492 $sth->{file_in_db} ||= $dbh->prepare(qq{
494 WHERE shareID = ? and
501 my @param = ($shareID,$path,$date,$size);
502 $sth->{file_in_db}->execute(@param);
503 my $rows = $sth->{file_in_db}->rows;
504 print STDERR "## found_in_db($shareID,$path,$date,$size) ",( $rows ? '+' : '-' ), join(" ",@param), "\n" if ($debug >= 3);
506 $beenThere->{$key}++;
508 $sth->{'insert_files'}->execute(@data) unless ($rows);
512 ####################################################
513 # recursing through filesystem structure and #
514 # and returning flattened files list #
515 ####################################################
516 sub recurseDir($$$$$$$$) {
518 my ($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID) = @_;
520 print STDERR "\nrecurse($hostname,$backupNum,$share,$dir,$shareID)\n" if ($debug >= 1);
522 my ($nr_files, $new_files, $nr_dirs, $new_dirs, $size) = (0,0,0,0,0);
527 print STDERR "# dirAttrib($backupNum, $share, $dir)\n" if ($debug >= 2);
528 my $filesInBackup = $files->dirAttrib($backupNum, $share, $dir);
530 # first, add all the entries in current directory
531 foreach my $path_key (keys %{$filesInBackup}) {
532 print STDERR "# file ",Dumper($filesInBackup->{$path_key}),"\n" if ($debug >= 3);
537 $filesInBackup->{$path_key}->{'relPath'},
538 $filesInBackup->{$path_key}->{'mtime'},
539 $filesInBackup->{$path_key}->{'type'},
540 $filesInBackup->{$path_key}->{'size'}
543 my $key = join(" ", (
547 $filesInBackup->{$path_key}->{'mtime'},
548 $filesInBackup->{$path_key}->{'size'}
552 if (! defined($beenThere->{$key}) && ! ($found = found_in_db($key, @data)) ) {
553 print STDERR "# key: $key [", $beenThere->{$key},"]" if ($debug >= 2);
555 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
556 $new_dirs++ unless ($found);
557 print STDERR " dir\n" if ($debug >= 2);
559 $new_files++ unless ($found);
560 print STDERR " file\n" if ($debug >= 2);
562 $size += $filesInBackup->{$path_key}->{'size'} || 0;
565 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
568 my $full_path = $dir . '/' . $path_key;
569 push @stack, $full_path;
570 print STDERR "### store to stack: $full_path\n" if ($debug >= 3);
572 # my ($f,$nf,$d,$nd) = recurseDir($bpc, $hostname, $backups, $backupNum, $share, $path_key, $shareID) unless ($beenThere->{$key});
584 print STDERR "## STACK ",join(", ", @stack),"\n" if ($debug >= 2);
586 while ( my $dir = shift @stack ) {
587 my ($f,$nf,$d,$nd, $s) = recurseDir($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID);
588 print STDERR "# $dir f: $f nf: $nf d: $d nd: $nd\n" if ($debug >= 1);
597 return ($nr_files, $new_files, $nr_dirs, $new_dirs, $size);