#!/usr/bin/perl -w
######################################################################
#
# $Id: webjob-mldbm-get-job-kvps,v 1.21 2012/01/07 08:01:23 mavrik Exp $
#
######################################################################
#
# Copyright 2004-2012 The WebJob Project, All Rights Reserved.
#
######################################################################
#
# Purpose: Get job- or client-related key/value pairs.
#
######################################################################

use strict;
use Fcntl qw(:DEFAULT :flock);
use File::Basename;
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 Getopt::Std;
use WebJob::MldbmRoutines;
use WebJob::Properties 1.008;

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);

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

  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('c:d:j:o:r', \%hOptions))
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # A client ID, '-c', is required.
  #
  # N.B. We do not test the contents of this right away, because it
  # may not be a client ID after all.
  #
  ####################################################################

  $$phProperties{'ClientId'} = (exists($hOptions{'c'})) ? $hOptions{'c'} : undef;

  if (!defined($$phProperties{'ClientId'}))
  {
    Usage($$phProperties{'Program'});
  }

  ####################################################################
  #
  # A database, '-d', is optional.
  #
  ####################################################################

  $$phProperties{'DbFile'} = (exists($hOptions{'d'})) ? $hOptions{'d'} : "/var/webjob/db/mldbm/client.db";

  ####################################################################
  #
  # A job, '-j', is required.
  #
  ####################################################################

  $$phProperties{'Job'} = (exists($hOptions{'j'})) ? $hOptions{'j'} : undef;

  if (!defined($$phProperties{'Job'}))
  {
    Usage($$phProperties{'Program'});
  }

  if ($$phProperties{'Job'} !~ /^$$phProperties{'CommonRegexes'}{'MldbmKeyName'}$/)
  {
    print STDERR "$$phProperties{'Program'}: Job='$$phProperties{'Job'}' Error='Value does not pass muster.'\n";
    exit(2);
  }

  ####################################################################
  #
  # The option list, '-o', is optional.
  #
  ####################################################################

  $$phProperties{'BeQuiet'} = 0;
  $$phProperties{'KeysOnly'} = 0;
  $$phProperties{'ValuesOnly'} = 0;

  $$phProperties{'Options'} = (exists($hOptions{'o'})) ? $hOptions{'o'} : undef;

  if (exists($hOptions{'o'}) && defined($hOptions{'o'}))
  {
    foreach my $sActualOption (split(/,/, $$phProperties{'Options'}))
    {
      foreach my $sTargetOption ('BeQuiet', 'KeysOnly', 'ValuesOnly')
      {
        if ($sActualOption =~ /^$sTargetOption$/i)
        {
          $$phProperties{$sTargetOption} = 1;
        }
      }
    }
  }

  if ($$phProperties{'KeysOnly'} && $$phProperties{'ValuesOnly'})
  {
    print STDERR "$$phProperties{'Program'}: Error='Conflicting options. Use either 'KeysOnly' or 'ValuesOnly', but not both.'\n";
    exit(2);
  }

  ####################################################################
  #
  # The reverse keys flag, '-r', is optional.
  #
  ####################################################################

  $$phProperties{'ReverseKeys'} = (exists($hOptions{'r'})) ? 1 : 0;

  ####################################################################
  #
  # If there are no arguments left, get all key/value pairs.
  #
  ####################################################################

  $$phProperties{'GetAll'} = (scalar(@ARGV) == 0) ? 1 : 0;

  ####################################################################
  #
  # Connect to the specified DB.
  #
  ####################################################################

  my ($phDb, $phDbContext, $sLocalError);

  $phDbContext = MldbmNewContext
  (
    {
      'DbFile'     => $$phProperties{'DbFile'},
      'DbFlags'    => O_RDONLY,
      'LockFlags'  => LOCK_SH,
      'LockMode'   => "<",
    }
  );
  if (!MldbmConnect($phDbContext, \$sLocalError))
  {
    print STDERR "$$phProperties{'Program'}: Error='$sLocalError'\n";
    exit(2);
  }
  $phDb = $$phDbContext{'DbHandle'};

  ####################################################################
  #
  # Iterate through the DB hash.
  #
  ####################################################################

  my ($sUnknownClients, $sUnknownJobs, $sUnknownKeys) = (0, 0, 0);

  if ($$phProperties{'ReverseKeys'})
  {
    my $sKey = $$phProperties{'ClientId'};
    if ($sKey !~ /^$$phProperties{'CommonRegexes'}{'MldbmKeyName'}$/)
    {
      print STDERR "$$phProperties{'Program'}: Warning='Key ($sKey) does not pass muster. Proceeding anyway.'\n";
    }
    if ($$phProperties{'GetAll'})
    {
      foreach my $sClientId (keys(%$phDb))
      {
        if ($sClientId !~ /^$$phProperties{'CommonRegexes'}{'ClientId'}$/)
        {
          print STDERR "$$phProperties{'Program'}: Warning='Client ($sClientId) does not pass muster. Proceeding anyway.'\n";
        }
        if (exists($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}))
        {
          if (exists($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey}))
          {
            my $sValue = (defined($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey})) ? $$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey} : "";
            if ($$phProperties{'KeysOnly'})
            {
              print STDOUT $sClientId, "\n";
            }
            elsif ($$phProperties{'ValuesOnly'})
            {
              print STDOUT $sValue, "\n";
            }
            else
            {
              print STDOUT join("=", $sClientId, $sValue), "\n";
            }
          }
          else
          {
            print STDERR "$$phProperties{'Program'}: Warning='Key ($sKey) does not exist in $$phProperties{'Job'} for $sClientId.'\n" unless ($$phProperties{'BeQuiet'});
            $sUnknownKeys = 1;
          }
        }
        else
        {
          print STDERR "$$phProperties{'Program'}: Warning='Job ($$phProperties{'Job'}) does not exist for $sClientId.'\n" unless ($$phProperties{'BeQuiet'});
          $sUnknownJobs = 1;
        }
      }
    }
    else
    {
      foreach my $sClientId (@ARGV)
      {
        if ($sClientId !~ /^$$phProperties{'CommonRegexes'}{'ClientId'}$/)
        {
          print STDERR "$$phProperties{'Program'}: Warning='Client ($sClientId) does not pass muster. Proceeding anyway.'\n";
        }
        if (exists($$phDb{$sClientId}))
        {
          if (exists($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}))
          {
            if (exists($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey}))
            {
              my $sValue = (defined($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey})) ? $$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey} : "";
              if ($$phProperties{'KeysOnly'})
              {
                print STDOUT $sClientId, "\n";
              }
              elsif ($$phProperties{'ValuesOnly'})
              {
                print STDOUT $sValue, "\n";
              }
              else
              {
                print STDOUT join("=", $sClientId, $sValue), "\n";
              }
            }
            else
            {
              print STDERR "$$phProperties{'Program'}: Warning='Key ($sKey) does not exist in $$phProperties{'Job'} for $sClientId.'\n" unless ($$phProperties{'BeQuiet'});
              $sUnknownKeys = 1;
            }
          }
          else
          {
            print STDERR "$$phProperties{'Program'}: Warning='Job ($$phProperties{'Job'}) does not exist for $sClientId.'\n" unless ($$phProperties{'BeQuiet'});
            $sUnknownJobs = 1;
          }
        }
        else
        {
          print STDERR "$$phProperties{'Program'}: Warning='Client ($sClientId) does not exist in database.'\n" unless ($$phProperties{'BeQuiet'});
          $sUnknownClients = 1;
        }
      }
    }
  }
  else
  {
    my $sClientId = $$phProperties{'ClientId'};
    if ($sClientId !~ /^$$phProperties{'CommonRegexes'}{'ClientId'}$/)
    {
      print STDERR "$$phProperties{'Program'}: Warning='Client ($sClientId) does not pass muster. Proceeding anyway.'\n";
    }
    if ($$phProperties{'GetAll'})
    {
      if (exists($$phDb{$sClientId}))
      {
        if (exists($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}))
        {
          foreach my $sKey (keys(%{$$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}}))
          {
            if (exists($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey}))
            {
              my $sValue = (defined($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey})) ? $$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey} : "";
              if ($$phProperties{'KeysOnly'})
              {
                print STDOUT $sKey, "\n";
              }
              elsif ($$phProperties{'ValuesOnly'})
              {
                print STDOUT $sValue, "\n";
              }
              else
              {
                print STDOUT join("=", $sKey, $sValue), "\n";
              }
            }
            else
            {
              print STDERR "$$phProperties{'Program'}: Warning='Key ($sKey) does not exist in $$phProperties{'Job'} for $sClientId.'\n" unless ($$phProperties{'BeQuiet'});
              $sUnknownKeys = 1;
            }
          }
        }
        else
        {
          print STDERR "$$phProperties{'Program'}: Warning='Job ($$phProperties{'Job'}) does not exist for $sClientId.'\n" unless ($$phProperties{'BeQuiet'});
          $sUnknownJobs = 1;
        }
      }
      else
      {
        print STDERR "$$phProperties{'Program'}: Warning='Client ($sClientId) does not exist in database.'\n" unless ($$phProperties{'BeQuiet'});
        $sUnknownClients = 1;
      }
    }
    else
    {
      if (exists($$phDb{$sClientId}))
      {
        if (exists($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}))
        {
          foreach my $sKey (@ARGV)
          {
            if ($sKey !~ /^$$phProperties{'CommonRegexes'}{'MldbmKeyName'}$/)
            {
              print STDERR "$$phProperties{'Program'}: Warning='Key ($sKey) does not pass muster. Proceeding anyway.'\n";
            }
            if (exists($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey}))
            {
              my $sValue = (defined($$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey})) ? $$phDb{$sClientId}{'Jobs'}{$$phProperties{'Job'}}{$sKey} : "";
              if ($$phProperties{'KeysOnly'})
              {
                print STDOUT $sKey, "\n";
              }
              elsif ($$phProperties{'ValuesOnly'})
              {
                print STDOUT $sValue, "\n";
              }
              else
              {
                print STDOUT join("=", $sKey, $sValue), "\n";
              }
            }
            else
            {
              print STDERR "$$phProperties{'Program'}: Warning='Key ($sKey) does not exist in $$phProperties{'Job'} for $sClientId.'\n" unless ($$phProperties{'BeQuiet'});
              $sUnknownKeys = 1;
            }
          }
        }
        else
        {
          print STDERR "$$phProperties{'Program'}: Warning='Job ($$phProperties{'Job'}) does not exist for $sClientId.'\n" unless ($$phProperties{'BeQuiet'});
          $sUnknownJobs = 1;
        }
      }
      else
      {
        print STDERR "$$phProperties{'Program'}: Warning='Client ($sClientId) does not exist in database.'\n" unless ($$phProperties{'BeQuiet'});
        $sUnknownClients = 1;
      }
    }
  }

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

  MldbmDisconnect($phDbContext, \$sLocalError);

  exit(3) if ($sUnknownClients == 1 && $sUnknownJobs == 0 && $sUnknownKeys == 0); # XER_UnknownClients
  exit(4) if ($sUnknownClients == 0 && $sUnknownJobs == 1 && $sUnknownKeys == 0); # XER_UnknownJobs
  exit(5) if ($sUnknownClients == 0 && $sUnknownJobs == 0 && $sUnknownKeys == 1); # XER_UnknownKeys
  exit(6) if ($sUnknownClients + $sUnknownJobs + $sUnknownKeys > 1); # XER_UnknownCombo

  1;


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

sub Usage
{
  my ($sProgram) = @_;
  print STDERR "\n";
  print STDERR "Usage: $sProgram [-r] [-d db] [-o option[,option[,...]]] -c client-id -j job [key ...]\n";
  print STDERR "\n";
  exit(1);
}


=pod

=head1 NAME

webjob-mldbm-get-job-kvps - Get job- or client-related key/value pairs.

=head1 SYNOPSIS

B<webjob-mldbm-get-job-kvps> B<[-r]> B<[-d db]> B<[-o option[,option[,...]]]> B<-c client-id> B<-j job> B<[key ...]>

=head1 DESCRIPTION

This utility gets one or more job- or client-related key/value pairs.

=head1 OPTIONS

=over 4

=item B<-c client-id>

Specifies the client ID to query.

=item B<-d db>

Specifies the MLDBM database to query.

=item B<-j job>

Specifies the job to query.

=item B<-o option,[option[,...]]>

Specifies the list of options to apply.  Currently, the following
options are supported:

=over 4

=item BeQuiet

Don't print warning messages.

=item KeysOnly

Only print keys rather than key/value pairs.

=item ValuesOnly

Only print values rather than key/value pairs.

=back

=item B<-r>

Reverse the meaning of the B<client-id> and B<key> arguments. This
option allows you to request the value of a single key for each client
specified.

=back

=head1 RETURN VALUES

Upon successful completion, a value of B<0> (B<XER_OK>) is returned.
Otherwise, one of the following error codes is returned:

=over 5

=item *

B<1 => B<XER_Usage>

=item *

B<2 => B<XER_Abort>

=item *

B<3 => B<XER_UnknownClients>

=item *

B<4 => B<XER_UnknownJobs>

=item *

B<5 => B<XER_UnknownKeys>

=item *

B<6 => B<XER_UnknownCombo>

=back

=head1 AUTHOR

Klayton Monroe

=head1 SEE ALSO

webjob-mldbm-create-job(1), webjob-mldbm-delete-job(1), webjob-mldbm-list-jobs(1), webjob-mldbm-set-job-kvps(1)

=head1 LICENSE

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

=cut
