]>
Commit | Line | Data |
---|---|---|
33d1abfe | 1 | #!@PERL@ |
e3d9712e AJ |
2 | |
3 | use Getopt::Std; | |
4 | use Data::Dumper; | |
5 | use FileHandle; | |
6 | use IPC::Open2; | |
33d1abfe AJ |
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 | ||
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 | |
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=(); | |
32fd6d8a | 95 | my $helpers_running = 0; |
e3d9712e AJ |
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 | } | |
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 | ||
159 | sub 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 | |
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"; | |
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 | |
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 |