nginx支持cgi 想在现有环境LNMP上部署一下Nagios,但是发现默认情况下Nginx自身是无法解析外部CGI程序的,于是官方给出了一个这样的做法http://wiki.nginx.org/NginxSimpleCGI,通过研究发现有两种方式可以实现让nginx支持cgi,两种方式都需要安装那几包.下面分别来介绍: 第一种方式操作步骤:一、安装perl库perl-FCGI、perl-FCGI-ProcManager、perl-IO-ALL(如下文件均可以去http://search.cpan.org下载)#wgethttp://www.cpan.org/modules/by-module/FCGI/FCGI-0.67.tar.gz#tar zxvf FCGI-0.67.tar.gz#cd FCGI-0.67#perl Makefile.PL#make && make install #wget http://search.cpan.org/CPAN/authors/id/B/BO/BOBTFISH/FCGI-ProcManager-0.24.tar.gz#tar zxvf FCGI-ProcManager-0.24.tar.gz#cd FCGI-ProcManager-0.24#perl Makefile.PL#make && make install #wgethttp://search.cpan.org/CPAN/authors/id/I/IN/INGY/IO-All-0.39.tar.gz#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 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, " 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内修改 三、配置NginxNginx配置如下 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."; 然后打开浏览器访问http://localhost/test.cgi,如果出现内容为Hello,world.则说明配置成功。 第二种方式:vi /usr/local/nginx/perl-fcgi.pl #!/usr/bin/perl## author Daniel Dominik Rudnicki# thanksto: Piotr Romanczuk# email daniel@sardzent.org# version 0.4.3# webpage http://www.nginx.eu/## BASED@ http://wiki.codemongers.com/NginxSimpleCGI### 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"; exit1; } if ( ! $logfile ) { print"\n\tERROR\t log file must declared\n" ."\tuse $0 with option -l filename\n\n"; exit1; } 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"; exit1; } if($unixsocket) { print" Daemon listening at UNIXsocket $unixsocket\n" if $versbose; addlog($logfile,"Deamon listening at UNIX socket $unixsocket"); }else { print" Daemon listening at TCP/IPsocket *:$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."); exit1; } if( $unixsocket ) { print" Creating UNIX socket\n"if $verbose; $socket= FCGI::OpenSocket( $unixsocket, 10 ); if( !$socket) { print" Couldn't createsocket\n"; addlog($logfile,"Couldn't create socket"); exit1; } print" Using UNIX socket$unixsocket\n" if $verbose; }else { print" Creating TCP/IPsocket\n" if $verbose; $portnumber= ":".$unixport; $socket= FCGI::OpenSocket( $unixport, 10 ); if( !$socket ) { print" Couldn't createsocket\n"; addlog($logfile,"Couldn't create socket"); exit1; } 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"; exit1; } 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 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"; exit1;} 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"; exit1;} init;#END() { } BEGIN() { }*CORE::GLOBAL::exit = sub { die"fakeexit\nrc=".shift()."\n"; }; eval q{exit};if ($@) { exitunless $@ =~ /^fakeexit/;} ; # fork partmy $pid = fork(); if( $pid == 0 ) { &main; exit0;} print " Forking worker processwith PID $pid\n" if $verbose;addlog($logfile, "Forking workerprocess with PID $pid");print " Update PID file$filepid\n" if $verbose;addlog($logfile, "Update PIDfile $filepid");$pid > io($filepid);print " Worker process running.\n" if $verbose;addlog ($logfile, "Parentprocess $$ 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}"); } #http://perldoc.perl.org/perlipc.html#Safe-Pipe-Opens # open$cgi_app, '-|', $req_params{SCRIPT_FILENAME}, $stdin_passthrough orprint("Content-type: text/plain\r\n\r\n"); print "Error: CGI appreturned 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; 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 isnot executable by this process.\n"; addlog($logfile,"Error: No such CGI app - $req_params{SCRIPT_FILENAME} may not exist or isnot executable by this process."); } }} nginx的配置 location ~ .*\.pl$ { fastcgi_pass unix:/usr/local/nginx/logs/perl-fcgi.sock; fastcgi_index index.pl; fastcgi_param SCRIPT_FILENAME /usr/local/nagios/sbin$fastcgi_script_name; include fastcgi.conf; } 启动脚本为: 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.sock2>/dev/null echo "stop perl-fcgi done" } start () { rm $dir/now_start_perl_fcgi.sh2>/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