postfwd/tools/postfwd-client.pl

161 lines
4.8 KiB
Perl
Executable file

#!/usr/bin/perl -w
## MODULES
#use strict;
use warnings;
use IO::Socket;
use IO::Pipe;
use Getopt::Long 2.25 qw(:config no_ignore_case bundling);
BEGIN {
eval { require Time::HiRes };
if ($@) {
warn "$@";
warn "Failed to include optional module Time::HiRes.";
} else {
Time::HiRes->import( qw(time) );
};
};
## PARAMETERS
my $syntax = "USAGE: client.pl [ OPTIONS ] <addr>:<port>";
my $sendstr = 'ccert_fingerprint=
size=64063
helo_name=english-breakfast.cloud9.net
reverse_client_name=english-breakfast.cloud9.net
queue_id=
encryption_cipher=
encryption_protocol=
etrn_domain=
ccert_subject=
request=smtpd_access_policy
protocol_state=RCPT
recipient=someone@domain.local
instance=6748.46adf3f8.62156.0
protocol_name=ESMTP
encryption_keysize=0
recipient_count=0
ccert_issuer=
sender=owner-postfix-users@postfix.org
client_name=english-breakfast.cloud9.net
client_address=168.100.1.7
';
my $delay = 0.5;
our $pipe = new IO::Pipe;
use vars qw( %options %kinder $kind $wait );
## COMMAND LINE
GetOptions( \%options,
'verbose|v+',
'quiet|q+',
'process|p=i',
'count|c=i',
'timeout|t=i',
'file|f=s',
) or die "$syntax\n";
die "$syntax\n" unless $ARGV[0];
map { $options{$_} ||= 1 } qw(count process);
$options{verbose} ||= 0;
$options{timeout} ||= 3;
if (defined $options{file}) {
(-f $options{file}) || die "can not find file '".$options{file}."'\n";
open (REQUEST, "<".$options{file}) || die "can not open file '".$options{file}."'\n";
$sendstr = join "", <REQUEST>;
close (REQUEST);
};
## FORK
$| = 1;
my $starttime = time();
FORK: for (my $i=0;$i<$options{process};$i++) {
$kind = fork();
last FORK unless $kind;
$kinder{$kind} = 1;
};
## WHO AM I?
($kind) ? parent_process() : child_process() ;
die "should never see me\n";
exit(1);
## PARENT CODE
sub parent_process {
$pipe->reader();
use POSIX ":sys_wait_h";
undef my @status;
# wait until children have finished
print ("parent process waiting for ".(scalar keys %kinder)." pids ".(join ' ', (keys %kinder))."\n") unless $options{quiet};
PARENT: do {
# check pipe for finished children
push @status, <$pipe>;
# check children
CHILD: foreach (keys %kinder) {
$wait = waitpid($_,&WNOHANG);
last CHILD unless ($wait == -1);
delete $kinder{$_};
};
# sleep a while to reduce cpu usage
select(undef, undef, undef, $delay);
print ("parent process waiting for ".(scalar keys %kinder)." pids ".(join ' ', (keys %kinder))."\n") if ($options{verbose} > 1);
} until (($wait == -1) or (($#status + 1) >= $options{process}));
printf ("parent process finished after %.2f seconds.\n", (time() - $starttime)) unless $options{quiet};
# display results
my $parent_requests = my $parent_errors = my $parent_valid = my $parent_invalid = my $parent_time = 0;
foreach (@status) {
my($child_requests,$child_errors,$child_valid,$child_invalid,$child_time) = split ';', $_;
$parent_requests += $child_requests;
$parent_errors += $child_errors;
$parent_valid += $child_valid;
$parent_invalid += $child_invalid;
$parent_time = $child_time if ($child_time > $parent_time);
};
$parent_time = $parent_time - $starttime;
my $parent_rps = ($parent_time) ? ($parent_requests / $parent_time) : 0;
printf "%d requests, %d errors, %d valid, %d invalid, %.2fs total time, %.2f requests per second\n",
$parent_requests,$parent_errors,$parent_valid,$parent_invalid,$parent_time,$parent_rps;
exit (0);
};
## CHILD CODE
sub child_process {
$pipe->writer();
my $ok = my $nok = 0;
undef my $getstr;
# open socket
my($addr,$port) = split ':', $ARGV[0];
if ( ($addr and $port) and my $socket = new IO::Socket::INET (
PeerAddr => $addr,
PeerPort => $port,
Proto => 'tcp',
Timeout => $options{timeout},
Type => SOCK_STREAM ) ) {
# submit requests
for (my $i=0; $i<$options{count}; $i++) {
printf ("CHILD-$$: asking service $addr:$port\n") if $options{verbose};
print $socket "$sendstr";
$getstr = <$socket>; <$socket>;
chomp($getstr);
printf ("CHILD-$$: answer from $addr:$port -> '$getstr'\n") if $options{verbose};
$getstr =~ s/^(action=)//;
# check answer
if ($1 and $getstr) {
$ok++;
printf ("CHILD-$$: OK: answer from $addr:$port -> '$getstr'\n") unless ( $options{quiet} or (($options{count} * $options{process}) > 50) );
} else {
$nok++;
warn ("CHILD-$$: FAIL: invalid answer from $addr:$port -> '$getstr'\n");
};
};
} else {
warn ("CHILD-$$: can not open socket to $addr:$port\n");
};
# create summary
my $summary = $options{count}.';'.($options{count} - ($ok + $nok)).';'.$ok.';'.$nok.';'.time()."\n";
print ("CHILD-$$: child summary: $summary") if ($options{verbose} > 1);
# send summary to parent
print $pipe "$summary";
exit (0);
};