blob: 3be732424fced2151177e5ee2d55774759ec5f2d [file] [log] [blame]
#!/usr/bin/perl -w
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##############################################################################
# $Id: vcld 1951 2008-12-12 13:48:10Z arkurth $
##############################################################################
=head1 NAME
VCL::vcld - VCL daemon module
=head1 SYNOPSIS
perl vcld
=head1 DESCRIPTION
This is the executable module for running the VCL management node daemon.
=cut
##############################################################################
package VCL::vcld;
# Specify the lib path using FindBin
use FindBin;
use lib "$FindBin::Bin/../lib";
# Configure inheritance
use base qw();
# Specify the version of this module
our $VERSION = '2.00';
# Specify the version of Perl to use
use 5.008000;
use strict;
use warnings;
use diagnostics;
use Symbol;
use POSIX;
use Getopt::Long;
use English;
use VCL::utils;
use VCL::DataStructure;
##############################################################################
# Turn on autoflush
$| = 1;
# Get the command line options
our $opt_d = '';
Getopt::Long::Configure('bundling', 'no_ignore_case');
GetOptions('d|debug' => \$opt_d,
'h|help' => \&help);
# Call daemonize if -d (debug) wasn't specified
if (!$opt_d) {
&daemonize;
}
# Variables to store child process information
our %child_pids = (); # keys are current child process IDs
our $child_count = 0; # current number of children
# Install signal handlers
$SIG{CHLD} = \&REAPER;
$SIG{INT} = \&HUNTSMAN;
$SIG{QUIT} = \&HUNTSMAN;
$SIG{HUP} = \&HUNTSMAN;
$SIG{TERM} = \&HUNTSMAN;
# Call main subroutine
&main();
#/////////////////////////////////////////////////////////////////////////////
=head2 main
Parameters :
Returns :
Description : Main VCL daemon engine subroutine. Queries database for request
and passes off data to make_new_child() to begin processing.
=cut
sub main () {
preplogfile($LOGFILE);
#===========================================================================
# BEGIN NEW CODE
# This section does some prep work before looping
my ($package, $filename, $line, $sub) = caller(0);
# Set the vcld environment variable to 0 so other subroutines know if this is the vcld or child process
$ENV{vcld} = 1;
notify($ERRORS{'OK'}, $LOGFILE, "vcld environment variable set to $ENV{vcld} for this process");
# Rename this process
rename_vcld_process();
# Create a hash to store all of the program state information
my %info;
# Get the management node info from the database
# get_management_node_info() will determine the hostname
if ($info{managementnode} = get_management_node_info()) {
notify($ERRORS{'OK'}, $LOGFILE, "retrieved management node information from database");
}
else {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "unable to retieve management node information from database");
exit;
}
# Define local variables from the management node hash for code simplicity
my $management_node_id = $info{managementnode}{id};
my $management_node_hostname = $info{managementnode}{hostname};
# Set environment variables for global management node information
$ENV{management_node_id} = $management_node_id;
notify($ERRORS{'OK'}, $LOGFILE, "management_node_id environment variable set: $management_node_id");
# Get the management node checkin interval from the database if defined
# Otherwise, the default is 12 seconds
my $management_node_checkin_interval = 12;
if (defined $info{managementnode}{checkininterval}) {
$management_node_checkin_interval = $info{managementnode}{checkininterval};
}
notify($ERRORS{'OK'}, $LOGFILE, "management node checkin interval is $management_node_checkin_interval seconds");
notify($ERRORS{'OK'}, $LOGFILE, "vcld started on $management_node_hostname");
#===========================================================================
while (1) {
SLEEP:
delete $ENV{request_id};
delete $ENV{reservation_id};
delete $ENV{state};
sleep $management_node_checkin_interval;
#===========================================================================
# Update lastcheckin for this management node
my $lastcheckin_timestamp = update_lastcheckin($management_node_id);
if ($lastcheckin_timestamp) {
notify($ERRORS{'DEBUG'}, $LOGFILE, "lastcheckin time updated for management node $management_node_id: $lastcheckin_timestamp");
# Update the local hash info to reflect the new timestamp
$info{managementnode}{lastcheckin} = $lastcheckin_timestamp;
}
else {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "could not update lastcheckin for management node $management_node_id");
}
# Get all the requests assigned to this management node
# get_management_node_requests() gets a subset of the information available
if ($info{request} = {get_management_node_requests($management_node_id)}) {
#notify($ERRORS{'DEBUG'}, $LOGFILE, "retieved request information for management node $management_node_id");
}
else {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "could not retieve request information for management node $management_node_id");
}
# See if there's anything to do
my $request_count = scalar keys %{$info{request}};
#notify($ERRORS{'DEBUG'}, $LOGFILE, "number of requests assigned to management node $management_node_id: $request_count");
#===========================================================================
# Loop through the requests assigned to this management node
REQUEST: foreach my $request_id (keys %{$info{request}}) {
#notify($ERRORS{'DEBUG'}, $LOGFILE, "management node $management_node_id has been assigned request id: $request_id");
# Store some request data into a local variables
my $request_state_name = $info{request}{$request_id}{state}{name};
my $request_laststate_name = $info{request}{$request_id}{laststate}{name};
my $request_start = $info{request}{$request_id}{start};
my $request_end = $info{request}{$request_id}{end};
my $request_preload = $info{request}{$request_id}{preload};
$ENV{request_id} = $request_id;
$ENV{reservation_id} = 0;
$ENV{state} = $request_state_name;
# Make sure the request state is valid
if ($request_state_name !~ /inuse|reserved|deleted|timeout|reclaim|reload|new|tomaintenance|image|imageprep|makeproduction|imageinuse|complete|failed|pending|maintenance|tovmhostinuse/) {
notify($ERRORS{'WARNING'}, $LOGFILE, "assigned request in unsupported state: $request_state_name");
next REQUEST;
}
# Don't process requests that are already pending
if ($request_state_name =~ /^(pending|maintenance)/) {
next REQUEST;
}
#===========================================================================
# Loop through the reservations associated with this request
RESERVATION: foreach my $reservation_id (keys %{$info{request}{$request_id}{reservation}}) {
$ENV{reservation_id} = $reservation_id;
# Check to see if the reservation is still in the hash before proceeding
# If request was deleted from database, it was also removed from this hash
if (!defined($info{request}{$request_id}{reservation}{$reservation_id})) {
#notify($ERRORS{'DEBUG'}, $LOGFILE, "reservation was deleted");
next RESERVATION;
}
# Store reservation variables into local variable
my $reservation_lastcheck = $info{request}{$request_id}{reservation}{$reservation_id}{lastcheck};
# Perform steps common to all states
#notify($ERRORS{'DEBUG'}, $LOGFILE, "assigned reservation in state: $request_state_name");
# The request_info hash stores all the information for this request
my %request_info;
# Figure out the status of this reservation based on reservation times and the request state
# check_time_result can be: start, preload, end, poll, old, remove, 0
my $check_time_result = check_time($request_start, $request_end, $reservation_lastcheck, $request_state_name, $request_laststate_name);
#notify($ERRORS{'DEBUG'}, 0, "check_time returned \'$check_time_result\'");
# Do nothing if check_time returned 0
# Check this before querying for the large set of request data
if (!$check_time_result) {
# do nothing - disabled debug output too much info for large numbr of requests
#notify($ERRORS{'DEBUG'}, $LOGFILE, "request will not be processed");
next RESERVATION;
}
elsif ($check_time_result eq "old") {
# Only complain
notify($ERRORS{'WARNING'}, $LOGFILE, "this is an old request");
next RESERVATION;
}
elsif ($check_time_result eq "remove") {
# Remove the request and associated reservations from database
# This also removes rows from computerloadlog table for associated reservations
if (delete_request($request_id)) {
notify($ERRORS{'OK'}, $LOGFILE, "request deleted");
}
else {
notify($ERRORS{'WARNING'}, $LOGFILE, "unable to delete rows from request, reservation, and computerloadlog tables for request");
}
# Remove the request key from the hash
delete $info{request}{$request_id};
next RESERVATION;
} ## end elsif ($check_time_result eq "remove") [ if (!$check_time_result)
elsif ($check_time_result eq "preload" && $request_preload) {
# Preload flag has already been set, don't process preload request again
notify($ERRORS{'DEBUG'}, $LOGFILE, "preload request has already been processed");
next RESERVATION;
}
# Make sure reservation is not currently being processed
if (reservation_being_processed($reservation_id)) {
notify($ERRORS{'WARNING'}, $LOGFILE, "reservation $reservation_id is already being processed");
next RESERVATION;
}
else {
notify($ERRORS{'DEBUG'}, $LOGFILE, "reservation $reservation_id is NOT already being processed");
}
# Get the full set of database data for this request
if (%request_info = get_request_info($request_id)) {
notify($ERRORS{'DEBUG'}, $LOGFILE, "retieved request information from database");
# Set request variables that may have changed by other processes to their original values
# They may change if this is a cluster reservation
$request_info{state}{name} = $request_state_name;
$request_info{laststate}{name} = $request_laststate_name;
$request_info{preload} = $request_preload;
}
else {
notify($ERRORS{'WARNING'}, $LOGFILE, "could not retieve request information from database");
next RESERVATION;
}
# Add the check_time result to the hash
$request_info{CHECKTIME} = $check_time_result;
# Get a new data structure object
my $data_structure;
eval {$data_structure = new VCL::DataStructure({request_data => \%request_info, reservation_id => $reservation_id});};
if (my $e = Exception::Class::Base->caught()) {
notify($ERRORS{'CRITICAL'}, 0, "unable to create DataStructure object" . $e->message);
next RESERVATION;
}
# Check if preload was returned by check_time and that preload flag is 0
# The preload flag will be set to 1 by new.pm module after it's done
if ($check_time_result =~ /preload/ && !($request_info{preload})) {
notify($ERRORS{'OK'}, $LOGFILE, "request start time within 25-35 minute window and preload flag is 0, processing preload request");
$request_info{PRELOADONLY} = 1;
}
# Add the reservation ID to be processed to the hash
$request_info{RESERVATIONID} = $reservation_id;
# Wait for a short amount of time before making new child
# This is to give processes a chance to start before subsequent processes
# check for conflicts such as overlapping computer reservations
notify($ERRORS{'DEBUG'}, $LOGFILE, "sleeping for 2 seconds before updating state to pending");
sleep 2;
# Update the request state to pending, laststate to next state
# Pending is set now so vcld doesn't try to process it again
# The previous state is already in the hash as the laststate value
# This will be passed to the next module so it knows where it came from
my $is_parent_reservation = $data_structure->is_parent_reservation();
if ($is_parent_reservation && update_request_state($request_id, "pending", $request_state_name)) {
notify($ERRORS{'OK'}, $LOGFILE, "request state updated to pending, laststate $request_state_name");
}
elsif (!$is_parent_reservation) {
notify($ERRORS{'OK'}, $LOGFILE, "child reservation: request state NOT updated to pending");
}
else {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "request state could not be updated to pending, reservation not processed");
next RESERVATION;
}
# Insert a computerloadlog entry to indicate processing has begin for this reservation
my $computer_id = $data_structure->get_computer_id();
if (insertloadlog($reservation_id, $computer_id, "begin", "beginning to process, state is $request_state_name")) {
notify($ERRORS{'OK'}, $LOGFILE, "inserted 'begin' entry into computerloadlog for reservation $reservation_id");
}
else {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "failed to insert 'begin' entry into computerloadlog for reservation $reservation_id");
}
# Make a new child process, passing it the request/reservation info
make_new_child({request_info => \%request_info, data_structure => $data_structure});
} ## end foreach my $reservation_id (keys %{$info{request...
} ## end foreach my $request_id (keys %{$info{request}})
delete $ENV{request_id};
delete $ENV{reservation_id};
delete $ENV{state};
#===========================================================================
# Get all the block requests assigned to this management node
my $blockrequest_data = get_management_node_blockrequests($management_node_id);
if (!defined $blockrequest_data) {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "could not retrieve block request information for management node $management_node_id");
next;
}
elsif (!$blockrequest_data) {
#notify($ERRORS{'OK'}, 0, "there are 0 block requests assigned to management node $management_node_id");
next;
}
#notify($ERRORS{'CRITICAL'}, $LOGFILE, "\$blockrequest_data", $blockrequest_data);
#next;
# Loop through the block requests assigned to this management node
BLOCKREQUEST: foreach my $blockrequest_id (keys %{$blockrequest_data}) {
#notify($ERRORS{'DEBUG'}, $LOGFILE, "checking block request id=$blockrequest_id");
BLOCKTIME: foreach my $blocktime_id (keys %{$blockrequest_data->{$blockrequest_id}{blockTimes}}) {
#notify($ERRORS{'DEBUG'}, $LOGFILE, "checking block time id=$blocktime_id");
# Get a new data structure object
my $data_structure;
eval {$data_structure = new VCL::DataStructure({blockrequest_data => $blockrequest_data, blockrequest_id => $blockrequest_id, blocktime_id => $blocktime_id});};
if (my $e = Exception::Class::Base->caught()) {
notify($ERRORS{'CRITICAL'}, 0, "unable to create DataStructure object" . $e->message);
next;
}
# Store some block request data into a local variables
my $blockrequest_name = $data_structure->get_blockrequest_name();
my $blockrequest_expire = $data_structure->get_blockrequest_expire();
my $blockrequest_processing = $data_structure->get_blockrequest_processing();
my $blocktime_start = $data_structure->get_blocktime_start();
my $blocktime_end = $data_structure->get_blocktime_end();
my $blocktime_processed = $data_structure->get_blocktime_processed();
my $blocktime_id = $data_structure->get_blocktime_id();
#use VCL::blockrequest;
#$data_structure->set_blockrequest_mode('start');
#my $br_start = VCL::blockrequest->new({%{$blockrequest_data->{$blockrequest_id}}, data_structure => $data_structure});
#notify($ERRORS{'OK'}, $LOGFILE, "***** Starting start process *****");
#$br_start->process();
#exit;
#notify($ERRORS{'OK'}, $LOGFILE, "***** DONE WITH START *****");
#sleep 5;
#$data_structure->set_blockrequest_mode('end');
#my $br_end = VCL::blockrequest->new({%{$blockrequest_data->{$blockrequest_id}}, data_structure => $data_structure});
#notify($ERRORS{'OK'}, $LOGFILE, "***** Starting end process *****");
#$br_end->process();
#notify($ERRORS{'OK'}, $LOGFILE, "***** DONE WITH END *****");
#exit;
# Check if the block request is already being processed
if ($blockrequest_processing) {
#notify($ERRORS{'DEBUG'}, $LOGFILE, "block request $blockrequest_id '$blockrequest_name' is already being processed");
next BLOCKREQUEST;
}
else {
#notify($ERRORS{'OK'}, $LOGFILE, "block request $blockrequest_id '$blockrequest_name' is not currently being processed");
}
# Check block request start, end and expire time
my $blockrequest_mode = check_blockrequest_time($blocktime_start, $blocktime_end, $blockrequest_expire);
# check_blockrequest_time will return 0 if nothing needs to be done and undefined if an error occurred
if (!defined $blockrequest_mode) {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "error occurred checking block request $blockrequest_id '$blockrequest_name' status");
next;
}
elsif (!$blockrequest_mode) {
#notify($ERRORS{'DEBUG'}, $LOGFILE, "block request $blockrequest_id will not be processed at this time");
next;
}
else {
#notify($ERRORS{'DEBUG'}, $LOGFILE, "block request $blockrequest_id will be processed, mode: $blockrequest_mode");
}
if ($blockrequest_mode eq 'start' && $blocktime_processed) {
#notify($ERRORS{'DEBUG'}, $LOGFILE, "block request $blockrequest_id '$blockrequest_name' blocktime_id $blocktime_id has already been processed");
next BLOCKREQUEST;
}
# Start processing block request
$data_structure->set_blockrequest_mode($blockrequest_mode);
# Attempt to set the blockRequest processing column to 1
if (update_blockrequest_processing($blockrequest_id, 1)) {
notify($ERRORS{'OK'}, $LOGFILE, "block request $blockrequest_id '$blockrequest_name' processing set to 1");
# Make a new child process, passing it the request/reservation info
make_new_child({data_structure => $data_structure, request_info => $blockrequest_data->{$blockrequest_id}});
}
else {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "unable to set block request $blockrequest_id '$blockrequest_name' processing to 1");
next;
}
} ## end foreach my $blocktime_id (keys %{$blockrequest_data...
} ## end foreach my $blockrequest_id (keys %{$blockrequest_data...
} ## end while (1)
} ## end sub main ()
#/////////////////////////////////////////////////////////////////////////////
=head2 make_new_child
Parameters :
Returns :
Description :
=cut
sub make_new_child {
my ($args) = @_;
my $request_data = $args->{request_info};
my $data_structure = $args->{data_structure};
$data_structure = 0 if !$data_structure;
# Assemble a consistent prefix for notify messages
my $request_id = $request_data->{id};
my $reservation_id = $request_data->{RESERVATIONID};
# Get the state name
my $state;
my $state_module;
if ($data_structure) {
$state = $data_structure->get_state_name();
$state_module = "VCL::$state";
}
else {
$state = $request_data->{state}{name};
$state_module = "VCL::$state";
}
# The timeout and deleted states have been combined into reclaim.pm
if ($state =~ /^(timeout|deleted)$/) {
notify($ERRORS{'OK'}, $LOGFILE, "request will be processed by reclaim.pm");
$state_module = "VCL::reclaim";
}
# The imageinuse state is now handled by inuse.pm
if ($state =~ /^(imageinuse)$/) {
notify($ERRORS{'OK'}, $LOGFILE, "request will be processed by inuse.pm");
$state_module = "VCL::inuse";
}
# The tomaintenance state is handled by new.pm
if ($state =~ /^(tomaintenance|imageprep|reload|tovmhostinuse)$/) {
notify($ERRORS{'OK'}, $LOGFILE, "request will be processed by new.pm");
$state_module = "VCL::new";
}
notify($ERRORS{'OK'}, $LOGFILE, "creating new process");
eval "use $state_module";
if (!$EVAL_ERROR) {
notify($ERRORS{'OK'}, $LOGFILE, "loaded $state_module module");
}
else {
notify($ERRORS{'WARNING'}, $LOGFILE, "$state_module module could not be loaded");
}
# For testing purposes on Windows
if ($^O =~ /win/i) {
# Set the request_id and reservation_id environment variables
$ENV{request_id} = $request_id;
$ENV{reservation_id} = $reservation_id;
# Set the vcld environment variable to 0 so other subroutines know if this is the vcld or child process
$ENV{vcld} = 0;
notify($ERRORS{'OK'}, $LOGFILE, "vcld environment variable set to $ENV{vcld} for this process");
my $kid;
if ($kid = ($state_module)->new({%{$request_data}, data_structure => $data_structure})) {
notify($ERRORS{'OK'}, $LOGFILE, "$state object created and initialized");
# Set the request_id and reservation_id environment variables
$kid->process();
}
else {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "$state object could not be created and initialized");
switch_state($request_data, 'failed', '', 'failed', 1);
}
# Set the request_id and reservation_id environment variables
delete $ENV{request_id};
delete $ENV{reservation_id};
delete $ENV{state};
# Restore the vcld environment variable to 1
$ENV{vcld} = 1;
# Only return from make_new_child if running on Windows for testing without actually forking
return;
} ## end if ($^O =~ /win/i)
# Build a signal set using POSIX::SigSet->new, contains only the SIGINT signal
my $sigset = POSIX::SigSet->new(SIGINT);
# Pass the POSIX::SigSet object to sigprocmask with the SIG_BLOCK flag to delay SIGINT signal delivery
sigprocmask(SIG_BLOCK, $sigset) or die "can't block SIGINT for fork: $!\n";
FORK: {
my $pid;
if ($pid = fork) {
# If here, this is the parent process
# Restore delivery of SIGINT signal for the parent process
sigprocmask(SIG_UNBLOCK, $sigset) or die "can't unblock SIGINT for fork: $!\n";
# Parent process records the child's PID and returns
$child_count++;
$child_pids{$pid} = 1;
notify($ERRORS{'OK'}, $LOGFILE, "current number of forked kids: $child_count");
return;
}
elsif (defined $pid) {
# If here, this is the child process
# Child must *NOT* return from this subroutine after this point. It must exit.
# If child returns it will become a parent process and spawn off its own children
# Configure the SIGINT signal to kill this process normally
$SIG{INT} = 'DEFAULT';
# Unblock the SIGINT signal
sigprocmask(SIG_UNBLOCK, $sigset) or die "can't unblock SIGINT for fork: $!\n";
# Set the vcld environment variable to 0 so other subroutines know if this is the vcld or child process
$ENV{vcld} = 0;
notify($ERRORS{'OK'}, $LOGFILE, "vcld environment variable set to $ENV{vcld} for this process");
# Set the request_id and reservation_id environment variables
$ENV{request_id} = $request_id;
$ENV{reservation_id} = $reservation_id if $reservation_id;
$ENV{state} = $state;
# Create a new VCL state object, passing it the reservation data
if (my $state_object = ($state_module)->new({%{$request_data}, data_structure => $data_structure})) {
notify($ERRORS{'OK'}, $LOGFILE, "$state_module object created and initialized");
# Call the state object's process() subroutine
$state_object->process();
}
else {
notify($ERRORS{'CRITICAL'}, $LOGFILE, "$state_module object could not be created and initialized, $!");
switch_state($request_data, 'failed', '', 'failed', 1);
}
exit;
} ## end elsif (defined $pid) [ if ($pid = fork)
elsif ($! =~ /No more process/) {
sleep 5;
redo FORK;
}
else {
# strange error
die "can't fork: $!\n";
}
} ## end FORK:
} ## end sub make_new_child
#/////////////////////////////////////////////////////////////////////////////
=head2 HUNTSMAN
Parameters :
Returns :
Description :
=cut
sub HUNTSMAN {
# Temporarily override the the SIGCHLD signal handler
# Set SIGCHLD handler to IGNORE, meaning nothing happens when a child process exits
local ($SIG{CHLD}) = 'IGNORE';
# Send SIGINT to child processes
kill 'INT' => keys %child_pids;
notify($ERRORS{'OK'}, $LOGFILE, "vcld process exiting, pid=$$");
exit;
}
#/////////////////////////////////////////////////////////////////////////////
=head2 REAPER
Parameters : None
Returns : Undefined
Description : The REAPER subroutine gets called whenever a child process
stops running or exits. This occurs because the subroutine is
configured as the handler for SIGCHLD signals. The system will
send a SIGCHLD signal whenever a child process stops running
or exits.
The REAPER subroutine manages the child PID hash when a VCL .
state process exits. It also captures the exit code of the
child process which died and makes sure the special $?
variable is set to this value.
=cut
sub REAPER {
# Save the information saved in $? before proceeding
# This is done to save the exit status of the child process which died
# If you don't save it, wait() will overwrite it
my $child_exit_status = $? >> 8;
my $signal_number = $? & 127;
my $dumped_core = $? & 128;
# Configure the REAPER() subroutine to handle SIGCHLD signals
$SIG{CHLD} = \&REAPER;
# Wait for a child process to terminate
# Should have already happened since this subroutine is only called when CHLD signals are sent
my $dead_pid = wait;
# Check if the child PID hash contains the pid of the process which just died
if (exists $child_pids{$dead_pid}) {
# Child which died was a VCL state process since its pid is in the hash
$child_count--;
delete $child_pids{$dead_pid};
notify($ERRORS{'OK'}, $LOGFILE, "VCL state process exited, pid=$dead_pid");
}
else {
# Child which died was some other process
notify($ERRORS{'DEBUG'}, $LOGFILE, "child process exited, pid=$dead_pid");
}
# Set the special $? variable back to the exit status of the child which died
# This is useful when utilities such as SSH are run in other places in the code
# The code which called the utility can check the exit status to see if it was successful
$? = $child_exit_status;
return;
} ## end sub REAPER
#/////////////////////////////////////////////////////////////////////////////
=head2 daemonize
Parameters :
Returns :
Description :
=cut
sub daemonize {
chdir '/' or die "Can't chdir to /: $!";
defined(my $pid = fork) or die "Can't fork $!";
exit if $pid;
#development
#$0 = "vcldev";
#production
#$0 = "vcld";
$0 = $PROCESSNAME;
print "Created process $$ renamed to $0 ...\n";
setsid or die "Can't start a new session: $!";
open STDIN, '/dev/null' or die "Can't read /dev/null $!";
open STDOUT, ">>$LOGFILE" or die "Can't write $LOGFILE $!";
open STDERR, ">>$LOGFILE" or die "Can't write $LOGFILE $!";
setsid or die "Can't start a new session: $!";
umask 0;
open(PIDFILE, ">" . $PIDFILE) or notify($ERRORS{'WARNING'}, $LOGFILE, "unable to open PID file: $PIDFILE, $!"); # so I can kill myself easily
print PIDFILE $$;
close(PIDFILE);
} ## end sub daemonize
#/////////////////////////////////////////////////////////////////////////////
=head2 help
Parameters :
Returns :
Description :
=cut
sub help {
my $message = <<"END";
--------------------------------------------
vcld is intented to run in daemon mode.
Please read the INSTALLATION file in the source directory.
END
print $message;
exit;
} ## end sub help