ติดตั้ง perl แบบ fast-cgi บน Centos เขียนเมื่อ 2012.08.12 โดย

มี request จาก client ว่าต้องการเปลี่ยน mrtg ไปเป็น Monitorix ที่ละเอียดกว่า ความต้องการสำคัญของ monitorix คือ perl ถ้าเป็นบน apache ก็ติดตั้งง่ายมาก แต่บน libevent base อย่าง Nginx ต้องให้ Interpreter ทั้งหลายทำงานในแบบ fast-cgi จึงต้องจับ perl มาทำงานในแบบ fast-cgi เสียก่อน
เริ่มจากติดตั้ง perl-FCGI
yum install -y perl-FCGI

package นี้จะหาได้จาก EPEL หรือ RepoForge
จากนั้นต้องเขียน wrapper สำหรับรับ request แบบ fast-cgi ให้กับ perl
nano /usr/bin/fastcgi-wrapper.pl

โดยใส่ script ตามนี้
#!/usr/bin/perl

use FCGI;
use Socket;
use POSIX qw(setsid);

require 'syscall.ph';

&daemonize;

#this keeps the program alive or something after exec'ing perl scripts
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( "127.0.0.1:8999", 10 ); #use IP sockets
        $request = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%req_params, $socket );
        if ($request) { request_loop()};
            FCGI::CloseSocket( $socket );
}

sub request_loop {
        while( $request->Accept() >= 0 ) {

           #processing any STDIN input from WebServer (for CGI-POST actions)
           $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;
                }
            }

            #running the cgi app
            if ( (-x $req_params{SCRIPT_FILENAME}) &&  #can I execute this?
                 (-s $req_params{SCRIPT_FILENAME}) &&  #Is this file empty?
                 (-r $req_params{SCRIPT_FILENAME})     #can I read this file?
            ){
        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 - ";
                        print "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};
                    }
                    # cd to the script's local directory
                    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 ";
                print "exist or is not executable by this process.\n";
            }

        }
}

เปลี่ยนให้เป็น executable ด้วย
chmod +x /usr/bin/fastcgi-wrapper.pl

จากนั้นก็เขียน init script เพื่อให้รองรับการทำงานในแบบ service
nano /etc/init.d/perl-fastcgi

โดยใส่ script ตามนี้
#!/bin/sh
#
# nginx – this script starts and stops the nginx daemon
#
# chkconfig: - 85 15
# description: Nginx is an HTTP(S) server, HTTP(S) reverse 
# proxy and IMAP/POP3 proxy server
# processname: nginx
# config: /opt/nginx/conf/nginx.conf
# pidfile: /opt/nginx/logs/nginx.pid

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0

perlfastcgi="/usr/bin/fastcgi-wrapper.pl"
prog=$(basename perl)

lockfile=/var/lock/subsys/perl-fastcgi

start() {
    [ -x $perlfastcgi ] || exit 5
    echo -n $"Starting $prog: "
    daemon $perlfastcgi
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}

stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}

restart() {
    stop
    start
}

reload() {
    echo -n $”Reloading $prog: ”
    killproc $nginx -HUP
    RETVAL=$?
    echo
}

force_reload() {
    restart
}
rh_status() {
    status $prog
}

rh_status_q() {
    rh_status >/dev/null 2>&1
}

case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart)
        $1
        ;;
    reload)
        rh_status_q || exit 7
        $1
        ;;
    force-reload)
        force_reload
        ;;
    status)
        rh_status
        ;;
    condrestart|try-restart)
        rh_status_q || exit 0
        ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
        exit 2
esac

เปลี่ยนให้เป็น executable ด้วย
chmod +x /etc/init.d/perl-fastcgi

ระบบ perl ในรูปแบบ fastcgi พร้อมทำงานแล้ว สั่งเริ่มการทำงานด้วย
service perl-fastcgi start

ตรวจสอบว่ามี port 8999 (เป็น port ค่าเริ่มต้นแก้ไขได้ใน fastcgi-wrapper.pl) เปิดทำงานอยู่หรือไม่
netstat -an | grep 8999

ถ้ามี port เปิดอยู่ก็พร้อมใช้งานโดยสมบูรณ์แล้วครับ
สำหรับ Nginx การตั้งค่าเพื่อให้ทำงาน perl script ได้ เพิ่ม
location ~ \.cgi$ {
    gzip off;
    include fastcgi_params;
    fastcgi_pass  127.0.0.1:8999;
    fastcgi_index index.pl;
    fastcgi_param   PATH_INFO  "/cgi-bin";
    fastcgi_param  SCRIPT_FILENAME  /path/to/cgi-bin$fastcgi_script_name;
}

จะเห็นว่าเหมือน fastcgi ในรูปแบบอื่นๆ คือส่ง script ผ่านไปยัง service port ที่เปิดรองรับการทำงานไว้



คำเตือนคำเตือน เนื้อหาต่างๆ ในบทความ รวมถึงรูปภาพทั้งหมดในบทความนี้ เป็นความเห็นส่วนตัวของผู้เขียนแต่ละคน ซึ่งแต่ละคนได้ทำการลงทะเบียน และเขียนบทความลงใน Modoeye Articles นี้โดยไม่มีค่าธรรมเนียมใดๆ บทความเหล่านี้เป้าหมายเพื่อการศึกษา และความบันเทิงเท่านั้น การนำส่วนหนึ่งส่วนใดของบทความไปใช้งาน ควรทำการอ้างอิงถึงผู้เขียนและแหล่งที่มาด้วย