File: postfwd-client.pl

package info (click to toggle)
postfwd 1.35-7
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid
  • size: 1,568 kB
  • sloc: perl: 5,574; sh: 292; makefile: 40
file content (160 lines) | stat: -rwxr-xr-x 4,952 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/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);
};