Dies ist ein Template für den Skripte-Unterbereich
InhaltPerlSkripteWeb.
Zuverläassiges laden von FastCGI? Scripten bei lighttpd
Name
include_fcgi.pl
Aufgabe
Ich sehe mich immer wieder damit konfrontiert einen lighttpd Server mit mehreren
FastCGI? Scripten auf zu setzen.
Der gebräuchliche Ansatz hier ist es ein Programm wie
spawn-fcgi zu benutzen, das als Deamon mit lighttpd kommuniziert. Die Vorteile sind da dass die Scripte nicht die selben Benutzer Rechte haben müssen wie der Server, dass der Server unabhängig von den Scripten neu gestartet werden kann und die Scripte gut überwacht werden können (z.B. Neustart bei Updates vom Script)
Der Nachteil ist aber, das bei einem Fehler im Deamon kein Script mehr Erreichbar da die Kommunikation von allen Scripten über ein Socket abläuft (wobei es egal ist ob es ein
FileSocket? oder
NetSocket? ist). Dies führt zudem unter Last manchmal dazu, dass Aktionen verzögert werden, weil das Socket gerade von einem Anderen benutzt wird. Besonders langsame und nicht sehr durchsatzstarke Systeme kommen hier schnell an ihre Grenzen.
Es ist natürlich möglich mehrere Deamons zu starten, die nur wenige Scripte verwalten. Der Konfigurationsaufwand steigt dabei aber. (und damit auch wie Wahrscheinlichkeit Fehler zu machen)
Einfacher ist es , so finde ich, beim Start des Servers alle
FastCGI?-Scripte zu suchen und mit jeweils einem Socket zu versehen und die Verwaltung direkt dem lighttpd-Server zu überlassen. Dieser hat die Fähigkeit beendete
FastCGI?-Scripte neu zu starten oder mehrere Scriptinstanzen auf einem Socket zu verwalten. Die fehlenden Updatemöglichkeiten lassen sich ohne großen Aufwand im Script selber implementieren und sogar besser lösen als es über ein externes Programm möglich wäre. Dabei laufen die Scripte natürlich mit den selben Rechten wie der Server. Aber dafür entfällt ein Programm das gepflegt und aktuell gehalten werden muss und kann es zu keinen Engpässen beim Datentransfer kommen. Gerade auf langsamen Maschinen ist das ein Vorteil. Außerdem kommt es nicht zu Störungen zwischen verschiedenen Scripten, wie es bei machen Implementation von
FastCGI?-Deamons vorkommt.
Es ist natürlich mühsam von Hand die
FastCGI?-Sripte in die Konfiguration einzutragen und auf Tippfehler zu achten und die für jedes umbenannte oder neue Script wiederholen zu müssen. Das kann besonders nervig werden, wenn man einen neuen Server einrichtet. Darum schrieb ich dieses Script. Die Lighttpd-Konfiguartion kennt die Option "
include_shell" die wird benutzt um über Scripte eine Konfiguration zu generieren. Ich nutze sie um ein Perlscript auszuführen, das
FastCGI? Scripte nach gewissen Kategorien sucht und die passende Konfiguration erstellt. Das erlaubt es durch einfaches einbinden dieses Scriptes alle
FastCGI? Scripte beim Serverstart zu finden und zu laden. Das beschleunigt und vereinfacht das einrichten eines Servers mit
FastCGI? Scripten enorm.
Aufruf mit Parametern
Einbetten in die lighttpd Konfiguration:
include_shell "/usr/local/lib/lighttpd/include_fcgi.pl"
Optionen, die an das Script übergeben werden können:
--config_file => Pfad zur lighttpd.conf
wenn der wert nicht gesetzt ist versucht das Script eine Konfiguration zu finden
--suffix => Dateiendung für FastCGIs
wenn nichts gesetzt, dann wird jede gefundene ausführbare Datei als FastCGI erkannt.
--bin_path => Suchpfad für FastCGIs
wenn es kein absoluter Pfad ist, dann wird "document_root" als Basispfad genommen
--max_process => Maximale Anzahl der Prozesse die ein CGI-Script parallel haben darf
Default ist 1
--add => Zusätzliche Argumente
--tmp => Verzeichnis in welche die Socket der FastCGIs abgelegt werden
wenn nichts gesetzt nimmt das Scipt den "restbesten" (siehe "tempdir" in File::Spec)
Skript
<pre> =#!/usr/bin/perl
use strict;
use warnings;
use File::Find;
use File::Spec;
use Getopt::Long qw(:config pass_through);
#map{ print STDERR "$_ => $ENV{$_}\n" }keys(%ENV);
#----------------------------------------------------------------------#
# configuartions
# main config file of lighttpd
# needed to determin "username" "groupname" and "document_root"
my @conf=qw();
# temp dir
my $tmp=File::Spec->tmpdir();
# seach only here for perl scrips
my $bin_path= 'fcgi-bin';
# file suffix
my @suffix = qw();
# max processes of the script allowed
my $max_process=1;
# additional options
my $add='';
my @defult_confs=qw(/etc/lighttpd.conf /etc/lighttpd/lighttpd.conf /usr/lib/lighttpd/lighttpd.conf /usr/local/lib/lighttpd/lighttpd.conf);
#----------------------------------------------------------------------#
# script
GetOptions (
"config_file=s" => \@conf,
"suffix=s" => \@suffix,
"bin_path=s" => \$bin_path,
"max_process=i" => \$max_process,
"add=s" =>\$add,
"tmp=s" =>\$tmp,
) || exit;
#find a config file if no defined.
# ( CWD is set to conf path )
if(!@conf)
{
my $first='';
for(@defult_confs)
{ $first=$_ and last if(-f $_) };
find(sub{
if($File::Find::name=~/\.conf$/)
{
my $fname=(File::Spec->splitpath())[2];
if($fname=~/lighttpd\.conf/)
{
if($first)
{ unshift(@conf,$File::Find::name); }
else
{ $first=$File::Find::name; }
}
elsif($fname=~/lighttpd\.conf/)
{ unshift(@conf,$File::Find::name); }
elsif($File::Find::name=~/lighttpd/)
{ push(@conf,$File::Find::name); }
}
},File::Spec->rel2abs('.'));
unshift(@conf,$first) if($first);
}
# read config to find doc-root user and group
my $usr='';
my $grp='';
my $base='';
{
OUTER:for(@conf)
{
open(my $fh, '<', $_) or exit;
while(my $l=<$fh>)
{
$base=$1 if($l=~m!^\s*server.document-root\s*=\s*"([^"]+)"!s);
$usr=$1 if($l=~m!^\s*server.username\s*=\s*"([^"]+)"!s);
$grp=$1 if($l=~m!^\s*server.groupname\s*=\s*"([^"]+)"!s);
last OUTER if($base && $usr && $grp);
}
close($fh);
}
}
exit unless($base && -d $base && $usr && $grp);
# get ids for user and group
my $uid = getpwnam($usr);
my $gid = getgrnam($grp);
exit unless(defined($uid) && defined($gid));
# find files
# files have to be executable by process
my %files=();
find(sub{
if(!@suffix || grep{$File::Find::name=~/\Q$_\E$/}@suffix)
{
my $fname=$File::Find::name;
my $rname=File::Spec->abs2rel($fname,$base);
$rname=~s!\\!/!gs;
$rname="/$rname";
my ($fperm,$fuid,$fgid)=(stat($fname))[2,4,5];
$fperm &= 07777;
if(-f $fname && (($fuid == $uid && $fperm & 00100) || ($fgid == $gid && $fperm & 00010) || $fperm & 00001) )
{ $files{$fname}=$rname; }
}
},File::Spec->rel2abs($base,$bin_path));
exit unless(%files);
# load mod_fastcgi
print qq#server.modules += ( "mod_fastcgi" )\n#;
# for each found script create a configuration.
my $cnt=0;
while(my($f,$n)=each(%files))
{
my $tmp_file=File::Spec->join($tmp,"fastpl.$cnt.socket");
print <<"EOC";
fastcgi.server += ( "$n" =>
((
"bin-path" => "$f",
"socket" => "$tmp_file",
"max_process" => $max_process,
"bin-copy-environment" => (
"PATH", "SHELL", "USER"
),
"broken-scriptfilename" => "enable"
$add
))
)
EOC
$cnt++;
}= </pre>
Ergänzungen, Kommentare
Kommentare werden am besten in folgender Form vorgenommen, damit sie im Inhaltsverzeichnis angezeigt werden (natürlich ohne das <verbatim>):
---### Main.??? - 14 Jul 2003 - Betreff