]> git.ipfire.org Git - thirdparty/squid.git/blob - tools/helper-mux/helper-mux.pl.in
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / tools / helper-mux / helper-mux.pl.in
1 #!@PERL@
2
3 use Getopt::Std;
4 use Data::Dumper;
5 use FileHandle;
6 use IPC::Open2;
7 use Pod::Usage;
8
9 =pod
10
11 =head1 NAME
12
13 helper-mux - Concurrency protocol multiplexer for Squid helpers
14
15 =head1 SYNOPSIS
16
17 B<helper-mux> helper-path [helper-options ...]
18
19 =head1 DESCRIPTION
20
21 B<helper-mux> purpose is to relieve some of the burden
22 B<squid> has when dealing with slow helpers. It does so by acting as a
23 middleman between B<squid> and the actual helpers, talking to B<squid> via
24 the multiplexed variant of the helper protocol and to the helpers
25 via the non-multiplexed variant.
26
27 Helpers are started on demand, and in theory the muxer can handle up to
28 1k helpers per instance. It is up to B<squid> to decide how many helpers
29 to start.
30
31 The 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
40 Path to the helper being multiplexed.
41
42 =item B<helper-options>
43
44 Command line options for the helper being multiplexed.
45
46 =back
47
48 =head1 KNOWN ISSUES
49
50 B<helper-mux> knows nothing about the actual messages being passed around,
51 and as such cannot yet compensate for broken helpers.
52
53 It is not yet able to manage dying helpers.
54
55 =head1 COPYRIGHT
56
57 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
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
80
81 #mux-ed format: "slot_num non_muxed_request"
82
83 # options handling
84 my %opts=();
85 $Getopt::Std::STANDARD_HELP_VERSION=1;
86 getopts('h', \%opts) or die ("unrecognized options");
87 if (defined $opts{h}) {
88 HELP_MESSAGE();
89 exit 0;
90 }
91 my $actual_helper_cmd=join(" ",@ARGV);
92
93 # variables initialization
94 my %helpers=();
95 my $helpers_running = 0;
96 my $rvec='';
97 vec($rvec,0,1)=1; #stdin
98 my $nfound;
99 my ($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;
108 while(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 }
136 foreach $req (split("\n",$_ )) {
137 dispatch_request($req);
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
159 sub dispatch_request {
160 my $line=$_[0];
161 my %h;
162
163 #print STDERR "dispatching request $_";
164 $line =~ /^(\d+) (.*)$/;
165 my $reqId=$1;
166 my $req=$2;
167
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";
182 }
183
184 $wh=$h->{wh};
185 $rh=$h->{rh};
186 $h->{lastcmd}=$req;
187 $h->{reqId}=$reqId;
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
194 sub 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";
199 print $helpers{$h}->{reqId}, " ", $resp;
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
210 sub 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
229 sub 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
236 EOF
237 }
238
239 sub 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
247 sub find_helper_slot {
248 my ($k,$v) = @_;
249 foreach (keys %helpers) {
250 return $_ if $helpers{$k}==$v;
251 }
252 return -1;
253 }
254
255 sub 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