分类: LINUX
2013-04-17 15:45:49
原文地址:nginx支持cgi 作者:飞鸿无痕
nginx支持cgi
想在现有环境LNMP上部署一下Nagios,但是发现默认情况下Nginx自身是无法解析外部CGI程序的,于是官方给出了一个这样的做法,通过研究发现有两种方式可以实现让nginx支持cgi,两种方式都需要安装那几包.下面分别来介绍:
第一种方式操作步骤:
一、安装perl库
perl-FCGI、perl-FCGI-ProcManager、perl-IO-ALL(如下文件均可以去下载)
#wget
#tar zxvf FCGI-0.67.tar.gz
#cd FCGI-0.67
#perl Makefile.PL
#make && make install
#wget /CPAN/authors/id/B/BO/BOBTFISH/FCGI-ProcManager-0.24.tar.gz
#tar zxvf .tar.gz
#cd FCGI-ProcManager-0.24
#perl Makefile.PL
#make && make install
#
#tar zxvf IO-All-0.39.tar.gz
#cd IO-All-0.39
#perl Makefile.PL
#make && make install
二、生成perl守护进程
#vi nginx-cgi.pl,然后加入如下内容
#!/usr/bin/perl use FCGI; use Socket; use FCGI::ProcManager; sub shutdown { FCGI::CloseSocket($socket); exit; } sub restart { FCGI::CloseSocket($socket); &main; } use sigtrap 'handler', \&shutdown, 'normal-signals'; use sigtrap 'handler', \&restart, 'HUP'; require 'syscall.ph'; use POSIX qw(setsid);
END() { } BEGIN() { } { no warnings; *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 { $proc_manager = FCGI::ProcManager->new( {n_processes => 5} ); $socket = FCGI::OpenSocket( "127.0.0.1:8999", 10 ) ; #use UNIX sockets - user running this script must have w access to the 'nginx' folder!! $request = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR, \%req_params, $socket, &FCGI::FAIL_ACCEPT_ON_INTR ); $proc_manager->pm_manage(); if ($request) { request_loop() } FCGI::CloseSocket($socket); }
sub request_loop { while ( $request->Accept() >= 0 ) { $proc_manager->pm_pre_dispatch();
#processing any STDIN input from WebServer (for CGI-POST actions) $stdin_passthrough = ''; { no warnings; $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 ); pipe( PARENT_ERR, CHILD_ERR ); my $pid = open( CHILD_O, "-|" ); 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; } $oldfh = select(PARENT_ERR); $| = 1; select(CHILD_O); $| = 1; select($oldfh); if ( $pid> 0 ) { close(CHILD_RD); close(CHILD_ERR); print PARENT_WR $stdin_passthrough; close(PARENT_WR); $rin = $rout = $ein = $eout = ''; vec( $rin, fileno(CHILD_O), 1 ) = 1; vec( $rin, fileno(PARENT_ERR), 1 ) = 1; $ein = $rin; $nfound = 0;
while ( $nfound = select( $rout = $rin, undef, $ein = $eout, 10 ) ) { die "$!" unless $nfound != -1; $r1 = vec( $rout, fileno(PARENT_ERR), 1 ) == 1; $r2 = vec( $rout, fileno(CHILD_O), 1 ) == 1; $e1 = vec( $eout, fileno(PARENT_ERR), 1 ) == 1; $e2 = vec( $eout, fileno(CHILD_O), 1 ) == 1;
if ($r1) { while ( $bytes = read( PARENT_ERR, $errbytes, 4096 ) ) { print STDERR $errbytes; } if ($!) { $err = $!; die $!; vec( $rin, fileno(PARENT_ERR), 1 ) = 0 unless ( $err == EINTR or $err == EAGAIN ); } } if ($r2) { while ( $bytes = read( CHILD_O, $s, 4096 ) ) { print $s; } if ( !defined($bytes) ) { $err = $!; die $!; vec( $rin, fileno(CHILD_O), 1 ) = 0 unless ( $err == EINTR or $err == EAGAIN ); } } last if ( $e1 || $e2 ); } close CHILD_RD; close PARENT_ERR; 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(PARENT_ERR); close(STDIN); close(STDERR);
#fcntl(CHILD_RD, F_DUPFD, 0); syscall( &SYS_dup2, fileno(CHILD_RD), 0 ); syscall( &SYS_dup2, fileno(CHILD_ERR), 2 );
#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"; } } } |
#chmod 777 nginx-cgi.pl
#/usr/local/nginx/sbin/nginx-cgi.pl >/dev/null & //启动FCGI
#echo "/usr/local/nginx/sbin/nginx-cgi.pl >/dev/null &" >>/etc/rc.local
注:FCGI监听地址为127.0.0.1:8999,如想改变可以在nginx-cgi.pl内修改
三、配置Nginx
Nginx配置如下
server { listen 80; index index.phpindex.cgi; server_name blog.carl.com; root /usr/local/nginx/html; location ~ \.php$ { fastcgi_pass unix:/usr/local/nginx/php-cgi.sock; fastcgi_indexindex.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~ \.cgi$ { root /usr/local/nginx/html; fastcgi_pass 127.0.0.1:8999; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } |
四、测试
#vi /usr/local/nginx/html/test.cgi
#!/usr/bin/perl print "Content-type: text/html\n\n"; print "Hello, world. "; |
然后打开浏览器访问,如果出现内容为Hello,world.则说明配置成功。
第二种方式:
vi /usr/local/nginx/perl-fcgi.pl
#!/usr/bin/perl
#
# author Daniel Dominik Rudnicki
# thanks to: Piotr Romanczuk
# email daniel@sardzent.org
# version 0.4.3
# webpage
#
# BASED @
#
#
# use strict;
use FCGI;
use Getopt::Long;
use IO::All;
use Socket;
sub init {
GetOptions( "h" => \$help,
"verbose!"=>\$verbose,
"pid=s" => \$filepid,
"l=s" => \$logfile,
"S:s" => \$unixsocket,
"P:i" => \$unixport) or usage();
usage() if $help;
print " Starting Nginx-fcgi\n" if $verbose;
print " Running with $> UID" if $verbose;
print " Perl $]" if $verbose;
if ( $> == "0" ) {
print "\n\tERROR\tRunning as a root!\n";
print "\tSuggested not to do so !!!\n\n";
exit 1;
}
if ( ! $logfile ) {
print "\n\tERROR\t log file must declared\n"
. "\tuse $0 with option -l filename\n\n";
exit 1;
}
print " Using log file $logfile\n" if $verbose;
"\n\n" >> io($logfile);
addlog($logfile, "Starting Nginx-cfgi");
addlog($logfile, "Running with $> UID");
addlog($logfile, "Perl $]");
addlog($logfile, "Testing socket options");
if ( ($unixsocket && $unixport) || (!($unixsocket) && !($unixport)) ) {
print "\n\tERROR\tOnly one option can be used!\n";
print "\tSuggested (beacuse of speed) is usage UNIX socket -S \n\n";
exit 1;
}
if ($unixsocket) {
print " Daemon listening at UNIX socket $unixsocket\n" if $versbose;
addlog($logfile, "Deamon listening at UNIX socket $unixsocket");
} else {
print " Daemon listening at TCP/IP socket *:$unixport\n" if $verbose;
#
addlog($logfile, "Daemon listening at TCP/IP socket *:$unixport");
}
if ( -e $filepid ) {
print "\n\tERROR\t PID file $filepid already exists\n\n";
addlog($logfile, "Can not use PID file $filepid, already exists.");
exit 1;
}
if ( $unixsocket ) {
print " Creating UNIX socket\n" if $verbose;
$socket = FCGI::OpenSocket( $unixsocket, 10 );
if ( !$socket) {
print " Couldn't create socket\n";
addlog($logfile, "Couldn't create socket");
exit 1;
}
print " Using UNIX socket $unixsocket\n" if $verbose;
} else {
print " Creating TCP/IP socket\n" if $verbose;
$portnumber = ":".$unixport;
$socket = FCGI::OpenSocket( $unixport, 10 );
if ( !$socket ) {
print " Couldn't create socket\n";
addlog($logfile, "Couldn't create socket");
exit 1;
}
print " Using port $unixport\n" if $verbose;
}
addlog($logfile, "Socket created");
if ( ! $filepid ) {
print "\n\tERROR\t PID file must declared\n"
. "\tuse $0 with option -pid filename\n\n";
exit 1;
}
print " Using PID file $filepid\n" if $verbose;
addlog($logfile, "Using PID file $filepid");
my $pidnumber = $$;
$pidnumber > io($filepid);
print " PID number $$\n" if $verbose;
addlog($logfile, "PID number $pidnumber");
}
sub addzero {
my ($date) = shift;
if ($date < 10) {
return "0$date";
}
return $date;
}
sub logformat {
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$iddst) = localtime(time);
my $datestring;
$year += 1900;
$mon++;
$mon = addzero($mon);
$mday = addzero($mday);
$min = addzero($min);
$datestring = "$year-$mon-$mday $hour:$min";
return($datestring);
}
sub addlog {
my ($log_file, $log_message) = @_;
my $curr_time = logformat();
my $write_message = "[$curr_time] $log_message";
$write_message >> io($log_file);
"\n" >> io($log_file);
}
sub printerror {
my $message = @_;
print "\n Nginx FastCGI\tERROR\n"
. "\t $message\n\n";
exit 1;
}
sub usage {
print "\n Nginx FastCGI \n"
. "\n\tusage: $0 [-h] -S string -P int\n"
. "\n\t-h\t\t: this (help) message"
. "\n\t-S path\t\t: path for UNIX socket"
. "\n\t-P port\t\t: port number"
. "\n\t-p file\t\t: path for pid file"
. "\n\t-l file\t\t: path for logfile"
. "\n\n\texample: $0 -S /var/run/nginx-perl_cgi.sock -l /var/log/nginx/nginx-cfgi.log -pid /var/run/nginx-fcgi.pid\n\n";
exit 1;
}
init;
#
END() { } BEGIN() { }
*CORE::GLOBAL::exit = sub { die "fakeexit\nrc=".shift()."\n"; }; eval q{exit};
if ($@) {
exit unless $@ =~ /^fakeexit/;
} ;
# fork part
my $pid = fork();
if( $pid == 0 ) {
&main;
exit 0;
}
print " Forking worker process with PID $pid\n" if $verbose;
addlog($logfile, "Forking worker process with PID $pid");
print " Update PID file $filepid\n" if $verbose;
addlog($logfile, "Update PID file $filepid");
$pid > io($filepid);
print " Worker process running.\n" if $verbose;
addlog ($logfile, "Parent process $$ is exiting");
exit 0;
sub main {
$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) ){
while ($req_len) {
$stdin_passthrough .= getc(STDIN);
$req_len--;
}
}
# running the cgi app
if ( (-x $req_params{SCRIPT_FILENAME}) &&
(-s $req_params{SCRIPT_FILENAME}) &&
(-r $req_params{SCRIPT_FILENAME})
){
foreach $key ( keys %req_params){
$ENV{$key} = $req_params{$key};
}
if ( $verbose ) {
addlog($logfile, "running $req_params{SCRIPT_FILENAME}");
}
#
#
open $cgi_app, '-|', $req_params{SCRIPT_FILENAME}, $stdin_passthrough or print("Content-type: text/plain\r\n\r\n"); print "Error: CGI app returned no output - Executing $req_params{SCRIPT_FILENAME} failed !\n"; # addlog($logfile, "Error: CGI app returned no output - Executing $req_params{SCRIPT_FILENAME} failed !");
if ($cgi_app) {
print <$cgi_app>;
close $cgi_app;
}
} 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";
addlog($logfile, "Error: No such CGI app - $req_params{SCRIPT_FILENAME} may not exist or is not executable by this process.");
}
}
}
nginx的配置
location ~ .*\.pl$ {启动脚本为:
Vi /usr/local/nginx/start_perl_cgi.sh
#!/bin/bash
#set -x
dir=/usr/local/nginx
stop ()
{
#pkill -f $dir/perl-fcgi.pl
kill $(cat $dir/logs/perl-fcgi.pid)
rm $dir/logs/perl-fcgi.pid 2>/dev/null
rm $dir/logs/perl-fcgi.sock 2>/dev/null
echo "stop perl-fcgi done"
}
start ()
{
rm $dir/now_start_perl_fcgi.sh 2>/dev/null
chown nobody.root $dir/logs
echo "$dir/perl-fcgi.pl -l $dir/logs/perl-fcgi.log -pid $dir/logs/perl-fcgi.pid -S $dir/logs/perl-fcgi.sock" >>$dir/now_start_perl_fcgi.sh
chown nobody.nobody $dir/now_start_perl_fcgi.sh
chmod u+x $dir/now_start_perl_fcgi.sh
sudo -u nobody $dir/now_start_perl_fcgi.sh
echo "start perl-fcgi done"
}
case $1 in
stop)
stop
;;
start)
start
;;
restart)
stop
start
;;
esac