I have an interesting dilemna. It's a problem that keeps coming back
to haunt me, and I've never solved it. I've searched and struggled and
pondering and hacked away, but no luck. Will you help me?

We run Mason as a FastCGI script that listens on a socket. Our web
server, nginx, sends requests to that socket. Now, in our case it's a
unix domain socket, but certainly it could be TCP with little trouble.

In the olden days, we had a nasty cascade of shell and perl scripts
that would run the Mason application. Somewhere along the line, it
would export the FCGI_SOCKET_PATH environment variable. There was only
one script and only one socket and only one connection. This worked,
but it was wrong, and we outgrew it.

I rewrote the Mason application recently, and the work-in-progress
appears below. It's not working yet, but it needs to be smarter now:
it will read the number of children from my site configuration file,
and fork() off children and patiently wait for them to do their
service.

All of it works except the CGI handler part, and only because of a
problem with the environment variable. This is strange, because I set
it within the perl process just before handling the CGI object. The
SAME process. That is, I do this:

    $ENV{FCGI_SOCKET_PATH} = "$vhost_path/tmp/fcgi-$num.sock";
    while ($cgi = new CGI::Fast) {
        $h = HTML::Mason::CGIHandler->new(%various_params);
        $h->handle_cgi_object($cgi); #### <--- crashes here
    }

(This is abbreviated substantially; the full non-working script appears below.)

Mason then delivers up this error message:

  Component path given to Interp->load must be absolute (was given )

It's not exactly informative. But it goes away when CGI::Fast is satisfied.

Meanwhile, if I export the variable from the shell it can see it. The
problem with that is that it needs to have N different values, one for
each worker process.

What I don't understand is why I can't see FCGI_SOCKET_PATH from the same
process. Why doesn't CGI::Fast have the same %ENV when I call it just
a few lines later? Is there some other more robust way to get that
socket to CGI::Fast than the creaking and rickety old unix environment?

Any help that anyone can give me would be greatly appreciated.

        -- Patrick


.------ Patrick M. Jordan ------.  Random unused band name:
| Systems/Network Administrator |  The Knights Who Say Nietzsche
`------ Antistatic Matrix ------'



#!/usr/bin/perl

use Cwd qw(abs_path);
use File::Basename;
use YAML qw(LoadFile);
use HTML::Mason::CGIHandler;
use CGI::Fast;

use strict;

my %children;
my ($conf, $last_conf_timestamp);

my $vhost_path = abs_path(dirname($0) . '/..');
my $vhost_name = (split /\//, $vhost_path)[-1];
my $pid_file_name = $vhost_path . "/tmp/application.pid";
my $site_conf_filename = $vhost_path . '/etc/site.yaml';

sub site_conf_timestamp{ (stat $site_conf_filename)[9] }

sub load_site_conf {
    return 0 if (&site_conf_timestamp == $last_conf_timestamp); # still current

    $last_conf_timestamp = &site_conf_timestamp;
    $conf = LoadFile($site_conf_filename);
    return 1;
}

BEGIN {
    $vhost_path = abs_path(dirname($0) . '/..');
    $vhost_name = (split /\//, $vhost_path)[-1];
    $site_conf_filename = $vhost_path . '/etc/site.yaml';
    &load_site_conf;
    my $sentinel = 'MASON_ORACLE_VARS';
    unless($ENV{$sentinel}) {
        $ENV{$sentinel} = 'SET';
        $ENV{'LD_LIBRARY_PATH'} = $conf->{database}{dir} . '/lib';
        $ENV{'TNS_ADMIN'} = $conf->{database}{tns_admin} ;
        exec $^X, $0, @ARGV;
    }
}

#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::| main |::

my $command = shift @ARGV;

if    ($command eq 'start') { &start }
elsif ($command eq 'stop')  { &stop  }
elsif ($command eq 'restart') { &stop; sleep 1; &start  }
else  { die "usage: $0 (start|stop|restart)\n" }

#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::| subs |::

sub start {
    if (-e $pid_file_name) {
        open PID, "$pid_file_name";
        my $pid = readline PID;
        close PID;
        die "master process already running ($pid)\n";
    }
    &parent;
}

sub stop {
    unless (-e $pid_file_name) {
        die "master process not running\n";
    }
    open PID, "$pid_file_name";
    my $pid = readline PID;
    close PID;
    kill 1, $pid;
    unlink $pid_file_name;
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

sub shutdown {
    foreach my $pid (keys %children) {
        kill 1, $pid;
    }
    unlink $pid_file_name;
}

sub parent {
    exit if fork;

    open PID, ">$pid_file_name";
    print PID $$;
    close PID;

    $0 = $vhost_name . ": master process";
    for my $num (1 .. 1) {
        if (my $pid = fork) {
            $children{$pid} = $num;
        } else {
            &child($num);
            exit;
        }
    }

    close STDIN; close STDOUT;
    $SIG{INT}  = 'shutdown';
    $SIG{HUP}  = 'shutdown';
    $SIG{TERM} = 'shutdown';
    $SIG{QUIT} = 'shutdown';

    1 while wait != -1;
    unlink $pid_file_name;
}

sub child {
    my $num = shift;
    $0 = $vhost_name . ": fastcgi worker [$num]";

    {
        package HTML::Mason::Commands;

        use DBI;
        use DBD::Oracle qw(:ora_types); # for ORA_RSET

        use URI;
        use URI::Split;

        use Business::CreditCard;       # Checksum credit cards
        use CGI::Cookie;
        use Cache::FileCache;
        use Crypt::PasswdMD5;
        use Data::Validate::Domain;
        use Date::Manip;
        use Digest::SHA1 qw(sha1_hex sha1_base64);
        use Email::Valid;
        use HTML::Entities qw(:DEFAULT encode_entities);
        use Mail::Send;
        use Net::Domain::TLD;
        use OSSP::uuid;
        use YAML qw(LoadFile);

        use vars (
            '%SITE',    # master data hash for all sites

            #-- Localized per-request variables
            '$Site',    # Shorthand ref to $SITE{$ENV{SITE_FQDN}}
            '$Conf',    # Shorthand ref to $Site->{conf}
            '$Form',    # Params from GET and POST
            '$DB',              # Persistent DB handle
            '%DB',              # Persistent DB handle
            '@DEBUG',   # Debug-level info accumulators
            '@DB_INFO',
            '@MESSAGE', # Messages to the user presented out-of-order
            '$TitleMod',        # local mods to titles for better bookmarking
            '%Cookie',  # cookies
            '$ORA_RSET',        # shorthand for DBD::Oracle's rowset data type
        );
    }

    sub HTML::Mason::FakeApache::param {
        my ($self, $key) = (@_);
        my %args = HTML::Mason::Utils::cgi_request_args(
            $self->query,
            $self->query->request_method
        );
        return ($key ? $args{$key} : keys %args);
    }

    my %cgi_handler_params = (
        default => {
            comp_root                   => "$vhost_path/html/",
            #data_dir                   => "$vhost_path/tmp",

            autohandler_name            => '_AUTO',
            dhandler_name               => '_DEFAULT',

            autoflush                   => 0,
            enable_autoflush            => 0,
            static_source_touch_file    => "$vhost_path/etc/site.yaml",

            # TODO: Port Mason params formerly used under mod_perl.
            # cache_class               => 'MemoryCache',
            # cache_depth               => 4,
            # default_expires_in        => '24 hours',
        },

        debug => {
            error_mode                  => 'output',
            #error_format               => 'html',
            error_format                => 'line',
            static_source               => 0,
        },

        no_debug => {
            error_mode                  => 'fatal',
            error_format                => 'line',
            static_source               => 1,
        },
    );

    #%ENV = ();
    my $h;
    my $cgi;

    $ENV{FCGI_SOCKET_PATH} = "$vhost_path/tmp/fcgi-$num.sock";
    eval {
        while ($cgi = new CGI::Fast) {

            if (!$h || &load_site_conf) {
                $h = HTML::Mason::CGIHandler->new(
                    (%{$cgi_handler_params{default}}),
                    ($conf->{debug}
                        ? (%{$cgi_handler_params{debug}})
                        : (%{$cgi_handler_params{no_debug}})
                    )
                );
            }

            eval { $h->handle_cgi_object($cgi) };

            if (my $raw_error = $@) {
                if ($@ =~ /could not find component for initial path/) {
                    print "Status: 404\n\n";
                    next;
                    # Or:
                    # $cgi->path_info('/errordocs/404.html');
                    # $h->handle_cgi_object($cgi);
                } else {
                    open GRAH, ">/tmp/grah.txt";
                    print GRAH "$raw_error\n";
                    print GRAH "----\n";
                    foreach my $k (sort keys %ENV) {
                        print GRAH "$k: $ENV{$k}\n";
                    }
                    close GRAH;
                    warn $raw_error;
                }
            }
        }
    };

    if (my $raw_error = $@) {
        open GRAH, ">/tmp/grah.txt";
        print GRAH "$raw_error\n";
        print GRAH "----\n";
        foreach my $k (sort keys %ENV) {
            print GRAH "$k: $ENV{$k}\n";
        }
        close GRAH;
        #warn $raw_error;
    }
}



-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Mason-users mailing list
Mason-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mason-users

Reply via email to