#!/usr/bin/perl -wT
######################################################################
#
# $Id: webjob-compress-upload,v 1.10 2012/01/07 08:01:16 mavrik Exp $
#
######################################################################
#
# Copyright 2006-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Compress the group of files created by a WebJob upload.
#
######################################################################

use strict;
use File::Basename;
use Getopt::Std;

######################################################################
#
# Main Routine
#
######################################################################

  ####################################################################
  #
  # Punch in and go to work.
  #
  ####################################################################

  my ($sProgram);

  $sProgram = basename(__FILE__);

  ####################################################################
  #
  # Get Options.
  #
  ####################################################################

  my (%hOptions);

  if (!getopts('m:r:p:x:', \%hOptions))
  {
    Usage($sProgram);
  }

  ####################################################################
  #
  # The compression mode, '-m', is optional.
  #
  ####################################################################

  my ($sMode, $sCompressor);

  $sMode = (exists($hOptions{'m'})) ? lc($hOptions{'m'}) : "gzip";

  if ($sMode =~ /^gzip$/)
  {
    $sCompressor = "gzip";
  }
  elsif ($sMode =~ /^bzip2$/)
  {
    $sCompressor = "bzip2";
  }
  elsif ($sMode =~ /^compress$/)
  {
    $sCompressor = "compress";
  }
  elsif ($sMode =~ /^none$/)
  {
    exit(0); # There's nothing to do.
  }
  else
  {
    print STDERR "$sProgram: Mode='$sMode' Error='Invalid compression mode. Use bzip2, compress, gzip, or none.'\n";
    exit(2);
  }

  ####################################################################
  #
  # A user-specified path, '-p', is optional.
  #
  ####################################################################

  my ($sPath);

  $sPath = (exists($hOptions{'p'})) ? $hOptions{'p'} : "/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin:/usr/local/webjob/bin";

  if ($sPath =~ /^([\w\\\/:.-]+)$/)
  {
    $sPath = $1;
  }
  else
  {
    print STDERR "$sProgram: Path='$sPath' Error='Path does not pass muster.'\n";
    exit(3);
  }
  $ENV{'PATH'} = $sPath;

  ####################################################################
  #
  # A .rdy file, '-r', is required.
  #
  ####################################################################

  my ($sRdyFile);

  if (!exists($hOptions{'r'}) || !defined($hOptions{'r'}) || length($hOptions{'r'}) < 1)
  {
    Usage($sProgram);
  }
  $sRdyFile = $hOptions{'r'};

  if ($sRdyFile =~ /^([\w\\\/.-]+\.rdy)$/)
  {
    $sRdyFile = $1;
  }
  else
  {
    print STDERR "$sProgram: File='$sRdyFile' Error='Name does not pass muster.'\n";
    exit(4);
  }

  ####################################################################
  #
  # A list of extra extensions, '-x', is optional.
  #
  ####################################################################

  my (@aExtensions, $sExtraExtensions);

  $sExtraExtensions = (exists($hOptions{'x'})) ? $hOptions{'x'} : "";

  @aExtensions = qw(.err .env .out .rdy);

  if ($sExtraExtensions =~ /^((?:\.[\w.-]+)(?:,\.[\w.-]+)*)$/)
  {
    foreach my $sExtension (split(/,/, $1))
    {
      push(@aExtensions, $sExtension);
    }
  }
  else
  {
    if (length($sExtraExtensions))
    {
      print STDERR "$sProgram: List='$sExtraExtensions' Error='The list of extra extensions does not pass muster.'\n";
      exit(6);
    }
  }

  ####################################################################
  #
  # If there's any arguments left, it's an error.
  #
  ####################################################################

  if (scalar(@ARGV) > 0)
  {
    Usage($sProgram);
  }

  ####################################################################
  #
  # Compute filenames, and verify their existence.
  #
  ####################################################################

  my (@aFiles);

  foreach my $sExtension (@aExtensions)
  {
    my $sFile = $sRdyFile;
    $sFile =~ s/\.rdy$/$sExtension/;
    if (!-f $sFile)
    {
      print STDERR "$sProgram: File='$sFile' Warning='File does not exist, so it will be skipped.'\n";
      next;
    }
    push(@aFiles, $sFile);
  }

  ####################################################################
  #
  # Compress files.
  #
  ####################################################################

  qx($sCompressor -f @aFiles);

  ####################################################################
  #
  # Cleanup and go home.
  #
  ####################################################################

  1;


######################################################################
#
# Usage
#
######################################################################

sub Usage
{
  my ($sProgram) = @_;
  print STDERR "\n";
  print STDERR "Usage: $sProgram [-m {bzip2|compress|gzip|none}] [-p path] [-x ext[,ext[,...]]] -r rdy\n";
  print STDERR "\n";
  exit(1);
}


=pod

=head1 NAME

webjob-compress-upload - Compress the group of files created by a WebJob upload.

=head1 SYNOPSIS

B<webjob-compress-upload> B<[-m {bzip2|compress|gzip|none}]> B<[-p path]> B<[-x ext[,ext[,...]]]> B<-r rdy>

=head1 DESCRIPTION

This utility compresses the group of files created by a WebJob upload.
The specified B<rdy> file identifies the group to be processed.  A
group consists of four files having the following naming convention:

    <file>.env
    <file>.err
    <file>.out
    <file>.rdy

The B<rdy> file is used as the group identifier because it is the last
file created by nph-webjob.cgi, and its existence implies that all
files in the group have been written to disk.  In other words, the
group is "ready" to be processed.

This utility was designed to work with WebJob's server-side triggers
and configuration overrides.  For example, suppose that you want to
automatically compress (using bzip2) the output produced by a job
called 'hourly'.  To do that, you would create a custom config file
override, and populate it with the controls shown below.

     --- nph-webjob.cfg ---
     PutTriggerEnable=Y
     PutTriggerCommandLine=webjob-compress-upload -m bzip2 -r %rdy
     --- nph-webjob.cfg ---

Make sure that webjob-compress-upload and your chosen compression
utility (bzip2 in this case) are in the Apache user's PATH.  If they
aren't, you can use the B<-p> option to specify an alternate PATH.

Store the newly created config file in the following directory, which
you'll probably need to create:

    /var/webjob/config/nph-webjob/commands/hourly

=head1 OPTIONS

=over 4

=item B<-m {bzip2|compress|gzip|none}>

Specifies the compression method to use.  The default value for this
option is 'gzip'.  If a value of 'none' is specified, no action is
taken, and the script simply exits.

=item B<-p path>

Specifies an alternate PATH to use.  The default value for this option
is:

  /sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin:/usr/local/bin:/usr/local/webjob/bin

Use this option if your compression utilities live outside of the
default PATH.

=item B<-r rdy>

Specifies the name of the B<rdy> file to process.

=item B<-x ext[,ext[,...]]>

Specifies a comma separated list of extra extensions.  The files
associated with these extensions will be added to the group of files
that are to be compressed.  Any files in the group that don't exist
will be skipped.

=back

=head1 AUTHOR

Klayton Monroe

=head1 SEE ALSO

webjob(1), nph-webjob.cgi(1)

=head1 LICENSE

All documentation and code are distributed under same terms and
conditions as B<WebJob>.

=cut
