您的位置:首页 > 运维架构 > Nginx

Nginx perl fcgi 配置

2012-07-03 16:55 316 查看
Nginx并不提供支持对外部程序的直接调用或者解析,所有的外部程序(perl,python)必须通过fastcgi接口来调用。这里使用perl-fcgi来使用nginx支持cgi。
一、安装之前先安装perl-cgi直接perl库
安装 perl库
cd/usr/local/src
wgethttp://www.cpan.org/modules/by-module/FCGI/FCGI-0.67.tar.gz
tar -zxvfFCGI-0.67.tar.gz
cd FCGI-0.67
perl Makefile.PL
make &&make install

cd/usr/local/src
wgethttp://search.cpan.org/CPAN/authors/id/G/GB/GBJK/FCGI-ProcManager-0.18.tar.gz
tar -zxvfFCGI-ProcManager-0.18.tar.gz
cdFCGI-ProcManager-0.18
perl Makefile.PL
make &&make install

cd /usr/local/src
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-cgi.pl脚本

#!/usr/bin/perl -w
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 =>2} );
#$socket = FCGI::OpenSocket( "127.0.0.1:8999", 50 );
$socket = FCGI::OpenSocket( "/tmp/fastcgi.sock", 50 );
$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();
$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;
}
}
if (
( -x $req_params{SCRIPT_FILENAME} ) &&
( -s $req_params{SCRIPT_FILENAME} ) &&
( -r $req_params{SCRIPT_FILENAME} )
)
{
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};
}
if ( $req_params{SCRIPT_FILENAME} =~/^(.*)\/[^\/]+$/ ) { chdir $1; }
close(PARENT_WR);
close(STDIN);
close(STDERR);
syscall( &SYS_dup2, fileno(CHILD_RD), 0 );
syscall( &SYS_dup2, fileno(CHILD_ERR), 2 );
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";
}
}
}

三、启动cgi
/usr/local/nginx/sbin/perl-fcgi.pl
运行没报错的话,使用ps aux | grep cgi应该可以看到进程了,并且会在/tmp目录下会生成fastcgi.sock文件
上面的方式启动后perl-fcgi是以执行它的用户身份运行的,对于web程序来说这是很不利的。老外用perl写了一个脚本Noah Friedman可以用指定的用户来运行某个程序,源程序在这里,这里也贴出来方便查阅
cat > /sbin/runas <<EOF

#!/bin/sh
exec ${PERL-perl} -Swx $0 ${1+"$@"}
#!perl [perl will skip all lines in this file before this line]

# with --- run program with special properties

# Copyright (C) 1995, 2000, 2002 Noah S. Friedman

# Author: Noah Friedman <friedman@splode.com>
# Created: 1995-08-14

# $Id: with,v 1.12 2004/02/16 22:51:49 friedman Exp $

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you can either send email to this
# program's maintainer or write to: The Free Software Foundation,
# Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.

# Commentary:

# TODO: create optional socket streams for stdin or stdout before invoking
# subprocess.

# Code:

use Getopt::Long;
use POSIX qw(setsid);
use Symbol;
use strict;

(my $progname = $0) =~ s|.*/||;
my $bgfn;
my $bgopt = 0;

my $opt_cwd;
my $opt_egid;
my $opt_euid;
my $opt_gid;
my $opt_groups;
my @opt_include;
my $opt_name;
my $opt_pgrp;
my $opt_priority;
my $opt_root;
my $opt_uid;
my $opt_umask;
my $opt_foreground = 0;

sub err
{
my $fh = (ref ($_[0]) ? shift : *STDERR{IO});
print $fh join (": ", $progname, @_), "\n";
exit (1);
}

sub get_includes
{
unshift @INC, @_;
push (@INC,
"$ENV{HOME}/lib/perl",
"$ENV{HOME}/lib/perl/include");

eval { require "syscall.ph" } if defined $opt_groups;
}

sub numberp
{
defined $_[0] && $_[0] =~ m/^-?\d+$/o;
}

sub group2gid
{
my $g = shift;
return $g if numberp ($g);
my $gid = getgrnam ($g);
return $gid if defined $gid && numberp ($gid);
err ($g, "no such group");
}

sub user2uid
{
my $u = shift;
return $u if numberp ($u);
my $uid = getpwnam ($u);
return $uid if defined $uid && numberp ($uid);
err ($u, "no such user");
}

sub set_cwd
{
my $d = shift;
chdir ($d) || err ("chdir", $d, $!);
}

sub set_egid
{
my $sgid = group2gid (shift);
my $egid = $) + 0;

$) = $sgid;
err ($sgid, "cannot set egid", $!) if ($) == $egid && $egid != $sgid);
}

sub set_gid
{
my $sgid = group2gid (shift);
my $rgid = $( + 0;
my $egid = $) + 0;

$( = $sgid;
$) = $sgid;
err ($sgid, "cannot set rgid", $!) if ($( == $rgid && $rgid != $sgid);
err ($sgid, "cannot set egid", $!) if ($) == $egid && $egid != $sgid);
}

sub big_endian_p
{
my $x = 1;
my @y = unpack ("c2", pack ("i", $x));
return ($y[0] == 1) ? 0 : 1;
}

# This function is more complex than it ought to be because perl does not
# export the setgroups function. It exports the getgroups function by
# making $( and $) return multiple values in the form of a space-separated
# string, but you cannot *set* the group list by assigning those variables.
# There is no portable way to determine what size gid_t is, so we must guess.
sub set_groups
{
my @glist = sort { $a <=> $b } map { group2gid ($_) } split (/[ ,]/, shift);

my $expected = join (" ", $(+0, reverse @glist);
my @p = (big_endian_p() ? ("n", "N", "i") : ("v", "V", "i"));

for my $c (@p)
{
err ("setgroups", $!)
if (syscall (&SYS_setgroups, @glist+0, pack ("$c*", @glist)) == -1);
return if ("$(" eq $expected);
}
err ("setgroups", "Could not determine gid_t");
}

sub set_pgrp
{
setpgrp ($$, shift) || err ("setpgrp", $!);
}

sub set_priority
{
my $prio = shift () + 0;
setpriority (0, 0, $prio) || err ("setpriority", $prio, $!);
}

sub set_root
{
my $d = shift;
chroot ($d) || err ("chroot", $d, $!);
chdir ("/");
}

sub set_euid
{
my $suid = user2uid (shift);
my $euid = $>;

$> = $suid;
err ($suid, "cannot set euid", $!) if ($> == $euid && $euid != $suid);
}

sub set_uid
{
my $suid = user2uid (shift);
my $ruid = $<;
my $euid = $>;

$< = $suid;
$> = $suid;
err ($suid, "cannot set ruid", $!) if ($< == $ruid && $ruid != $suid);
err ($suid, "cannot set euid", $!) if ($> == $euid && $euid != $suid);
}

sub background
{
my $pid = fork;
die "$@" if $pid < 0;
if ($pid == 0)
{
# Backgrounded programs may expect to be able to read input from the
# user if stdin is a tty, but we will no longer have any job control
# management because of the double fork and exit. This can result in
# a program either blocking on input (if still associated with a
# controlling terminal) and stopping, or stealing input from a
# foreground process (e.g. a shell). So redirect stdin to /dev/null.
open (STDIN, "< /dev/null") if (-t STDIN);
return *STDERR{IO};
}

exit (0) unless $opt_foreground;
wait;
exit ($?);
}

sub dosetsid
{
background ();
setsid (); # dissociate from controlling terminal
return *STDERR{IO};
}

sub daemon
{
# Don't allow any file descriptors, including stdin, stdout, or
# stderr to be propagated to children.
$^F = -1;
dosetsid ();
# Duped in case we've closed stderr but can't exec anything.
my $saved_stderr = gensym;
open ($saved_stderr, ">&STDERR");
close (STDERR);
close (STDOUT);
close (STDIN);
return $saved_stderr;
}

sub notty
{
# Don't allow any file descriptors other than stdin, stdout, or stderr to
# be propagated to children.
$^F = 2;
dosetsid ();
# Duped in case we've closed stderr but can't exec anything.
my $saved_stderr = gensym;
open ($saved_stderr, ">&STDERR");
open (STDIN, "+</dev/null");
open (STDERR, "+<&STDIN");
open (STDOUT, "+<&STDIN");
return $saved_stderr;
}

sub set_bg_option
{
my %bgfntbl =
( 1 => \&background,
2 => \&daemon,
4 => \¬ty,
8 => \&dosetsid,
);

$bgopt = $_[0];
$bgfn = $bgfntbl{$bgopt};
}

sub parse_options
{
Getopt::Long::config (qw(bundling autoabbrev require_order));
my $succ = GetOptions
("h|help", sub { usage () },
"c|cwd=s", \$opt_cwd,
"d|display=s", \$ENV{DISPLAY},
"H|home=s", \$ENV{HOME},
"G|egid=s", \$opt_egid,
"g|gid=s", \$opt_gid,
"I|include=s@", \@opt_include,
"l|groups=s", \$opt_groups,
"m|umask=s", \$opt_umask,
"n|name=s", \$opt_name,
"P|priority=i", \$opt_priority,
"p|pgrp=i", \$opt_pgrp,
"r|root=s", \$opt_root,
"U|euid=s", \$opt_euid,
"u|uid=s", \$opt_uid,

"f|fg|foreground", \$opt_foreground,

"b|bg|background", sub { set_bg_option (1); $opt_foreground = 0 },
"a|daemon|demon", sub { set_bg_option (2) },
"N|no-tty|notty", sub { set_bg_option (4) },
"s|setsid", sub { set_bg_option (8) },
);
usage () unless $succ;

my $n = 0;
do { $n++ if $bgopt & 1 } while ($bgopt >>= 1);
err ("Can only specify one of --background, --daemon, --notty, or --setsid")
if ($n > 1);
}

sub usage
{
print STDERR "$progname: @_\n\n" if @_;
print STDERR "Usage: $progname {options} [command {args...}]\n
Options are:
-h, --help You're looking at it.
-D, --debug Turn on interactive debugging in perl.
-I, --include DIR Include DIR in \@INC path for perl.
This option may be specified multiple times to append
search paths to perl.

-d, --display DISP Run with DISP as the X server display.
-H, --home HOME Set \$HOME.
-n, --name ARGV0 Set name of running program (argv[0]).

-c, --cwd DIR Run with DIR as the current working directory.
This directory is relative to the root directory as
specified by \`--root', or \`/'.
-r, --root ROOT Set root directory (via \`chroot' syscall) to ROOT.

-G, --egid EGID Set \`effective' group ID.
-g, --gid GID Set both \`real' and \`effective' group ID.
-l, --groups GLIST Set group list to comma-separated GLIST.
-U, --euid EUID Set \`effective' user ID.
-u, --uid UID Set both \`real' and \`effective' user ID.

-m, --umask UMASK Set umask.
-P, --priority NICE Set scheduling priority to NICE (-20 to 20).
-p, --pgrp PGRP Set process group.

The following options cause the resulting process to be backgrounded
automatically but differ in various ways:

-b, --background Run process in background. This is the default with
the --daemon, --no-tty, and --setsid options.

-f, --foreground Do not put process into the background when using
the --daemon, --no-tty, and --setsid options.
In all other cases the default is to remain in the
foreground.

-a, --daemon Run process in \"daemon\" mode.
This closes stdin, stdout, and stderr, dissociates
the process from any controlling terminal, and
backgrounds the process.

-N, --no-tty Run process in background with no controlling
terminal and with stdin, stdout, and stderr
redirected to /dev/null.

-s, --setsid Dissociate from controlling terminal.
This automatically backgrounds the process but
does not redirect any file descriptors.\n";
exit (1);
}

sub main
{
parse_options ();
usage () unless @ARGV;

get_includes (@opt_include);

umask (oct ($opt_umask)) if defined $opt_umask;
set_gid ($opt_gid) if defined $opt_gid;
set_egid ($opt_egid) if defined $opt_egid;
set_groups ($opt_groups) if defined $opt_groups;
set_root ($opt_root) if defined $opt_root;
set_cwd ($opt_cwd) if defined $opt_cwd;
set_priority ($opt_priority) if defined $opt_priority;
set_uid ($opt_uid) if defined $opt_uid;
set_euid ($opt_euid) if defined $opt_euid;

my $stderr = $bgfn ? &$bgfn () : *STDERR{IO};

my $runprog = $ARGV[0];
if ($opt_name)
{
shift @ARGV;
unshift @ARGV, $opt_name;
}
local $^W = 0; # avoid implicit warnings from exec
exec ($runprog @ARGV) || err ($stderr, "exec", $runprog, $!);
}

main ();

# local variables:
# mode: perl
# eval: (auto-fill-mode 1)
# end:

# with ends here
EOF

使用www用户启动fcgi
runas --daemon -g www -u www /bin/perl-fcgi

四、配置nginx
cat > /usr/local/nginx/conf/fcgi_perl.conf <<EOF

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;

EOF

nginx.conf配置

server {
listen 80;
server_name localhost;
location ~ .*/cgi-bin/.*\.(cgi|fcgi|fcg)
{
include fcgi_perl.conf;
fastcgi_pass unix:/tmp/fastcgi.sock;
fastcgi_param SCRIPT_FILENAME /data/www/entry$fastcgi_script_name;
}
}

要设置nginx运行用户和运行perl_fcgi.pl的用户一致,要不会出现访问/tmp/fastcgi.sock的权限问题,也可以使用chmod 666 /tmp/fastcgi.sock 来给fastcgi.sock权限
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: