#!/usr/bin/env perl
# polipo-run - Starts/restarts "polipo" (repeatedly, if necessary)
# License:  BSD-style [for this file only]
# Revision: 070709

#---------------------------------------------------------------------
#                            module setup
#---------------------------------------------------------------------

require 5.6.1;
use strict;
use Carp;
use warnings;
use Proc::ProcessTable;
                                # Trap warnings
$SIG {__WARN__} = sub { die @_; };

#---------------------------------------------------------------------
#                           basic constants
#---------------------------------------------------------------------

use constant ZERO  => 0;        # Zero
use constant ONE   => 1;        # One

use constant FALSE => 0;        # Boolean FALSE
use constant TRUE  => 1;        # Boolean TRUE

#---------------------------------------------------------------------
#                         program parameters
#---------------------------------------------------------------------

# Note:  The first parameter here is special. This parameter is set at
# "build" time by the "build" system.

my $BASEDIR             = "__META_PREFIX__";
my $CACHEDIR            = '/var/cache/polipo';
my $FLAGFILE_LIVEDISTRO = '/etc/sysconfig/ISLIVEDISTRO';
my $LOGDIR              = "$BASEDIR/log";
my $WWWDIR              = "$BASEDIR/www";

#---------------------------------------------------------------------
#                          global variables
#---------------------------------------------------------------------

my $MyPID = $$;                 # Process-ID number

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

my $TableObject = new Proc::ProcessTable;

sub GetPidUsingCmdLine
{
    my ($pattern) = @_;

    if (!defined ($pattern))
    {
        &LogMsg ("Internal error: missing pattern");
        exit ONE;
    }

    my $ProcessTable = $TableObject->table();

    for my $Process (@$ProcessTable)
    {
        my $cmd = $Process->cmndline;
        next unless defined $cmd;
        $cmd =~ s@^(/[A-Za-z0-9_\-/]+/)?(perl|python) +@@;
        next unless $cmd =~ m@$pattern@;
        my $PID = $Process->pid;
        next if $MyPID == $PID;
        return $PID;
    }

    undef;
}

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

sub StopOldPolipos
{
    my $SIGKILL =  9;
    my $SIGTERM = 15;
    my $signum  = $SIGTERM;
    my $done    = FALSE;
    my $pattern;
    my $PID;

    while (!$done)
    {
        $done    = TRUE;
        $pattern = '\bpolipo-run\b';

        if (defined ($PID = &GetPidUsingCmdLine ($pattern)))
        {
            print "Stopping old polipo-run (pid $PID)\n";
            kill ($signum, $PID);
            $done = FALSE;
        }

        $pattern = << 'END';
^(/[A-Za-z0-9_\-/]+/|\./)?polipo .*/polipo\.log
END
        $pattern =~ s@\s+\z@@s;

        if (defined ($PID = &GetPidUsingCmdLine ($pattern)))
        {
            print "Stopping old polipo (pid $PID)\n";
            kill ($signum, $PID);
            $done = FALSE;
        }

        $pattern = '\bpolipo -x\b';

        if (defined ($PID = &GetPidUsingCmdLine ($pattern)))
        {
            print "Stopping old polipo -x (pid $PID)\n";
            kill ($signum, $PID);
            $done = FALSE;
        }

        $pattern = '^(/[A-Za-z0-9_\-/]+/|\./)?polipo[_\-]*trimcache';

        if (defined ($PID = &GetPidUsingCmdLine ($pattern)))
        {
            print "Stopping old polipo-trimcache (pid $PID)\n";
            kill ($signum, $PID);
            $done = FALSE;
        }

        last if $done;
        sleep (ONE);
        $signum = $SIGKILL;
    }

    undef;
}

#---------------------------------------------------------------------
#                            main routine
#---------------------------------------------------------------------

sub Main
{
    my $data;                   # Data buffer
    my $pid;                    # Process-ID number
                                # Error-message prefix string
    my $EPREFIX = 'polipo-run error';

#---------------------------------------------------------------------
# Initial setup.

                                # Flag: Running in LiveDistro mode
    my $IsLiveDistro = (-f $FLAGFILE_LIVEDISTRO) ? TRUE : FALSE;

    &StopOldPolipos();          # Stop existing instances
    exit if fork;               # Proceed in the background

    $CACHEDIR =~ s@/*\z@/@;
    $LOGDIR   =~ s@/*\z@/@;
    $WWWDIR   =~ s@/*\z@/@;

    die "$EPREFIX: Invalid CACHEDIR setting\n"
        unless $CACHEDIR =~ m@^/[a-z0-9_\-\./]+/\z@i;

    die "$EPREFIX: Invalid LOGDIR setting\n"
        unless $LOGDIR =~ m@^/[a-z0-9_\-\./]+/\z@i;

    die "$EPREFIX: Invalid WWWDIR setting\n"
        unless $WWWDIR =~ m@^/[a-z0-9_\-\./]+/\z@i;

# Note: If the  "polipo" cache directory  already  exists,  it  may be
# huge. Therefore, "chown -R" and "chmod -R" should *not* be used with
# the directory in question.

    if ($IsLiveDistro)          # Running in LiveDistro mode?
    {                           # Yes - Reset the cache directory
        system "rm -fr $CACHEDIR";
    }

    system "mkdir -p             $CACHEDIR";
    system "chown nobody.nogroup $CACHEDIR";
    system "chmod 770            $CACHEDIR";

    system "mkdir -p                $LOGDIR $WWWDIR";
    system "chown -R nobody.nogroup $LOGDIR $WWWDIR";
    system "chmod -R 770            $LOGDIR $WWWDIR";

    die "$EPREFIX: Can't create $CACHEDIR\n" unless -d $CACHEDIR;
    die "$EPREFIX: Can't create $LOGDIR\n"   unless -d $LOGDIR;
    die "$EPREFIX: Can't create $WWWDIR\n"   unless -d $WWWDIR;

    my $POLIPO_EXE = "$BASEDIR/sbin/polipo";
    my $POLIPO_LOG = "$BASEDIR/log/polipo.log";

    if ((!-f $POLIPO_EXE) || (!-x $POLIPO_EXE))
    {
        die "$EPREFIX: Not a valid executable: $POLIPO_EXE\n";
    }

#---------------------------------------------------------------------
# Additional LiveDistro setup.

    my $CFGFILE = "$BASEDIR/etc/polipo/config";
    my $WSZ     = '[\011\040]*';

    if ($IsLiveDistro)          # Running in LiveDistro mode?
    {                           # Yes
        unlink $POLIPO_LOG;
        $POLIPO_LOG = '/dev/null';

        open (IFD, "<$CFGFILE") || die "$EPREFIX: $!:\n$CFGFILE\n";
        binmode IFD;
        undef $/;
        $data = <IFD>;
        $data = "" if !defined $data;
        close IFD;

        $data =~ s@\s*\z@\n@;
        $data =~ s@(^|\n)${WSZ}(diskCacheRoot|chunkHighMark|objectHighMark)\b@$1# $2@g;

        if ($data !~ s@(^|\n)${WSZ}#${WSZ}(diskCacheRoot${WSZ}=${WSZ}"")@$1$2@)
        {
            $data .= "diskCacheRoot = \"\"\n";
        }

        if ($data !~ s@(^|\n)${WSZ}#${WSZ}(chunkHighMark${WSZ}=${WSZ}819200)\b@$1$2@)
        {
            $data .= "chunkHighMark = 819200\n";
        }

        if ($data !~ s@(^|\n)${WSZ}#${WSZ}(objectHighMark${WSZ}=${WSZ}128)\b@$1$2@)
        {
            $data .= "objectHighMark = 128\n";
        }

        open (OFD, ">$CFGFILE") || die "$EPREFIX: $!:\n$CFGFILE\n";
        print OFD $data;
        close OFD;
    }

#---------------------------------------------------------------------
# Main loop,

    while (TRUE)
    {
        if ((!-f $POLIPO_EXE) || (!-x $POLIPO_EXE))
        {
            die "$EPREFIX: Executable disappeared: $POLIPO_EXE\n";
        }

        my $time1 = time;
        system "$POLIPO_EXE logFile=$POLIPO_LOG";
        my $time2 = time;

        if (($time2 - $time1) < 5)
        {
            die "$EPREFIX: Couldn't start \"polipo\"\n";
        }
    }

    undef;                      # Shouldn't be reached
}

#---------------------------------------------------------------------
#                            main program
#---------------------------------------------------------------------

&Main();                        # Call the main routine
exit ONE;                       # Shouldn't be reached
