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};
45 my $dbh = DBI->connect($dsn, $user, "", { RaiseError => 1, AutoCommit => 0 });
49 if ( !getopts("cdm:v:i", \%opt ) ) {
51 usage: $0 [-c|-d] [-m num] [-v|-v level] [-i]
54 -c create database on first use
55 -d delete database before import
56 -m num import just num increments for one host
57 -v num set verbosity (debug) level (default $debug)
58 -i update HyperEstraier full text index
64 print "Debug level at $opt{v}\n";
71 print "updating HyperEstraier files ";
73 my $sth = $dbh->prepare(qq{
78 shares.share AS sharename,
79 files.backupNum AS backupNum,
80 files.name AS filename,
81 files.path AS filepath,
83 files.type AS filetype,
85 files.shareid AS shareid
87 INNER JOIN shares ON files.shareID=shares.ID
88 INNER JOIN hosts ON hosts.ID = shares.hostID
89 INNER JOIN backups ON backups.num = files.backupNum and backups.hostID = hosts.ID AND backups.shareID = shares.ID
94 my $dot = int($sth->rows / 15);
96 print $sth->rows, " files ($dot/#) ";
99 my $t = shift || return;
100 my $iso = BackupPC::Lib::timeStamp($t);
106 my $max = int($sth->rows / $dot);
108 $index_path = $TopDir . '/' . $index_path;
109 $index_path =~ s#//#/#g;
111 print "index $index_path...";
113 my $db = HyperEstraier::Database->new();
114 $db->open($index_path, $HyperEstraier::Database::DBWRITER | $HyperEstraier::Database::DBCREAT);
117 while (my $row = $sth->fetchrow_hashref()) {
119 # create a document object
120 my $doc = HyperEstraier::Document->new;
122 # add attributes to the document object
123 $doc->add_attr('@uri', 'file:///' . $row->{'fid'});
125 foreach my $c (qw/fid hname sname sharename backupNum filename filepath shareid/) {
126 $doc->add_attr($c, $row->{$c}) if ($row->{$c});
129 $doc->add_attr('date', fmt_date($row->{'date'}));
131 # add the body text to the document object
132 my $path = $row->{'filepath'};
133 $doc->add_text($path);
134 $path =~ s/(.)/$1 /g;
135 $doc->add_hidden_text($path);
137 print STDERR $doc->dump_draft,"\n" if ($debug > 1);
139 # register the document object to the database
140 $db->put_doc($doc, $HyperEstraier::Database::PDCLEAN);
143 if ($i % $dot == 0) {
158 ###################################create tables############################3
162 my $index = shift || return;
163 my ($table,$col,$unique) = split(/_/, $index);
166 $dbh->do(qq{ create $unique index $index on $table($col) });
169 print "creating tables...\n";
173 ID SERIAL PRIMARY KEY,
174 name VARCHAR(30) NOT NULL,
180 create table shares (
181 ID SERIAL PRIMARY KEY,
182 hostID INTEGER NOT NULL references hosts(id),
183 name VARCHAR(30) NOT NULL,
184 share VARCHAR(200) NOT NULL,
185 localpath VARCHAR(200)
190 create table backups (
191 hostID INTEGER NOT NULL references hosts(id),
192 num INTEGER NOT NULL,
193 date integer NOT NULL,
194 type CHAR(4) not null,
195 shareID integer not null references shares(id),
196 size integer not null,
197 PRIMARY KEY(hostID, num, shareID)
201 #do_index('backups_hostid,num_unique');
205 ID SERIAL PRIMARY KEY,
206 num INTEGER NOT NULL,
207 name VARCHAR(255) NOT NULL,
214 ID SERIAL PRIMARY KEY,
215 shareID INTEGER NOT NULL references shares(id),
216 backupNum INTEGER NOT NULL,
217 name VARCHAR(255) NOT NULL,
218 path VARCHAR(255) NOT NULL,
219 date integer NOT NULL,
220 type INTEGER NOT NULL,
221 size INTEGER NOT NULL,
222 dvdid INTEGER references dvds(id)
226 print "creating indexes:";
228 foreach my $index (qw(
251 foreach my $table (qw(files dvds backups shares hosts)) {
253 $dbh->do(qq{ DELETE FROM $table });
260 #################################INSERT VALUES#############################
263 $hosts = $bpc->HostInfoRead();
269 $sth->{insert_hosts} = $dbh->prepare(qq{
270 INSERT INTO hosts (name, IP) VALUES (?,?)
273 $sth->{hosts_by_name} = $dbh->prepare(qq{
274 SELECT ID FROM hosts WHERE name=?
277 $sth->{backups_count} = $dbh->prepare(qq{
280 WHERE hostID=? AND num=? AND shareid=?
283 $sth->{insert_backups} = $dbh->prepare(qq{
284 INSERT INTO backups (hostID, num, date, type, shareid, size)
288 $sth->{insert_files} = $dbh->prepare(qq{
290 (shareID, backupNum, name, path, date, type, size)
291 VALUES (?,?,?,?,?,?,?)
295 my $t = shift || return;
297 my ($ss,$mm,$hh) = gmtime($t);
298 $out .= "${hh}h" if ($hh);
299 $out .= sprintf("%02d:%02d", $mm,$ss);
303 foreach my $host_key (keys %{$hosts}) {
305 my $hostname = $hosts->{$host_key}->{'host'} || die "can't find host for $host_key";
307 $sth->{hosts_by_name}->execute($hosts->{$host_key}->{'host'});
309 unless (($hostID) = $sth->{hosts_by_name}->fetchrow_array()) {
310 $sth->{insert_hosts}->execute(
311 $hosts->{$host_key}->{'host'},
312 $hosts->{$host_key}->{'ip'}
315 $hostID = $dbh->last_insert_id(undef,undef,'hosts',undef);
318 print "host ".$hosts->{$host_key}->{'host'}.": ";
320 # get backups for a host
321 my @backups = $bpc->BackupInfoRead($hostname);
322 my $incs = scalar @backups;
323 print "$incs increments\n";
328 foreach my $backup (@backups) {
331 last if ($opt{m} && $inc_nr > $opt{m});
333 my $backupNum = $backup->{'num'};
334 my @backupShares = ();
336 printf("%-10s %2d/%-2d #%-2d %s %5s/%5s files (date: %s dur: %s)\n",
337 $hosts->{$host_key}->{'host'},
338 $inc_nr, $incs, $backupNum,
339 $backup->{type} || '?',
340 $backup->{nFilesNew} || '?', $backup->{nFiles} || '?',
341 strftime($t_fmt,localtime($backup->{startTime})),
342 fmt_time($backup->{endTime} - $backup->{startTime})
345 my $files = BackupPC::View->new($bpc, $hostname, \@backups, 1);
346 foreach my $share ($files->shareList($backupNum)) {
350 $shareID = getShareID($share, $hostID, $hostname);
352 $sth->{backups_count}->execute($hostID, $backupNum, $shareID);
353 my ($count) = $sth->{backups_count}->fetchrow_array();
354 # skip if allready in database!
355 next if ($count > 0);
358 print strftime($t_fmt,localtime())," ", $share;
360 my ($f, $nf, $d, $nd, $size) = recurseDir($bpc, $hostname, $files, $backupNum, $share, "", $shareID);
362 $sth->{insert_backups}->execute(
365 $backup->{'endTime'},
374 my $dur = (time() - $t) || 1;
375 printf(" %d/%d files %d/%d dirs %0.2f MB [%.2f/s dur: %s]\n",
377 ($size / 1024 / 1024),
389 print "total duration: ",fmt_time(time() - $start_t),"\n";
395 my ($share, $hostID, $hostname) = @_;
397 $sth->{share_id} ||= $dbh->prepare(qq{
398 SELECT ID FROM shares WHERE hostID=? AND name=?
401 $sth->{share_id}->execute($hostID,$share);
403 my ($id) = $sth->{share_id}->fetchrow_array();
405 return $id if (defined($id));
407 $sth->{insert_share} ||= $dbh->prepare(qq{
409 (hostID,name,share,localpath)
413 my $drop_down = $hostname . '/' . $share;
414 $drop_down =~ s#//+#/#g;
416 $sth->{insert_share}->execute($hostID,$share, $drop_down ,undef);
417 return $dbh->last_insert_id(undef,undef,'shares',undef);
425 my ($key, $shareID,undef,$name,$path,$date,undef,$size) = @_;
427 return $beenThere->{$key} if (defined($beenThere->{$key}));
429 $sth->{file_in_db} ||= $dbh->prepare(qq{
431 WHERE shareID = ? and
438 my @param = ($shareID,$path,$date,$size);
439 $sth->{file_in_db}->execute(@param);
440 my $rows = $sth->{file_in_db}->rows;
441 print STDERR "## found_in_db($shareID,$path,$date,$size) ",( $rows ? '+' : '-' ), join(" ",@param), "\n" if ($debug >= 3);
443 $beenThere->{$key}++;
445 $sth->{'insert_files'}->execute(@data) unless ($rows);
449 ####################################################
450 # recursing through filesystem structure and #
451 # and returning flattened files list #
452 ####################################################
453 sub recurseDir($$$$$$$$) {
455 my ($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID) = @_;
457 print STDERR "\nrecurse($hostname,$backupNum,$share,$dir,$shareID)\n" if ($debug >= 1);
459 my ($nr_files, $new_files, $nr_dirs, $new_dirs, $size) = (0,0,0,0,0);
464 print STDERR "# dirAttrib($backupNum, $share, $dir)\n" if ($debug >= 2);
465 my $filesInBackup = $files->dirAttrib($backupNum, $share, $dir);
467 # first, add all the entries in current directory
468 foreach my $path_key (keys %{$filesInBackup}) {
469 print STDERR "# file ",Dumper($filesInBackup->{$path_key}),"\n" if ($debug >= 3);
474 $filesInBackup->{$path_key}->{'relPath'},
475 $filesInBackup->{$path_key}->{'mtime'},
476 $filesInBackup->{$path_key}->{'type'},
477 $filesInBackup->{$path_key}->{'size'}
480 my $key = join(" ", (
484 $filesInBackup->{$path_key}->{'mtime'},
485 $filesInBackup->{$path_key}->{'size'}
489 if (! defined($beenThere->{$key}) && ! ($found = found_in_db($key, @data)) ) {
490 print STDERR "# key: $key [", $beenThere->{$key},"]" if ($debug >= 2);
492 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
493 $new_dirs++ unless ($found);
494 print STDERR " dir\n" if ($debug >= 2);
496 $new_files++ unless ($found);
497 print STDERR " file\n" if ($debug >= 2);
499 $size += $filesInBackup->{$path_key}->{'size'} || 0;
502 if ($filesInBackup->{$path_key}->{'type'} == BPC_FTYPE_DIR) {
505 my $full_path = $dir . '/' . $path_key;
506 push @stack, $full_path;
507 print STDERR "### store to stack: $full_path\n" if ($debug >= 3);
509 # my ($f,$nf,$d,$nd) = recurseDir($bpc, $hostname, $backups, $backupNum, $share, $path_key, $shareID) unless ($beenThere->{$key});
521 print STDERR "## STACK ",join(", ", @stack),"\n" if ($debug >= 2);
523 while ( my $dir = shift @stack ) {
524 my ($f,$nf,$d,$nd, $s) = recurseDir($bpc, $hostname, $files, $backupNum, $share, $dir, $shareID);
525 print STDERR "# $dir f: $f nf: $nf d: $d nd: $nd\n" if ($debug >= 1);
534 return ($nr_files, $new_files, $nr_dirs, $new_dirs, $size);