#!/usr/bin/perl -w
# ex: set tabstop=4 ai expandtab softtabstop=4 shiftwidth=4:
# -*- mode: c-basic-indent: 4; tab-width: 4; indent-tabs-mode: nil -*-

use strict;
use warnings;

our $VERSION = 3.1;

=head1 NAME

agent.pl

=head1 DESCRIPTION

=head1 SYNOPSIS

=over

=cut

use FindBin;
use lib "$FindBin::Bin/../lib";

use perfSONAR_PS::CircuitMonitoringGUI::TopologyCollector;
use perfSONAR_PS::CircuitMonitoringGUI::TopologyFinder;
use perfSONAR_PS::CircuitMonitoringGUI::ServiceFinder;
use perfSONAR_PS::Utils::Daemon qw/daemonize setids lockPIDFile unlockPIDFile/;

use POSIX;
use Getopt::Long;
use Config::General;
use Log::Log4perl qw/:easy/;

my $CONFIG_FILE;
my $LOGGER_CONF;
my $PIDFILE;
my $DEBUGFLAG;
my $HELP;
my $RUNAS_USER;
my $RUNAS_GROUP;

my ( $status, $res );

$status = GetOptions(
    'config=s'  => \$CONFIG_FILE,
    'logger=s'  => \$LOGGER_CONF,
    'pidfile=s' => \$PIDFILE,
    'verbose'   => \$DEBUGFLAG,
    'user=s'    => \$RUNAS_USER,
    'group=s'   => \$RUNAS_GROUP,
    'help'      => \$HELP
);

unless ( $CONFIG_FILE ) {
    print "Error: no configuration file specified\n";
    exit( -1 );
}

my %conf = Config::General->new( $CONFIG_FILE )->getall();

$PIDFILE = $conf{"pid_file"} unless ( $PIDFILE );

unless ( $PIDFILE ) {
    print "Error: no pid file specified\n";
    exit( -1 );
}


( $status, $res ) = lockPIDFile( $PIDFILE );
if ( $status != 0 ) {
    print "Error: $res\n";
    exit( -1 );
}

my $fileHandle = $res;

# Now that we've dropped privileges, create the logger. If we do it in reverse
# order, the daemon won't be able to write to the logger.
my $logger;
unless ( $LOGGER_CONF ) {
    use Log::Log4perl qw(:easy);

    my $output_level = $INFO;
    if ( $DEBUGFLAG ) {
        $output_level = $DEBUG;
    }

    my %logger_opts = (
        level  => $output_level,
        layout => '%d (%P) %p> %F{1}:%L %M - %m%n',
    );

    Log::Log4perl->easy_init( \%logger_opts );
    $logger = get_logger( "perfSONAR_PS" );
}
else {
    use Log::Log4perl qw(get_logger :levels);

    my $output_level;
    if ( $DEBUGFLAG ) {
        $output_level = $DEBUG;
    }

    my %logger_opts = (
        level  => $output_level,
        layout => '%d (%P) %p> %F{1}:%L %M - %m%n',
    );

    Log::Log4perl->init( $LOGGER_CONF );
    $logger = get_logger( "perfSONAR_PS" );
    $logger->level( $output_level ) if $output_level;
}

my $config = perfSONAR_PS::Utils::Config->new();
($status, $res) = $config->init({ file => $CONFIG_FILE });
unless ($status == 0) {
    $logger->error($res);
    exit(-1);
}

my $service_finder = perfSONAR_PS::CircuitMonitoringGUI::ServiceFinder->new();
($status, $res) = $service_finder->init({ config => $config });
unless ($status == 0) {
    $logger->error($res);
    exit(-1);
}

my $topology_finder = perfSONAR_PS::CircuitMonitoringGUI::TopologyFinder->new();
($status, $res) = $topology_finder->init({ config => $config, service_finder => $service_finder });
unless ($status == 0) {
    $logger->error($res);
    exit(-1);
}

my $topology_collector = perfSONAR_PS::CircuitMonitoringGUI::TopologyCollector->new();
($status, $res) = $topology_collector->init({ config => $config, service_finder => $service_finder, topology_finder => $topology_finder });
unless ($status == 0) {
    $logger->error($res);
    exit(-1);
}

# Check if the daemon should run as a specific user/group and then switch to
# that user/group.
if ( not $RUNAS_GROUP ) {
    if ( $conf{"group"} ) {
        $RUNAS_GROUP = $conf{"group"};
    }
}

if ( not $RUNAS_USER ) {
    if ( $conf{"user"} ) {
        $RUNAS_USER = $conf{"user"};
    }
}

if ( $RUNAS_USER and $RUNAS_GROUP ) {
    if ( setids( USER => $RUNAS_USER, GROUP => $RUNAS_GROUP ) != 0 ) {
        $logger->error("Error: Couldn't drop priviledges");
        exit( -1 );
    }
}
elsif ( $RUNAS_USER or $RUNAS_GROUP ) {
    # they need to specify both the user and group
    $logger->error("You need to specify both the user and group if you specify either");
    exit( -1 );
}

# Before daemonizing, set die and warn handlers so that any Perl errors or
# warnings make it into the logs.
my $insig = 0;
$SIG{__WARN__} = sub {
    $logger->warn("Warned: ".join( '', @_ ));
    return;
};

$SIG{__DIE__} = sub {                       ## still dies upon return
        die @_ if $^S;                      ## see perldoc -f die perlfunc
        die @_ if $insig;                   ## protect against reentrance.
        $insig = 1;
        $logger->error("Died: ".join( '', @_ ));
        $insig = 0;
        return;
};

if ( not $DEBUGFLAG ) {
    ( $status, $res ) = daemonize();
    if ( $status != 0 ) {
        $logger->error( "Couldn't daemonize: " . $res );
        exit( -1 );
    }
}

unlockPIDFile( $fileHandle );

$SIG{PIPE} = $SIG{INT} = $SIG{TERM} = $SIG{HUP} = $SIG{CHLD} = \&catch_sig;

$topology_collector->run();

$topology_collector->kill_children();

sub catch_sig {
    my ( $signame ) = @_;

    return unless ($signame);

    $topology_collector->signal_handler($signame);
}

1;

__END__

=head1 SEE ALSO

L<FindBin>, L<perfSONAR_PS::Agents::CircuitMonitoring>, L<perfSONAR_PS::Utils::Daemon>, 
L<POSIX>, L<Getopt::Long>, L<Config::General>, L<Log::Log4perl>,

To join the 'perfSONAR Users' mailing list, please visit:

  https://mail.internet2.edu/wws/info/perfsonar-ps-users

The perfSONAR-PS subversion repository is located at:

  http://anonsvn.internet2.edu/svn/perfSONAR-PS/trunk

Questions and comments can be directed to the author, or the mailing list.
Bugs, feature requests, and improvements can be directed here:

  http://code.google.com/p/perfsonar-ps/issues/list

=head1 VERSION

$Id$

=head1 AUTHOR

Aaron Brown <aaron@internet2.edu>

=head1 LICENSE

You should have received a copy of the Internet2 Intellectual Property Framework
along with this software.  If not, see
<http://www.internet2.edu/membership/ip.html>

=head1 COPYRIGHT

Copyright (c) 2002-2011, Internet2

All rights reserved.

=cut
