package Mogstored::ChildProcess::FIDSizes; use strict; use base 'Mogstored::ChildProcess'; use warnings; use Errno qw(ENOENT); # Gearman version 1.06: bug fix with closing pipe to parent. we don't actually use Gearman::Client # object, but it comes with the updated Gearman::Worker which we do care about use Gearman::Client 1.06; use Gearman::Worker; use Storable (); # Note: in this case, this module is loaded *before* the fork (which # happens in Gearman::Server's start_worker) in the parent mogstored's # process, so be careful nothing heavy/gross is added to the FIDSizes # worker. my $docroot; my $worker; sub pre_exec_init { $ENV{MOG_DOCROOT} = Perlbal->service('mogstored')->{docroot}; } sub run { $0 = "mogstored [fidsizes]"; $worker = Gearman::Worker->new; $worker->register_function(fid_sizes => \&gw_fidsizes); $docroot = $ENV{MOG_DOCROOT} or die "MOG_DOCROOT environment variable not set"; while (1) { $worker->work(); } } sub gw_fidsizes { my $job = shift; my $args = Storable::thaw($job->arg); my ($start, $end, $devices) = @$args; my @output; foreach my $device (@$devices) { my $entries = do_one_device($start, $end, "$docroot/dev$device"); push @output, [$device, $entries]; } return \Storable::nfreeze(\@output); } sub do_one_device { my ($start, $end, $base) = @_; die "'$base' isn't a directory" unless -d $base; die "start and end are not both defined" unless length $start && length $end; die "end cannot come before start" unless $end >= $start; die "start out of range" if $start > 9_999_999_999; die "end out of range" if $end > 9_999_999_999; die "start less than 0" if $start < 0; die "end less than 0" if $end < 0; my $hdir_start = int($start / 1000); my $hdir_end = int($end / 1000); my $file_start = $start % 1000; my $file_end = $end % 1000; my @files; for (my $hdir = $hdir_start; $hdir <= $hdir_end; $hdir++) { my $nfid = sprintf '%07d', $hdir; my ($b, $mmm, $ttt) = ( $nfid =~ m{(\d)(\d{3})(\d{3})} ); my $hdir_path = "$base/$b/$mmm/$ttt"; my $rv = opendir(my $dh, "$base/$b/$mmm/$ttt"); unless ($rv) { if ($! != ENOENT) { die "Unable to read directory $hdir_path: $!\n"; } next; } foreach my $file (sort readdir($dh)) { next if ($file eq '.' || $file eq '..'); unless ($file =~ m/\Q$nfid\E(\d{3})\.fid/) { warn "Spurious file during readdir: $hdir_path/$file\n"; next; } my $hhh = $1; my ($fid) = ($nfid . $hhh) =~ /^0*(\d+)$/; next if (($hdir == $hdir_start && $hhh < $file_start) || ($hdir == $hdir_end && $hhh > $file_end)); my $filepath = "$hdir_path/$file"; my $size = (stat($filepath))[7]; push @files, [$fid, $size]; die "Results too large" if @files > 10_000; } } return \@files; } 1;