Nginx : exécution FastCGI Perl

Boris HUISGEN July 16, 2013

administration hébergement nginx fastcgi perl

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;
   }
}

See also

Nginx : FastCGI CGI/Perl par socket UNIX
Read more
PHP : configuration de PHP-FPM
Read more
Nginx : astuces pour un hébergement mutualisé
Read more