package Net::Paraget::Client; # # $Id: Client.pm,v 1.5 2001/05/10 03:46:08 lrclause Exp $ # use strict; use English; use Carp; use IO::File; use File::Basename; use File::Spec; use Net::Paraget::PerformanceReport; use Class::MethodMaker get_set => [ qw( assignment output_file start_time end_time pid return_code manager ) ], boolean => [ qw( started cleanedup no_further_updates ) ], new_hash_init => [ qw( new hash_init ) ]; sub state { my ( $self, $string, $priority ) = @_; main::state( 'client ' . $self->info() . ": " . $string, $priority ); } sub info { my ( $self ) = @_; my $assignment_info = $self->assignment->info(); my $output_file = $self->output_file(); my $pid = $self->pid() || 'none'; my $return_code = $self->return_code() || 'none'; return "assignment $assignment_info, return code $return_code"; } sub start { my ( $self ) = @_; if ( $self->started() ) { carp "client already started with pid ", $self->pid; return; } my $assignment = $self->assignment(); my $manager = $self->manager(); my $url_basename = basename $assignment->file(); my $offset = $assignment->interval->start(); my $output_basename = "$url_basename.$offset"; my $outfile = File::Spec->catfile( $manager->tmp_dir(), $output_basename ); $self->output_file( $outfile ); # clear out the file beforehand my $f = IO::File->new( ">$outfile" ) or die "cannot open $outfile for writing"; $f->close(); $self->state( "starting", 1 ); my $pid = fork; defined $pid or die "error forking: $ERRNO"; if ( $pid != 0 ) { $self->hash_init( pid => $pid, started => 1, start_time => time, ); $assignment->server()->at_start(); return; } my $url = $assignment->mirror->url()."/".$assignment->file(); my $program = $manager->child_program(); exec ( $program, '--url', $url, '--offset', $offset, '--output', $outfile, '--size', $manager->authoritative_size(), ) or exit 1; } sub cleanup { my ( $self ) = @_; if ( $self->cleanedup() ) { return; } $self->end_time( time ); waitpid $self->pid(), 0; $self->return_code( $CHILD_ERROR << 8 ); $self->set_cleanedup(); if ($self->had_error()) { $self->assignment()->server()->at_failure(); } else { $self->assignment()->server()->at_success(); } } sub amount_completed { my ( $self ) = @_; return -s $self->output_file() || 0; } sub elapsed_time { my ( $self ) = @_; return 0 unless $self->start_time(); return $self->end_time() - $self->start_time() if $self->cleanedup(); return time - $self->start_time(); } sub stop { my ( $self ) = @_; if ( not $self->alive() ) { $self->cleanup(); return; } my $pid = $self->pid(); my @signals = qw( INT TERM KILL ); foreach my $signal ( @signals ) { if ( kill $signal, $pid ) { $self->cleanup(); return; } warn "error killing process $pid with $signal: $ERRNO" if $self->alive(); } warn "error: failed to kill process $pid: $ERRNO"; } sub alive { my ( $self ) = @_; return ( $self->pid() and kill 0, $self->pid() ); } sub checkup { my ( $self ) = @_; $self->cleanup() unless $self->alive() or $self->cleanedup(); } sub speed { my ( $self ) = @_; my $elapsed_time = $self->elapsed_time(); return undef if $elapsed_time == 0; return $self->amount_completed() / $elapsed_time; } sub report { my ( $self ) = @_; my $r = Net::Paraget::PerformanceReport->new ( assignment => $self->assignment(), work_time => $self->elapsed_time(), amount_completed => $self->amount_completed(), had_error => $self->had_error, ); return $r; } sub had_error { my ( $self ) = @_; return 0 unless $self->cleanedup(); return ( $self->return_code() != 0 and not $self->fulfilled_assignment() ); } sub further_updates { my ( $self ) = @_; return not $self->no_further_updates(); } sub fulfilled_assignment { my ( $self ) = @_; my $interval = $self->assignment->interval(); my $end = $interval->extendable_to(); my $need = $end - $interval->start(); return $self->amount_completed() >= $need; } sub estimated_time_to_completion { my ( $self ) = @_; my $speed = $self->speed(); my $completed_size = $self->amount_completed(); my $total_size = $self->assignment->interval->size(); my $amount_left = $total_size - $completed_size; return undef if not $speed; return $amount_left / $speed; } 1;