Nginx : exécution FastCGI Perl

Boris HUISGEN
Boris HUISGEN
|

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;
   }
}
Boris HUISGEN
Boris HUISGEN
Blog owner
  • #fastcgi
  • #nginx
  • #perl