笔者的nginx安装目录为/opt/nginx 虚拟机配置文件为/opt/nginx/conf/vhosts.conf 目前php fast-cgi已支持 以下操作均在su下完成 ===================== tar -zxvf FCGI-0.67.tar.gz
cd FCGI-0.67
perl Makefile.PL
make && make install 先在/bin目录下放置一个perl写的分发器,取名叫perl-fcgi vim /bin/perl-fcgi #!/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); #&daemonize; we don't daemonize when running under runsv #this keeps the program alive or something after exec'ing perl scripts 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 { $socket = FCGI::OpenSocket( "127.0.0.1:10081", 10 ); #use IP sockets #$socket = FCGI::OpenSocket( "/var/run/nginx/perl_cgi-dispatch.sock", 10 ); #use UNIX sockets - user running this script must have w access to the 'nginx' folder!! #foreach $item (keys %ENV) { delete $ENV{$item}; } $proc_manager = FCGI::ProcManager->new( {n_processes => 5} ); #$socket = FCGI::OpenSocket( "/opt/nginx/fcgi/cgi.sock", 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 +x /bin/perl-fcgi
然后试试看能否启动
/bin/perl-fcgi &
若是成功则会出现以下信息
FastCGI: server (pid 21315): initialized
FastCGI: manager (pid 17915): server (pid 21315) started
如果遇到错误Can’t locate FCGI.pm,那么执行下面的命令
perl -MCPAN -e 'install FCGI'
perl -MCPAN -e 'install FCGI::ProcManager'
cd /usr/include; h2ph *.h */*.h
第一、二条命令是给perl安装FCGI模块,第三条是注册perl能识别的头文件,然后重新执行/bin/perl-fcgi, 如果正常的话,那么执行:
netstat -tunlp (列表中应该出现)
tcp 0 0 127.0.0.1:10081 0.0.0.0:* LISTEN 1004/perl-fcgi-pm
启用分发器
/bin/perl-fcgi > /dev/null 2>&1 &
将其写自启动(如rc.local)
echo "/bin/perl-fcgi > /dev/null 2>&1 &" >> /sbin/php-cgi.sh
上面的方式启动后perl-fcgi是以执行它的用户身份运行的,对于web程序来说这是很不利的。老外用perl写了一个脚本Noah Friedman可以用指定的用户来运行某个程序,源程序如下: vim /sbin/runas #!/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 # 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 chmod +x /sbin/runas 我的vsftpd、nginx、php-fpm都是以www运行,则perl-cgi也用www运行
runas --daemon -g www -u www /bin/perl-fcgi echo "runas --daemon -g www -u www /bin/perl-fcgi" >> /sbin/php-cgi.sh 配置nginx
vim /opt/nginx/conf/fcgi_perl.conf fastcgi_pass 127.0.0.1:10081; fastcgi_index index.cgi; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 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 GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx; 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; fastcgi_read_timeout 60; 下面是一个cgi虚拟机的配置示例 vim /opt/nginx/conf/nginx.conf server { listen 80; index index.cgi root /var/www; location ~ .*\.cgi?$ { include fcgi_perl.conf; } } }
service nginx restart
|