#!/usr/bin/perl

=head1 NAME

winbindd-wrapper - wrapper around winbindd to start the PacketFence winbindd processes

=head1 SYNOPSIS

winbindd-wrapper [options]

 Options:
   -d      Daemonize
   -h      Help
   -v      Verbose

=cut

use warnings;
use strict;
use Getopt::Std;
use Net::Pcap 0.16;
use File::Basename qw(basename);
use POSIX qw(:signal_h pause :sys_wait_h SIG_BLOCK SIG_UNBLOCK);
use Pod::Usage;
use Fcntl qw(:flock);
use Systemd::Daemon qw{ -soft };

#pf::log must always be initilized first
BEGIN {
    # log4perl init
    use constant INSTALL_DIR => '/usr/local/pf';
    use lib INSTALL_DIR . "/lib";
    use pf::log(service => 'winbindd-wrapper');
}

use pf::file_paths qw($var_dir);
use pf::config qw(%Config %ConfigDomain);
use pf::constants qw($FALSE $TRUE);
use pf::services;
use pf::util;
use pf::services::util;
use pf::StatsD;
use pf::person;
use Time::HiRes qw(time sleep);

# initialization
# --------------
# assign process name (see #1464)
our $PROGRAM_NAME = $0 = "winbindd-wrapper";
our @REGISTERED_TASKS;
our $IS_CHILD = 0;
our %CHILDREN;
our @TASKS_RUN;
our $ALARM_RECV = 0;

my $logger = get_logger( $PROGRAM_NAME );
my $old_child_sigaction = POSIX::SigAction->new;

POSIX::sigaction(
    &POSIX::SIGCHLD,
    POSIX::SigAction->new(
        'child_sighandler' , POSIX::SigSet->new(), &POSIX::SA_NODEFER
    ),
    $old_child_sigaction
) or die("winbindd-wrapper could not set SIGCHLD handler: $!");

POSIX::sigaction(
    &POSIX::SIGALRM,
    POSIX::SigAction->new(
        'alarm_sighandler', POSIX::SigSet->new(), &POSIX::SA_NODEFER
    )
) or die("winbindd-wrapper could not set SIGALRM handler: $!");

POSIX::sigaction(
    &POSIX::SIGINT,
    POSIX::SigAction->new(
        'normal_sighandler' , POSIX::SigSet->new(), &POSIX::SA_NODEFER
    )
) or die("winbindd-wrapper could not set SIGINT handler: $!");

POSIX::sigaction(
    &POSIX::SIGTERM,
    POSIX::SigAction->new(
        'normal_sighandler' , POSIX::SigSet->new(), &POSIX::SA_NODEFER
    )
) or die("winbindd-wrapper could not set SIGTERM handler: $!");

POSIX::sigaction(
    &POSIX::SIGUSR1,
    POSIX::SigAction->new(
        'usr1_sighandler' , POSIX::SigSet->new(), &POSIX::SA_NODEFER
    )
) or die("winbindd-wrapper could not set SIGUSR1 handler: $!");

my %args;
getopts( 'dhvr', \%args );

pod2usage( -verbose => 1 ) if ( $args{h} );

my $daemonize = $args{d};
my $verbose   = $args{v};
my $restart   = $args{r};

my $pidfile = "${var_dir}/run/winbindd-wrapper.pid";

our $HAS_LOCK = 0;
open(my $fh,">>$pidfile");
flock($fh, LOCK_EX | LOCK_NB) or die "cannot lock $pidfile another winbindd-wrapper is running\n";
$HAS_LOCK = 1;

our $running = 1;
our $process = 0;

# standard signals and daemonize
daemonize($PROGRAM_NAME) if ($daemonize);
our $PARENT_PID = $$;


sub start {
    run_all_winbindd();
    Systemd::Daemon::notify( READY => 1, STATUS => "Ready", unset => 1 );
    while($running) {
        pause;
    }
}

start();

cleanup();

END {
    if ( !$args{h} && $HAS_LOCK ) {
        unless($IS_CHILD) {
            Systemd::Daemon::notify( STOPPING => 1 );
            deletepid();
            $logger->info("stopping winbindd-wrapper");
        }
    }
}

exit(0);

=head1 SUBROUTINES

=head2 cleanup

cleans after children

=cut

sub cleanup {
    signal_children('INT');
}

=head2 signal_children

sends a signal to all active children

=cut

sub signal_children {
    my ($signal) = @_;
    kill ( $signal, keys %CHILDREN);
}

=head2 normal_sighandler

the signal handler to shutdown the service

=cut

sub normal_sighandler {
    $running = 0;
}

=head2 run_all_winbindd

run all run_all_winbindd

=cut

sub run_all_winbindd {
    my $mask = POSIX::SigSet->new(POSIX::SIGCHLD());
    sigprocmask(SIG_BLOCK,$mask);
    foreach my $id (keys(%ConfigDomain)) {
        run_winbindd($id);
    }
    sigprocmask(SIG_UNBLOCK,$mask);
}

=head2 run_winbindd

creates a new child to run a task

=cut

sub run_winbindd {
    my ($id) = @_;
    my $chroot_path = pf::domain::chroot_path($id);
    my $binary = $Config{services}{winbindd_binary};
    my $config_file = "/etc/samba/$id.conf";
    my $log_directory = "/var/log/samba$id";

    my $pid = fork();
    if($pid) {
        $CHILDREN{$pid} = $id;
    } elsif ($pid == 0) {
        POSIX::sigaction(
            &POSIX::SIGCHLD,
            $old_child_sigaction,
        ) or die("winbindd-wrapper could not set SIGCHLD handler: $!");
        $IS_CHILD = 1;
        Log::Log4perl::MDC->put('tid', $$);
        unless(exec("sudo chroot $chroot_path $binary -s $config_file -l $log_directory --foreground")) {
            $logger->error("Failed to start the winbindd process $id");
            exit 1;
        }
    } else {
    }
}

=head2 child_sighandler

reaps the children

=cut

sub child_sighandler {
    local ($!, $?);
    while(1) {
        my $child = waitpid(-1, WNOHANG);
        last unless $child > 0;
        my $id = delete $CHILDREN{$child};
        if($running) {
            run_winbindd($id);
        }
    }
}

=head2 is_parent_alive

Checks to see if parent is alive

=cut

sub is_parent_alive {
    kill (0,$PARENT_PID)
}

=head2 alarm_sighandler

the alarm signal handler

=cut

sub alarm_sighandler {
    $ALARM_RECV = 1;
}

=head2 usr1_sighandler

the handler to cancel any currently running database queries

=cut

sub usr1_sighandler {
   db_cancel_current_query();
}

=head1 AUTHOR

Inverse inc. <info@inverse.ca>

Minor parts of this file may have been contributed. See CREDITS.

=head1 COPYRIGHT

Copyright (C) 2005-2018 Inverse inc.

Copyright (C) 2005 Kevin Amorin

Copyright (C) 2005 David LaPorte

=head1 LICENSE

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
of the License, 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
USA.

=cut


