Chinaunix首页 | 论坛 | 博客
  • 博客访问: 454835
  • 博文数量: 481
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 1040
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-06 14:09
文章分类

全部博文(481)

文章存档

2013年(483)

我的朋友

分类: LINUX

2013-04-17 15:45:49

原文地址:nginx支持cgi 作者:飞鸿无痕


nginx支持cgi

 

想在现有环境LNMP上部署一下Nagios,但是发现默认情况下Nginx自身是无法解析外部CGI程序的,于是官方给出了一个这样的做法,通过研究发现有两种方式可以实现让nginx支持cgi,两种方式都需要安装那几包.下面分别来介绍:

 

第一种方式操作步骤:

一、安装perl

perl-FCGIperl-FCGI-ProcManagerperl-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$ { 
                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.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

阅读(524) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~