package Cpanel::Easy::Utils::BackGround;

# cpanel - Cpanel/Easy/Utils/BackGround.pm        Copyright(c) 2014 cPanel, Inc.
#                                                           All rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cpanel license. Unauthorized copying is prohibited

use strict;
use warnings;
no warnings qw(redefine);
use Time::HiRes ();

use Cpanel::OSSys;
use Acme::Spork;    # no -reopen_stdfhs (see spork() call below*)
*Acme::Spork::setsid = *Cpanel::OSSys::setsid;

sub run_install_in_bg {
    my ( $self, $profile ) = @_;

    if ( $Acme::Spork::VERSION < 0.000008 ) {
        $self->_header();

        $self->print_alert_color( 'red', q{The perl module '[_1]' (v[_2]) is too old to use. It must be at least version '[_3]'.}, 'Acme::Spork', $Acme::Spork::VERSION, '0.0.8' );

        if ( ref $self->{'ui_obj'} eq 'Cpanel::Easy::Apache::UI::HTML' ) {
            $self->print_alert( q{To install it <a target="_blank" href="[_1]">click here</a>}, '/scripts2/module_installers_live_install?lang=perl&mod=Acme::Spork' );
        }
        else {
            $self->print_alert( q{To install it execute this command as root '[_1]'}, '/usr/local/cpanel/scripts/perlinstaller Acme::Spork' );
        }

        $self->_footer();
        exit;
    }

    require Cpanel::Rand;
    my $file = Cpanel::Rand::get_tmp_file_by_name(qw( /tmp/ .cpanel_easy- ));
    chmod 0600, $file;

    open my $file_fh, '>', $file or die;
    close $file_fh;

    my $spid = Acme::Spork::spork(
        sub {
            my ( $self, $profile, $file ) = @_;
            open STDIN,  '</dev/null';
            open STDOUT, ">$file";       # * this is why we don't use -reopen_stdfhs
            open STDERR, '>&STDOUT';

            # so if browser/terminal dies this will continue and still only allow one at a time
            unlink $self->{'pid_file'};
            $self->{'pid_obj'}->{'unlink_end_use_current_pid_only'} = 1;
            $self->{'pid_obj'}->pid_file( $self->{'pid_file'} );

            $0 = "[bg installer] $0";
            my $rc = $self->install_profile($profile);
            $self->{'_'}{'restore_backup'}++ if !$rc;
            Cpanel::FileUtils::touchfile("$file.ensurepkgs_error") if $self->{'_'}{'ensurepkgs_failure'};
            if ( $self->{'_'}{'restore_backup'} ) {
                Cpanel::FileUtils::touchfile("$file.had_error");
                Cpanel::FileUtils::touchfile("$file.rawopts_used") if $self->{'_'}{'rawopt_was_used'};
                Cpanel::FileUtils::touchfile("$file.rawenv_used")  if $self->{'_'}{'rawenv_was_used'};
            }

            print "-process complete-\n";    # do not remove or change
        },
        $self,
        $profile,
        $file,
    );

    return ( $file, $spid );
}

sub tail_file_while_pid {
    my ( $self, $args_hr ) = @_;
    my $keep = 0;
    $keep = 1 if $self->get_param('debug');

    return if $args_hr->{'pid'} eq '1';

    local $SIG{'HUP'}  = $self->get_basic_sig_handler( 'HUP',  1, 1, 'Continuing Build in background' );
    local $SIG{'TERM'} = $self->get_basic_sig_handler( 'TERM', 1, 1, 'Continuing Build in background' );
    local $SIG{'INT'}  = $self->get_basic_sig_handler( 'INT',  1, 1, 'Continuing Build in background' );
    local $SIG{'QUIT'} = $self->get_basic_sig_handler( 'QUIT', 1, 1, 'Continuing Build in background' );

    $self->debug( { 'message' => [ q{Checking arguments to tail '[_1]' while PID '[_2]' is running}, $args_hr->{'file'}, $args_hr->{'pid'}, ], } );

    # file is good ?
    if ( !$args_hr->{'file'} || !-e $args_hr->{'file'} || !-r $args_hr->{'file'} ) {

        local $! =
            !-e $args_hr->{'file'} ? 2
          : !-r $args_hr->{'file'} ? 13
          :                          22;

        $self->print_to_log_and_screen( $self->maketext( q{Argument '[_1]' passed to '[_2]' is unuseable: [_3]}, 'file', 'tail_file_while_pid()', $!, ), );

        unlink $args_hr->{'file'} if !$keep;
        return;
    }

    # print is ok ?
    if ( ref $args_hr->{'print'} ne 'CODE' ) {
        $args_hr->{'print'} = sub {
            shift;
            return shift;
        };
    }

    # pid is ok ?
    $args_hr->{'pid'} = int $args_hr->{'pid'};
    if ( !$args_hr->{'pid'} ) {

        local $! = 22;
        $self->print_to_log_and_screen( $self->maketext( q{Argument '[_1]' passed to '[_2]' is unuseable: [_3]}, 'pid', 'tail_file_while_pid()', $!, ), );

        unlink $args_hr->{'file'} if !$keep;
        return;
    }

    $self->{'pid_obj'} = Unix::PID->new() if !$self->{'pid_obj'};
    if ( !$self->{'pid_obj'}->is_pid_running( $args_hr->{'pid'} ) ) {

        if ( open my $sprk_fh, '<', $args_hr->{'file'} ) {
            sleep 2;    # do not remove this

            my $line;
          LINE_READ:
            while ( $line = readline($sprk_fh) ) {

                chomp $line;
                last LINE_READ if $line =~ m{-process complete-};    # do not print this line

                print $args_hr->{'print'}->( $self, "$line\n" ) if $line;

                # extra check, (english only) already printed it might as well stop now
                last LINE_READ if $line =~ m{\!\! Ending at};
                last LINE_READ if !defined $line;
            }
            close $sprk_fh;
        }
        else {
            local $! = 3;
            $self->print_to_log_and_screen( $self->maketext( q{Argument '[_1]' passed to '[_2]' is unuseable: [_3]}, 'pid', 'tail_file_while_pid()', $!, ), );
        }

        unlink $args_hr->{'file'} if !$keep;
        return;
    }

    $self->debug(
        {
            'skip_caller_info' => 1,
            'message'          => [ q{Starting to tail '[_1]' while PID '[_2]' is running}, $args_hr->{'file'}, $args_hr->{'pid'}, ],
        }
    );

    require File::Tail;
    local $self->{'tail_obj'} = File::Tail->new(
        'tail'        => -1,                   # start at beginning of file
        'name'        => $args_hr->{'file'},
        'interval'    => 1,
        'maxinterval' => 2

          # 'nowait' => 1, # have read() return an empty string instead of block
    );

    sleep 1;

    $self->debug(
        {
            'skip_caller_info' => 1,
            'message'          => [ q{Stringified '[_1]': '[_2]'}, 'tail_obj', $self->{'tail_obj'}, ],
        }
    );

    $|++;
    my $line;
  TAIL:
    while ( defined( $line = $self->_get_line ) ) {

        chomp $line;
        last TAIL if $line =~ m{-process complete-};    # do not print this line

        print $args_hr->{'print'}->( $self, "$line\n" ) if $line;

        # extra check, (english only) already printed it might as well stop now
        last TAIL if $line =~ m{\!\! Ending at};
        last TAIL if !defined $line;
    }

    unlink $args_hr->{'file'} if !$keep;
}

sub _get_line {
    my ($self) = @_;

    local $SIG{'ALRM'} = sub { exit; };
    alarm(600);

    my $line = $self->{'tail_obj'}->read;

    alarm(0);

    last TAIL if !defined $line;
    return defined $line ? $line : undef;
}

1;
