head	1.10;
access;
symbols
	HELM_PEER_PORT_BRANCH:1.1.1.1
	dlr:1.1.1
	TIGRIS_NOV_12_2000:1.9
	OLDHELM:1.9.0.4
	TIGRIS_NOV_11_2000:1.9
	TIGRIS_SEP_13_2000:1.9.0.2
	TIGRIS_BASELINE:1.5;
locks; strict;
comment	@# @;


1.10
date	2000.11.13.19.51.38;	author kmaples;	state dead;
branches;
next	1.9;

1.9
date	2000.10.05.19.51.56;	author kmaples;	state Exp;
branches;
next	1.8;

1.8
date	2000.10.02.22.37.31;	author kmaples;	state Exp;
branches;
next	1.7;

1.7
date	2000.09.28.18.06.20;	author kmaples;	state Exp;
branches;
next	1.6;

1.6
date	2000.09.27.21.07.32;	author kmaples;	state Exp;
branches;
next	1.5;

1.5
date	2000.09.22.23.42.20;	author kmaples;	state Exp;
branches;
next	1.4;

1.4
date	2000.09.20.22.59.21;	author kmaples;	state Exp;
branches;
next	1.3;

1.3
date	2000.09.18.21.47.54;	author kmaples;	state Exp;
branches;
next	1.2;

1.2
date	2000.09.18.20.45.29;	author kmaples;	state Exp;
branches;
next	1.1;

1.1
date	2000.09.18.20.03.31;	author kmaples;	state Exp;
branches
	1.1.1.1;
next	;

1.1.1.1
date	2001.02.21.00.36.23;	author dlr;	state Exp;
branches;
next	;


desc
@@


1.10
log
@Deprecated by .in file from newhelm.
@
text
@# ================================================================
# Copyright (c) 2000 CollabNet.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. The end-user documentation included with the redistribution, if
# any, must include the following acknowlegement: "This product includes
# software developed by CollabNet (http://www.Collab.Net/)."
# Alternately, this acknowlegement may appear in the software itself, if
# and wherever such third-party acknowlegements normally appear.
#
# 4. The hosted project names must not be used to endorse or promote
# products derived from this software without prior written
# permission. For written permission, please contact info@@collab.net.
#
# 5. Products derived from this software may not use the "Tigris" name
# nor may "Tigris" appear in their names without prior written
# permission of CollabNet.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# ====================================================================
#
# This software consists of voluntary contributions made by many
# individuals on behalf of CollabNet.
#


################################################################
# Name:              ParentChildArgumentBridge.pl
# Original Author:   kmaples@@collab.net
# $Id: ParentChildArgumentBridge.pl,v 1.9 2000/10/05 19:51:56 kmaples Exp $
#
# This library defines the interface between 'parent' script, and 
# 'child' script by negotiating the arguments of each.
#
#---------------------------------------------------------------

# This is a hack to make sandbox exposed to the calling scripts for 
# logging purposes:
#-------------------
sub get_sandbox {
#-------------------
    return($ENV{'SANDBOX'});
}
#-------------------
sub get_htdoc_root {
#-------------------
    return($ENV{'HTDOC_ROOT'});
}


#-------------------
sub perform {
#-------------------
    # Syntax: perform(action);
    # Defines the interface between the parent scripts and the child scripts
    # by maping values from @@ARGV to the args needed by the child scripts.

    my $action = shift;

    # Arguments:

    $htdoc_root         = $ENV{'HTDOC_ROOT'};
    $sandbox            = $ENV{'SANDBOX'};
    $domain_name        = $ARGV[0]  || '';
    $project_name       = $ARGV[1]  || '';
    $brand_name         = $ARGV[2]  || '';
    $description        = $ARGV[3]  || '';
    $module_name        = $ARGV[4]  || '';
    $module_description = $ARGV[5]  || '';
    $is_web             = $ARGV[6]  || '';
    $was_web            = $ARGV[7]  || '';
    $bugs_password      = $ARGV[8]  || '';
    $bugs_realname      = $ARGV[9]  || '';
    $bugs_username      = $ARGV[10] || '';
    $bugs_new_username  = $ARGV[11] || '';

    ## This holds arrays of values for each action.
    ## The idea here is to abstract these into something static which
    ## can be stored in one place and shared amongst related functions.

    my %action_to_args = (
        'apache-project-add'     
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $description],
        'apache-project-delete'  
            => [$htdoc_root, $project_name, $domain_name, $brand_name],
        'cvs-module-add'     
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $module_name, $module_description, $is_web],
        'cvs-module-delete'  
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $module_name, $is_web],
        'cvs-module-edit'    
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $module_name, $module_description, $is_web, $was_web],
        'cvs-project-add'    
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name],
        'cvs-project-delete'     
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name],
        'bugzilla-project-add'
            => [$sandbox, $project_name, $domain_name, $bugs_password],
        'bugzilla-project-delete'
            => [$sandbox, $project_name, $domain_name, $bugs_password],
        'bugzilla-module-add'
            => [$sandbox, $project_name, $domain_name, $module_name, $module_description],
        'bugzilla-module-delete'
            => [$sandbox, $project_name, $domain_name, $module_name, $module_description],
	'bugzilla-user-add'
	    => [$bugs_password, $bugs_realname, $bugs_username],
        'bugzilla-user-edit'
            => [$bugs_realname, $bugs_username, $bugs_new_username],
        'bugzilla-user-delete'
            => [$bugs_username],
    );
 
    my $cmd     = 'unix command line';
    my $ret     = 0;
 
    # No point in continuing if the action wasn't defined: 
    return(0) unless $action_to_args{$action};

    # This is a work-around for the fact that the current working directory
    # doesn't really have any meaning to the calling servlet - so we have
    # to call child scripts based on their relation to the sandbox:
    my $script_path = $ENV{'SANDBOX'} . '/helm/perl/scripts';
    
    # Nasty sharp pointy things: this just retrieves the arg list
    # stored in the hashtable for this particular action:   
    my @@args = @@{ $action_to_args{$action} };
 
    # A safe system call.  Note: DON'T use backticks for something
    # like this, or you'll likely lose all of the child's diagnostic
    # messages:
    $ret = system("$script_path/$action",@@args);
    
    # invert success here - the system will return '0' for true, '1'
    # for false - note that we're returning a diagnostic message,
    # as logging of 'die' conditions are the responsibility of the
    # called rather than the caller:
    if($ret == 0){
        print "EXECUTED: $action @@args\n";
        return(1);
    } else {
        print "FAILED: $action @@args\n";
        return(0);
    }
}

# return true:
1;


@


1.9
log
@Amended script to included args which need to be passed to the new
bugzilla-user-* scripts
@
text
@d51 1
a51 1
# $Id: ParentChildArgumentBridge.pl,v 1.8 2000/10/02 22:37:31 kmaples Exp $
@


1.8
log
@Removed references to 'is_private' flag.  Not only is this unnecessary, but
according Leonard, it 'kills puppies and causes dutch elm disease'.
@
text
@d51 1
a51 1
# $Id: ParentChildArgumentBridge.pl,v 1.7 2000/09/28 18:06:20 kmaples Exp $
d94 3
d126 1
a126 1
	    => [],
d128 1
a128 1
            => [],
d130 1
a130 1
            => [],
@


1.7
log
@Fixed reference to $module_description.
@
text
@d51 1
a51 1
# $Id: ParentChildArgumentBridge.pl,v 1.6 2000/09/27 21:07:32 kmaples Exp $
d93 1
a93 2
    $is_private         = $ARGV[8]  || '';
    $bugs_password      = $ARGV[9]  || '';
d105 1
a105 1
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $module_name, $module_description, $is_web, $is_private],
d109 1
a109 1
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $module_name, $module_description, $is_web, $was_web, $is_private],
d111 1
a111 1
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $is_private],
@


1.6
log
@Amended bridge to refer to new bugzilla-user-* scripts (no arguments are
currently defined for these(.
@
text
@d51 1
a51 1
# $Id: ParentChildArgumentBridge.pl,v 1.5 2000/09/22 23:42:20 kmaples Exp $
d110 1
a110 1
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $module_name, $is_web, $was_web, $is_private],
@


1.5
log
@Many changes and fixes; specified arguments to pass to bugzilla-*-delete
scripts; fixed some omissions preventing project dirs from being properly
deleted, etc.
@
text
@d51 1
a51 1
# $Id: ParentChildArgumentBridge.pl,v 1.4 2000/09/20 22:59:21 kmaples Exp $
d123 6
@


1.4
log
@Modifications to get info for database access via environmentals; amended
the @@ARGV list to include a 'bugs_password' to be passed in.
@
text
@d51 1
a51 1
# $Id: ParentChildArgumentBridge.pl,v 1.3 2000/09/18 21:47:54 kmaples Exp $
d118 1
a118 1
            => [],
d122 1
a122 1
            => [],
@


1.3
log
@Preparatory work for bugzilla components
@
text
@d51 1
a51 1
# $Id: ParentChildArgumentBridge.pl,v 1.2 2000/09/18 20:45:29 kmaples Exp $
d94 1
d116 1
a116 1
            => [$sandbox, $project_name, $domain_name],
@


1.2
log
@Refactoring and simplification.
@
text
@d51 1
a51 1
# $Id: ParentChildArgumentBridge.pl.in,v 1.12 2000/09/13 22:49:12 kmaples Exp $
d115 1
a115 1
            => [],
d119 1
a119 1
            => [],
@


1.1
log
@Some refactoring, and deprecated the use of the '.in' file in favor of
environmentals (which were being used as such anyway).
@
text
@d70 2
d76 2
a77 4
    # really just a passthrough for '_perform()', but defines the 
    # interface between the parent scripts and the child scripts
    # by defining the hashes that map value 'token' to @@ARGV, and 
    # 'action' token to the list of args it takes/requires.
a81 22
    # This replaces the arbitrary assignment of args to tokens, and is
    # shared by all scripts of this kind.  Keys are then mapped to the
    # corresponding element lists specified in the %action_to_args
    # hash, which in turn specifies which of these args gets passed
    # to the 'child' scripts.  NOTE: numbering starts from $ARGV[4] to
    # account for the first four args which are being set at installation
    # time.  At some point, it is expected that they will be passed
    # in as args as well by the calling servlet:

    my %arg_values = (
        'htdoc_root'         => $ENV{'HTDOC_ROOT'},
        'sandbox'            => $ENV{'SANDBOX'},
        'domain_name'        => $ARGV[0]  || '',
        'project_name'       => $ARGV[1]  || '',
        'brand_name'         => $ARGV[2]  || '',
        'description'        => $ARGV[3]  || '',
        'module_name'        => $ARGV[4]  || '',
        'module_description' => $ARGV[5]  || '',
        'is_web'             => $ARGV[6]  || '',
        'was_web'            => $ARGV[7]  || '',
        'is_private'         => $ARGV[8]  || '',
    );
d83 13
a95 2
    ## This holds arrays of tokens for each action which map to the
    ## command-line arguments put into the %arg_values hash.
a97 2
    ## Note that the presence of a token here in the value side of the
    ## hash means that it is implicitly required:
d101 1
a101 1
            => ['sandbox', 'htdoc_root', 'project_name', 'domain_name', 'brand_name', 'description'],
d103 1
a103 1
            => ['htdoc_root', 'project_name', 'domain_name', 'brand_name'],
d105 1
a105 1
            => ['sandbox', 'htdoc_root', 'project_name', 'domain_name', 'brand_name', 'module_name', 'module_description', 'is_web', 'is_private'],
d107 1
a107 1
            => ['sandbox', 'htdoc_root', 'project_name', 'domain_name', 'brand_name', 'module_name', 'is_web'],
d109 1
a109 1
            => ['sandbox', 'htdoc_root', 'project_name', 'domain_name', 'brand_name', 'module_name', 'is_web', 'was_web', 'is_private'],
d111 1
a111 1
            => ['sandbox', 'htdoc_root', 'project_name', 'domain_name', 'brand_name', 'is_private'],
d113 1
a113 1
            => ['sandbox', 'htdoc_root', 'project_name', 'domain_name', 'brand_name'],
d126 3
d136 1
a136 2
    # stored in the hashtable for this particular action:
    
a138 12
    # Now we replace those keys with their values as declared:
    foreach(@@args){
        # bail out if no value defined for 'required' key:
        my $current_value = $arg_values{$_};
        unless(defined($current_value)){
            print "WARNING: No value was set for '$_' token.\n";
            return(0);
        }
        # replace the key w/ its actual value:
        $_ = $current_value;
    }

@


1.1.1.1
log
@Replacing the HEAD with the HELM_PEER_PORT_BRANCH.
@
text
@d51 1
a51 1
# $Id: ParentChildArgumentBridge.pl.in,v 1.32 2001/01/15 20:54:40 kmaples Exp $
d58 12
a69 1

d74 4
a77 2
    # Defines the interface between the parent scripts and the child scripts
    # by maping values from @@ARGV to the args needed by the child scripts.
d81 22
a102 44
    # From now on, we'll use '--named' arguments:

    use Getopt::Long;

    # And now, the args and their values: 

    # --domain_name         helm:HELM_DOMAIN.NAME
    # --project_name        helm:HELM_PROJECT.NAME
    # --project_id          helm:HELM_PROJECT.PROJECT_ID
    # --brand_name          --ignore---
    # --description         helm:HELM_PROJECT.SHORT_DESCRIPTION
    # --module_name         --hardcoded, e.g., 'www' --
    # --module_description  --hardcoded--
    # --is_web              --hardcoded--
    # --issues_realname     joist:User.name
    # --issues_username     joist:User.loginID
    # --issues_role         'super|admin|devel|observ'

    ## Init the vars used to hold the options: 
    foreach($domain_name, $project_name, $project_id, $brand_name,
        $description, $module_name, $module_description, $is_web,
        $issues_realname, $issues_username, $issues_role, $new_project_name){

        $_ ||= '';
    }


    GetOptions(

      'domain_name=s'        => \$domain_name,        
      'project_name=s'       => \$project_name,       
      'project_id=i'         => \$project_id,         
      'brand_name=s'         => \$brand_name,         
      'description=s'        => \$description,        
      'module_name=s'        => \$module_name,        
      'module_description=s' => \$module_description, 
      'is_web=s'             => \$is_web,             
      'issues_realname=s'    => \$issues_realname,    
      'issues_username=s'    => \$issues_username,    
      'issues_role=s'        => \$issues_role,        

      # Replacement values:  

      'new_project_name=s'   => \$new_project_name,
d105 2
a106 109
    $htdoc_root         = $ENV{'HTDOC_ROOT'};
    $sandbox            = $ENV{'SANDBOX'};

    my $project_database;   # This is the 'translated' database name, after
                            # accounting for project id. 

    my $multiple_dbs;       # DB setting.

    ## These numbers correspond to the bit sets of permissions
    ## in issuezilla ('SELECT * FROM groups' to view them):
    my $observer_user  = "0";
    my $developer_user = "96";
    my $admin_user     = "127";
    ## Unused, but this represents the highest bitset a user can have:
    my $super_user     = "9223372036854775807";

    ## Terrible hack requiring refactoring:  this is now a static identifier 
    ## used to create a bogus password entry so that helm doesn't have to be
    ## consulted ... since 'localconfig' is already being read in below, I need
    ## to move the identifier there so that this needen't be duplicated:

    $ENV{ISSUZILLA_PASSWORD} = 'password'; 

    ## Scary, scary ... havent figured out exactly how I want to do this yet,
    ## but I need to set mysql root values for the issuezilla-project-create
    ## script:

    $ENV{DATABASE_ROOTUSER} = 'root' || '';
    $ENV{DATABASE_ROOTPASS} = '0xb0bd0bb5' || '';
    $ENV{DATABASE_PORT}     = '3306' || '';    
    $ENV{DATABASE_HOST}     = 'localhost' || '';    

    ## Terrible, terrible hack:  if $action is an issuezilla thing, we
    ## need to determine which database to talk to.  This reqires 
    ## checking which mode issuezilla is in (one or many issue databases).
    ## So we do the same thing which happens with issuezilla at run-time:
    ## set the environmental for HTTP_PROJECT_ID (this would normally be
    ## set by SecureNoodle in the http headers), read in issuezilla's 
    ## 'localconfig' file, and then swipe the value it sets for the 
    ## database name.  Then we set $ENV{DATABASE_NAME} so that the children 
    ## use the correct DB:
    
    if($action =~ m/^issuezilla/){
        my $izconfig = $sandbox . '/issuezilla/localconfig';

        $ENV{HTTP_HELM_HELM_PROJECT_PROJECT_ID} = $project_id;

        print "  Set HTTP_HELM_HELM_PROJECT_PROJECT_ID to $ENV{HTTP_HELM_HELM_PROJECT_PROJECT_ID}\n";

        unless( do "$izconfig" ){
           print "FAILED: $action - unable to find issuezilla config file\n";
           return(0);
        }

        ## Since we just sourced it in, set a variable to hold the 
        ## single/multiple db flag.  This may be examined by the scripts 
        ## which manage user accounts, as their behavior differs depending.

        $multiple_dbs = $::use_project_id_database || 0;  

        if($multiple_dbs){
            print "  Database flag set to MULTIPLE\n";
        } else {
            print "  Database flag set to SINGLE\n";
        }

        # Now evaluate and set the user's role:      

        if($issues_role      =~ m/^super/){
            $issues_role = $super_user;
        } elsif($issues_role =~ m/^admin/){ 
            $issues_role = $admin_user;
        } elsif($issues_role =~ m/^devel/){
            $issues_role = $developer_user;
        } else {
            # Default to the observer, then:
            $issues_role = $observer_user;
        }

        ## Now, if we're in single-db mode, disregard all the hard
        ## work we just went to and flatten the roles, since everyone
        ## but the admins are observers, and it's left to the admins
        ## to manually manage their users through IZ:

        if(!$multiple_dbs && ( $issues_role < $admin_user )){
            $issues_role = $observer_user;
        }

        ## Now set the database:  
        $project_database = $db_name;        
        print "  Setting project database to $project_database\n";
    }

    ## This is largely for sandboxing - if the prefix var is sent in env.sh at
    ## install time, then this gets prepended to the email addresses:
    my $iz_mail_pfx = '' || '';
    
    ## And this, therefore, requires refactoring from the child scripts
    ## as the logic is repeated in the child scripts:
    if($iz_mail_pfx){ 
        ## Horrible - this is so list inserts make sense in sandboxes:  
        $ENV{MAIL_PREFIX} = $iz_mail_pfx;
     
        my @@tmp = split(':', $issues_username);
        foreach(@@tmp){ $_ = $iz_mail_pfx . $_ }
        $issues_username = join(':',@@tmp); 
    }

    ## This holds arrays of values for each action.
d109 2
d113 22
a134 54

    'apache-project-add'     
            => [$htdoc_root, $project_name],
    'apache-project-delete'  
            => [$htdoc_root, $project_name],
    'apache-project-edit'
            => [$htdoc_root, $project_name, $new_project_name],

    'cvs-module-add'     
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $module_name, $module_description, $is_web],
    'cvs-module-delete'  
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $module_name, $is_web],
    'cvs-module-edit'    
            => [$sandbox, $htdoc_root, $project_name, $domain_name, $brand_name, $module_name, $module_description, $is_web],

    'cvs-project-add'    
            => [$sandbox, $project_name],
    'cvs-project-delete'     
            => [$sandbox, $project_name],
    'cvs-project-edit'
            => [$sandbox, $project_name, $new_project_name],

    'issuezilla-project-add'
            => [    $sandbox, $project_name, $project_database, 
                    $domain_name, $multiple_dbs],
    'issuezilla-project-delete'
            => [    $sandbox, $project_name, $project_database, 
                    $domain_name, $multiple_dbs],
    'issuezilla-project-edit'
            => [    $sandbox, $project_name, $project_database,
                    $domain_name, $multiple_dbs, $new_project_name],

    'issuezilla-module-add'
            => [    $sandbox, $project_name, $project_database, 
                    $domain_name, $module_name, $module_description,
                    $multiple_dbs],
    'issuezilla-module-delete'
            => [    $sandbox, $project_name, $project_database, 
                    $domain_name, $module_name, $module_description,
                    $multiple_dbs],
    'issuezilla-user-add'
	        => [    $project_name, $project_database, $domain_name,
                    $issues_realname, $issues_username, $issues_role,
                    $multiple_dbs ],
    'issuezilla-user-edit'
            => [    $project_name, $project_database, $domain_name, 
                    $issues_realname, $issues_username, $issues_role,
                    $multiple_dbs],
    'issuezilla-user-delete'
            => [    $project_name, $project_database, $domain_name, 
                    $issues_username, $multiple_dbs],
    'issuezilla-user-disableall'
            => [    $project_database, $multiple_dbs  ],

a138 3
 
    # No point in continuing if the action wasn't defined: 
    return(0) unless $action_to_args{$action};
d146 2
a147 1
    # stored in the hashtable for this particular action:   
d150 12
a177 85
}
# Alpha and Omega:
BEGIN {
    $ENV{'PATH'} = "/bin:/usr/bin:/usr/local/bin";

    unless($ENV{'SANDBOX'}){
        print "FATAL: SANDBOX not set; exiting.\n";
        exit(1); 
    }

    # Open the logfile for logging at the beginning of execution.
    # The primary reason for this is to 'die' before doing anything
    # that we won't be able to log, and opening the log filehandle
    # in the beginning ensures this.

    # Grab just the filename portion of this script to use as a
    # log identifier:
    $::script_name = $0;
    $::script_name =~ s/^.*\/([\w]+)$/$1/;

    # Autoflush buffer:
    $| = 1;

    # If this program is called by another program, we'll send STDOUT
    # and SDTERR to the logfile - this way the children's messages will
    # stick with the parent's logs.  Conversely, if this is called
    # interactively (on the command line), we'll send it to both the 
    # terminal and the logfile:

    # This needs to be established dynamically; I don't know the
    # preferred method for arriving at this value:
    my $logdir   = '/home/dlr/sandbox/log/';

    my $logfile  = 'HelmPerlScripts.log';

    if(!(-t STDIN && -t STDOUT)){
        # Point STDOUT and STDERR to the logfile, or bail out:
        unless(open(STDOUT,">> $logdir" . "$logfile") && open(STDERR,">&STDOUT")){
            die "Can't open handle for logging: $!\n";
        }
    } else {
        # Point STDOUT and STDERR to the logfile, or bail out:
        unless(open(STDOUT,"| tee -a $logdir" . "$logfile") && open(STDERR,">&STDOUT")){
            die "Can't open handle for logging: $!\n";
        }
    }

    # This shouldn't really be necessary, but it couldn't hurt:
    select(STDERR); $| = 1;
    select(STDOUT); $| = 1;

    # This might appear odd, but since STDOUT is now a filehandle
    # for the logfile, try to get a lock on it, or bail out:
    unless(flock(STDOUT, 2|4)){ # Ask nice first.
        sleep 1;
        unless(flock(STDOUT, 2)){
            die "Unable to lock logfile: $!\n";
        }
   }

    # This is all in order to print a nice header for the log
    # entry:

    my $time   = time();
    my $date   = localtime($time);
    my $header = "[$date]\n[$$] START: $::script_name " . join(' ',@@ARGV) . "\n";

    print $header;
}
END {
    # Note that because END blocks are executed from bottom to top,
    # this block should appear BEFORE any other END blocks trying
    # to print to the same filehandle.  This might seem unnecessary,
    # but since we did point these at a file, we really ought to
    # clean up after ourselves:

    my $footer = "[$$] END: $::script_name\n\n";

    print $footer;
    
    # I haven't figured out why this is the case, but if 'tee' is 
    # used, trying to close these causes the program to hang while
    # waiting for an eof.  Just bugging out seems to work, though.
#    close(STDOUT) or die "Couldn't close logging handle STDOUT: $!\n";
#    close(STDERR) or die "Couldn't close logging handle STDERR: $!\n";
@


