Suite à la configuration du support PHP sous Nginx, il m’a été
nécessaire de pouvoir exécuter des scripts CGI et Perl.
Comme avec PHP, la configuration s’appuie sur le module FastCGI
de Nginx. Par contre côté interpréteur, spawn-fcgi ne peut pas être utilisé. Un interpréteur Perl doit donc être préchargé et en attente des requêtes clients depuis sa socket (au choix : UNIX ou TCP si l’exécution doit être locale ou déportée).
Voici le script de l’interpréteur perl-wrapper.pl :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
#!/usr/bin/perl
use FCGI;
use Socket;
use POSIX qw(setsid);
require 'syscall.ph';
&daemonize;
END() { } BEGIN() { }
*CORE::GLOBAL::exit = sub { die "fakeexit\nrc=".shift()."\n"; };
eval q{exit};
if ($@) {
exit unless $@ =~ /^fakeexit/;
};
&main;
sub daemonize() {
chdir '/' or die "Can't chdir to /: $!";
defined(my $pid = fork) or die "Can't fork: $!";
exit if $pid;
setsid or die "Can't start a new session: $!";
umask 0;
}
sub main {
$socket = FCGI::OpenSocket( "/tmp/fcgi-perl.sock", 10 );
$request = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%req_params, $socket );
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;
}
cose(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";
}
}
}
|
Ensuite, le script de startup fcgi-perl.sh :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
#!/bin/sh
#
# Boris HUISGEN <bhuisgen@hbis.fr>
#
PROVIDES=fcgi-perl
WRAPPER=/usr/local/etc/nginx/perl-wrapper.pl
SOCKET=/tmp/fcgi-perl.sock
SOCKET_USER=www
SOCKET_GROUP=www
SOCKET_PERMS=600
# script
cmd=$1
pcgi_start(){
echo "Starting $PROVIDES..."
perl -I /usr/local/lib/perl5/site_perl/5.8.9/mach/sys/ $WRAPPER &
until [ -S $SOCKET ]
do
sleep 1
done
chown $SOCKET_USER:$SOCKET_GROUP $SOCKET
chmod $SOCKET_PERMS $SOCKET
}
pcgi_stop(){
echo "Stopping $PROVIDES..."
pkill -f $WRAPPER
rm $SOCKET
}
pcgi_restart(){
pcgi_stop
pcgi_start
}
pcgi_status(){
[ -S $SOCKET ] && echo "$PROVIDES running" || echo "$PROVIDES NOT running"
}
pcgi_help(){
echo "Usage: $0 {start|stop|restart|status}"
}
case ${cmd} in
[Ss][Tt][Aa][Rr][Tt]) pcgi_start;;
[Ss][Tt][Oo][Pp]) pcgi_stop;;
[Rr][Ee][Ss][Tt][Aa][Rr][Tt]) pcgi_restart;;
[Ss][Tt][Aa][Tt][Uu][Ss]) pcgi_status 0;;
*) pcgi_help ;;
esac
|
Le support CGI/Perl au niveau de Nginx est calqué sur celui de PHP : un mapping d’extension + un nom de socket :
server {
listen 80;
server_name cgi.site.fr;
location / {
root /home/www/site.fr/cgi-bin;
index index.cgi;
}
location ~ \.cgi$ {
fastcgi_pass unix:/tmp/fcgi-perl.sock;
fastcgi_index index.cgi;
fastcgi_param HTTP_ACCEPT_ENCODING gzip,deflate;
fastcgi_param SCRIPT_FILENAME /home/www/site.fr/cgi-bin/$fastcgi_script_name;
include fastcgi_params;
}
}
Pour détecter tout problème d’exécution, lancez le wrapper perl en console et vérifiez que le module perl-sys-syscall
(devel/p5-Sys-Syscall) est installé. Si le wrapper ne parvient pas à trouver le fichier syscall.ph, il faut le générer :
# cd /usr/include
# h2ph * sys/*
Le fichier va être ajouté dans un nouveau répertoire, pour ma part /usr/local/lib/perl5/site_perl/5.8.9/mach/sys/,
que j’ajoute en option à Perl dans le script de startup. Ces modifications seront à effectuer en concordance avec votre
installation.