#!/usr/bin/perl -w 
#
# Zymonic Business Process and Information Management System
# Copyright Zednax Limited 2008 -
# For Authors and Changelog see the subversion history
use strict;

package main;

# Library path
BEGIN
{
    use Zymonic::Utils qw(clean debug death_handler get_array);
    $main::SIG{__DIE__} = \&death_handler;
    no warnings;
    $SIG{CHLD}  = 'IGNORE';
    $SIG{CHILD} = 'IGNORE';    # Second instance for Solaris compatibility
    use warnings;
}

my $fh;
open $fh, ">&STDERR";
$Zymonic::Utils::debugfile = $fh;

# Script 'globals'
my @servers = ();

# Modules
use Zymonic::Decryptor::Server;
use Zymonic::Config;
use Zymonic::Decryptor::Message;
use Module::Reload;

# Clean path
$ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/sbin:/usr/sbin";

my $system   = $ARGV[0] || '';
my $hostname = $ARGV[1] || '';
my $ports    = $ARGV[2] || '1';
my $nofork   = $ARGV[3] || 0;

unless ( ( $ports =~ /^\d+$/ ) and ( -d '/etc/zymonic/' . $system ) and $hostname )
{
    print "usage: zymonic_decryptor.pl [decryptor system] [hostname] [ports to use (integer)]\n";
    exit(0);
}

foreach my $i ( 1 .. $ports )
{
    my $sc_pid;

    unless ($nofork)
    {
        die "Can't create server child process: $!" unless defined( $sc_pid = fork() );
    }

    if ( !$sc_pid )
    {

        # Child process...
        server_child( $system, $hostname, $sc_pid );

        exit(0) if defined $sc_pid;

    }
    else
    {
        push( @servers, $sc_pid );

        # Sleep to avoid port allocation conflicts
        sleep(1);
    }
}

# Wait for all children to exit - although this should never happen...
foreach (@servers) { waitpid( $_, 0 ); }

exit(0);

sub server_child
{
    my $system   = shift;
    my $hostname = shift;
    my $sc_pid   = shift;

    # Load a decryptor object
    my $config = Zymonic::Config->new( system_name => $system, config_dir => "/etc/zymonic", );
    my $dso = Zymonic::Decryptor::Server->new(
        config   => $config,
        db       => $config->{DB},
        hostname => $hostname,
    );

    # Wait for connections...
    while ( my $connection = $dso->accept )
    {
        my $log_id = '';
        eval {
            $log_id = $dso->connection_start( $connection->peerhost );
            1;
          }
          or do
        {
            death_handler( $@, '', 'return' );
        };

        my $c_child = undef;
        $c_child = fork() unless $nofork or $dso->nofork();

        # If we can't fork we will have to handle the connection in 'blocking' mode -
        # hence we don't bother checking for child pid == 0 here...
        if ($c_child)
        {

            # Do something here?
        }
        else
        {

            # Handle the connection
            connection_child( $log_id, $dso, $connection, $c_child );
            $connection->close();

            # Only exit if we're really a child.
            if ( defined $c_child )
            {
                $dso->disconnect;
                $config->{DB}->close_db_connection;
                exit(0);
            }
        }

    }
}

sub connection_child
{
    my $log_id     = shift;
    my $dso        = shift;
    my $connection = shift;
    my $c_child    = shift;

    my $responded = 0;

    eval {

        # Re-establish the DB connection etc.
        $dso->postfork($c_child);

        # Get the data
        my $in = $dso->receive($connection);

        # Pass to the message handler
        Module::Reload->check;
        my $mh = Zymonic::Decryptor::Message->new(
            xmldef           => { class => $in->{messagetype} },
            zname            => $in->{messagetype},
            decryptor_server => $dso
        );
        my $response = $mh->respond($in);

        # return the response.
        print $connection $dso->freeze_and_pack($response);
        $responded = 1;

        # Mark the connection closed in the log
        $dso->connection_close($log_id);

        1;
      }
      or do
    {
        my $err = death_handler( $@, '', 'return' );
        eval { print $connection $dso->freeze_and_pack( { error => $err->{message}->{content} } ); };
    };

}

