#!/usr/bin/perl -w

use strict;
use Template;
use File::Find;

=head1 NAME

postcopy - set up a vserver after it has been built with vserver

=head1 SYNOPSIS

 postcopy [ server_name ]

=head1 DESCRIPTION

Postcopy sets up a virtual server after it has been built with
`vserver server_name build'

It installs critical configuration files from a base directory (see
LOCATIONS, below), which are defined with Template Toolkit templates.

=head1 LOCATIONS

UTSL

=cut

use constant CONFIG_DIR => "/etc/vservers/.defaults/postcopy";
use constant ALL_SERVERS => CONFIG_DIR . "/all";
sub CONFIG_DIR_SERVER ($) { CONFIG_DIR."/host-$_[0]" };
sub CONFIG_DIR_MARK ($) { CONFIG_DIR."/mark-$_[0]" };
use constant VSERVERS_BASE => "/vservers";
sub VSERVER_ROOT ($) { VSERVERS_BASE."/$_[0]" };

use vars qw($domainname);
chomp($domainname = `cat /etc/vservers/domainname`);
$domainname ||= "clunker.utsl.gen.nz";

sub VSERVER_FQDN ($) { "$_[0].$domainname" };

# $garbage is extracted from the Pod, below
use vars qw($garbage);
use Pod::Constants add_files => sub {
    m{/(.*)/} && eval '$garbage = qr/'.$1.'/x'
};

=head1 INTERNAL FUNCTIONS

=head2 add_files

Usage:

  add_files(\%hash, $base_dir)

Adds all files under C<$base_dir> into C<%hash>.  The keys of the hash
are the relative path within C<$base_dir>.  The values are the full
paths of files within C<$base_dir>.

Certain files are skipped: anything matching this regular expression:

  /^\.?\#|(~|\.(bak|old|orig|rej))$/

Will be ignored, and any directory called "CVS" will be ignored as
well.

=cut

sub add_files(\%$) {

    my ($hash, $base_dir) = @_;

    find(sub {
	     # don't look in CVS directories
	     /CVS/ && do { $File::Find::prune = 1; return};

	     # ignore various garbage files (mostly emacs files :)
	     /$garbage/x && return;

	     # only normal files
	     -f or return;

	     # get filename and strip $setup_dir from the front
	     my $filename = $File::Find::name;
	     $filename =~ s{^$base_dir/}{};

	     # add to the list of files
	     $hash->{$filename} = $File::Find::name;
	 }, $base_dir);

}

#=====================================================================
#  MAIN SECTION STARTS HERE
#=====================================================================
# this variable holds a hash of templates to run
my %files;

# read the input server name
my $server_name = shift;

# check sanity
die "usage: postcopy servername"
    unless ( defined $server_name and
	     -d VSERVERS_BASE."/$server_name" );

# get a list of files
for my $dir ( ALL_SERVERS, CONFIG_DIR_SERVER($server_name),
	      # potentially per-role stuff here later
	      ) {
    add_files(%files, $dir);
}


# create the Template object
my $template = Template->new
    ({
      INCLUDE_PATH => join(":", ALL_SERVERS),
      EVAL_PERL => 1,
      ABSOLUTE => 1,
      OUTPUT_PATH => VSERVER_ROOT($server_name),
     });

=head1 INTERPOLATED VARIABLES

The following variables are defined for use in the templates:

=over

=item hostname

Set to the new vserver's host name.  This will be something like
"ook", "vserver1", etc.

=item ip_addr

The IPv4 address of this vserver, in dotted quad notation.  eg,
C<192.168.32.17>

=item fqdn

The fully-qualified DNS domain name of this host.

=item domainname

The fully-qualified DNS domain name of this host, sans the hostname
part.

=item host_hostname

An example piece of system information.  This is the hostname of the
system that houses the vserver that is being installed.  I'm
considering renaming this variable to "C<daddy>".

=item host_ip

IP address of the hosting system.  Just in case we need to phone home
(though arguably we should be able to resolve C<host_hostname> to the
same address).

=item your_variable_here

Some dynamic text

=back

=cut

# prepare some variables to interpolate into the Templates
use Sys::Hostname;
my $vars =
    {
     # the server name as given to us
     hostname => $server_name,
     ip_addr => join (".", unpack ("C*", scalar
				   (gethostbyname($server_name)
			|| die "Cannot resolve $server_name"))),
     # "Fully Qualified Domain Name"
     fqdn => VSERVER_FQDN($server_name),

     # the host name of the housing RedHat system - probably
     # linux10321 or something like that.  Might come in handy.
     host_hostname => hostname(),
     host_ip => join (".", unpack ("C*", scalar
				   (gethostbyname(hostname())))),

     domainname => $domainname,

     # more templating variables
     your_variable_here => "your_value_here",
    };

use Data::Dumper;

print "Template Variables:\n";
print Dumper $vars;

# run the templates
while ( my ($target_filename, $source_filename) = each %files) {

    if ( -x $source_filename ) {
	my ($working_dir) = ($target_filename =~ m{^(.*?)(/?[^/]*)$});
 
	print "Running $source_filename in $working_dir\n";
	$ENV{uc($_)} = $vars->{$_} foreach (keys %$vars);
	system("cd ".VSERVER_ROOT($server_name)."/$working_dir &&"
		."$source_filename") == 0
		or die "$source_filename failed with rc=$?";
    } else {
    	print "Processing TT $source_filename -> $target_filename\n";
    	$template->process($source_filename, $vars, $target_filename)
		or die $Template::ERROR;
    }
}

print "All Done!\n";

