2 # gen-indices generates bug index files, and is released
3 # under the terms of the GPL version 2, or any later version, at your
4 # option. See the file README and COPYING for more information.
6 # Copyright (c) 2005/08/03 Anthony Towns
7 # Copyright 2007, 2008 by Don Armstrong <don@donarmstrong.com>.
13 use MLDBM qw(DB_FILE Storable);
14 use Fcntl qw/O_RDWR O_CREAT O_TRUNC/;
21 use List::Util qw(min);
23 use Debbugs::Common qw(make_list);
27 gen-indices - Generates index files for the cgi scripts
34 --index-path path to index location
35 --quick update changed bugs
36 --debug, -d debugging level (Default 0)
37 --help, -h display this help
38 --man, -m display manual
46 Only update changed bugs
50 Debug verbosity. (Default 0)
54 Display brief useage information.
67 # Use portable Storable images
68 $MLDBM::DumpMeth=q(portable);
71 my %options = (debug => 0,
78 GetOptions(\%options,'quick!','index_path|index-path=s','debug|d+','help|h|?','man|m') or pod2usage(2);
79 pod2usage(1) if $options{help};
80 pod2usage(-verbose=>2) if $options{man};
82 use Debbugs::Config qw(:config);
83 use Debbugs::Common qw(getparsedaddrs getbugcomponent lockpid);
84 use Debbugs::Status qw(readbug split_status_fields);
87 chdir($config{spool_dir}) or die "chdir $config{spool_dir} failed: $!";
89 my $verbose = $options{debug};
90 my $indexdest = $options{index_path} || $config{spool_dir};
92 my $initialdir = "db-h";
95 if (defined $ARGV[0] and $ARGV[0] eq "archive") {
96 $initialdir = "archive";
100 if (not lockpid($config{spool_dir}.'/lock/gen-indices')) {
101 if ($options{quick}) {
102 # If this is a quick run, just exit
103 print STDERR "Another gen-indices is running; stopping\n" if $verbose;
106 print STDERR "Another gen-indices is running; stopping\n";
110 # NB: The reverse index is special; it's used to clean up during updates to bugs
111 my @indexes = ('package', 'tag', 'severity','owner','submitter-email','status','correspondent','affects','reverse');
115 if (not $options{quick}) {
116 # We'll trade memory for speed here if we're not doing a quick rebuild
117 for my $indexes (@indexes) {
118 $fast_index{$indexes} = {};
120 $indexes = \%fast_index;
123 $indexes = \%slow_index;
126 my $start_time = time;
127 for my $i (@indexes) {
128 $slow_index{$i} = {};
129 if ($options{quick}) {
130 if (-e "$indexdest/by-$i${suffix}.idx") {
131 system('cp','-a',"$indexdest/by-$i${suffix}.idx","$indexdest/by-$i${suffix}.idx.new") == 0
132 or die "Error creating the new index";
133 my $stat = stat("$indexdest/by-$i${suffix}.idx") or die "Unable to stat $indexdest/by-$i${suffix}.idx";
134 $time = defined $time ? min($time,$stat->mtime) : $stat->mtime;
136 tie %{$slow_index{$i}}, MLDBM => "$indexdest/by-$i$suffix.idx.new",
138 or die "$0: can't create by-$i$suffix-idx.new: $!";
141 tie %{$slow_index{$i}}, MLDBM => "$indexdest/by-$i$suffix.idx.new",
142 O_RDWR|O_CREAT|O_TRUNC, 0666
143 or die "$0: can't create by-$i$suffix-idx.new: $!";
146 $time = 0 if not defined $time;
150 my ($index, $bug, @values) = @_;
152 if (exists $indexes->{reverse}{"$index $bug"}) {
153 # We do this insanity to work around a "feature" in MLDBM
154 for my $key (@{$indexes->{reverse}{"$index $bug"}}) {
155 my $temp = $indexes->{$index}{$key};
156 delete $temp->{$bug};
157 $indexes->{$index}{$key} = $temp;
158 $indexes->{$index}{"count $key"}--;
160 delete $indexes->{reverse}{"$index $bug"};
162 for my $key (@values) {
163 $indexes->{$index}->{"count $key"}++;
164 # We do this insanity to work around a "feature" in MLDBM
165 my $temp = $indexes->{$index}->{$key};
167 $indexes->{$index}->{$key} = $temp;
169 $indexes->{reverse}{"$index $bug"} = [@values];
172 sub emailfromrfc822 {
174 $email =~ s/\s*\(.*\)\s*//;
175 $email = $1 if ($email =~ m/<(.*)>/);
179 my $modification_made = 0;
182 my @dirs = ($initialdir);
183 while (my $dir = shift @dirs) {
184 printf "Doing dir %s ...\n", $dir if $verbose;
186 opendir(DIR, "$dir/.") or die "opendir $dir: $!";
187 my @subdirs = readdir(DIR);
190 my @list = map { m/^(\d+)\.summary$/?($1):() } @subdirs;
191 push @dirs, map { m/^(\d+)$/ && -d "$dir/$1"?("$dir/$1"):() } @subdirs;
193 for my $bug (@list) {
194 print "Up to $cnt bugs...\n" if (++$cnt % 100 == 0 && $verbose);
195 my $stat = stat(getbugcomponent($bug,'summary',$initialdir));
196 if (not defined $stat) {
197 print STDERR "Unable to stat $bug $!\n";
200 next if $stat->mtime < $time;
201 my $fdata = split_status_fields(readbug($bug, $initialdir));
202 $modification_made = 1;
203 addbugtoindex("package", $bug, make_list($fdata->{package}));
204 addbugtoindex("tag", $bug, make_list($fdata->{keywords}));
205 addbugtoindex("affects", $bug, make_list($fdata->{"affects"}));
206 addbugtoindex('submitter-email', $bug,
207 map {lc($_->address)} getparsedaddrs($fdata->{originator}));
208 addbugtoindex("severity", $bug, $fdata->{"severity"});
209 addbugtoindex("owner", $bug,
210 map {lc($_->address)} getparsedaddrs($fdata->{"owner"}));
212 # do this in eval to avoid exploding on jacked logs
214 my $log = Debbugs::Log->new(bug_num => $bug);
216 while (my $record = $log->read_record()) {
217 next unless $record->{type} eq 'incoming-recv';
218 # we use a regex here, because a full mime parse will be slow.
219 my ($from) = $record->{text} =~ /^From:\s+(.+?)^\S/ism;
220 push @correspondents, map {lc($_->address)} getparsedaddrs($from);
222 addbugtoindex('correspondent',$bug,@correspondents) if @correspondents;
225 print STDERR "Problem dealing with log of $bug: $@";
230 if (not $options{quick}) {
231 # put the fast index into the slow index
232 for my $key1 (keys %fast_index) {
233 for my $key2 (keys %{$fast_index{$key1}}) {
234 $slow_index{$key1}{$key2} = $fast_index{$key1}{$key2};
236 print "Dealt with index $key1\n" if $verbose;
240 for my $i (@indexes) {
241 untie %{$slow_index{$i}};
242 # Only move if we've made changes, otherwise unlink
243 if ($modification_made) {
244 move("$indexdest/by-$i$suffix.idx.new", "$indexdest/by-$i$suffix.idx");
245 # We do this, because old versions of touch don't support -d '@epoch'
246 system('touch','-d',"1/1/1970 UTC + ${start_time}secs","$indexdest/by-$i$suffix.idx");
249 unlink("$indexdest/by-$i$suffix.idx.new");
253 unlink($config{spool_dir}.'/lock/gen-indices')