# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: kinkie@squid-cache.org-20091126085544-5nra5e7mx7zlnjyu # target_branch: file:///media/src/squid/repo/squid-trunk/ # testament_sha1: 2f024cf11fcbc92407229d841fad5e6cbb676d14 # timestamp: 2009-11-26 09:59:20 +0100 # base_revision_id: kinkie@squid-cache.org-20091117234802-\ # inelwy41iv2pynwg # # Begin patch === added file 'tools/helper-mux.README' --- tools/helper-mux.README 1970-01-01 00:00:00 +0000 +++ tools/helper-mux.README 2009-10-11 18:40:50 +0000 @@ -0,0 +1,16 @@ +The helper multiplexer's purpose is to relieve some of the burden +squid has when dealing with slow helpers. It does so by acting as a +middleman between squid and the actual helpers, talking to squid via +the multiplexed variant of the helper protocol and to the helpers +via the non-multiplexed variant. + +Helpers are started on demand, and in theory the muxer can handle up to +1k helpers per instance. It's up to squid to decide how many helpers +to start. + +The muxer knows nothing about the actual messages being passed around, +and as such can't really (yet?) compensate for broken helpers. +It is not yet able to manage dying helpers, but it will. + +The helper can be controlled using various signals: +- SIGHUP: dump the state of all helpers to STDERR === added file 'tools/helper-mux.pl' --- tools/helper-mux.pl 1970-01-01 00:00:00 +0000 +++ tools/helper-mux.pl 2009-10-11 21:48:13 +0000 @@ -0,0 +1,206 @@ +#!/usr/bin/perl +# helper multiplexer. Talks to squid using the multiplexed variant of +# the helper protocol, and maintains a farm of synchronous helpers +# helpers are lazily started, as many as needed. +# see helper-mux.README for further informations +# +# AUTHOR: Francesco Chemolli +# +# SQUID Web Proxy Cache http://www.squid-cache.org/ +# ---------------------------------------------------------- +# +# Squid is the result of efforts by numerous individuals from +# the Internet community; see the CONTRIBUTORS file for full +# details. Many organizations have provided support for Squid's +# development; see the SPONSORS file for full details. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + +use Getopt::Std; +use Data::Dumper; +use FileHandle; +use IPC::Open2; + +#mux-ed format: "slot_num non_muxed_request" + +# options handling +my %opts=(); +$Getopt::Std::STANDARD_HELP_VERSION=1; +getopts('h', \%opts) or die ("unrecognized options"); +if (defined $opts{h}) { + HELP_MESSAGE(); + exit 0; +} +my $actual_helper_cmd=join(" ",@ARGV); + +# variables initialization +my %helpers=(); +my $rvec=''; +vec($rvec,0,1)=1; #stdin +my $nfound; +my ($rd,$wr,$cl); + +# signal handlers +$SIG{'HUP'}=\&dump_state; +$SIG{'CHLD'}=\&reaper; +# TODO: signal handling for child dying + +# main loop +$|=1; +while(1) { + print STDERR "selecting\n"; + $nfound=select($rd=$rvec,undef,undef,undef); + #$nfound=select($rd=$rvec,undef,$cl=$rvec,undef); + print STDERR "nfound: $nfound\n"; + if ($nfound == -1 ) { + print STDERR "error in select: $!\n"; + if ($!{ERESTART} || $!{EAGAIN} || $!{EINTR}) { + next; + } + exit 1; + } + #print STDERR "cl: ", unpack("b*", $cl) ,"\n"; + print STDERR "rd: ", unpack("b*", $rd) ,"\n"; + # stdin is special + #if (vec($cl,0,1)==1) { #stdin was closed + # print STDERR "stdin closed\n"; + # exit(0); + #} + if (vec($rd,0,1)==1) { #got stuff from stdin + #TODO: handle leftover buffers? I hope that 40kb are enough.. + $nread=sysread(STDIN,$_,40960); # read 40kb + # clear the signal-bit, stdin is special + vec($rd,0,1)=0; + if ($nread==0) { + print STDERR "nothing read from stdin\n"; + exit 0; + } + foreach $req (split("\n",$_)) { + dispatch_request($_); + } + } + # find out if any filedesc was closed + if ($cl != 0) { + #TODO: better handle helper restart + print STDERR "helper crash?"; + exit 1; + } + #TODO: is it possible to test the whole bitfield in one go? + # != won't work. + foreach $h (keys %helpers) { + my %hlp=%{$helpers{$h}}; + #print STDERR "examining helper slot $h, fileno $hlp{fno}, filemask ", vec($rd,$hlp{fno},1) , "\n"; + if (vec($rd,$hlp{fno},1)==1) { + #print STDERR "found\n"; + handle_helper_response($h); + } + #no need to clear, it will be reset when iterating + } +} + +sub dispatch_request { + my $line=$_[0]; + my %h; + + #print STDERR "dispatching request $_"; + $line =~ /^(\d+) (.*)$/; + my $slot=$1; + my $req=$2; + + if (!exists($helpers{$slot})) { + $helpers{$slot}=init_subprocess(); + } + $h=$helpers{$slot}; + $wh=$h->{wh}; + $rh=$h->{rh}; + $h->{lastcmd}=$req; + print $wh "$req\n"; +} + +# gets in a slot number having got some response. +# reads the response from the helper and sends it back to squid +# prints the response back +sub handle_helper_response { + my $h=$_[0]; + my ($nread,$resp); + $nread=sysread($helpers{$h}->{rh},$resp,40960); + #print STDERR "got $resp from slot $h\n"; + print $h, " ", $resp; + delete $helpers{$h}->{lastcmd}; +} + +# a subprocess is a hash with members: +# pid => $pid +# rh => read handle +# wh => write handle +# fno => file number of the read handle +# lastcmd => the command "in flight" +# a ref to such a hash is returned by this call +sub init_subprocess { + my %rv=(); + my ($rh,$wh,$pid); + $pid=open2($rh,$wh,$actual_helper_cmd); + if ($pid == 0) { + die "Failed to fork helper process"; + } + select($rh); $|=1; + select($wh); $|=1; + select(STDOUT); + $rv{rh}=$rh; + $rv{wh}=$wh; + $rv{pid}=$pid; + $rv{fno}=fileno($rh); + print STDERR "fileno is $rv{fno}\n"; + vec($rvec,$rv{fno},1)=1; + return \%rv; +} + +sub HELP_MESSAGE { + print STDERR <) { + print "OK\n"; +} +print STDERR "stdin closed, exit\n"; === added file 'tools/helper-ok.pl' --- tools/helper-ok.pl 1970-01-01 00:00:00 +0000 +++ tools/helper-ok.pl 2009-11-26 08:54:06 +0000 @@ -0,0 +1,8 @@ +#!/usr/bin/perl + +$|=1; +while (<>) { + sleep 10; + print "OK\n"; +} +print STDERR "stdin closed, exit\n"; # Begin bundle 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 4u5IpwoSD6rbwwA=