]> git.ipfire.org Git - thirdparty/squid.git/blame - tools/helper-mux/helper-mux.pl.in
SourceFormat Enforcement
[thirdparty/squid.git] / tools / helper-mux / helper-mux.pl.in
CommitLineData
33d1abfe 1#!@PERL@
e3d9712e
AJ
2
3use Getopt::Std;
4use Data::Dumper;
5use FileHandle;
6use IPC::Open2;
33d1abfe
AJ
7use Pod::Usage;
8
9=pod
10
11=head1 NAME
12
13helper-mux - Concurrency protocol multiplexer for Squid helpers
14
15=head1 SYNOPSIS
16
17B<helper-mux> helper-path [helper-options ...]
18
19=head1 DESCRIPTION
20
21B<helper-mux> purpose is to relieve some of the burden
22B<squid> has when dealing with slow helpers. It does so by acting as a
23middleman between B<squid> and the actual helpers, talking to B<squid> via
24the multiplexed variant of the helper protocol and to the helpers
25via the non-multiplexed variant.
26
27Helpers are started on demand, and in theory the muxer can handle up to
281k helpers per instance. It is up to B<squid> to decide how many helpers
29to start.
30
31The helper can be controlled using various signals:
32- SIGHUP: dump the state of all helpers to STDERR
33
34=head1 OPTIONS
35
36=over 8
37
38=item B<helper-path>
39
40Path to the helper being multiplexed.
41
42=item B<helper-options>
43
44Command line options for the helper being multiplexed.
45
46=back
47
48=head1 KNOWN ISSUES
49
50B<helper-mux> knows nothing about the actual messages being passed around,
51and as such cannot yet compensate for broken helpers.
52
53It is not yet able to manage dying helpers.
54
55=head1 COPYRIGHT
56
4ac4a490 57 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
33d1abfe
AJ
58 *
59 * Squid software is distributed under GPLv2+ license and includes
60 * contributions from numerous individuals and organizations.
61 * Please see the COPYING and CONTRIBUTORS files for details.
62
63 Copyright (C) Francesco Chemolli <kinkie@squid-cache.org>
64
65 This program is free software; you can redistribute it and/or modify
66 it under the terms of the GNU General Public License as published by
67 the Free Software Foundation; either version 2 of the License, or
68 (at your option) any later version.
69
70 This program is distributed in the hope that it will be useful,
71 but WITHOUT ANY WARRANTY; without even the implied warranty of
72 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
73 GNU General Public License for more details.
74
75 You should have received a copy of the GNU General Public License
76 along with this program; if not, write to the Free Software
77 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
78
79=cut
e3d9712e
AJ
80
81#mux-ed format: "slot_num non_muxed_request"
82
83# options handling
84my %opts=();
85$Getopt::Std::STANDARD_HELP_VERSION=1;
86getopts('h', \%opts) or die ("unrecognized options");
87if (defined $opts{h}) {
88 HELP_MESSAGE();
89 exit 0;
90}
91my $actual_helper_cmd=join(" ",@ARGV);
92
93# variables initialization
94my %helpers=();
32fd6d8a 95my $helpers_running = 0;
e3d9712e
AJ
96my $rvec='';
97vec($rvec,0,1)=1; #stdin
98my $nfound;
99my ($rd,$wr,$cl);
100
101# signal handlers
102$SIG{'HUP'}=\&dump_state;
103$SIG{'CHLD'}=\&reaper;
104# TODO: signal handling for child dying
105
106# main loop
107$|=1;
108while(1) {
109 print STDERR "selecting\n";
110 $nfound=select($rd=$rvec,undef,undef,undef);
111 #$nfound=select($rd=$rvec,undef,$cl=$rvec,undef);
112 print STDERR "nfound: $nfound\n";
113 if ($nfound == -1 ) {
114 print STDERR "error in select: $!\n";
115 if ($!{ERESTART} || $!{EAGAIN} || $!{EINTR}) {
116 next;
117 }
118 exit 1;
119 }
120 #print STDERR "cl: ", unpack("b*", $cl) ,"\n";
121 print STDERR "rd: ", unpack("b*", $rd) ,"\n";
122 # stdin is special
123 #if (vec($cl,0,1)==1) { #stdin was closed
124 # print STDERR "stdin closed\n";
125 # exit(0);
126 #}
127 if (vec($rd,0,1)==1) { #got stuff from stdin
128 #TODO: handle leftover buffers? I hope that 40kb are enough..
129 $nread=sysread(STDIN,$_,40960); # read 40kb
130 # clear the signal-bit, stdin is special
131 vec($rd,0,1)=0;
132 if ($nread==0) {
133 print STDERR "nothing read from stdin\n";
134 exit 0;
135 }
32fd6d8a
CT
136 foreach $req (split("\n",$_ )) {
137 dispatch_request($req);
e3d9712e
AJ
138 }
139 }
140 # find out if any filedesc was closed
141 if ($cl != 0) {
142 #TODO: better handle helper restart
143 print STDERR "helper crash?";
144 exit 1;
145 }
146 #TODO: is it possible to test the whole bitfield in one go?
147 # != won't work.
148 foreach $h (keys %helpers) {
149 my %hlp=%{$helpers{$h}};
150 #print STDERR "examining helper slot $h, fileno $hlp{fno}, filemask ", vec($rd,$hlp{fno},1) , "\n";
151 if (vec($rd,$hlp{fno},1)==1) {
152 #print STDERR "found\n";
153 handle_helper_response($h);
154 }
155 #no need to clear, it will be reset when iterating
156 }
157}
158
159sub dispatch_request {
160 my $line=$_[0];
161 my %h;
162
163 #print STDERR "dispatching request $_";
164 $line =~ /^(\d+) (.*)$/;
32fd6d8a 165 my $reqId=$1;
e3d9712e
AJ
166 my $req=$2;
167
32fd6d8a
CT
168 undef $h;
169 # Find a free helper
170 foreach $slot ( 1 .. ($helpers_running)) {
171 if (!defined($helpers{$slot}->{lastcmd})) {
172 $h = $helpers{$slot};
173 last;
174 }
175 }
176 # If none create one
177 if (!defined($h)) {
178 $helpers_running = $helpers_running + 1;
179 $helpers{$helpers_running}=init_subprocess();
180 $h = $helpers{$helpers_running};
181 # print STDERR "Now $helpers_running helpers running\n";
e3d9712e 182 }
32fd6d8a 183
e3d9712e
AJ
184 $wh=$h->{wh};
185 $rh=$h->{rh};
186 $h->{lastcmd}=$req;
32fd6d8a 187 $h->{reqId}=$reqId;
e3d9712e
AJ
188 print $wh "$req\n";
189}
190
191# gets in a slot number having got some response.
192# reads the response from the helper and sends it back to squid
193# prints the response back
194sub handle_helper_response {
195 my $h=$_[0];
196 my ($nread,$resp);
197 $nread=sysread($helpers{$h}->{rh},$resp,40960);
198 #print STDERR "got $resp from slot $h\n";
32fd6d8a 199 print $helpers{$h}->{reqId}, " ", $resp;
e3d9712e
AJ
200 delete $helpers{$h}->{lastcmd};
201}
202
203# a subprocess is a hash with members:
204# pid => $pid
205# rh => read handle
206# wh => write handle
207# fno => file number of the read handle
208# lastcmd => the command "in flight"
209# a ref to such a hash is returned by this call
210sub init_subprocess {
211 my %rv=();
212 my ($rh,$wh,$pid);
213 $pid=open2($rh,$wh,$actual_helper_cmd);
214 if ($pid == 0) {
215 die "Failed to fork helper process";
216 }
217 select($rh); $|=1;
218 select($wh); $|=1;
219 select(STDOUT);
220 $rv{rh}=$rh;
221 $rv{wh}=$wh;
222 $rv{pid}=$pid;
223 $rv{fno}=fileno($rh);
224 print STDERR "fileno is $rv{fno}\n";
225 vec($rvec,$rv{fno},1)=1;
226 return \%rv;
227}
228
229sub HELP_MESSAGE {
230 print STDERR <<EOF
231$0 options:
232 -h this help message
233 arguments:
234 the actual helper executable and its arguments.
235 it's advisable to prefix it with "--" to avoid confusion
236EOF
237}
238
239sub dump_state {
240 $SIG{'HUP'}=\&dump_state;
241 print STDERR "Helpers state:\n",Dumper(\%helpers),"\n";
242}
243
244# finds and returns the slot number of a helper, -1 if not found
245# args: - key in helpers
246# - value to look for
247sub find_helper_slot {
248 my ($k,$v) = @_;
249 foreach (keys %helpers) {
250 return $_ if $helpers{$k}==$v;
251 }
252 return -1;
253}
254
255sub reaper {
256 my $child=wait;
257 print STDERR "child $child died\n";
258 $SIG{'CHLD'}=\&reaper;
259 $slot = find_helper_slot('pid',$child);
260 print STDERR "slot is $slot\n";
261 #TODO: find the died child, if it was mid-process through a request
262 # send a "BH" to squid and de-init its data-structs here
263 exit 1;
264}
265