Nginx : exécution FastCGI Perl
Boris HUISGEN July 16, 2013
Je vous joins ici la dernière version des scripts que j’utilise pour permettre l’exécution Perl en FastCGI avec Nginx (pour SmokePing, BackupPC, etc …)
Le script du démon Perl fcgi-perl.pl est à adapter à votre environnement système (utilisateur d’exécution, chemin de la socket UNIX). Le script de démarrage est à présent intégrable avec Heartbeat/Corosync …
Dépendances logicielles
root@muse:~# aptitude install libfcgi-perl libfile-pid-perl
Script FastCGI Perl
root@muse:~# cat /etc/nginx/fcgi-perl.pl
#!/usr/bin/perl
#
# FastCGI perl daemon
#
# Boris HUISGEN <bhuisgen@hbis.fr>
#
my ($pid_file) = "/var/run/fcgi-perl.pid"; # PID filename
my ($socket_file) = "/var/run/fcgi-perl.sock"; # UNIX socket filename
my ($socket_backlog) = 10; # socket backlog
my ($socket_uid) = 33; # socket UID
my ($socket_gid) = 33; # socket GID
my ($socket_perms) = 0600; # socket permissions
#
# script
#
use File::Pid;
use FCGI;
use Socket;
use POSIX qw(setsid);
require 'syscall.ph';
my ($pid_fd);
&daemonize;
*CORE::GLOBAL::exit = sub { die "fakeexit\nrc=".shift()."\n"; };
eval q{exit};
if ($@) {
exit unless $@ =~ /^fakeexit/;
};
&main;
sub cleanup() {
$pid_fd->remove if defined $pid_fd;
unlink($socket_file);
}
sub daemonize() {
chdir '/' or die "Failed to chdir to /: $!";
defined(my $pid = fork) or die "Failed to fork: $!";
exit if $pid;
setsid or die "Failed to start a new session: $!";
umask 0022;
$pid_fd = File::Pid->new({
file => $pid_file,
});
if ($pid_fd->running) { die "Instance already running" };
$pid_fd->write;
}
sub main {
$socket = FCGI::OpenSocket($socket_file, $socket_backlog);
$request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%req_params, $socket);
chown($socket_uid, $socket_gid, $socket_file);
chmod($socket_perms, $socket_file);
if ($request) {
request_loop()
};
FCGI::CloseSocket($socket);
}
sub request_loop {
while($request->Accept() >= 0) {
$stdin_passthrough ='';
$req_len = 0 + $req_params{'CONTENT_LENGTH'};
if (($req_params{'REQUEST_METHOD'} eq 'POST') && ($req_len != 0) ){
my $bytes_read = 0;
while ($bytes_read < $req_len) {
my $data = '';
my $bytes = read(STDIN, $data, ($req_len - $bytes_read));
last if ($bytes == 0 || !defined($bytes));
$stdin_passthrough .= $data;
$bytes_read += $bytes;
}
}
if ( (-x $req_params{SCRIPT_FILENAME}) &&
(-s $req_params{SCRIPT_FILENAME}) &&
(-r $req_params{SCRIPT_FILENAME}) ){
pipe(CHILD_RD, PARENT_WR);
my $pid = open(KID_TO_READ, "-|");
unless(defined($pid)) {
print "Content-type: text/plain\r\n\r\n";
print "Error: CGI app returned no output - Executing $req_params{SCRIPT_FILENAME} failed !\n";
next;
}
if ($pid > 0) {
close(CHILD_RD);
print PARENT_WR $stdin_passthrough;
close(PARENT_WR);
while(my $s = <KID_TO_READ>) { print $s; }
close KID_TO_READ;
waitpid($pid, 0);
} else {
foreach $key ( keys %req_params){
$ENV{$key} = $req_params{$key};
}
if ($req_params{SCRIPT_FILENAME} =~ /^(.*)\/[^\/]+$/) {
chdir $1;
}
close(PARENT_WR);
close(STDIN);
#fcntl(CHILD_RD, F_DUPFD, 0);
syscall(&SYS_dup2, fileno(CHILD_RD), 0);
#open(STDIN, "<&CHILD_RD");
exec($req_params{SCRIPT_FILENAME});
die("exec failed");
}
}
else {
print "Content-type: text/plain\r\n\r\n";
print "Error: No such CGI app - $req_params{SCRIPT_FILENAME} may not exist or is not executable by this process.\n";
}
}
}
END {
$pid_fd->remove if defined $pid_fd;
unlink($socket_file);
}
Script de démarrage init.d
root@muse:~# cat /etc/init.d/fcgi-perl
#! /bin/sh
### BEGIN INIT INFO
# Provides: fcgi-perl
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: fcgi-perl
# Description: start the fcgi-perl daemon
### END INIT INFO
# Author: Boris HUISGEN <bhuisgen@hbis.fr>
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC=fcgi-perl
NAME=fcgi-perl
DAEMON=/etc/nginx/$NAME.pl
PIDFILE=/var/run/fcgi-perl.pid
SOCKETFILE=/var/run/fcgi-perl.sock
[ -x "$DAEMON" ] || exit 0
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
. /lib/init/vars.sh
. /lib/lsb/init-functions
do_start()
{
[ -e $PIDFILE ] && return 1
echo "Starting $PROVIDES..."
$DAEMON &
[ ! -e $PIDFILE ] && sleep 1
[ ! -e $PIDFILE ] && return 2
return 0
}
do_stop()
{
[ ! -e $PIDFILE ] && return 1
echo "Stopping $PROVIDES..."
kill `cat $PIDFILE`
rm -f $PIDFILE
rm -f $SOCKETFILE
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc -p $PIDFILE "$DAEMON" "$NAME" && exit 0 || exit $?
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;;
*) log_end_msg 1 ;;
esac
;;
*)
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
exit 3
;;
esac
:
Configuration Nginx
server {
listen 80;
server_name localhost;
location / {
root /var/www/default;
index index.cgi index.html;
}
location ~ \.cgi$ {
fastcgi_pass unix:/var/run/fcgi-perl.sock;
fastcgi_index index.cgi;
fastcgi_param HTTP_ACCEPT_ENCODING gzip,deflate;
include fastcgi_params;
}
}