#!/usr/bin/perl

use strict;
use FindBin;
use lib ("$FindBin::RealBin/../lib");

use Getopt::Long;
use Dimedis::Logger;
use Fcntl ':flock';
use Sys::Hostname;

$| = 1;

my $USAGE = <<__EOU;

Usage: logger-prepare-sync [options]

Options:
    -l log-dir      Logging directory (Default: /usr/projects/logger/var/logs)
    -p project      Project name (Default: project)
    -t timeout      Lock timeout (Default: 30 seconds)

Description:
    This program runs locally on all logging clients. It prepares
    the local logging directory (for the given project) by
    renaming it to a unique temporary name and waits until all
    file locks in the directory are gone. This ensures that no
    programs are still writing to open log files inside the
    directory.

__EOU

main: {
    my $log_dir = undef;
    my $project = undef;
    my $timeout = 30;
    my $help    = 0;
    
    my $ok = GetOptions (
        "log-dir|l=s"   => \$log_dir,
        "project|p=s"   => \$project,
        "timeout|t=i"   => \$timeout,
        "help|h"        => \$help,
    );
    
    usage() unless $ok and not @ARGV;
    usage() if $help;

    my $logger = Dimedis::Logger->new;
    
    $logger->init(
        log_dir => $log_dir,
        project => $project,
    );

    $project = $logger->get_project;
    $log_dir = $logger->get_log_dir;

    my @d = localtime(time);
    my $stamp = sprintf("%04d%02d%02d-%02d%02d%02d",
        $d[5]+1900, $d[4]+1, $d[3],
        $d[2], $d[1], $d[0]
    );

    my $dir = $logger->get_log_dir."/$project";
    my $dir_sync = $dir.".".hostname().".$stamp.$$.sync";

    die "Sync direction '$dir_sync' exists!" if -d $dir_sync;

    if ( not -d $dir ) {
        print "* No log directory found: $dir\n";
        print "* Exiting!\n";
        exit 0;
    }

    rename($dir, $dir_sync)
        or die "Can't rename '$dir' to '$dir_sync': $!";

    my @files = glob("$dir_sync/*");

    my @fh;
    foreach my $file ( @files ) {
        print "* Locking $file ... ";
        open (my $fh, ">>", $file)
            or die "Can't append to '$file': $!";

        eval {
            local $SIG{ALRM} = sub { die "alarm\n" };
            alarm $timeout;
            flock($fh, LOCK_EX);
            alarm 0;
        };

        if ( $@ and $@ eq "alarm\n" ) {
            print "Error\n";
            print "Error: timeout on locking '$file'\nAborting!\n";
            exit 1;
        }
        elsif ( $@ ) {
            die $@;
        }
        
        print "Ok\n";
        
        push @fh, $fh;
    }
    
    close($_) for @fh;
    
    print "* SYNC_DIR=$dir_sync\n";
    
    exit 0;
}

sub usage {
    print $USAGE;
    exit 1;
}
