#!/usr/bin/perl -w
######################################################################
#
# $Id: webjob-jqd-create-queue,v 1.12 2012/01/07 08:01:18 mavrik Exp $
#
######################################################################
#
# Copyright 2007-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Create one or more job queues.
#
######################################################################

use FindBin qw($Bin $RealBin); use lib ("$Bin/../lib/perl5/site_perl", "$RealBin/../lib/perl5/site_perl", "/usr/local/webjob/lib/perl5/site_perl", "/opt/local/webjob/lib/perl5/site_perl");

use strict;
use File::Basename;
use Getopt::Std;
use WebJob::JqdRoutines 1.024;
use WebJob::Properties 1.035;

BEGIN
{
  ####################################################################
  #
  # The Properties hash is essentially private. Those parts of the
  # program that wish to access or modify the data in this hash need
  # to call GetProperties() to obtain a reference.
  #
  ####################################################################

  my (%hProperties);

  ####################################################################
  #
  # Initialize regex variables.
  #
  ####################################################################

  $hProperties{'CommonRegexes'} = PropertiesGetGlobalRegexes();

  ####################################################################
  #
  # Define helper routines.
  #
  ####################################################################

  sub GetProperties
  {
    return \%hProperties;
  }
}

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

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

  my ($phProperties);

  $phProperties = GetProperties();

  $$phProperties{'Program'} = basename(__FILE__);

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

  my (%hOptions);

  if (!getopts('d:g:lm:o:pqu:', \%hOptions))
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # A job queue directory, '-d', is optional.
  #
  ####################################################################

  $$phProperties{'JobQueueDirectory'} = (exists($hOptions{'d'})) ? $hOptions{'d'} : "/var/webjob/spool/jqd";

  ####################################################################
  #
  # The default queue group, '-g', is optional.
  #
  ####################################################################

  my ($sGid);

  $sGid = $(; $sGid =~ s/^(\d+).*$/$1/;

  $$phProperties{'QueueGroup'} = (exists($hOptions{'g'})) ? $hOptions{'g'} : $sGid;

  ####################################################################
  #
  # The lock queue flag, '-l', is optional.
  #
  ####################################################################

  $$phProperties{'LockQueue'} = (exists($hOptions{'l'})) ? 1 : 0;

  ####################################################################
  #
  # The default queue permissions, '-m', are optional.
  #
  ####################################################################

  $$phProperties{'QueuePermissions'} = (exists($hOptions{'m'})) ? $hOptions{'m'} : "0775";

  ####################################################################
  #
  # The default queue owner, '-o', is optional.
  #
  ####################################################################

  my ($sUid);

  $sUid = $<; $sUid =~ s/^(\d+).*$/$1/;

  $$phProperties{'QueueOwner'} = (exists($hOptions{'o'})) ? $hOptions{'o'} : $sUid;

  ####################################################################
  #
  # The public flag, '-p', is optional.
  #
  ####################################################################

  $$phProperties{'Public'} = (exists($hOptions{'p'})) ? 1 : 0;

  ####################################################################
  #
  # The be quiet flag, '-q', is optional.
  #
  ####################################################################

  $$phProperties{'BeQuiet'} = (exists($hOptions{'q'})) ? 1 : 0;

  ####################################################################
  #
  # The default umask, '-u', is optional.
  #
  ####################################################################

  $$phProperties{'Umask'} = (exists($hOptions{'u'})) ? $hOptions{'u'} : "0002";

  umask(oct($$phProperties{'Umask'}));

  ####################################################################
  #
  # If there isn't at least one argument left, it's an error.
  #
  ####################################################################

  if (scalar(@ARGV) < 1)
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # Make sure the job queue directory exists.
  #
  ####################################################################

  if (!-d $$phProperties{'JobQueueDirectory'})
  {
    print STDERR "$$phProperties{'Program'}: Error='Base directory ($$phProperties{'JobQueueDirectory'}) does not exist.'\n";
    exit(2);
  }

  ####################################################################
  #
  # Acquire the index lock. This is an exclusive lock.
  #
  ####################################################################

  my (%hIndexLockArgs);

  $$phProperties{'QueueIndex'} = $$phProperties{'JobQueueDirectory'} . "/" . "queue.index";

  %hIndexLockArgs =
  (
    'LockFile' => $$phProperties{'QueueIndex'} . ".lock",
    'LockMode' => "+<", # The file must exist or this will fail.
  );
  if (!JqdLockFile(\%hIndexLockArgs))
  {
    print STDERR "$$phProperties{'Program'}: Error='$hIndexLockArgs{'Error'}'\n";
    exit(2);
  }

  ####################################################################
  #
  # If the queue index does not exist, create it.
  #
  ####################################################################

  if (!-f $$phProperties{'QueueIndex'})
  {
    if (!open(FH, "> $$phProperties{'QueueIndex'}"))
    {
      print STDERR "$$phProperties{'Program'}: Error='File ($$phProperties{'QueueIndex'}) could not be created ($!).'\n";
      exit(2);
    }
    close(FH);
  }

  ####################################################################
  #
  # Conditionally create forward/reverse queue maps.
  #
  ####################################################################

  my (%hForwardQueueMap, %hReverseQueueMap);

  my %hQueueMapArgs =
  (
    'ForwardQueueMap'   => \%hForwardQueueMap,
    'JobQueueDirectory' => $$phProperties{'JobQueueDirectory'},
    'KeyRegex'          => "(?:" . $$phProperties{'CommonRegexes'}{'ClientId'} . "|" . $$phProperties{'CommonRegexes'}{'ClientSuppliedFilename'} . ")",
    'ReverseQueueMap'   => \%hReverseQueueMap,
    'SharedLock'        => 0, # We already have an exclusive lock.
    'ValueRegex'        => "\\d+",
  );
  if (!JqdSetupQueueMaps(\%hQueueMapArgs))
  {
    print STDERR "$$phProperties{'Program'}: Error='$hQueueMapArgs{'Error'}'\n";
    exit(2);
  }

  ####################################################################
  #
  # Attempt to create each queue in the specified list. Spit out a
  # warning if the queue's top-level directory already exists.
  #
  ####################################################################

  my ($sIndexUpdated, $sQueueId) = (0, 0);

  foreach my $sQueue (@ARGV)
  {
    if ($$phProperties{'Public'})
    {
      if (exists($hForwardQueueMap{$sQueue}))
      {
        $sQueueId = $hForwardQueueMap{$sQueue};
      }
      else
      {
        foreach my $i (1..999)
        {
          if (!exists($hReverseQueueMap{$i}))
          {
            $sQueueId = $i;
            last;
          }
        }
        $sIndexUpdated = 1;
      }
    }
    my %hLArgs =
    (
      'BeQuiet'           => $$phProperties{'BeQuiet'},
      'JobQueueDirectory' => $$phProperties{'JobQueueDirectory'},
      'LockQueue'         => $$phProperties{'LockQueue'},
      'Queue'             => $sQueue,
      'QueueGroup'        => $$phProperties{'QueueGroup'},
      'QueueOwner'        => $$phProperties{'QueueOwner'},
      'QueuePermissions'  => $$phProperties{'QueuePermissions'},
    );
    if (!JqdCreateQueue(\%hLArgs))
    {
      print STDERR "$$phProperties{'Program'}: Error='$hLArgs{'Error'}'\n";
      last;
    }
    if ($$phProperties{'Public'} && $sIndexUpdated)
    {
      $hForwardQueueMap{$sQueue} = $sQueueId;
      $hReverseQueueMap{$sQueueId} = $sQueue;
    }
  }

  ####################################################################
  #
  # Conditionally update the queue index.
  #
  ####################################################################

  if ($sIndexUpdated)
  {
    $$phProperties{'TempQueueIndex'} = $$phProperties{'JobQueueDirectory'} . "/" . "queue.index.temp";
    if (!open(FH, "> $$phProperties{'TempQueueIndex'}"))
    {
      print STDERR "$$phProperties{'Program'}: Error='File ($$phProperties{'TempQueueIndex'}) could not be opened ($!)'\n";
      JqdUnlockFile(\%hIndexLockArgs);
      exit(2);
    }
    foreach my $sQueue (sort(keys(%hForwardQueueMap)))
    {
      print FH "$sQueue=$hForwardQueueMap{$sQueue}\n";
    }
    close(FH);
    if (!rename($$phProperties{'TempQueueIndex'}, $$phProperties{'QueueIndex'}))
    {
      print STDERR "$$phProperties{'Program'}: Error='Unable update the queue.index ($!).";
      JqdUnlockFile(\%hIndexLockArgs);
      exit(2)
    }
  }

  ####################################################################
  #
  # Shutdown and go home.
  #
  ####################################################################

  JqdUnlockFile(\%hIndexLockArgs);

  1;


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

sub Usage
{
  my ($sProgram) = @_;
  print STDERR "\n";
  print STDERR "Usage: $sProgram [-lpq] [-d jqd-dir] [-g group] [-m mode] [-o owner] [-u umask] queue [queue ...]\n";
  print STDERR "\n";
  exit(1);
}


=pod

=head1 NAME

webjob-jqd-create-queue - Create one or more job queues.

=head1 SYNOPSIS

B<webjob-jqd-create-queue> B<[-lpq]> B<[-d jqd-dir]> B<[-g group]> B<[-m mode]> B<[-o owner]> B<[-u umask]> B<queue [queue ...]>

=head1 DESCRIPTION

This utility creates one or more job queues.

=head1 OPTIONS

=over 4

=item B<-d jqd-dir>

Specifies the base directory where queues are to be created. This
directory must exist. The default value is /var/webjob/spool/jqd.

=item B<-g group>

Specifies the queue's default group. This group will be assigned to
every directory created.

=item B<-l>

This option indicates that the queue is to be locked upon creation.

=item B<-m mode>

Specifies the queue's default mode (i.e., permissions). This mode will
be applied to every directory created.

=item B<-o owner>

Specifies the queue's default owner. This owner will be assigned to
every directory created.

=item B<-p>

Marks all queues being created as public. This results in each queue
being assigned a unique queue ID.

=item B<-q>

Be quiet (i.e., only report errors) while executing.

=item B<-u umask>

Specifies the default umask.

=back

=head1 AUTHOR

Klayton Monroe

=head1 SEE ALSO

webjob-jqd-delete-queue(1)

=head1 LICENSE

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

=cut
