From 080b7ba9920ba0c833e57b06eee9485196fabe13 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 3 Jan 2011 17:18:06 -0500 Subject: [PATCH] getting plain cgi scripts working with nginx setup --- .gitignore | 2 + web/Makefile | 17 +++- web/cgiwrap-fcgi.pl.in | 155 +++++++++++++++++++++++++++++++++ web/html/cgi-bin/test.cgi | 7 ++ web/lib/perl/base.pm | 26 +++++- web/nginx.conf.in | 28 +++++- web/perl_locations.d/base.conf | 2 +- 7 files changed, 229 insertions(+), 8 deletions(-) create mode 100644 web/cgiwrap-fcgi.pl.in create mode 100755 web/html/cgi-bin/test.cgi diff --git a/.gitignore b/.gitignore index 6925f8e..fc28eec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ */*.log */*.pid +*/*.sock web/nginx.conf +web/cgiwrap-fcgi.pl web/*_temp/* \ No newline at end of file diff --git a/web/Makefile b/web/Makefile index 794ecae..59f6630 100644 --- a/web/Makefile +++ b/web/Makefile @@ -1,16 +1,29 @@ ROOT ?= $(PWD) HTTP_PORT ?= 49228 +NGINX_CONF_IN := nginx.conf.in +ALL_NGINX_CONF_INPUTS := \ + $(NGINX_CONF_IN) \ + $(wildcard perl_requirements.d/*) \ + $(wildcard perl_locations.d/*) serve: nginx.conf nginx -p $(ROOT)/ -c $(ROOT)/$< -nginx.conf: nginx.conf.in +nginx.conf: $(ALL_NGINX_CONF_INPUTS) cat $< | \ sed -e 's@__ROOT__@$(ROOT)@g' \ -e 's@__HTTP_PORT__@$(HTTP_PORT)@g' > $@ -.PHONY: serve +serve-cgiwrap: cgiwrap-fcgi.pl + perl $< + + +cgiwrap-fcgi.pl: cgiwrap-fcgi.pl.in + cat $< | sed -e 's@__ROOT__@$(ROOT)@g' > $@ + + +.PHONY: serve serve-cgiwrap diff --git a/web/cgiwrap-fcgi.pl.in b/web/cgiwrap-fcgi.pl.in new file mode 100644 index 0000000..09355fc --- /dev/null +++ b/web/cgiwrap-fcgi.pl.in @@ -0,0 +1,155 @@ +#!/usr/bin/env 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("__ROOT__/nginx-cgiwrap-dispatch.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"; + } + } +} diff --git a/web/html/cgi-bin/test.cgi b/web/html/cgi-bin/test.cgi new file mode 100755 index 0000000..fa8f09b --- /dev/null +++ b/web/html/cgi-bin/test.cgi @@ -0,0 +1,7 @@ +#!/usr/bin/env perl + +printf("Content-Type: text/plain\n\n"); +printf("Hello Test\n"); + +1; +__END__ diff --git a/web/lib/perl/base.pm b/web/lib/perl/base.pm index 48117e3..52d3d74 100644 --- a/web/lib/perl/base.pm +++ b/web/lib/perl/base.pm @@ -1,17 +1,35 @@ package base; + +use strict; +use warnings; use nginx; sub handler { - my $r = shift; + my $r = shift(); + $r->send_http_header("text/plain"); - return OK if $r->header_only; + return OK if $r->header_only(); - $r->print("hello!\n"); - $r->rflush; + $r->print("Hello There!\n"); + $r->rflush(); if (-f $r->filename or -d _) { $r->print($r->uri, " exists!\n"); + } else { + $r->print($r->uri, " does not exist!\n"); + } + + my $directory = '/home/me/tmp'; + # '/home/me/src/LearningPerl/src/web'; + + my $didopen = opendir(DIR, $directory); + if ($didopen) { + while (my $file = readdir(DIR)) { + $r->print("$file\n"); + } + + closedir(DIR); } } diff --git a/web/nginx.conf.in b/web/nginx.conf.in index eb51d86..fb04cc7 100644 --- a/web/nginx.conf.in +++ b/web/nginx.conf.in @@ -24,13 +24,39 @@ http { autoindex on; server { + root __ROOT__/html; + index index.html; + listen __HTTP_PORT__; server_name localhost; access_log __ROOT__/access.log main; - root __ROOT__/html; include __ROOT__/perl_locations.d/*; + + location ~ ^/cgi-bin/.*\.cgi$ { + gzip off; + fastcgi_pass unix:__ROOT__/nginx-cgiwrap-dispatch.sock; + fastcgi_index index.cgi; + fastcgi_param SCRIPT_FILENAME __ROOT__/html$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; + } } + } diff --git a/web/perl_locations.d/base.conf b/web/perl_locations.d/base.conf index d0a74db..4e887de 100644 --- a/web/perl_locations.d/base.conf +++ b/web/perl_locations.d/base.conf @@ -1,4 +1,4 @@ -location / { +location /embedded { perl base::handler; }