The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#!/usr/local/bin/perl
    eval 'exec perl -S $0 "$@"'
        if 0;

##############################################################################
#
#        May be distributed under the terms of the Artistic License
#
#                  Copyright @ 1998, Hewlett-Packard, Inc.,
#                            All Rights Reserved
#
##############################################################################
#
#   @(#)$Id: deploy_content,v 1.2 1999/08/09 01:38:20 randyr Exp $
#
#   Description:    Take the info file passed in and manage the verification
#                   of the fields, the population of data into the staging
#                   and release areas, and effect the posting of the data to
#                   other servers in the mirror pool if requested.
#
#   Functions:      read_info
#                   send_mail
#                   mirror_propagate
#
#   Libraries:      IMS::ReleaseMgr::Signature
#                   Carp                    Core lib (better error messages)
#                   File::Copy              Core lib (smart 'mv' clone)
#                   File::Path              Core lib (emulate 'rm -rf' in Perl)
#                   Fcntl                   Core lib (flock constants)
#                   IO::File                Core lib (I/O classes)
#                   Getopt::Long            Core lib (cmd-line parsing)
#                   Sys::Hostname           Core lib (smart 'hostname' command)
#                   Net::Domain             CPAN lib for getting FQDN
#                   Mail::Internet          CPAN lib for sending UNIX mail
#                   Mail::Header              part of the above
#                   LWP::UserAgent          \
#                   HTTP::Request::Common    > CPAN libs, used for mirroring
#                   URI::URL                /
#
#   Global Consts:  $cmd                    This tool's name
#                   $USAGE                  The tool usage error message
#
#   Environment:    PATH                    This has to be trimmed drastically
#                                             for security
#
##############################################################################
use vars qw($cmd);
($cmd = $0) =~ s|.*/||o;

use 5.004;

use strict;
use vars qw($USAGE $server_root $incoming_dir $stage_dir $htdocs_dir $log_dir
            $push_log_dir $timestamp $hostname $file $webmaster $push_log_file
            $trace $tfile $script %info @info $FH @res_text $basefile $tarfile
            $workfile @command $email_list $error_email_list $basedir %opts
            $config $mirror_group $MIRROR_RETRIES $MIRROR_WAIT_PERIOD $tar
            $VERSION $revision $compression_support);
use subs qw(read_info send_mail mirror_propagate Archive::Tar::error);

use Carp                   qw(carp croak);
use Cwd                    'cwd';
use File::Copy             'move';
use File::Path             qw(rmtree mkpath);
use Fcntl                  ':flock';
use Getopt::Long           'GetOptions';
use Sys::Hostname          'hostname';
use Net::Domain            'hostfqdn';
use LWP::UserAgent;
use HTTP::Request::Common  qw(POST $DYNAMIC_FILE_UPLOAD);
use URI::URL;
require IO::File;
require Mail::Header;
require Mail::Internet;
require Text::Wrap;
require Archive::Tar;

use IMS::ReleaseMgr::Utils     qw(write_log_line send_mail show_version
                                  file_mirror_specification
                                  file_mirror_host_list
                                  file_error
                                  DBI_mirror_specification
                                  DBI_mirror_host_list
                                  DBI_error);
use IMS::ReleaseMgr::Transfer  'mirror_upload';
use IMS::ReleaseMgr::Signature 'md5_signature';

($basedir = $0) =~ s|^(.*)/(.*)$|$1|o;
$basedir = cwd if ($basedir eq '.');
$ENV{PATH} = "$basedir:/bin:/usr/bin:/sbin:/opt/ims/bin";
$VERSION = do {my @r=(q$Revision: 1.2 $=~/\d+/g);sprintf "%d."."%02d"x$#r,@r};;
$revision = q{$Id: deploy_content,v 1.2 1999/08/09 01:38:20 randyr Exp $ };

$USAGE = "Usage: $cmd mirror_group file [ -H host ] [ -t level ] [ -T file ]
\t[ -c file ]

Where:
-H host\t\tUse 'host' for identification (instead of system value)
-t num\t\tEnable tracing (num sets level)
-T file\t\tSend trace information to 'file' instead of tty
-c file\t\tRead configuration from 'file' instead of DBMS

``file'' is the release manager ticket file, and must end in ``.info''.
``mirror_group'' is the name of the host-pool grouping that this process is
running as a part of. It is used as a search key in the DBMS.
";
if (grep($_ eq '-h', @ARGV))
{
    print "$USAGE\n$revision\n";
    exit 0;
}
exit show_version if (grep(/-version/i, @ARGV));

$MIRROR_RETRIES     = 3;
$MIRROR_WAIT_PERIOD = 30;

$SIG{__WARN__} = sub { warn @_ unless $_[0] =~ /bad free/oi; };
Getopt::Long::config 'no_ignore_case';
GetOptions(\%opts, 'H=s', 't=i', 'T=s', 'c=s') or croak "$USAGE\nStopped";
$mirror_group = shift || croak "$USAGE\nStopped";
$file         = shift || croak "$USAGE\nStopped";

if (defined $opts{c} and $opts{c})
{
    $config = file_mirror_specification(-file => $opts{c});
    croak "$cmd was unable to get data for $mirror_group from file $opts{c}," .
        " stopped" unless (defined $config);
}
else
{
    $config = DBI_mirror_specification(-mirror => $mirror_group);
    croak "$cmd was unable to get data for $mirror_group from Oracle, stopped"
        unless (defined $config);
}

$hostname = hostname;

$trace        = $opts{t}                   || 0;
$tfile        = $opts{T}                   || '-';
$server_root  = $config->{SERVER_ROOT}     || '/opt/ims';
$incoming_dir = $config->{INCOMING_DIR}    || "$server_root/incoming";
$stage_dir    = $config->{STAGING_DIR}     || "$server_root/staging";
$htdocs_dir   = $config->{DOCUMENT_ROOT}   || "$server_root/htdocs";
$log_dir      = $config->{LOGGING_DIR}     || "$server_root/logs";
$push_log_dir = $config->{PKG_LOGGING_DIR} || "$log_dir/Pushes";
$webmaster    = $config->{WEBMASTER}       || '';      # webmaster@host.com';
$script       = $config->{STAGE_3_TOOL}    || 'process_content';
#
# The Archive::Tar package handles compression automatically. However, that
# means that the file could possibly arrive with a ".gz" suffix to the name.
# Later, when deriving the tarfile name, we only want to accept that suffix
# as a contender if this mirror is configured to use compression.
#
$compression_support = (defined $config->{COMPRESSION} and
                        $config->{COMPRESSION} !~ /no|false|0/i);

if ($trace)
{
    #
    # Set up a file for tracing to write to. In absence of -T, we use STDOUT
    #
    write_log_line $tfile, sprintf("$cmd [$$] [%s] Started with tracing",
                                   (scalar localtime));
}

if ($trace & 8) # bxxxx1xxx
{
    write_log_line($tfile,
                   map {
                       sprintf("$cmd [$$] CONFIG: %18s => %s",
                               $_, $config->{$_})
                   } (sort keys %$config));
}

write_log_line("$log_dir/$cmd", sprintf("%s [$$] Called to deploy for $file",
                                        scalar localtime));

# Make sure that the argument the program was called with is
# actually a name of a file present in the either $stage_dir or $incoming_dir.
# If not, exit with an error message.
if (-f "$stage_dir/$file")
{
    $workfile = "$stage_dir/$file";
}
elsif (-f "$incoming_dir/$file")
{
    $workfile = "$incoming_dir/$file";
}
else
{
    croak "Error: Can't find $stage_dir/$file or $incoming_dir/$file, stopped";
}

$FH = new IO::File "< $workfile";
if (! defined $FH)
{
    croak "Error opening $file for reading: $!, stopped";
}
chomp(@info = <$FH>);
$FH->close;

# Parse the lines into hash keys, leave only the old-style digital signature
# (if present) in @info:
read_info \%info, \@info;
if ($trace & 1)
{
    write_log_line($tfile,
                   sprintf("$cmd [$$] [%s] Package destination is %s",
                           (scalar localtime), $info{dest}));
}
$email_list = (defined $info{email} and $info{email}) ? $info{email} : '';
$email_list =~ tr/, /,/s;
$email_list =~ s/^,//o;
$error_email_list = (length $email_list) ?
    "$error_email_list,$webmaster" : $webmaster;

# Take the argument, delete the part of the path which denotes the directory
# and the suffix ".info".
($basefile = $file) =~ s/\.info$//o;

# If the tarred file is not there, exit with an error message
if (-f "$incoming_dir/$basefile.tar")
{
    $tarfile = "$incoming_dir/$basefile.tar";
}
elsif ($compression_support and -f "$incoming_dir/$basefile.tar.gz")
{
    $tarfile = "$incoming_dir/$basefile.tar.gz";
}
elsif (-f "$server_root/$basefile.tar")
{
    $tarfile = "$server_root/$basefile.tar";
}
elsif ($compression_support and -f "$server_root/$basefile.tar.gz")
{
    $tarfile = "$server_root/$basefile.tar.gz";
}
else
{
    write_log_line($tfile,
                   sprintf("$cmd [$$] [%s] Found no tar archive %s.tar",
                           (scalar localtime), $basefile))
        if ($trace);
    write_log_line("$log_dir/$cmd",
                   sprintf("%s [$$] Fatal error: No $basefile.tar found",
                           scalar localtime));
    rename $workfile, "$workfile.NO_TAR";
    croak "Error: can't find $server_root/$basefile.tar or " .
        "$incoming_dir/$basefile.tar, stopped";
}

if ($trace & 4)
{
    my $time_now = scalar localtime;
    write_log_line $tfile, "$cmd [$$] [$time_now] Work file: $workfile";
    write_log_line $tfile, "$cmd [$$] [$time_now] Tar  file: $tarfile";
}

# time calculation - roughly equivalent to date +%y%m%d
{
    my ($sec, $min, $hour, $mday, $month, $year) = localtime;
    $month++;
    $timestamp = sprintf("%02d%02d%02d-%02d%02d",
                         $year, $month, $mday, $hour, $min);
}

($file = $info{dest}) =~ s|^/||o;
mkpath($push_log_dir, 0, 0755);
$push_log_file = "$push_log_dir/$file.info-$timestamp";
if ($trace & 2)
{
    write_log_line $tfile, sprintf("$cmd [$$] [%s] Detail in $push_log_file",
                                   scalar localtime);
}

#
# pick up the info file and move it
#     Moving it prevents the monitor from trying to run another process to
#     handle this data.
#
move $workfile, $push_log_file;
if (move $tarfile, $stage_dir)
{
    $tarfile =~ s|^.*/||;
    $tarfile = "$stage_dir/$tarfile";
}

chdir $stage_dir;

if (-d $file)
{
    # Hmm... the topic dir is already here...
    for (1 .. 36)
    {
        # 36 iterations with a 5-sec sleep-- give them 3 minutes to finish
        sleep 5;
        last if (! -d $file);
    }
}

#
# Check again and move things back around if the dir is still there
#
if (-d $file)
{
    move $push_log_file, "$stage_dir/$file-$timestamp.info";
    move $tarfile,       "$stage_dir/$file-$timestamp.tar";

    write_log_line($tfile,
                   sprintf("$cmd [$$] [%s] $stage_dir/$file is in use",
                           scalar localtime))
        if ($trace);
    write_log_line("$log_dir/$cmd",
                   sprintf("%s [$$] Deploy failed: Directory $stage_dir/" .
                           "$file already exists", scalar localtime));

    send_mail($error_email_list, "$cmd: Deploy failure for $file",
              [ "The uploaded package intended for project $file could not\n",
                "be deployed because the directory $stage_dir was already\n",
                "in use. The info and tar files have been preserved in:\n",
                "\n",
                "\t$stage_dir/$file-$timestamp.info\n",
                "\t$stage_dir/$file-$timestamp.tar\n",
                "\n",
                "$revision\n" ]);

    croak "Error: Directory $stage_dir/$file already in use, stopped";
}

#
# Give a heads-up on whether the content arrived in compressed form
#
if (defined $info{compressed} and ($info{compressed} =~ /^\d+$/))
{
    write_log_line($tfile,
                   sprintf("$cmd [$$] [%s] Content sent compressed (level %d)",
                           scalar localtime, $info{compressed}))
        if (($info{compressed} > 0) && ($trace & 2));
}

#
# Check the MD5 stamp if one is present. This is not yet required, but probably
# will be once the overall system is finished on both ends.
#
if (defined $info{md5} and $info{md5})
{
    write_log_line($tfile,
                   sprintf("$cmd [$$] [%s] Checking MD5 stamp on $tarfile",
                           scalar localtime))
        if ($trace & 4);

    my $signature = md5_signature $tarfile;
    if ($signature ne $info{md5})
    {
        #
        # Signatures don't match. We cannot release this content.
        #
        write_log_line($tfile,
                       sprintf("$cmd [$$] [%s] MD5 stamp did not match",
                               scalar localtime));
        if ($trace & 8)
        {
            move $push_log_file, "$stage_dir/$file-$timestamp.info";
            move ($tarfile, "$stage_dir/$file-$timestamp." .
                            ($tarfile =~ /\.tar\.gz/ ? 'tar.gz' : 'tar'));
            send_mail($error_email_list, "$cmd: Deploy failure for $file",
                      [ "The uploaded package intended for project $file was" .
                        " not deployed due to a mis-match of content\n",
                        "checksums. The files have been preserved in:\n",
                        "\n",
                        "\t$stage_dir/$file-$timestamp.info\n",
                        "\t$stage_dir/$file-$timestamp.tar\n",
                        "\n",
                        "$revision\n" ]);
        }
        else
        {
            unlink $push_log_file;
            unlink $tarfile;
            send_mail($error_email_list, "$cmd: Deploy failure for $file",
                      [ "The uplaoded package intended for project $file was" .
                        " not deployed due to a mis-match of content\n",
                        "checksums. The files have been removed.\n",
                        "\n",
                        "$revision\n" ]);
        }
        croak "Error: Checksum mismatch for $stage_dir/$file, stopped";
    }
    else
    {
        write_log_line($tfile,
                       sprintf("$cmd [$$] [%s] MD5 stamp verified",
                               scalar localtime))
            if ($trace & 3);
    }
}

# Untar the topic to be pushed
$tar = new Archive::Tar;
unless (defined $tar)
{
    write_log_line($tfile,
                   sprintf("$cmd [$$] [%s] Error creating Archive::Tar " .
                           "object: %s",
                           (scalar localtime), Archive::Tar::error));
    croak "$cmd: make_archive: Error allocating Archive::Tar object: " .
        Archive::Tar::error . "\n";
}
#
# We don't have to actually worry about the "compressed" header, since the
# Archive::Tar class auto-detects compressed content.
#
unless (defined $tar->extract_archive($tarfile))
{
    write_log_line($tfile,
                   sprintf("$cmd [$$] [%s] Error reading tar-file: %s",
                           (scalar localtime), Archive::Tar::error));
    croak "$cmd: make_archive: Error reading from $tarfile: " .
        Archive::Tar::error . "\n";
}

#
# At this point, safe to assume that it worked.
#
# Use process_content (or user-specified tool) to push out new data
#
# $file is the destination, minus leading /. Tar should have left a directory
# by that name in $stage_dir.
#
unless (-d $file and -x $file)
{
    send_mail($error_email_list, "$cmd: Error: No directory named $file",
              [ "The archive file $tarfile did not create ``$file'' as a\n",
                "directory in $stage_dir. Possibly a malformed package.\n",
                "\n",
                "$revision\n" ]);
    write_log_line($tfile,
                   sprintf("$cmd [$$] [%s] Found no directory $file in tar " .
                           "archive", (scalar localtime)))
        if ($trace);
    write_log_line("$log_dir/$cmd",
                   sprintf("%s [$$] Found no directory $file in tar archive",
                           scalar localtime));
    croak "Error: The archive file $tarfile did not create a ``$file''\n" .
        "in the directory $stage_dir. Possibly a malformed package. Stopped";
}
chdir $file;
@res_text = ("$info{name}\n", "\n");
$FH = new IO::File ">> $push_log_file";
if (! defined $FH)
{
    send_mail($error_email_list,
              "$cmd: Error opening logging file $push_log_file",
              [ "Error opening $push_log_file for appending: $!\n",
                "Package not deployed.\n",
                "\n",
                "$revision\n" ]);
    write_log_line($tfile,
                   sprintf("$cmd [$$] [%s] Error opening logging file " .
                           "$push_log_file: $!", (scalar localtime)))
        if ($trace);
    write_log_line("$log_dir/$cmd",
                   sprintf("%s [$$] Error opening $push_log_file: $!",
                           scalar localtime));
    croak "Error opening $push_log_file for appending: $!, stopped";
}

#
# Construct the command to execute, based on the value of $script and the
# command-line args that need to be carried over.
#

@command = split(/\s+/, $script);
push(@command, '-t' => $trace)   if ($trace);
push(@command, '-T' => $tfile)   if ($tfile ne '-');
push(@command, '-H' => $opts{H}) if (defined $opts{H} and $opts{H});
push(@command, '-c' => $opts{c}) if (defined $opts{c} and $opts{c});
push(@command, $mirror_group);
if ($trace & 3)
{
    write_log_line $tfile, sprintf("$cmd [$$] [%s] Running: @command",
                                   scalar localtime);
}

# Have had some problems with IO::Pipe in the past
open(PIPE, "@command 2>&1 |");

while (defined($_ = <PIPE>))
{
    print $FH "Info:result\t$_";
    push(@res_text, $_);
}

close(PIPE);
if ($? >> 8)
{
    write_log_line $tfile, sprintf("$cmd [$$] [%s] Error from $command[0]: $!",
                                       scalar localtime)
        if ($trace);
    send_mail($error_email_list, "$cmd: Error from processing command",
              [ "Error from ``@command'': $!\n",
                "Package contents probably not deployed correctly.\n",
                "\n",
                "$revision\n" ]);
    chdir $stage_dir;
    rmtree $file;
    croak "Error from ``@command'': $!, stopped";
}
$FH->close;

send_mail($email_list, 'Server Data Updated', \@res_text)
    unless (defined $info{nomail} and $info{nomail} =~ /1|yes|true/o);

chdir $stage_dir;
# This routine from File::Path does a clean, recursive deletion
rmtree $file;

#
# Mirror the data if we are using an RDBMS model. If there are no mirrors,
# this routine won't do anything.
#
if (! mirror_propagate($tarfile, $mirror_group, \%info, $config,
                       ((defined $opts{c} and $opts{c}) ?
                        "$opts{c}.mir" : undef)))
{
    write_log_line $tfile, sprintf("$cmd [$$] [%s] Error trying to propagate" .
                                   " to mirrors for group $mirror_group",
                                   scalar localtime)
        if ($trace);
    write_log_line("$log_dir/$cmd",
                   sprintf("%s [$$] Unable to mirror package $tarfile.",
                           scalar localtime));
    #
    # Remove the staging directory, as we don't want to interfere with future
    # release attempts (mirrors be darned...)
    #
    chdir $stage_dir;
    unlink $tarfile;
    send_mail($error_email_list, 'Mirror Propagation Failure',
              ["Propagation of the file $tarfile to the elements of\n",
               "mirror group $mirror_group has failed. Content is only on " .
               ($opts{H} || hostfqdn) . ".\n" ]);
    croak "Error occured in mirroring the file $tarfile, stopped";
}
# remove the tar file (info file already moved)
unlink $tarfile;

write_log_line("$log_dir/$cmd",
               sprintf("%s [$$] Deployment to $info{dest} successful",
                       scalar localtime));
write_log_line("$log_dir/$cmd",
               sprintf("%s [$$] Finished",
                       scalar localtime));

exit 0;

##############################################################################
#
#   Sub Name:       read_info
#
#   Description:    Read the info file, converting the Info:xxxx lines into
#                   hash keys and trimming the input list down to all "extra"
#                   lines (which will later be treated as the checksum/
#                   signature of the tar file).
#
#   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
#                   $href     in      hashref   The hash to store key/value
#                                                 pairs in
#                   $lref     in      listref   The lines from the info file.
#                                                 On exit will be pared down to
#                                                 just the checksum lines.
#
#   Globals:        $trace
#                   $tfile
#
#   Environment:    None.
#
#   Returns:        Success:    1
#                   Failure:    dies
#
##############################################################################
sub read_info
{
    my ($href, $lref) = @_;

    my (@remnants, $line, $key, $value);

    for $line (@{$lref})
    {
        # Skip comments (blanks might be allowed in @remnants, though)
        next if $line =~ /^\#/o;

        if ($line =~ /^Info:/o) # Typical content
        {
            ($key, $value) = split(/\s+/, $line, 2);
            $key =~ s/^Info://o;
            $href->{$key} = $value;
        }
        else
        {
            push(@remnants, $line);
        }
    }

    if ($trace & 4)
    {
        for $key (sort keys %{$href})
        {
            write_log_line($tfile, "$cmd [$$] [read_info] $key=$href->{$key}")
        }
    }

    #
    # Return the listref containing only those lines we didn't use
    #
    @{$lref} = @remnants;
    1;
}

##############################################################################
#
#   Sub Name:       mirror_propagate
#
#   Description:    Query the RDBMS for the other hosts that are a part of this
#                   mirror pool. Use the upload URL to propagate the tar file
#                   to the other hosts.
#
#   Arguments:      NAME      IN/OUT  TYPE      DESCRIPTION
#                   $file     in      scalar    Pathname of the tar file
#                   $mirror   in      scalar    Name of the mirror group
#                   $info     in      hashref   Reference to the infofile vals
#                   $config   in      hashref   Ref to the configuration vals
#        (optional) $hostfile in      scalar    If passed, name of the file to
#                                                 read mirror host list from
#
#   Globals:        $cmd
#                   $basedir
#
#   Environment:    None.
#
#   Returns:        Success:    1
#                   Failure:    0
#
##############################################################################
sub mirror_propagate
{
    my ($file, $mirror, $info, $config, $hostfile) = @_;

    my ($hostlist, $FQDN, $host, $URI, $UA, $project, $res, $bad, $error_list);

    return 1 unless
        (defined $info->{upload} and ($info->{upload} =~ /1|yes|true/i));

    #
    # Nasty loops happen if we don't clear this
    #
    $info->{upload} = 'no';

    if (defined $hostfile and $hostfile)
    {
        $hostlist = file_mirror_host_list(-file => $hostfile);
        unless (defined $hostlist)
        {
            write_log_line($tfile,
                           sprintf("$cmd [$$] [%s] Error getting mirror list" .
                                   " from file: %s",
                                   scalar localtime, file_error))
                if ($trace);
            write_log_line("$log_dir/$cmd",
                           sprintf("%s [$$] Error getting mirror list from " .
                                   "file: %s",
                                   scalar localtime, file_error));
            carp "$cmd was unable to get data for $mirror_group from file " .
                "$hostfile, stopping";
            return 0;
        }
    }
    else
    {
        $hostlist = DBI_mirror_host_list(-mirror => $mirror_group);
        unless (defined $hostlist)
        {
            write_log_line($tfile,
                           sprintf("$cmd [$$] [%s] Error getting mirror list" .
                                   " from DBI: %s",
                                   (scalar localtime), DBI_error))
                if ($trace);
            write_log_line("$log_dir/$cmd",
                           sprintf("%s [$$] Error getting mirror list from " .
                                   "DBI: %s",
                                   scalar localtime, DBI_error));
            carp "$cmd was unable to get data for $mirror_group from " .
                "Oracle, stopping";
            return 0;
        }
    }

    return 1 unless ((scalar @$hostlist) > 1);

    # Get this machine's fully-qualified hostname + domain
    $FQDN = $opts{H} || hostfqdn;
    ($project = $info->{dest}) =~ s|^/||o;

    if ($info->{transport} eq 'http')
    {
        ($res, $bad, $error_list) = mirror_upload($file, $project, $FQDN,
                                                  $hostlist, $config, $info);

        if (! $res)
        {
            my $date = scalar localtime;

            for $host (@$bad)
            {
                $res = shift(@$error_list);

                write_log_line("$log_dir/$cmd",
                               "$date [$$] Mirror FAILED to host $host: $res");
                write_log_line("$tfile",
                               "$cmd [$$] [$date] Mirroring FAILED to host " .
                               "$host: $res")
                    if ($trace);
            }

            return 0;
        }
    }
    else # ftp
    {
        my $date = scalar localtime;

        write_log_line("$log_dir/$cmd",
                       "$date [$$] FTP mirroring should be done by client");
        write_log_line("$tfile",
                       "$cmd [$$] [$date] FTP mirroring is not supported at " .
                       "the server level")
            if ($trace);

        # Not *really* an error
        return 1;
    }

    1;
}

__END__

=head1 NAME

deploy_content - Perform the tasks associated with deploying a new package

=head1 SYNOPSIS

deploy_content [ B<-t> I<num> ] [ B<-T> I<file> ] [ B<-c> I<file> ] mirror_group file

=head1 DESCRIPTION

The B<deploy_content> application is the second stage on the server-side of
the release manager process. It is generally called by B<rlsmgrd> when there
is a package in the incoming area that requires deployment.

The role of this tool is to validate the new package file, verify the
checksum, dissolve the archive into a staging area, and invoke the third-stage
tool to manage the relocation of data from the staging area to the actual
web server directories.

=head1 OPTIONS

B<deploy_content> requires that a I<mirror group> be specified on the 
command-line. This group name uniquely identifies a group of one or more
servers that handle a given externally-visible hostname. Following this, there
needs to be the name of the info file for the package being processed. This
will be sought in either the incoming or staging directories (which are
defined in the mirror group specification). In addition to these required
values, the following options are recognized:

=over

=item B<-t I<num>>

Specify a tracing level to be used for diagnostics (see the B<-T> option
below). The value is used internally as a bit-mask, so a value of 5 is in
fact specifying the combination of 1 and 4, while exluding 2. Currently, only
the first four bits are used. (A detailed description of what each bit does
will soon follow.)

=item B<-T I<file>>

Specifiy the file that diagnostics are written to. Not to be confused with
the general tool logfile, which generally only notes the very high-level
events. If not specified, and a non-zero value for B<-t> is specified, this
will default to ``rlsmgrd-trace'' in the same logging directory as other logs
are written to.

=item B<-c I<file>>

Instruct the tool to read configuration from the specified file rather than
the Oracle database. This is meant mainly for debugging and for mirror groups
that contain only one host. This is not a good idea for mirror groups with
two or more hosts, as the database is specifically utilized so as to avoid
configurations diverging from one host to the next. (Description of the
configuration file format will be added later.)

=back

=head1 CONFIGURATION SPECIFICATION

In order to read configuration data from the Oracle RDBMS, the tool must
have a database name/address, and a user ID and password. It would be insecure
to pass these either on the command-line or via environment variables. To that
end, if the tool attempts to use the Oracle data source (in the absence of
a B<-c> option, above) then it looks for a control file in the same directory
as the tool itself resides in. The name of the control file must be the mirror
group name as passed on the command line, with a suffix of ``C<.rc>'' added.

The file itself should contain only one or two lines. The first line should
be of the form:

        username:password

The password should I<not> be encrypted. The second line, if specified, should
be the database name. If this is not specified, the value of the environment
variable B<ORACLE_SID> is used. It is assumed that the environment variable
B<ORACLE_HOME> is already set.

This specification will be used by all release manager tools located in the
same directory.

=head1 FILES

=over

=item $TOOL_DIR/*.rc

Where C<$TOOL_DIR> is the directory in which the tool is installed, the
files that provide Oracle information, for the sake of connecting to the
Oracle server. This file is not referenced if the B<-c> option is passed.

=back

=head1 SEE ALSO

L<rlsmgrd>, L<process_content>

=head1 AUTHOR

Randy J. Ray

=cut