#!/usr/bin/perl -w
#------------------------------------------------------------------
# 
# Karma Copyright (C) 1999  Sean Hull <shull@pobox.com>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
#
#------------------------------------------------------------------
#
# karmad -
#
# Oracle monitoring software.
#
#------------------------------------------------------------------

#$ENV{ORACLE_HOME} = '/home/oracle/product/8.0.5';
#
# check for the Mail::Send package before using..
#
$main::USE_EMAIL_NOTIFICATION = 1;
unless (eval "require Mail::Send") {
    $main::USE_EMAIL_NOTIFICATION = 0;
}


require 5.004;
#use Socket;

BEGIN {
    unless (eval "require DBI") {
	print 
	    "You must have DBI installed to use karma.\n",
	    "Please install it first, and try again.\n";
	exit;
    }
}

BEGIN {
    unless (eval "require DBD::Oracle") {
	print
	    "You must have DBD::Oracle installed to use karma.\n",
	    "Please install it first, and try again.\n";
	exit;
    }
}

use strict;
use File::Basename;
use Getopt::Std;
use IO::File;
use karma;


#
# get the command line options
#
$main::opt_d = undef;
$main::opt_v = undef;
$main::opt_h = undef;
$main::opt_k = undef;
$main::opt_w = undef;
$main::opt_c = undef;
$main::opt_l = undef;
getopts('hc:k:l:vwd:');




if ($main::opt_v) {
    printVersion ();
}

if ($main::opt_w) {
    printWarranty ();
}

if ($main::opt_h) {
    printHelp ();
}

if ($main::opt_d) {
    $main::DEBUG_LEVEL = $main::opt_d;
}

#
# get the current working directory...
# daemonize changes us to "/" so the daemon doesn't 
# lock any mounted volumes
#
if ($main::WINDOWS == 0) {
    $main::wd = `/bin/pwd`;
} else {
    $main::wd = Win32::GetCwd ();
}

chomp $main::wd;
#
# I'm sure this isn't the perl way, but for now...
#
#debugMessage ("WORKING DIR:$main::wd\n");

$main::KARMA_HOME = $main::wd;
if (defined $ENV{KARMA_HOME}) {
    if ((-e "$main::PATH_DELIM$ENV{KARMA_HOME}") &&
	(-d "$main::PATH_DELIM$ENV{KARMA_HOME}") &&
	(-w "$main::PATH_DELIM$ENV{KARMA_HOME}")) {
	$main::KARMA_HOME="$main::PATH_DELIM$ENV{KARMA_HOME}";
    } elsif ((-e "$main::wd$main::PATH_DELIM$ENV{KARMA_HOME}") &&
	     (-d "$main::wd$main::PATH_DELIM$ENV{KARMA_HOME}") &&
	     (-w "$main::wd$main::PATH_DELIM$ENV{KARMA_HOME}")) {
	$main::KARMA_HOME="$main::wd$main::PATH_DELIM$ENV{KARMA_HOME}";
    }
}

debugMessage ("KARMA_HOME:$main::KARMA_HOME\n", 1);

# 
# background oneself before starting
#
# (this is handled by Win32::Process in karmactl on windows)
#
#if ($main::WINDOWS == 0) {
#    daemonize ();
#}

$main::startTime = time();
$main::currTime = $main::startTime;


#
# set the logfile name
#
$main::LOG_FILE_NAME = "$main::KARMA_HOME$main::PATH_DELIM" . 'karma.log';
if (defined ($main::opt_l)) {
    if ((($main::WINDOWS == 0) && ($main::opt_l =~ /^\/.*$/)) ||
	(($main::WINDOWS == 1) && ($main::opt_l =~ /^\w:\\.*$/))) {
	$main::LOG_FILE_NAME = $main::opt_l;
    } else {
	if ($main::opt_l =~ /^\..*$/) {
	    $main::opt_l =~ s/^\.//;
	}
	$main::LOG_FILE_NAME = $main::wd .
	    $main::PATH_DELIM . $main::opt_l;
    }
#    $main::LOG_FILE_NAME = "$main::opt_l";
}

if ((-e $main::LOG_FILE_NAME) && 
    (not (-w $main::LOG_FILE_NAME))) {
    $main::LOG_FILE_NAME = '-';
}

$main::info_file = undef;

#
# open the error logfile
# this shouldn't be global...
#
$main::logfile = new IO::File ">>$main::LOG_FILE_NAME";
if (not (defined $main::logfile)) {
    $main::logfile = new IO::File ">>-";
    debugMessage ("Cannot open logfile: $main::LOG_FILE_NAME, using STDOUT", 1);
}

#-----------------------------------------------------------------------
#
# TYPES
#
#-----------------------------------------------------------------------


#-----------------------------------------------------------------------
#
# CONSTANTS
#
#-----------------------------------------------------------------------
$main::cDAYSECS = 86400;

$main::cNO_SHOW = 0;
$main::cOK_STATUS = 1;
$main::cNO_STATUS = 2;
$main::cWARNING_STATUS = 3;
$main::cALERT_STATUS = 4;

$main::cSHORT_EMAIL = "short";
$main::cFULL_EMAIL = "full";

$main::cNOTIFY_ALRT="notify_alert";
$main::cNOTIFY_WARN="notify_warning";
$main::cNOTIFY_EMAIL="notify_email";

$main::cSTATUS_HEIGHT=25;
$main::cSTATUS_WIDTH=35;
$main::cHEAD_HEIGHT=50;
#
# constants for db_info{TNS} array
#
$main::cDB_HANDLE=0;
$main::cDB_USER=1;
$main::cDB_PASS=2;
$main::cDB_REFRESH=3;
$main::cDB_PREFGROUP=4;

$main::cSTATUS = 0;
$main::cUPD_TIME = 1;
$main::cFORCE_UPD = 1;
$main::cNO_FORCE = 0;

$main::cDEF_GRP_NAME = "default";

#
# for config_notify
#
$main::cNOTIFY_INT = 0;
#$main::cNOTIFY_SIZE = 1;
$main::cNOTIFY_SERV = 1;

#
# min wakeup in minutes
#
$main::cWAKEUP = 15;
$main::MIN_WAKEUP = $main::cWAKEUP;

#
# default notify minutes
#
$main::cNOTIFY_WAKEUP = $main::cWAKEUP;
$main::cNOTIFY_MSG = $main::cFULL_EMAIL;

#
# HTML COLORS
#
# karma page colors
#
$main::cKARMA_TEXT_COLOR="#FF9966";
#$main::cKARMA_LINK_COLOR=$main::cKARMA_TEXT_COLOR;
$main::cKARMA_LINK_COLOR="#CC6600";
$main::cTEXT_COLOR="#FF9933";
$main::cEMPHASIS_TEXT="#CC3300";

#$main::cBODY_BG_COLOR="#3366CC";
$main::cINFO_BG_COLOR="#003399";
$main::cHEAD_BG_COLOR="#000066";
$main::cMAIN_TABLE_BG="#006666";

$main::cBORDER_COLOR="#00CCCC";
$main::cBORD_COL_DARK="#003333";

#
# misc page colors
#

$main::KARMA_FIFO_NAME = "$main::KARMA_HOME$main::PATH_DELIM.karmafifo";
$main::PID_FILE_NAME = "$main::KARMA_HOME$main::PATH_DELIM.karma.pid";

#
# this specifies the base location of generated karma html files
#
$main::KARMA_DOC_ROOT = "$main::KARMA_HOME$main::PATH_DELIM" . 'doc_root';
if ($main::opt_k) {
    if ($main::opt_k =~ /^\/.*$/) {
	$main::KARMA_DOC_ROOT = $main::opt_k;
    } else {
	if ($main::opt_k =~ /^\..*$/) {
	    $main::opt_k =~ s/^\.//;
	}
	$main::KARMA_DOC_ROOT = $main::wd .
	    $main::PATH_DELIM . $main::opt_k;
    }
}

if (not (-e $main::KARMA_DOC_ROOT) ||
    not (-d $main::KARMA_DOC_ROOT) ||
    not (-w $main::KARMA_DOC_ROOT)) {

    print ("Please make sure that doc_root \"$main::KARMA_DOC_ROOT\" is a directory and\n");
    print ("that it is writable.\n");
    exit;
}

#@main::test_array = (["Status", "Name", "Value"], 
#	       [$main::cOK_STATUS, "Aeon", "25"],
#	       [$main::cWARNING_STATUS, "Scafandra", "50"],
#	       [$main::cALERT_STATUS, "Una", "75"],
#	       [$main::cNO_STATUS, "Trevor", "100"]);
# 
#$main::testRef = \@main::test_array; 

#
# you can specify these in the karma.conf file if you want 'em 
# different 
#
$main::USE_BLINK_WARNING=1;
$main::USE_BLINK_ALERT=1;

#
# by default different preference groups are not separated, however
# setting this to true here or in the config file means there will
# be a header row between each group of monitored databases
#
$main::USE_PREFGROUP_SECTIONS=0;

#-----------------------------------------------------------------------
#
# GLOBALS 
#
# Organized as follows:
#
# TNS is one of the TNS_NAMES specified in "karma:" directive lines
#    in the karma.conf file (must be defined in tnsnames.ora file)
#
# SERVICE is one of (redolog, rollback, latch, tablespace, slowsql,
#                    hitratios, extents, fragmentation, mts, os,
#                    alertlog, up, repqueue, reperror)
#
# PREFGROUP is on of (factory, default, or some user defined name)
#
# FTYPE is one of (help, info)
#
# GLOBAL VARIABLE               DESCRIPTION
# -----------------------       --------------------------------
# $main::statements{SERVICE}    a string holding the sql statements
#                               executed in the getSERVICEInfo routine
#                               to judge the status of this service
#
# $main::files{FTYPE}{SERVICE}  a string holding a filename
#
# $main::config_info{prefGroup}{SERVICE}
#                               an array of values for this service
#                               gathered from the karma.conf file
#                               [0] interval
#                               [1] last updated?
#
# $main::config_notify{prefGroup}{type}[]
#                               type can be one of {warn, alert}
#                               [0] interval
#                               [1] array of email addresses
#
# $main::config_email{prefGroup}{size}
#                               list email address in an array
#                               1 array for short email notifications
#                               1 array for full email notifications
#
# $main::db_info{TNS}[n]        an array of info related to each
#                               database we're connecting to
#                               [0]  database handle
#                               [1]  username
#                               [2]  password
#                               [3]  refresh
#
# $main::stats{SERVICE}{TNS}[0] stores the status of each service
#                               monitored for each database.
#                               1 - OK STATUS
#                               2 - NO STATUS
#                               3 - WARNING STATUS
#                               4 - ALERT STATUS
#
# $main::stats{SERVICE}{TNS}[1] time last updated (in seconds - unix time)
#
# $main::names{short}{SERVICE}  short versions of the service names
#                               for display as main.html column headers
# $main::names{long}{SERVICE}   long versions of the service names
#                               for display in info page titles.
# $main::names{shown}{SERVICE}  whether this column is shown in karma
#                               html table (1 if shown, 0 otherwise)
#
#-----------------------------------------------------------------------

#----------------------------------------------
#
# sql statements
#
#----------------------------------------------

#
# redo log query
#
$main::statements{redolog} = "
SELECT TO_CHAR(first_time, 'J'), TO_CHAR (first_time, 'SSSSS'),
       group#, sequence#, TO_CHAR (first_time, 'DD/MM/YYYY HH24:MI')
FROM v\$log
ORDER BY 1,2";

# 
# rollback segment query
#
#$main::statements{rollback} = "
#select ((gets-waits) * 100/gets)
#from v\$rollstat";

$main::statements{rollback} = "
select a.name, b.status, b.gets, b.waits
from v\$rollname a, v\$rollstat b
where a.usn = b.usn";

#
# latch query
#
#$main::statements{latch} = "
#select (gets-misses) * 100 / gets
#from v\$latch
#where gets > 0";

$main::statements{latch} = "
SELECT   name, gets, misses
FROM     v\$latch
ORDER BY name";

#
# tablespace query
#
$main::statements{tablespace} = "
SELECT  a.tablespace_name, a.total, b.used
FROM    (SELECT  tablespace_name, SUM (bytes) total
         FROM    dba_data_files
         GROUP BY tablespace_name) a,
        (SELECT  tablespace_name, SUM (bytes) used
         FROM    dba_segments
         GROUP BY tablespace_name) b
WHERE   a.tablespace_name = b.tablespace_name (+)";

#
# slow sql query
#
#$main::statements{slowsql} = "
#SELECT disk_reads / DECODE (executions, 0, 1, executions), 
#       sql_text
#FROM v\$sqlarea
#WHERE disk_reads / DECODE (executions, 0, 1, executions) > ?";

$main::statements{slowsql} = "
SELECT disk_reads, executions, sql_text
FROM v\$sqlarea";

#
# hit ratios query
#
$main::statements{hitratios} = "
SELECT name, value
FROM v\$sysstat
WHERE name IN ('consistent gets', 'db block gets', 'physical reads')";

#
# extents query
#
$main::statements{extents} = "
SELECT segment_name, max_extents, count(*), owner
FROM dba_segments
WHERE owner NOT IN ('SYS', 'SYSTEM')
GROUP BY segment_name, owner, max_extents";

#
# fragmentation query
#
$main::statements{fragmentation} = "
SELECT tablespace_name, initial_extent, next_extent, pct_increase
FROM dba_tablespaces
WHERE tablespace_name NOT IN ('SYSTEM')";

#
# mts query
#
$main::statements{mts} = "
SELECT name, busy, idle
FROM v\$dispatcher";

#
# OS query
#
$main::statements{os} = "
SELECT load_one, load_five, load_fifteen, pctidle,
       TO_CHAR (timestamp, 'HH24:MI')
FROM karma_os_stats";

#
# alert log query
#
$main::statements{alertlog} = "
SELECT facility, errno, TO_CHAR (timestamp, 'HH24:MI'), text
FROM karma_alertlog_errors
WHERE timestamp > (SYSDATE - 1) ORDER BY timestamp DESC";

#
# up query (none needed for now)
#
$main::statements{up} = "
SELECT name, value
FROM v\$sysstat
ORDER BY name";

#
# db info query
#
$main::statements{db} = "
SELECT name, value
FROM v\$parameter
ORDER BY name";

#
# repqueue
#
$main::statements{repqueue} = "
SELECT  t.deferred_tran_id, t.delivery_order, 
        to_char(t.start_time, 'DD/MM/YYYY HH24:MI:SS') 
FROM    deftrandest d, deftran t
WHERE   d.deferred_tran_id = t.deferred_tran_id
AND     d.delivery_order = t.delivery_order
ORDER BY t.start_time";

#
# reperror (return errors in the last day)
#
$main::statements{reperror} = "
SELECT   deferred_tran_id, origin_tran_db, destination,
         to_char(start_time, 'HH24:MI:SS') , error_number
FROM     deferror
WHERE    start_time > sysdate - 1
ORDER BY start_time";

#
# more info file extentsions... actual filename will be
# dbname.$REDOLOG_FILE for example.
#
$main::INDEX_FILE_NAME = "$main::KARMA_DOC_ROOT$main::PATH_DELIM" . 'karma.html';

$main::files{info}{redolog}       = "redolog.html";
$main::files{info}{rollback}      = "rollback.html";
$main::files{info}{slowsql}       = "slowsql.html";
$main::files{info}{alertlog}      = "alertlog.html";
$main::files{info}{hitratios}     = "hitratios.html";
$main::files{info}{extents}       = "extents.html";
$main::files{info}{latch}         = "latch.html";
$main::files{info}{fragmentation} = "fragmentation.html";
$main::files{info}{mts}           = "mts.html";
$main::files{info}{tablespace}    = "tablespace.html";
$main::files{info}{os}            = "os.html";
$main::files{info}{up}            = "up.html";
$main::files{info}{db}            = "db.html";
$main::files{info}{repqueue}      = "repqueue.html";
$main::files{info}{reperror}      = "reperror.html";

#
# help files
#
$main::files{help}{redolog}       = "services.html#Redologs";
$main::files{help}{rollback}      =
    "services.html#Rollback_Segment_Contention";
$main::files{help}{slowsql}       = "services.html#Slow_SQL";
$main::files{help}{alertlog}      = "services.html#Alert_Log_Errors";
$main::files{help}{hitratios}     = "services.html#Hit_Ratios";
$main::files{help}{extents}       = "services.html#Extents";
$main::files{help}{latch}         = "services.html#Latch_Contention";
$main::files{help}{fragmentation} = "services.html#Fragmentation";
$main::files{help}{mts}           = "services.html#MTS_Multi_Threaded_Server";
$main::files{help}{tablespace}    = "services.html#Tablespace_Quotas";
$main::files{help}{os}            = "services.html#OS_Statistics";
$main::files{help}{up}            = "services.html#UP_Status";
$main::files{help}{db}            = "services.html#Initialization_Parameters";
$main::files{help}{repqueue}      = "services.html#Deferred_Transaction_Queue";
$main::files{help}{reperror}      =
    "services.html#Deferred_Transaction_Error_Queue";

#
# shortened service names (no more than 5 characters)
#
$main::names{short}{redolog}       = "rdlg";
$main::names{short}{rollback}      = "rlbk";
$main::names{short}{slowsql}       = "ssql";
$main::names{short}{alertlog}      = "alog";
$main::names{short}{hitratios}     = "hitr";
$main::names{short}{extents}       = "exts";
$main::names{short}{latch}         = "ltch";
$main::names{short}{fragmentation} = "frag";
$main::names{short}{mts}           = "mts";
$main::names{short}{tablespace}    = "tbsp";
$main::names{short}{os}            = "os";
$main::names{short}{up}            = "up";
$main::names{short}{db}            = "name";
$main::names{short}{repqueue}      = "repq";
$main::names{short}{reperror}      = "rper";

#
# long service names (for info page titles)
#
$main::names{long}{redolog}       = "Redolog Switching";
$main::names{long}{rollback}      = "Rollback Segment Contention";
$main::names{long}{slowsql}       = "Slow SQL";
$main::names{long}{alertlog}      = "Alertlog Errors";
$main::names{long}{hitratios}     = "Hit Ratios";
$main::names{long}{extents}       = "Extents";
$main::names{long}{latch}         = "Latch Contention";
$main::names{long}{fragmentation} = "Fragmentation";
$main::names{long}{mts}           = "Multi-threaded Server";
$main::names{long}{tablespace}    = "Tablespace Quotas";
$main::names{long}{os}            = "OS Statistics";
$main::names{long}{up}            = "Database Up";
$main::names{long}{db}            = "Database Name";
$main::names{long}{repqueue}      = "Replication Queue";
$main::names{long}{reperror}      = "Replication Errors";

$main::names{shown}{redolog}       = 0;
$main::names{shown}{rollback}      = 0;
$main::names{shown}{slowsql}       = 0;
$main::names{shown}{alertlog}      = 0;
$main::names{shown}{hitratios}     = 0;
$main::names{shown}{extents}       = 0;
$main::names{shown}{latch}         = 0;
$main::names{shown}{fragmentation} = 0;
$main::names{shown}{mts}           = 0;
$main::names{shown}{tablespace}    = 0;
$main::names{shown}{os}            = 0;
$main::names{shown}{up}            = 0;
$main::names{shown}{db}            = 0;
$main::names{shown}{repqueue}      = 0;
$main::names{shown}{reperror}      = 0;

#
# which columns will be displayed and how often
# stored in a hash of arrays where the array contains
# frequency, alert, and warn values respectively
#
# THESE ARE THE "FACTORY" default settings...

# check every five minutes, alert if switching more the every 15 minutes
# warn if switching more than every 30
#
$main::config_info{factory}{redolog}      = [(5,30,15)];

# check every minute... need to set the rest
#
$main::config_info{factory}{rollback}      = [(5,99,97)];

# check every 15 minutes
#
$main::config_info{factory}{slowsql}       = [(15,100,200)];

# check the alertlog table every 5 minutes, alert if there's been
# an error in the last hour, warn if there's been an error in the
# last day
#
$main::config_info{factory}{alertlog}      = [(5,60,86400)];

# check the hitratios every 5 minutes, alert if less than 70%
# warn if less than 95%
#
$main::config_info{factory}{hitratios}     = [(5,95,70)];

# check for fragmentation every 15 minutes... not sure how to
# set the rest
#
$main::config_info{factory}{extents}       = [(15,2,1)];

# check for latch contention every 5 minutes... need to set the rest 
#
$main::config_info{factory}{latch}         = [(5,99,97)];

#
# check for fragmentation every 15 minutes... 
# setting the thresholds is as yet undefined
#
$main::config_info{factory}{fragmentation} = [(15,0,0)];

# check mts contention every 5 minutes
#
$main::config_info{factory}{mts}           = [(5,50,75)];

# check every minute, if greater than 95% alert, if greater than 85%
# send warning
#
$main::config_info{factory}{tablespace}    = [(1,85,95)];

# check os stats every 5 minutes, alert for load over 10, warn for
# load over 5
#
$main::config_info{factory}{os}            = [(5,5,10)];

#
# check that the deftran queue is not too large
#
$main::config_info{factory}{repqueue}      = [(10,100,150)];

#
# deferred transaction errors
#
$main::config_info{factory}{reperror}      = [(10,5,25)];

# 
# always check that the db is up, default every 5 minutes, refresh
# html page (with tag) every 60 seconds by default
#
$main::config_info{factory}{up}            = [(5,60,0)];


$main::stats = undef;

#$main::currTime = 0;

#-----------------------------------------------------------------------
#
# FUNCTION PROTOTYPES
#
# I know they're not necessary, but I like 'em...
#
#-----------------------------------------------------------------------
sub main ();
sub printHelp ();
sub getStatus ($$$);
sub getServices ($$);
sub getRedologStatus ($$$);
sub getRollbackStatus ($$$);
sub getLatchStatus ($$$);
sub getTablespaceStatus ($$$);
sub getSlowsqlStatus ($$$);
sub getAlertlogStatus ($$$);
sub getHitratiosStatus ($$$);
sub getMTSStatus ($$$);
sub getExtentsStatus ($$$);
sub getFragmentationStatus ($$$);
sub getDbStatus ($);
sub getUpStatus ($);
sub getRepqueueStatus ($$$);
sub getReperrorStatus ($$$);
sub getInfo ($$$);
sub getStatusStr ($);
sub getCurrStatus ($$);
sub getEmailSize ($);
#sub getNotifySize ($$);
sub getNotifyMinutes ($$);
sub getPrefGroup ($);
sub getNotifyEmails ($$);
sub getNotifyServices ($$);
sub readConfig ($);
sub showInfoPage ($$$$);
sub showKarmaTableRow ($$);
sub showKarmaTableHeader ();
sub showKarmaHeadMain ();
sub showKarmaFootMain ();
sub showInfoHead ($$);
sub showInfoFoot ($$);
sub showServiceStatus ($$);
sub showIndexPage ($);
sub exitKarma ();
sub logMessageExit ($);
sub sendEmail ($$@);
sub setConfig ($$$$$);
sub setConfigNotify ($$$$);
sub setConfigEmail ($$$);
sub setPrefGroup ($$);
sub doNotification ($$);
sub sendNotification ($$@);
sub checkService ($$);
sub shouldUpdateService ($$$);
sub shouldShowService ($$);
sub shouldShowServiceHeader ($);
sub getServiceWarn ($$);
sub getServiceAlert ($$);
sub setDefConfig ();
sub isValidTNS ($);
sub doDBChecks ($);
sub writePidFile ();

# signal handlers
sub catchHUP;
sub catchTERM;
sub showStatus;
sub refreshServices;

#
# install signal handlers
#
# (not sure at all how this is handled under windows yet)
#
if ($main::WINDOWS == 0) {
    $SIG{HUP} = \&catchHUP;         # reread config file
    $SIG{TERM} = \&catchTERM;       # normal kill, die gracefully, cleanup
    $SIG{USR1} = \&showStatus;      # user signal, return status
    $SIG{USR2} = \&refreshServices; # refresh each service, checking the db
                                 # appropriately
}

#
# this ensures the perl will not buffer output to the logfile
# - thanks to Duncan Lawie <duncanl@demon.net> 
#
select ($main::logfile); $| = 1;
select (STDOUT);


$main::USE_DBI_VARS = 0;


#
# read in configuration information from the 
# conf file specified on the command line
#
if ((defined ($main::opt_c)) &&
    (-f "$main::KARMA_HOME$main::PATH_DELIM$main::opt_c") &&
    (-r "$main::KARMA_HOME$main::PATH_DELIM$main::opt_c")) {

    $main::CONF_FILE_NAME = "$main::KARMA_HOME$main::PATH_DELIM$main::opt_c";
    logMessage ("Using config file: $main::CONF_FILE_NAME \n");

    #
    #  try the KARMA_HOME directory...
    #

} elsif ((-f "$main::KARMA_HOME$main::PATH_DELIM" . 'karma.conf') &&
	 (-r "$main::KARMA_HOME$main::PATH_DELIM" . 'karma.conf')) {
    debugMessage ("Using \"$main::KARMA_HOME$main::PATH_DELIM" . 
		   'karma.conf' . "\" config file.\n", 1);
    $main::CONF_FILE_NAME = "$main::KARMA_HOME$main::PATH_DELIM" .
	'karma.conf';


#
# try the home directory (assume name ".karma.conf"
#  (this will be ok on Win32, $ENV{HOME} won't be defined)
} elsif (( -f "$ENV{HOME}$main::PATH_DELIM.karma.conf") &&
	 (-r "$ENV{HOME}$main::PATH_DELIM.karma.conf")) {

    logMessage ("$main::opt_c file not found, using $ENV{HOME}$main::PATH_DELIM.karma.conf instead.\n");

    $main::CONF_FILE_NAME = "$ENV{HOME}$main::PATH_DELIM.karma.conf";


#
# if none was specified on the command line, check
# for /etc/karma.conf
#
} elsif (($main::WINDOWS == 0) &&
	 (-f "/etc/karma.conf") &&
	 (-r "/etc/karma.conf")) {
    $main::CONF_FILE_NAME = "/etc/karma.conf";
    logMessage ("$main::opt_c file not found, using $main::CONF_FILE_NAME instead.\n");

#
# if we still haven't found the karma.conf file,
# check the current directory
#
} elsif ((-f "$main::wd$main::PATH_DELIM" . 'karma.conf') &&
	 (-r "$main::wd$main::PATH_DELIM" . 'karma.conf')) {
    $main::CONF_FILE_NAME = "$main::wd$main::PATH_DELIM" . 'karma.conf';
    logMessage ("$main::opt_c file not found, using $main::CONF_FILE_NAME instead.\n");
#
# last chance, try the DBI_USER, DBI_PASS, DBI_DSN, and use all
# factory defaults
#
} elsif ((defined ($ENV{DBI_DSN})) && (length($ENV{DBI_DSN}) > 0) &&
	 (defined ($ENV{DBI_USER})) && (length($ENV{DBI_USER}) > 0) &&
	 (defined ($ENV{DBI_PASS})) && (length($ENV{DBI_PASS}) > 0)) {
    $main::USE_DBI_VARS = 1;


# 
# without a config file we just exit
#
} else {

    print 
	"The karma.conf configuration file was not found.\n",
	"Please make sure the file $main::KARMA_HOME$main::PATH_DELIM", 
	'karma.conf', ",\n",
	"$main::PATH_DELIM", 'etc', "$main::PATH_DELIM", 'karma.conf',
	"exists and is readable, otherwise\n",
	"specify another file with -c. \n";
    exit;
}

#logMessageWTime ("configfile: $main::CONF_FILE_NAME\n");

# 
# background oneself before starting
#
# it's very important that this routine be called at the right
# place, for instance, I was calling daemonize *after* readConfig
# and perl didn't handle the $dbh connections to Oracle properly...
# Oracle was returning ORA-3114 errors, and I was baffled...
#
# perl seems to pass the file handle ok though, because the logfile
# is opened before forking, and is written to after...
#
if ($main::WINDOWS == 0) {
    daemonize ();
}

#
# changing this datastructure slowly... if all works well
# we should do away with the original pref_groups
#
$main::pref_groups = undef;

if ($main::USE_DBI_VARS == 1) {
    setDefConfig ();
} else {
    readConfig ($main::CONF_FILE_NAME);
}

#show_pref_groups ();
#show_notify_config ();
#exit;


#print ("hello, anybody home?\n");

#
# testing... print filenames
#
if ($main::WINDOWS == 0) {
    debugMessage ("   IS Win32:NO\n", 1);
} else {
    debugMessage ("   IS Win32:YES\n", 1);
}
debugMessage ("WORKING DIR:$main::wd\n", 1);
debugMessage ("   LOG FILE:$main::LOG_FILE_NAME\n", 1);
debugMessage ("CONFIG FILE:$main::CONF_FILE_NAME\n", 1);
debugMessage (" INDEX FILE:$main::INDEX_FILE_NAME\n", 1);
debugMessage ("   PID FILE:$main::PID_FILE_NAME\n", 1);
#debugMessage (" INFO FILE:$main::INFO_FILE_NAME\n", 1);
debugMessage ("DOCROOT DIR:$main::KARMA_DOC_ROOT\n", 1);
debugMessage (" KARMA HOME:$main::KARMA_HOME\n", 1);

#
# this has to come after daemonize, otherwise we'll write
# the wrong pid!
#
$main::KARMA_PID = $$;
writePidFile ();

logMessageWTime ("Started karmad.\n");

if ($main::USE_EMAIL_NOTIFICATION == 1) {
    logMessage ("Using EMAIL notification.\n");
} else {
    logMessage ("Using LOGFILE notification.\n");
}


#
# ok, now we're ready to run
#
main ();


#-----------------------------------------------------------------------
#
# SUBROUTINES
#
#-----------------------------------------------------------------------



#-----------------------------------------------------------------------
#
# main
#
#-----------------------------------------------------------------------
sub main () {

    my $lastTime = $main::currTime;
    my $diffTime = 0;

    #
    # loop forever... only way to exit is via catchTERM
    #
    #  (Thanks to Dennis <dennis@funkplanet.com> for
    #   the suggestion to move this first call out
    #   of the main loop.)
    #
    doDBChecks ($main::cFORCE_UPD);
    $lastTime = $main::currTime;
    
    while (1) {
	$main::currTime = time ();
	$diffTime = 0;
	if ($main::currTime > $lastTime) {
	    $diffTime = $main::currTime - $lastTime;
	}

	if ($diffTime > $main::MIN_WAKEUP) {
	    doDBChecks ($main::cNO_FORCE);
	    $lastTime = $main::currTime;
	    $diffTime = 0;
	}
	
        sleep ($main::MIN_WAKEUP * 60 - $diffTime);
    }
}


#-----------------------------------------------------------------------
#
# print the html page on stdout
#
#-----------------------------------------------------------------------
sub showIndexPage ($) {
    my ($inMinutes) = @_;

    debugMessage ("showIndexPage: opening IDXF:$main::INDEX_FILE_NAME\n",2);
    # 
    # ideally would like to pass this handle to the showKarmaHeadMain
    # showKarmaTableHeader etc routines, but right now it's not working
    #
    $main::index_file = new IO::File ">$main::INDEX_FILE_NAME"
#	or die "Can't open $main::INDEX_FILE_NAME:: $!";
	or logMessageExit ("Can't open $main::INDEX_FILE_NAME:: $!");


    debugMessage ("showIndexPage: IDXF:$main::INDEX_FILE_NAME open ok.\n",2);

    my $tnsKey = undef;
    my $prefKey = undef;
    my $colCount = 0;
    my $i = 0;

    if (not (defined ($main::index_file))) {
	logMessage ("Cannot write to index file: $main::INDEX_FILE_NAME\n");
    } else {

#    open (main::INDEX_FILE, ">$main::INDEX_FILE_NAME");

	showKarmaHeadMain ();

	#
	# build status table
	#
	print $main::index_file 
	    ("<table border=\"1\" cellpadding=\"0\"",
	     "cellspacing=\"0\" bordercolor=\"$main::cBORDER_COLOR\">\n");
	
	#
	# title row
	#
	$colCount = showKarmaTableHeader ();
	
	#
	# one row per database
	#
#	foreach $tnsKey (keys %main::db_info) {
#	    showKarmaTableRow ($tnsKey, $inMinutes);
#	}


	foreach $prefKey (keys %main::pref_groups) {

	    if ($main::USE_PREFGROUP_SECTIONS == 1) {
		print $main::index_file 
		    "<tr bgcolor=\"$main::cHEAD_BG_COLOR\">",
		    "<td colspan=\"$colCount\"><center>",
		    "<font face=\"Arial, Helvetica, sans-serif\"",
		    "color=\"$main::cKARMA_TEXT_COLOR\" size=\"4\">",
		    "$prefKey</font></center></td></tr>\n";
	    }
	    $i = 0;
	    while (defined ($main::pref_groups{$prefKey}[$i])) {
		$tnsKey = $main::pref_groups{$prefKey}[$i];
		showKarmaTableRow ($tnsKey, $inMinutes);
		$i++;
	    }

	}
	print $main::index_file ("</TABLE>\n");
	
	showKarmaFootMain ();
	
	
	$main::index_file->close;
    }


}


#-----------------------------------------------------------------------
#
# first parameter is the type of statistic
# second parameter is the tns name
#
#-----------------------------------------------------------------------
sub showServiceStatus ($$) {
    my ($inType, $inTNS) = @_;

    my $theFile = $main::files{info}{$inType};
    my $theStatus = $main::cNO_STATUS; 
    my $theImage = "";
    my $theMessage = "";

    if (defined $main::stats{$inType}{$inTNS}[$main::cSTATUS]) {
	$theStatus = $main::stats{$inType}{$inTNS}[$main::cSTATUS];
    }

    if (not ($theStatus)) {
	debugMessage ("Working on T: $inType TNS:$inTNS\n", 2);
    }

    if ($theStatus == $main::cALERT_STATUS) {
	if ($main::USE_BLINK_ALERT == 1) {
	    $theImage = "blink_red_status";
	} else {
	    $theImage = "red_status";
	}
	$theMessage = "ALRT";
    } elsif ($theStatus == $main::cWARNING_STATUS) {
	if ($main::USE_BLINK_WARNING == 1) {
	    $theImage = "blink_yellow_status";
	} else {
	    $theImage = "yellow_status";
	}
	$theMessage = "WARN";
    } elsif ($theStatus == $main::cNO_STATUS) {
	$theImage = "purple_status";
	$theMessage = "NR";
    } elsif ($theStatus == $main::cOK_STATUS) {
	$theImage = "green_status";
	$theMessage = "OK";
    }

    #
    # if the database is not up, all links are off except "up" status
    # link, which will be ALERT_STATUS, and will basically just say
    # the database is down.
    # 
    if ($theStatus == $main::cNO_SHOW) {
	print $main::index_file ("-");
    } else {
	if (($main::stats{up}{$inTNS}[$main::cSTATUS] == $main::cALERT_STATUS) &&
	    (not ($inType =~ /^up$/i))) {
	    print $main::index_file ("<IMG SRC=\"images$main::PATH_DELIM$theImage\" BORDER=0 border=\"0\" ALT=\"$inTNS - DB DOWN\">\n");
	} else {
	    print $main::index_file ("<A HREF=\"info$main::PATH_DELIM$inTNS.$theFile\" target=\"_self\"><IMG SRC=\"images$main::PATH_DELIM$theImage\" BORDER=0 ALT=\"$inTNS - $inType Info\"></A>\n");
	}
}
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getRepqueueStatus ($$$) {
    my ($repqueue_threshold_warn, $repqueue_threshold_alert, $inTNS) = @_;

    my $repqueueStatus = $main::cNO_STATUS;
    my $currStatus = $main::cNO_STATUS;
    my $repqueueTableRef = [()];
    my $repqueueRowCount = 0;
    my @repqueueTable = ();


    if ($main::db_info{$inTNS}[$main::cDB_HANDLE]) {

	my $curr_row = [];
	my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{repqueue});

	if (defined $DBI::errstr) {
	    logMessageWTime ("getRepqueueStatus:$inTNS prepare - $DBI::errstr\n");
	}

	if (defined $sth) {
	    my $rv = $sth->execute ();

	    #
	    # redolog more info table titles
	    #
	    @{$repqueueTableRef->[$repqueueRowCount]} = ("Level", "Def Tran ID",
							 "Order", "Timestamp");
	    $repqueueRowCount = 1;
	    $curr_row = [];
	    $curr_row = $sth->fetchrow_arrayref;
	    while (defined ($curr_row->[0])) {
		
		
		@{$repqueueTableRef->[$repqueueRowCount]} = ($currStatus, $curr_row->[0],
							     $curr_row->[1], $curr_row->[2]);
		
		$curr_row = $sth->fetchrow_arrayref;
		$repqueueRowCount++;
	    }
	    
	    
	    #
	    # set warning level based on number of transactions in
	    # deftran queue
	    #
	    $repqueueStatus = $main::cOK_STATUS;
	    if ($repqueueRowCount - 1 > $repqueue_threshold_alert) {
		$repqueueStatus = $main::cALERT_STATUS;
	    } elsif ($repqueueRowCount - 1 > $repqueue_threshold_warn) {
		$repqueueStatus = $main::cWARNING_STATUS;
	    }
	    
	    if ($sth) {
		$sth->finish;
	    }

	}
    }

    showInfoPage ($repqueueTableRef, "repqueue", $inTNS, undef);
    return $repqueueStatus;
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getReperrorStatus ($$$) {
    my ($reperror_threshold_warn, $reperror_threshold_alert, $inTNS) = @_;

    my $reperrorStatus = $main::cNO_STATUS;
    my $currStatus = $main::cNO_STATUS;
    my $reperrorTableRef = [()];
    my $reperrorRowCount = 0;
    my @reperrorTable = ();


    if ($main::db_info{$inTNS}[$main::cDB_HANDLE]) {

	my $curr_row = [];
	my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{reperror});

	if (defined $DBI::errstr) {
	    logMessageWTime ("getReperrorStatus:$inTNS prepare - $DBI::errstr\n");
	}

	if (defined $sth) {
	    my $rv = $sth->execute ();
	    
	    #
	    # redolog more info table titles
	    #
	    @{$reperrorTableRef->[$reperrorRowCount]} = ("Level", "Def Tran ID",
							 "Origin", "Destination", 
							 "Timestamp", "Error");
	    $reperrorRowCount = 1;
	    $curr_row = [];
	    $curr_row = $sth->fetchrow_arrayref;
	    while (defined ($curr_row->[0])) {
		
		@{$reperrorTableRef->[$reperrorRowCount]} = 
		    ($currStatus, $curr_row->[0],
		     $curr_row->[1], $curr_row->[2],
		     $curr_row->[3], $curr_row->[4]);
		
		$curr_row = $sth->fetchrow_arrayref;
		$reperrorRowCount++;
	    }
	    
	    
	    #
	    # set warning level based on number of transactions in
	    # deftran queue
	    #
	    $reperrorStatus = $main::cOK_STATUS;
	    if ($reperrorRowCount - 1 > $reperror_threshold_alert) {
		$reperrorStatus = $main::cALERT_STATUS;
	    } elsif ($reperrorRowCount - 1 > $reperror_threshold_warn) {
		$reperrorStatus = $main::cWARNING_STATUS;
	    }
	    
	    if ($sth) {
		$sth->finish;
	    }

	}
    }




    showInfoPage ($reperrorTableRef, "reperror", $inTNS, undef);
    return $reperrorStatus;
}

#-----------------------------------------------------------------------
#
# check how often redo logs switch.  If they're more often than 30
# minutes, put us at alert status
# (Should we also check v$loghist? probably yes)
#
#-----------------------------------------------------------------------
sub getRedologStatus ($$$) {
    my ($redolog_threshold_warn, $redolog_threshold_alert, $inTNS) = @_;

    my $redologStatus = $main::cNO_STATUS;
    my $currStatus = $main::cNO_STATUS;
    my $redoTableRef = [()];
    my $redoRowCount = 0;
    my @redoTable = ();

#    if ($dbh) {
#    if ($dbh{$inTNS}) {
    if ($main::db_info{$inTNS}[$main::cDB_HANDLE]) {

	my $curr_row = [];
#	my $prev_row = [];
	my $currtime = 0;
	my $prevtime = 0;
	my $diff = 0;
#	my $sth = $dbh{$inTNS}->prepare ($main::statements{redolog});
	my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{redolog});

	if (defined $DBI::errstr) {
	    logMessageWTime ("getRedologStatus:$inTNS prepare - $DBI::errstr\n");
	}

	if (defined $sth) {
	    my $rv = $sth->execute;
	    
	    #$prev_row = $sth->fetchrow_arrayref ();
	    
	    #
	    # redolog more info table titles
	    #
	    @{$redoTableRef->[$redoRowCount]} = ("Level", "Group#",
						 "Sequence#", "Timestamp");
	    $redoRowCount = 1;
	    $curr_row = [];
	    $currtime = 0;
	    $prevtime = 0;
	    $diff = 0;
	    $curr_row = $sth->fetchrow_arrayref;
	    while (defined ($curr_row->[0])) {
		
		
		$prevtime = $currtime;
		$currtime = $main::cDAYSECS * $curr_row->[0] + $curr_row->[1];
		$diff = $currtime - $prevtime;
		
		
		if ($diff < ($redolog_threshold_alert * 60)) {
		    $currStatus = $main::cALERT_STATUS;
		} elsif ($diff < ($redolog_threshold_warn * 60)) {
		    $currStatus = $main::cWARNING_STATUS;
		} else {
		    $currStatus = $main::cOK_STATUS;
	    }
		
		
		#
		# escalate warning/alert level if necessary
		#
		if (($redologStatus == $main::cNO_STATUS) || 
		    ($redologStatus == $main::cOK_STATUS) ||
		    ($currStatus == $main::cALERT_STATUS)) {
		    $redologStatus = $currStatus;
		} elsif (($currStatus == $main::cALERT_STATUS) &&
			 ($redologStatus == $main::cWARNING_STATUS)) {
		    $redologStatus = $currStatus;
		}
		
		
		
		@{$redoTableRef->[$redoRowCount]} = ($currStatus, $curr_row->[2],
						     $curr_row->[3], $curr_row->[4]);
		
		$curr_row = $sth->fetchrow_arrayref;
		$redoRowCount++;
	    }
	    
	    if ($redoRowCount == 1) {
		$redologStatus = $main::cWARNING_STATUS;
	    }
	    
	    if ($sth) {
		$sth->finish;
	    }
	}
    }


    showInfoPage ($redoTableRef, "redolog", $inTNS, undef);
    return $redologStatus;
}

#-----------------------------------------------------------------------
#
# check for rollback segment contention

# (gets-waits)*100/gets is the hitratio for that rollback segment
# We're simply checking that this is > 99.
#
#-----------------------------------------------------------------------
sub getRollbackStatus ($$$) {
    my ($roll_threshold_alert, $roll_threshold_warn, $inTNS) = @_;

    my $rollbackStatus = $main::cNO_STATUS;
    my $rollbackTableRef = [()];
    my $rollbackRowCount = 0;
    my $gets = 0;
    my $gets_waits = 0;
    my $currStatus = $main::cNO_STATUS;
    my $sth = undef;
    my $rv = 0;
    my $rollbackRatio = 0;
    my $curr_row = undef;

#    if ($dbh{$inTNS}) {
    if ($main::db_info{$inTNS}[$main::cDB_HANDLE]) {

#	$sth = $dbh{$inTNS}->prepare ($main::statements{rollback});
	$sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{rollback});

	if (defined $DBI::errstr) {
	    logMessageWTime ("getRollbackStatus:$inTNS prepare - $DBI::errstr\n");
	}

	if (defined $sth) {
	    $rv = $sth->execute;
	    $gets = 0;
	    $gets_waits = 0;
	    $currStatus = $main::cNO_STATUS;
	    
	    #
	    # rollback segment more info table titles
	    #
	    @{$rollbackTableRef->[0]} = ("Level", 
					 "Name",
					 "Status",
					 "Gets",
					 "Waits",
					 "Hitratio");
	    $rollbackRowCount = 1;
	    $curr_row = $sth->fetchrow_arrayref;
	    while (defined ($curr_row->[0])) {
		$gets = $curr_row->[2];
		$gets_waits = $gets - $curr_row->[3];
		$currStatus = $main::cNO_STATUS;
		if ($gets > 0) {
		    $rollbackRatio = ($gets_waits) * 100 / $gets;
		    
		    if ($rollbackRatio < $roll_threshold_alert) {
			$currStatus = $main::cALERT_STATUS;
		    } elsif ($rollbackRatio < $roll_threshold_warn) {
			$currStatus = $main::cWARNING_STATUS;
		    } else {
			$currStatus = $main::cOK_STATUS;
		    }
		}
		
		#
		# escalate warning/alert level if necessary
		#
		if (($rollbackStatus == $main::cNO_STATUS) || 
		    ($rollbackStatus == $main::cOK_STATUS) ||
		    ($currStatus == $main::cALERT_STATUS)) {
		    $rollbackStatus = $currStatus;
		} elsif (($currStatus == $main::cALERT_STATUS) &&
			 ($rollbackStatus == $main::cWARNING_STATUS)) {
		    $rollbackStatus = $currStatus;
		}
		
		
		@{$rollbackTableRef->[$rollbackRowCount]} = ($currStatus, 
							     $curr_row->[0],
							     $curr_row->[1],
							     $curr_row->[2],
							     $curr_row->[3],
							     $rollbackRatio);
		
		$rollbackRowCount++;
		$curr_row = $sth->fetchrow_arrayref;
	    }
	    if ($sth) {
		$sth->finish;
	    }
	}
    }


    showInfoPage ($rollbackTableRef, "rollback", $inTNS, undef);
    return $rollbackStatus;
}

#-----------------------------------------------------------------------
# 
# check for latch contention
# not sure if this query is strictly correct.  Need a confirmation.
#
#-----------------------------------------------------------------------
sub getLatchStatus ($$$) {
    my ($latch_threshold_alert, $latch_threshold_warn, $inTNS) = @_;

    my $latchStatus = $main::cNO_STATUS;
    my $currStatus = $main::cNO_STATUS;
    my $latchTableRef = [()];
    my $latchRowCount = 0;
    my $latchRatio = 0;
    my 	$curr_row = [];
    my $gets = 0;
    my $misses = 0;
    my $gets_misses = 0;
    my $sth = undef;
    my $rv = 0;

#    if ($dbh{$inTNS}) {
    if ($main::db_info{$inTNS}[$main::cDB_HANDLE]) {

#	$sth = $dbh{$inTNS}->prepare ($main::statements{latch});
	$sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{latch});
	if (defined $DBI::errstr) {
	    logMessageWTime ("getLatchStatus:$inTNS prepare - $DBI::errstr\n");
	}

	if (defined $sth) {
	    $rv = $sth->execute;
	    
	    $latchStatus = $main::cOK_STATUS;
	    $latchRatio = 0;
	    $gets = 0;
	    $misses = 0;
	    $gets_misses = 0;
	    
	    #
	    # latch more info table titles
	    #
	    @{$latchTableRef->[0]} = ("Level", 
				      "Name",
				      "Gets",
				      "Misses",
				      "Hitratio");
	    $latchRowCount = 1;
	    $curr_row = $sth->fetchrow_arrayref;
	    while (defined ($curr_row->[0])) {
		
		$currStatus = $main::cNO_STATUS;
		$gets = $curr_row->[1];
		$misses = $curr_row->[2];
		$gets_misses = $gets - $misses;
		if (($gets == 0) && ($misses == 0)) {
		    $latchRatio = 100;
		} elsif ($gets > 0) {
		    $latchRatio = ($gets_misses) * 100 / $gets;
		}
		if ($latchRatio < $latch_threshold_alert) {
		    $currStatus = $main::cALERT_STATUS;
		} elsif ($latchRatio < $latch_threshold_warn) {
		    $currStatus = $main::cWARNING_STATUS;
		} else {
		    $currStatus = $main::cOK_STATUS;
		}
		
		
		#
		# escalate warning/alert level if necessary
		#
		if (($latchStatus == $main::cNO_STATUS) || 
		    ($latchStatus == $main::cOK_STATUS) ||
		    ($currStatus == $main::cALERT_STATUS)) {
		    $latchStatus = $currStatus;
		} elsif (($currStatus == $main::cALERT_STATUS) &&
			 ($latchStatus == $main::cWARNING_STATUS)) {
		    $latchStatus = $currStatus;
		}
		
		
		@{$latchTableRef->[$latchRowCount]} = ($currStatus, 
						       $curr_row->[0],
						       $curr_row->[1],
						       $curr_row->[2],
						       $latchRatio);
		
		
		$latchRowCount++;
		$curr_row = $sth->fetchrow_arrayref;
	    }
	    
	    if ($sth) {
		$sth->finish;
	    }
	}

    }


    showInfoPage ($latchTableRef, "latch", $inTNS, undef);
    return $latchStatus;
}



#-----------------------------------------------------------------------
# 
# Right now this is checking that tablespaces are not above a certain
# threshold (%used).  It *SHOULD* just check if there is an object
# whose next extent is bigger than the largest free extent, or the
# largest free space...
#
#-----------------------------------------------------------------------
sub getTablespaceStatus ($$$) {
    my ($tablespace_threshold_warn, $tablespace_threshold_alert, $inTNS) = @_;

    my $tablespaceStatus = $main::cNO_STATUS;
    my $curr_row = [()];
    my $auto_row = [()];
    my $currStatus = $main::cNO_STATUS;
    my $pctused = 0;
    my $used_size = 0;
    my $total_size = 0;
    my $tablespaceTableRef = [()];
    my $tablespaceRowCount = 0;
    my $sth = undef;
    my $sthAuto = undef;
    my $rv = 0;
    my $autoextend = "NO";

#    if ($dbh{$inTNS}) {
    if ($main::db_info{$inTNS}[$main::cDB_HANDLE]) {

#	$sth = $dbh{$inTNS}->prepare ($main::statements{tablespace});
	$sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{tablespace});
	if (defined $DBI::errstr) {
	    logMessageWTime ("getTablespaceStatus:$inTNS prepare - $DBI::errstr\n");
	}
	$sthAuto = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare 
	    ("SELECT autoextensible FROM dba_data_files WHERE tablespace_name = ?");
	if (defined $DBI::errstr) {
	    logMessageWTime ("getTablespaceStatus:$inTNS prepare - $DBI::errstr\n");
	}

	if (defined $sth) {
	    $rv = $sth->execute;
	    $tablespaceStatus = $main::cNO_STATUS;
	    $currStatus = $main::cNO_STATUS;
	    
	    #
	    # latch more info table titles
	    #
	    @{$tablespaceTableRef->[0]} = ("Level", 
					   "Name",
					   "Used (K)",
					   "Total (K)",
					   "% Used",
					   "Auto");
	    $tablespaceRowCount = 1;
	    $curr_row = $sth->fetchrow_arrayref;
	    while (defined ($curr_row->[0])) {
		
		$currStatus = $main::cNO_STATUS;
		
		$pctused = 0;
		$total_size = $curr_row->[1];
		$used_size = $curr_row->[2];
		if (not defined ($used_size)) {
		    $used_size = 0;
		}
		
		if ($total_size > 0) {
		    $pctused = 100 * $used_size / $total_size;
		}
		
		#
		# check the tablespace for autoextend on *ANY* datafile
		#
		$autoextend = "NO";
		$rv = $sthAuto->execute ($curr_row->[0]);
		$auto_row = $sthAuto->fetchrow_arrayref;
		while (defined ($auto_row->[0])) {
		    if ($auto_row->[0] =~ /^YES$/) {
			$autoextend = "YES";
		    }
		    $auto_row = $sthAuto->fetchrow_arrayref;
		}
		
		#
		# if data file is in autoextend mode, just leave it at
		# no_status.  It's too tough to decide anything else.
		# If we were going to check maxbytes, we'd also have to
		# check to os, because this could be (a) greater than
		# the max datafile size in this OS, and (b) greater
		# than the amount of disk space left on the device
		# 
		
#	    if (not ($curr_row->[3] =~ /^YES$/)) {
		if ($pctused > $tablespace_threshold_alert) {
		    $currStatus = $main::cALERT_STATUS;
		} elsif ($pctused > $tablespace_threshold_warn) {
		    $currStatus = $main::cWARNING_STATUS;
		} else {
		    $currStatus = $main::cOK_STATUS;
		}
#	    }
		
		#
		# escalate warning/alert level if necessary
		#
		if (($tablespaceStatus == $main::cNO_STATUS) || 
		    ($tablespaceStatus == $main::cOK_STATUS) ||
		    ($currStatus == $main::cALERT_STATUS)) {
		    $tablespaceStatus = $currStatus;
		} elsif (($currStatus == $main::cALERT_STATUS) &&
			 ($tablespaceStatus == $main::cWARNING_STATUS)) {
		    $tablespaceStatus = $currStatus;
		}
	    
		
		
		@{$tablespaceTableRef->[$tablespaceRowCount]} = 
		    ($currStatus, 
		     $curr_row->[0],
		     $total_size / 1024,
		     $used_size / 1024,
		     $pctused,
		     $autoextend);
		
		
		$tablespaceRowCount++;
		$curr_row = $sth->fetchrow_arrayref;
		
	    }
	    if ($sth) {
		$sth->finish;
	    }
	}
    }

    showInfoPage ($tablespaceTableRef, "tablespace", $inTNS, undef);
    return $tablespaceStatus;
}




#-----------------------------------------------------------------------
# 
#
# check for slow sql queries based on the number of disk reads (v$sqlarea)
#
# NOTE/WARNING:  The way this routine is written may or may not be the
# best way.  I read ALL sql statements from v$sqlarea in through
# the cursor, then examine disk_reads per executions to find out if
# we should consider this a slow query.  We then determine whether to
# include  it in the more info page.
#
#-----------------------------------------------------------------------
sub getSlowsqlStatus ($$$) {
    my ($slowsql_threshold_warn, $slowsql_threshold_alert, $inTNS) = @_;

    my $slowsqlStatus = $main::cNO_STATUS;
    my $slowsqlTableRef = [()];
    my $disk_reads = 0;
    my $executions = 0;
    my $slowsqlRowCount = 0;
    my $read_execs = 0;
    my $rv = 0;
    my $sth = undef;
    my $curr_row = undef;
    my $currStatus = 0;

#    if ($dbh{$inTNS}) {
    if ($main::db_info{$inTNS}[$main::cDB_HANDLE]) {

#	$sth = $dbh{$inTNS}->prepare ($main::statements{slowsql});
	$sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{slowsql});

	if (defined $DBI::errstr)  {
	    logMessageWTime ("getSlowsqlStatus: prepare - $DBI::errstr\n");
	}

	#
	# include all sql statements within 2 times the
	# warning level
	#
#	$rv = $sth->execute ($slowsql_threshold_warn * 2);

	if (defined $sth) {
	    $rv = $sth->execute ();
	    
	    $currStatus = $main::cNO_STATUS;
	    
	    #
	    # latch more info table titles
	    #
	    @{$slowsqlTableRef->[0]} = ("Level", 
					"Disk I/O",
					"Query Text");
	    $slowsqlRowCount = 1;
	    $curr_row = $sth->fetchrow_arrayref;
	    while ($curr_row->[0]) {
		
		$disk_reads = $curr_row->[0];
		$executions = $curr_row->[1];
		if ($executions > 0) {
		    $read_execs = $disk_reads / $executions;
		} else {
		    $read_execs = $disk_reads;
		}
		
		#
		# default to ok status, if we find no rows
		# to display at all
		#
		if ($slowsqlStatus == $main::cNO_STATUS) {
		    $currStatus = $main::cOK_STATUS;
		}
		
		if ($read_execs > (2 * $slowsql_threshold_warn)) {
		    
		    if ($read_execs < $slowsql_threshold_alert) {
			$currStatus = $main::cALERT_STATUS;
		    } elsif ($read_execs < $slowsql_threshold_warn) {
			$currStatus = $main::cWARNING_STATUS;
		    } else {
			$currStatus = $main::cOK_STATUS;
		    }
		    
		    # 
		    # escalate warning/alert level if necessary
		    #
		    if (($slowsqlStatus == $main::cNO_STATUS) || 
			($slowsqlStatus == $main::cOK_STATUS) ||
			($currStatus == $main::cALERT_STATUS)) {
			$slowsqlStatus = $currStatus;
		    } elsif (($currStatus == $main::cALERT_STATUS) &&
			     ($slowsqlStatus == $main::cWARNING_STATUS)) {
			$slowsqlStatus = $currStatus;
		    }
		    
		    
		    @{$slowsqlTableRef->[$slowsqlRowCount]} = 
			($currStatus, 
			 $read_execs,
			 $curr_row->[2]);
		    
		}
		$slowsqlRowCount++;
		$curr_row = $sth->fetchrow_arrayref;
		
	    }
	    
	    if ($sth) {
		$sth->finish;
	    }
	}
        # build more info page
	showInfoPage ($slowsqlTableRef, "slowsql", $inTNS, undef);

    }




    return $slowsqlStatus;

}


#-----------------------------------------------------------------------
# 
# check the alert log for ORA errors
#
# if the KARMA_ALERTLOG_ERRORS table doesn't exist, we'll just report
# NO_STATUS, and exit.
#
#-----------------------------------------------------------------------
sub getAlertlogStatus ($$$) {
    my ($alertlog_threshold_warn, $alertlog_threshold_alert, $inTNS) = @_;

    my $alertlogStatus = $main::cNO_STATUS;
    my $alertlogTableRef = [()];
    my $alertlogRowCount = 0;
    my $currStatus = 0;

#    $sth = $dbh{$inTNS}->prepare ("SELECT table_name FROM user_tables WHERE table_name = \'KARMA_ALERTLOG_ERRORS\'");
    my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ("SELECT table_name FROM user_tables WHERE table_name = \'KARMA_ALERTLOG_ERRORS\'");

    if (defined $DBI::errstr)  {
	logMessageWTime ("getAlertlogStatus: prepare - $DBI::errstr\n");
    }

    if (defined $sth) {
	my $rv = $sth->execute;
	my $row_ref = $sth->fetchrow_arrayref;
	my $curr_row = undef;
	
	# 
	# to fix the "Database or Listener DOWN!" messaage, which shouldn't come
	# up if the KARMA_* tables are missing... obviously
	#
	@{$alertlogTableRef->[0]} = ("Level",
				     "Facility",
				     "Error",
				     "Time",
				     "Error Text");
	
	$alertlogRowCount = 1;
	
	if (defined ($row_ref->[0])) {
	    
	    if ($sth) {
		$sth->finish;
	    }
	    
#	$sth = $dbh{$inTNS}->prepare ($main::statements{alertlog});
	    $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{alertlog});
	    
	    if (defined $DBI::errstr)  {
		logMessageWTime ("getAlertlogStatus: prepare - $DBI::errstr\n");
	    }
	
	    if (defined $sth) {
		$rv = $sth->execute;
		
		$currStatus = $main::cOK_STATUS;
		$alertlogStatus = $main::cOK_STATUS;
		
#	$alertlogRowCount = 1;
		$curr_row = $sth->fetchrow_arrayref;
		
		
		while (defined ($curr_row->[0])) {
		    
		    $currStatus = $main::cOK_STATUS;
		    
		    
		    if (($curr_row->[0] =~ /ORA/) && 
			(($curr_row->[1] == 600) || ($curr_row->[1] == 7445))) {
			$currStatus = $main::cALERT_STATUS;
		    } else {
			$currStatus = $main::cWARNING_STATUS;
		    }
		    
		    # 
		    # escalate warning/alert level if necessary
		    #
		    if (($alertlogStatus == $main::cNO_STATUS) || 
			($alertlogStatus == $main::cOK_STATUS) ||
			($currStatus == $main::cALERT_STATUS)) {
			$alertlogStatus = $currStatus;
		    } elsif (($currStatus == $main::cALERT_STATUS) &&
			     ($alertlogStatus == $main::cWARNING_STATUS)) {
			$alertlogStatus = $currStatus;
		    }
		    
		    
		    @{$alertlogTableRef->[$alertlogRowCount]} = 
			($currStatus, 
			 $curr_row->[0],
			 $curr_row->[1],
			 $curr_row->[2],
			 $curr_row->[3]);
		    
		    $alertlogRowCount++;
		    $curr_row = $sth->fetchrow_arrayref;
		    
		    
		}
		
	    }
	    
	    if ($sth) {
		$sth->finish;
	    }
	}
    }

    showInfoPage ($alertlogTableRef, "alertlog", $inTNS, undef);

    return $alertlogStatus;

}


#-----------------------------------------------------------------------
# 
# check for low hit ratios (block buffer, dictionary cache etc)
#  
# problem here... Doesn't produce proper more info report.
#
#-----------------------------------------------------------------------
sub getHitratiosStatus ($$$) {
    my ($hitratios_threshold_warn, $hitratios_threshold_alert, $inTNS) = @_;

    my $hitratiosStatus = $main::cNO_STATUS;
    my $hitratiosRowCount = 0;
    my $consistent_gets = undef;
    my $physical_reads = undef;
    my $db_block_gets = undef;
    my $hitratiosTableRef = [()];
    my $hitratiosRow = [()];
    my $mem_gets = 0;
    my $hitratio = 0;
    my $currStatus = 0;

    $hitratiosTableRef = [()];
    my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{hitratios});

    if (defined $DBI::errstr)  {
	logMessageWTime ("getHitratiosStatus: prepare - $DBI::errstr\n");
    }

    if (defined $sth) {
	my $rv = $sth->execute;
	
	@{$hitratiosTableRef->[0]} = ("Level",
				      "Consistent Gets",
				      "DB Block Gets",
				      "Physical Reads",
				      "Hit Ratio");
	
	$currStatus = $main::cNO_STATUS;
	$consistent_gets = undef;
	$physical_reads = undef;
	$db_block_gets = undef;
	
	$hitratiosRowCount = 1;
	$hitratiosRow = $sth->fetchrow_arrayref;
	
	while (defined ($hitratiosRow->[0])) {
	    
	    if ($hitratiosRow->[0] =~ /consistent gets/) {
		$consistent_gets = $hitratiosRow->[1];
	    } elsif ($hitratiosRow->[0] =~ /physical reads/) {
		$physical_reads = $hitratiosRow->[1];
	    } elsif ($hitratiosRow->[0] =~ /db block gets/) {
		$db_block_gets = $hitratiosRow->[1];
	    }
	    
	    
	    $currStatus = $main::cOK_STATUS;
	    
	    if (defined ($consistent_gets) && 
		defined ($physical_reads) && 
		defined ($db_block_gets)) {
		
		$mem_gets = $consistent_gets + $db_block_gets;
		
		if ($mem_gets <= 0) {
		    $currStatus = $main::cALERT_STATUS;
		} else {
		    $hitratio = (($mem_gets - $physical_reads) / $mem_gets) * 100; 
		    
		    if ($hitratio < $hitratios_threshold_alert) {
			$currStatus = $main::cALERT_STATUS;
		    } elsif ($hitratio < $hitratios_threshold_warn) {
			$currStatus = $main::cWARNING_STATUS;
		    } else {
			$currStatus = $main::cOK_STATUS;
		    }
		}		
		
		# 
		# escalate warning/alert level if necessary
		#
		if (($hitratiosStatus == $main::cNO_STATUS) || 
		    ($hitratiosStatus == $main::cOK_STATUS) ||
		    ($currStatus == $main::cALERT_STATUS)) {
		    $hitratiosStatus = $currStatus;
		} elsif (($currStatus == $main::cALERT_STATUS) &&
			 ($hitratiosStatus == $main::cWARNING_STATUS)) {
		    $hitratiosStatus = $currStatus;
		}
		
		
		@{$hitratiosTableRef->[$hitratiosRowCount]} = 
		    ($currStatus, 
		     $consistent_gets,
		     $db_block_gets,
		     $physical_reads,
		     $hitratio);
		
		$hitratiosRowCount++;
	    }
	    
	    $hitratiosRow = $sth->fetchrow_arrayref;
	    
	}
	
	if ($sth) {
	    $sth->finish;
	}
    }
    showInfoPage ($hitratiosTableRef, "hitratios", $inTNS, undef);
    return $hitratiosStatus;

}

#-----------------------------------------------------------------------
# 
# check for tablespace, heap, and b-tree fragmentation in db
# 
# 1. all extent sizes in a given tablespace must be the same
#    as the tablespace defaults, otherwise, flag each one as
#    potentially fragmentating (flags a WARNING because it 
#    may not mean any actual objects are affected...)
#
# perhaps this #2 should be with getExtentsStatus
#
# 2. extent size must be a multiple of db_block_size *
#    db_file_multiblock_read_count, which must be the same
#    as the initial/next extent of the tablespace
#
# Not sure if the above is the best implementation.  Looking
# for suggestions on how to implement this.
#
#-----------------------------------------------------------------------
sub getFragmentationStatus ($$$) {
    my ($fragmentation_threshold_warn, 
	$fragmentation_threshold_alert, 
	$inTNS) = @_;

    my $temp = $fragmentation_threshold_warn + $fragmentation_threshold_alert;
    my $fragmentationStatus = $main::cNO_STATUS;
    my $fragmentationTableRef = undef;
    my %fragmentationData;
    my $currStatus = $main::cNO_STATUS;
    my $db_block_size = 0;
    my $db_file_multiblock_read_count = 0;
    my $fragmentationRowCount = 0;
    my $rv = undef;
    my $array_ref = undef;

    my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ("SELECT name, value FROM v\$parameter WHERE name IN ('db_block_size', 'db_file_multiblock_read_count')");

    if (defined $DBI::errstr)  {
	logMessageWTime ("getFragmentationStatus: prepare - $DBI::errstr\n");
    }

    if (defined $sth) {
	$rv = $sth->execute;
	$array_ref = $sth->fetchrow_arrayref;
	while (defined ($array_ref->[0])) {
	    
	    
	    if ($array_ref->[0] =~ /db_block_size/) {
		$db_block_size = $array_ref->[1];
	    } elsif ($array_ref->[0] =~ /db_file_multiblock_read_count/) {
		$db_file_multiblock_read_count = $array_ref->[1];
	    }
	    
	    
	    $array_ref = $sth->fetchrow_arrayref;
	}
	if ($sth) {
	    $sth->finish;
	}
    }
    #
    #
    #
    my $block_multiple = 0;
    if ($db_block_size && $db_file_multiblock_read_count) {
	$block_multiple = $db_block_size * $db_file_multiblock_read_count;
    }

    if ($block_multiple > 0) {


	$sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{fragmentation});

	if (defined $DBI::errstr)  {
	    logMessageWTime ("getFragmentationStus: prepare - $DBI::errstr\n");
	}

	if (defined $sth) {
	    $rv = $sth->execute;
	    $array_ref = $sth->fetchrow_arrayref;
	    
	    if (defined ($array_ref->[0])) {
		$fragmentationStatus = $main::cOK_STATUS;
	    }
	    
	    @{$fragmentationTableRef->[0]} = ("Level",
					      "Type",
					      "Name",
					      "Initial",
					      "Next",
					      "% Increase");
	    
	    $currStatus = $main::cNO_STATUS;
	    $fragmentationRowCount = 1;
	    while (defined ($array_ref->[0])) {
		
		#
		# store the tablespace data for later
		# 
		# This is the size we want all our initial and
		# next extents to be
		#
		$fragmentationData{$array_ref->[0]} = $array_ref->[1];
		
		
		#
		# right now we're warning if any of our tablespaces have a 
		# initial or next extent which is not a multiple of our
		# db_block_size * db_file_multiblock_read_count or if
		# they have a % increase which is non-zero.
		#
		if (($array_ref->[1] % $block_multiple != 0) || 
		    ($array_ref->[2] % $block_multiple != 0) ||
		    ($array_ref->[3] != 0)) {
		    $currStatus = $main::cWARNING_STATUS;
		    
		    #
		    # otherwise ok status
		    #
		} else {
		    $currStatus = $main::cOK_STATUS;
		}
		
		
		# 
		# escalate warning/alert level if necessary
		#
		if (($fragmentationStatus == $main::cNO_STATUS) || 
		    ($fragmentationStatus == $main::cOK_STATUS) ||
		    ($currStatus == $main::cALERT_STATUS)) {
		    $fragmentationStatus = $currStatus;
		} elsif (($currStatus == $main::cALERT_STATUS) &&
			 ($fragmentationStatus == $main::cWARNING_STATUS)) {
		    $fragmentationStatus = $currStatus;
		}
		
		
		@{$fragmentationTableRef->[$fragmentationRowCount]} = 
		    ($currStatus, 
		     "Tablespace",
		     $array_ref->[0],
		     $array_ref->[1],
		     $array_ref->[2],
		     $array_ref->[3]);
		
		$fragmentationRowCount++;
		$array_ref = $sth->fetchrow_arrayref;
	    }
	    
	    if ($sth) {
		$sth->finish;
	    }
	    
	}
	my $astatement = "SELECT owner, segment_name, segment_type, tablespace_name, initial_extent, next_extent, pct_increase FROM dba_segments WHERE owner NOT IN ('SYS', 'SYSTEM')";
	$sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($astatement);
	if (defined $DBI::errstr)  {
	    logMessageWTime ("getFragmentationStus: prepare - $DBI::errstr\n");
	}

	if (defined $sth) {
	    $rv = $sth->execute;
	    
	    $array_ref = $sth->fetchrow_arrayref;
	    while ($array_ref->[0]) {
		
		if (($array_ref->[4] != $fragmentationData{$array_ref->[3]}) ||
		    ($array_ref->[5] != $fragmentationData{$array_ref->[3]})) {
		    $currStatus = $main::cALERT_STATUS;
		} else {
		    $currStatus = $main::cOK_STATUS;
		}
		
		# 
		# escalate warning/alert level if necessary
		#
		if (($fragmentationStatus == $main::cNO_STATUS) || 
		    ($fragmentationStatus == $main::cOK_STATUS) ||
		    ($currStatus == $main::cALERT_STATUS)) {
		    $fragmentationStatus = $currStatus;
		} elsif (($currStatus == $main::cALERT_STATUS) &&
			 ($fragmentationStatus == $main::cWARNING_STATUS)) {
		    $fragmentationStatus = $currStatus;
		}
		
		
		@{$fragmentationTableRef->[$fragmentationRowCount]} = 
		    ($currStatus, 
		     $array_ref->[2],
		     "$array_ref->[0].$array_ref->[1]",
		     $array_ref->[4],
		     $array_ref->[5],
		     $array_ref->[6]);
		
		$fragmentationRowCount++;
		$array_ref = $sth->fetchrow_arrayref;
	    }
	    
	    if (defined $sth) {
		$sth->finish;
	    }
	}
    }

    showInfoPage ($fragmentationTableRef, "fragmentation", $inTNS, undef);
    return $fragmentationStatus;

}

#-----------------------------------------------------------------------
# 
# returns checks load average, %idle, and i/o wait
# not sure how to implement this for remote machines yet
#
#-----------------------------------------------------------------------
sub getOSStatus ($$$) {
    my ($os_threshold_warn, $os_threshold_alert, $inTNS) = @_;

    my $osStatus = $main::cNO_STATUS;
    my $curr_row = [];
    my $osTableRef = [()];
    my $currStatus = 0;

#    $sth = $dbh{$inTNS}->prepare ("SELECT table_name FROM user_tables WHERE table_name = \'KARMA_OS_STATS\'");
    my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ("SELECT table_name FROM user_tables WHERE table_name = \'KARMA_OS_STATS\'");

    if (defined $DBI::errstr)  {
	logMessageWTime ("getOSStatus: prepare - $DBI::errstr\n");
    }

    if (defined $sth) {
	my $rv = $sth->execute;
	$curr_row = $sth->fetchrow_arrayref;
	
	#
	# display one row regardless, so the "No Data Found." message
	# will come up as appropriate
	#
	$osTableRef = [()];
	@{$osTableRef->[0]} = ("Level", 
			       "1 Min", 
			       "5 Min", 
			       "15 Min",
			       "% Idle",
			       "Time");
	
	my $osRowCount = 1;
	
	if (defined ($curr_row->[0])) {
	    
#	$sth = $dbh{$inTNS}->prepare ($main::statements{os});
	    $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{os});
	    $rv = $sth->execute;
	    
	    #$osTableRef = [()];
	    #@{$osTableRef->[0]} = ("Level", 
	    #		      "1 Min", 
	    #		      "5 Min", 
	    #		      "15 Min",
	    #		      "% Idle",
	    #		      "Time");
	    #$osRowCount = 1;
	    
	    $curr_row = $sth->fetchrow_arrayref;
	    while (defined ($curr_row->[0])) {
		
		if ($curr_row->[0] > $os_threshold_alert) {
		    $currStatus = $main::cALERT_STATUS;
		} elsif ($curr_row->[0] > $os_threshold_warn) {
		    $currStatus = $main::cWARNING_STATUS;
		} else {
		    $currStatus = $main::cOK_STATUS;
		}
		
		#
		# escalate warning/alert level if necessary
		#
		if (($osStatus == $main::cNO_STATUS) || 
		    ($osStatus == $main::cOK_STATUS) ||
		    ($currStatus == $main::cALERT_STATUS)) {
		    $osStatus = $currStatus;
		} elsif (($currStatus == $main::cALERT_STATUS) &&
			 ($osStatus == $main::cWARNING_STATUS)) {
		    $osStatus = $currStatus;
		}
		
		@{$osTableRef->[$osRowCount]} = 
		    ($currStatus, 
		     $curr_row->[0],
		     $curr_row->[1],
		     $curr_row->[2],
		     $curr_row->[3],
		     $curr_row->[4]);
		
		
		$osRowCount++;
		$curr_row = $sth->fetchrow_arrayref;
	    }
	}
	
	if ($sth) {
	    $sth->finish;
	}
    }

    showInfoPage ($osTableRef, "os", $inTNS, undef);
    return $osStatus;

}

#-----------------------------------------------------------------------
# 
# checks the MTS shared server and dispatcher processes to make sure
# there isn't too much contention
#
#-----------------------------------------------------------------------
sub getMTSStatus ($$$) {
    my ($mts_threshold_warn, $mts_threshold_alert, $inTNS) = @_;

    my $mtsStatus = $main::cNO_STATUS;
    my $busy = 0;
    my $busy_idle = 0;
    my $busy_percent = 0;
    my $curr_row = [()];
    my $mtsRowCount = 0;
    my $mtsTableRef = [()];
    my $idle = 0;
    my $currStatus = 0;

#    $sth = $dbh{$inTNS}->prepare($main::statements{mts});
    my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare($main::statements{mts});

    if (defined $DBI::errstr)  {
	logMessageWTime ("getMTSStatus: prepare - $DBI::errstr\n");
    }

    if (defined $sth) {
	my $rv = $sth->execute;
	
	$mtsTableRef = [()];
	@{$mtsTableRef->[0]} = ("Level", 
				"Name", 
				"Busy", 
				"Idle",
				"% Busy");
	
	
	$mtsRowCount = 1;
	
	$curr_row = $sth->fetchrow_arrayref;    
	while (defined ($curr_row->[0])) {
	    $busy = $curr_row->[1];
	    $idle = $curr_row->[2];
	    $busy_idle = $busy + $idle;
	    $busy_percent = 0;
	    if ($busy_idle) {
		$busy_percent = $busy/$busy_idle * 100;
		
		if ($busy_percent > $mts_threshold_alert) {
		    $currStatus = $main::cALERT_STATUS;
		} elsif ($busy_percent > $mts_threshold_warn) {
		    $currStatus = $main::cWARNING_STATUS;
		} else {
		    $currStatus = $main::cOK_STATUS;
		}
	    }
	    
	    #
	    # escalate warning/alert level if necessary
	    #
	    if (($mtsStatus == $main::cNO_STATUS) || 
		($mtsStatus == $main::cOK_STATUS) ||
		($currStatus == $main::cALERT_STATUS)) {
		$mtsStatus = $currStatus;
	    } elsif (($currStatus == $main::cALERT_STATUS) &&
		     ($mtsStatus == $main::cWARNING_STATUS)) {
		$mtsStatus = $currStatus;
	    }
	    
	    @{$mtsTableRef->[$mtsRowCount]} = 
		($currStatus, 
		 $curr_row->[0],
		 $busy,
		 $idle,
		 $busy_percent,
		 $curr_row->[4]);
	    
	    
	    $mtsRowCount++;
	    $curr_row = $sth->fetchrow_arrayref;
	}
	
	if ($sth) {
	    $sth->finish;
	}
    }

    showInfoPage ($mtsTableRef, "mts", $inTNS, undef);
    return $mtsStatus;

}

#-----------------------------------------------------------------------
# 
#
#
#-----------------------------------------------------------------------
sub getUpStatus ($) {
    my ($inTNS) = @_;
    my $upStatus = $main::cALERT_STATUS;
    my $upRow = [()];
    my $upTableRef = [()];
    my $upRowCount = 0;
    my $showMessage = undef;

    $upStatus = $main::cALERT_STATUS;
    $upRow = [()];
    $upTableRef = [()];
    $upRowCount = 0;

    #
    # testing
    #
    #my $pingResult = 0;
    #$main::db_info{$inTNS}[$main::cDB_HANDLE]->ping;
    #logMessage ("PING DB:$inTNS VAL:$pingResult\n");

    #
    # attempt to connect again at regular intervals
    # in case the db comes back up
    # 
    if (defined $main::db_info{$inTNS}[$main::cDB_HANDLE]) {
	my $ping = $main::db_info{$inTNS}[$main::cDB_HANDLE]->ping;
	if ($ping == 0) {
	    undef $main::db_info{$inTNS}[$main::cDB_HANDLE];
	}
    }
    if (not (defined ($main::db_info{$inTNS}[$main::cDB_HANDLE]))) {
	$main::db_info{$inTNS}[$main::cDB_HANDLE] = DBI->connect ("DBI:Oracle:$inTNS", 
					     $main::db_info{$inTNS}[$main::cDB_USER],
					     $main::db_info{$inTNS}[$main::cDB_PASS],
						      {RaiseError => 0,
						       PrintError => 0});
	if (defined ($DBI::errstr))  {
	    if ($DBI::err =~ /^$/) {
		$showMessage = 'Listener DOWN!';
	    } elsif ($DBI::err =~ /^$/) {
		$showMessage = 'Database DOWN!';
	    } else {
		$showMessage = "Connectivity problem - $DBI::errstr";
	    }
	    logMessageWTime ("getUpStatus:$inTNS - $DBI::errstr\n");
	}
    }


    #
    # if our database handle is ok, we're up...
    #
    if ((defined $main::db_info{$inTNS}) &&
	(defined ($main::db_info{$inTNS}[$main::cDB_HANDLE])) &&
	($main::db_info{$inTNS}[$main::cDB_HANDLE]->ping == 1)) {

	debugMessage ("getUpStatus:$inTNS - We have db handle, and can ping db...\n", undef);
	$upStatus = $main::cOK_STATUS;

	my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare($main::statements{up});

	if (defined $DBI::errstr)  {
	    logMessageWTime ("getUpStatus: prepare - $DBI::errstr\n");
	}

	debugMessage ("getUpStatus:$inTNS, prepare ok.\n", 2);

	if (defined $sth) {
	    my $rv = $sth->execute;
	    
	    $upTableRef = [()];
	    @{$upTableRef->[0]} = ("Level", 
				   "Statistic", 
				   "Value");
	    
	    $upRowCount = 1;
	    
	    $upRow = $sth->fetchrow_arrayref;
	    while (defined ($upRow->[0])) {
#	    debugMessage ("getUpStatus:$inTNS, $upRow->[0], $upRow->[1]\n",2);
		@{$upTableRef->[$upRowCount]} = 
		    ($main::cNO_STATUS, 
		     $upRow->[0],
		     $upRow->[1]);
		
		$upRowCount++;
		$upRow = $sth->fetchrow_arrayref;
		
	    }
	    if (defined $sth) {
		$sth->finish;
	    }
	    
	}
	showInfoPage ($upTableRef, "up", $inTNS, undef);

    } else {

	undef $main::db_info{$inTNS}[$main::cDB_HANDLE];
	debugMessage ("getUpStatus:$inTNS - NO db handle, or failed PING...\n", 1);
	#
	# don't show unless we have a db handle
	#
	showInfoPage (undef, "up", $inTNS, $showMessage);
    }

    debugMessage ("getUpStatus:$inTNS, $upStatus, returning\n",2);
    return $upStatus;
}


#-----------------------------------------------------------------------
# 
#
#-----------------------------------------------------------------------
sub getDbStatus ($) {
    my ($inTNS) = @_;
    my $dbStatus = $main::cOK_STATUS;
    my $dbRow = [()];
    my $dbTableRef = [()];
    my $dbRowCount = 0;
    my $sth = undef;
    my $rv = 0;

    debugMessage ("getDbStatus:$inTNS\n",2);
    if (defined ($main::db_info{$inTNS}[$main::cDB_HANDLE])) {
	
	my $test = $main::db_info{$inTNS}[$main::cDB_HANDLE]->ping;
	if (defined $test) {
	    debugMessage ("getDbStatus:$inTNS - ping returns OK\n",2);
	} else {
	    debugMessage ("getDbStatus:$inTNS - ping FAILS\n",2);
	}

	$dbStatus = $main::cOK_STATUS;

	debugMessage ("getDbStatus:$inTNS - before prepare\n",2);
	$sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare($main::statements{db});

	if (defined $DBI::errstr)  {
	    logMessageWTime ("getDbStatus:$inTNS prepare - $DBI::errstr\n");
	}


	debugMessage ("getDbStatus:$inTNS - prepare ok\n",2);


	if (defined $sth) {
	    $rv = $sth->execute;
	    debugMessage ("getDbStatus:$inTNS - exec ok\n",2);
	    
	    $dbTableRef = [()];
	    @{$dbTableRef->[0]} = ("Level", 
				   "Parameter", 
				   "Value");
	    
	    $dbRowCount = 1;
	    
	    $dbRow = $sth->fetchrow_arrayref;
	    while (defined ($dbRow->[0])) {
		debugMessage ("getDbStatus:$inTNS - $dbRow->[0]\n",2);
		@{$dbTableRef->[$dbRowCount]} = 
		    ($main::cNO_STATUS, 
		     $dbRow->[0],
		     $dbRow->[1]);
		
		$dbRowCount++;
		$dbRow = $sth->fetchrow_arrayref;
	    }
	}
	debugMessage ("getDbStatus:$inTNS - normal info page\n",2);
	showInfoPage ($dbTableRef, "db", $inTNS, undef);

    } else {
	debugMessage ("getDbStatus:$inTNS - down info page\n",2);
	showInfoPage (undef, "db", $inTNS, 
		      'Cannot connect.  Check "up" status for details');
    }

    if (defined $sth) {
	$sth->finish;
    }

    return $dbStatus;
}


#-----------------------------------------------------------------------
# 
# Check object extents 
#  1. an object which is at it's maxextents (or near)
# NOT IMPLEMENTED YET:
#  2. an object whose next extent won't fit in the tablespace
#
#-----------------------------------------------------------------------
sub getExtentsStatus ($$$) {
    my ($extents_threshold_warn, $extents_threshold_alert, $inTNS) = @_;

    my $extentsStatus = $main::cNO_STATUS;
    my $currStatus = $main::cNO_STATUS;
    my $extentsTableRef = [()];
    my $extentsRowCount = 1;
    my $extentsRow = [()];

    $extentsTableRef = [()];
    @{$extentsTableRef->[0]} = ("Level", 
				"Name",
				"Owner",
				"Current", 
				"Max");

    $extentsRowCount = 1;

    my $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->prepare ($main::statements{extents});
    
    if (defined $DBI::errstr) {
	logMessageWTime ("getExtentsStatus:$inTNS prepare - $DBI::errstr\n");
    }

    if (defined $sth) {
	my $rv = $sth->execute;


	my $extents_row = $sth->fetchrow_arrayref;
	while (defined ($extents_row->[0])) {
	    
	    #
	    # looks like we're at alert status...
	    #
	    if ($extents_row->[2] ==
		($extents_row->[1] - $extents_threshold_alert)) {
		$currStatus = $main::cALERT_STATUS;
		
		#
		# warn status... don't set if we're already at main::cALERT_STATUS
		#
	    } elsif ($extents_row->[2] == 
		     ($extents_row->[1] - $extents_threshold_warn)) {
		$currStatus = $main::cWARNING_STATUS;
		
		# 
		# if we haven't set the status yet, we're at ok status.  This
		# may change with subsequent iterations of this loop
		#
	    } else {
		$currStatus = $main::cOK_STATUS;
	    }
	    
	    #
	    # escalate warning/alert level if necessary
	    #
	    if (($extentsStatus == $main::cNO_STATUS) || 
		($extentsStatus == $main::cOK_STATUS) ||
		($currStatus == $main::cALERT_STATUS)) {
		$extentsStatus = $currStatus;
	    } elsif (($currStatus == $main::cALERT_STATUS) &&
		     ($extentsStatus == $main::cWARNING_STATUS)) {
		$extentsStatus = $currStatus;
	    }
	    
	    if (($currStatus == $main::cALERT_STATUS) ||
		($currStatus == $main::cWARNING_STATUS)) {
		@{$extentsTableRef->[$extentsRowCount]} = 
		    ($currStatus, 
		     $extents_row->[0],
		     $extents_row->[3],
		     $extents_row->[2],
		     $extents_row->[1]);
		
		
		$extentsRowCount++;
	    }
	    $extents_row = $sth->fetchrow_arrayref;
	}
	
	if ($sth) {
	    $sth->finish;
	}
    }

    showInfoPage ($extentsTableRef, "extents", $inTNS, undef);
    return $extentsStatus;

}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getInfo ($$$) {
    my ($forceUpdate, $inTNS, $inMinutes) = @_;

    debugMessage ("getInfo: $inTNS, $inMinutes\n", 2);
    #
    # we do this in readConfig now, and leave them between
    # iterations of "main" because not every service is updated
    # on the minute.  old values are then kept, and services
    # that are run have their values updated
    #
    my %theStatus;
    my $key = undef;
#    my $thePrefGroup = $main::db_info{$inTNS}[$main::cDB_PREFGROUP];
    my $thePrefGroup = getPrefGroup ($inTNS);

#    foreach $key (keys %{$main::config_info{default}}) {
    foreach $key (keys %{$main::names{long}}) {
	if ($key =~ /^up$/) {
	    $theStatus{up} = $main::cALERT_STATUS;
	} elsif ($main::names{shown}{$key} == 1) {
#	    $theStatus{$key} = $main::cNO_STATUS;
	    if (defined ($main::config_info{$thePrefGroup}{$key})) {
		$theStatus{$key} = $main::cNO_STATUS;
	    } else {
		$theStatus{$key} = $main::cNO_SHOW;
	    }
	}
    }


    $theStatus{up} = getStatus ($thePrefGroup, "up", $inTNS);
    $main::stats{up}{$inTNS}[$main::cSTATUS] = $theStatus{up};
    $main::stats{up}{$inTNS}[$main::cUPD_TIME] = $main::currTime;

    debugMessage ("getInfo:$inTNS - before get db status\n",2);

    #
    # dbStatus doesn't seem to be used here...
    #
    $theStatus{db} = getStatus ($thePrefGroup, "db", $inTNS);
    $main::stats{db}{$inTNS}[$main::cSTATUS] = $theStatus{up};
    $main::stats{db}{$inTNS}[$main::cUPD_TIME] = $main::currTime;

    if ($theStatus{up} == $main::cOK_STATUS) {

#	foreach $key (keys %{$main::config_info{default}}) {
	foreach $key (keys %{$main::names{long}}) {
		debugMessage ("getInfo:$inTNS - checking $key}\n",2);
	    if ((not ($key =~ /^up$/)) && ($main::names{shown}{$key} == 1)) {
		$theStatus{$key} = $main::stats{$key}{$inTNS}[$main::cSTATUS];
	    }
	}
    } else {
	debugMessage ("getInfo:$inTNS - seting NO_STATUS\n",2);
	foreach $key (keys %{$main::names{long}}) {
	    if ((not ($key =~ /^up$/)) &&
		(defined ($main::config_info{$thePrefGroup}{$key}))) {
		$main::stats{$key}{$inTNS}[$main::cSTATUS] = $main::cNO_STATUS;
	    }
	}
    }

    debugMessage ("getInfo:$inTNS - set default ok.\n", 2);

    if ($theStatus{up} == $main::cOK_STATUS) {

#	foreach $key (keys %{$main::config_info{default}}) {
	foreach $key (keys %{$main::names{long}}) {

	    if ((shouldUpdateService ($thePrefGroup, $key, $inMinutes) == 1) ||
		($forceUpdate == $main::cFORCE_UPD)) {
		#if ($key =~ /^os$/) {
		    # this shouldn't use the testRef
		#    showInfoPage ($main::testRef, "os", $inTNS, undef);
		#}
		if ((not ($key =~ /^up$/)) &&
		    ($main::names{shown}{$key} == 1)) {
		    $theStatus{$key} = $main::cNO_SHOW;
		    if (defined ($main::config_info{$thePrefGroup}{$key}[0])) {
			$theStatus{$key} = 
			    getStatus ($thePrefGroup, $key, $inTNS);
		    }


		    $main::stats{$key}{$inTNS}[$main::cSTATUS] = 
			$theStatus{$key};
		    
		    #
		    # is this the only place that the service update time
		    # is set?  How are they all not getting updated?
		    #
		    $main::stats{$key}{$inTNS}[$main::cUPD_TIME] = 
			$main::currTime;
		}
	    }
	}
    }
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub showKarmaTableHeader () {
#    my ($index_file) = @_;

    my $colCount = 1;

    print $main::index_file ("<tr bgcolor=\"$main::cMAIN_TABLE_BG\" align=\"center\" valign=\"middle\"> \n");

    #
    # this will be added to the hash...
    #
    print $main::index_file ("<td height=\"$main::cSTATUS_HEIGHT\" width=\"$main::cSTATUS_WIDTH\"><font size=\"4\"><b>\n");
    print $main::index_file ("<font face=\"Arial, Helvetica, sans-serif\"\n");
    print $main::index_file ("color=\"$main::cKARMA_TEXT_COLOR\">\n");
    print $main::index_file ("<A HREF=\"help$main::PATH_DELIM$main::files{help}{db}\" ALT=\"Help - $main::names{long}{db}\">$main::names{short}{db}</A>\n");
    print $main::index_file ("</font></b></font></td>\n");

    my $key = undef;

    foreach $key (keys %{$main::names{long}}) {
	#
	# need to use pref group prefs if defined
	#
	if ((not ($key =~ /^db$/i)) &&
	    (($key =~ /^up$/i) ||
#	    ($main::config_info{default}{$key}[$main::cSTATUS] > 0))) {
	     (shouldShowServiceHeader ($key) == 1))) {
#	    print $main::index_file ("<TD bgcolor=\"$main::cHD_BG_COLOR\" cellspacing=0 cellpadding=0 ALIGN=CENTER><H4>\n");
#	    print $main::index_file ("<I><A HREF=\"help$main::PATH_DELIM$main::files{help}{$key}\" ALT=\"Help - $main::names{long}{$key}\">$main::names{short}{$key}</A></I>\n");


	    #
	    # new stuff
	    #
	    print $main::index_file ("<td height=\"$main::cSTATUS_HEIGHT\" width=\"$main::cSTATUS_WIDTH\"><font size=\"4\"><b>\n");
	    print $main::index_file ("<font face=\"Arial, Helvetica, sans-serif\"\n");
	    print $main::index_file ("color=\"$main::cKARMA_TEXT_COLOR\">\n");

	    print $main::index_file ("<A HREF=\"help$main::PATH_DELIM$main::files{help}{$key}\">$main::names{short}{$key}</A>\n");


#	    print $main::index_file ("<I>$key</I>\n");
#	    print $main::index_file ("$key\n");
#	    print $main::index_file ("</H4></TD>\n");
	    print $main::index_file ("</font></b></font></td>\n");

	    $colCount++;
	}
    }

    print $main::index_file ("</TR>\n");

    return $colCount;
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub showKarmaTableRow ($$) { 
    #my ($inRow, $inTNS, $inMinutes) = @_;
    my ($inTNS, $inMinutes) = @_;

    my $key = undef;

    print $main::index_file ("<tr>");
    print $main::index_file ("<td width=\"$main::cHEAD_HEIGHT\" height=\"$main::cSTATUS_HEIGHT\"><font face=\"Arial, Helvetica, sans-serif\" color=\"$main::cKARMA_TEXT_COLOR\"><a href=\"generic_info.html\" target=\"main\">\n");
    print $main::index_file ("<A HREF=\"info$main::PATH_DELIM$inTNS.$main::files{info}{db}\">$inTNS</A>\n");
    print $main::index_file ("</a></font></td>\n");

#    my $thePrefGroup = $main::db_info{$inTNS}[$main::cDB_PREFGROUP];
    my $thePrefGroup = getPrefGroup ($inTNS);

    #
    # need to use pref group prefs if defined
    #
#	    ($main::config_info{default}{$key}[$main::cSTATUS] > 0))) {
	    #&& ($inMinutes % $main::config_info{default}{$key}[$main::cSTATUS] == 0)) {
	    #print $main::index_file ("<TD ALIGN=CENTER>\n");

    foreach $key (keys %{$main::names{long}}) {
	if ((not ($key =~ /^db$/i)) && ($main::names{shown}{$key} == 1)) {
#	    ($key =~ /^up$/i)) {
#	     (shouldShowService ($thePrefGroup, $key) == 1))) {
	    print $main::index_file ("<td align=\"center\" valign=\"middle\">\n");

#	    if (defined ($main::config_info{$thePrefGroup}{$key})) {
		showServiceStatus ($key, $inTNS);
#	    }

	    print $main::index_file ("</TD>\n");
	    
	}
    }

    print $main::index_file ("</TR>\n");

}


#-----------------------------------------------------------------------
#
# print the help message and exit
#
#-----------------------------------------------------------------------
sub printHelp () {

    print 
	"\n",
	" d - debug level (default 0)\n",
	" v - print version info and exit\n",
	" h - print this help info\n",
	" k - karma root directory\n",
	" w - print the warranty\n",
	" c - specify the karma configuration file\n",
	" l - specify logfile (default is stdout)\n",
	"\n",
	"$0 [-h] [-k karma_root] [-c karma.conf]\n";
    
    exit;

}


#-----------------------------------------------------------------------
#
# read the configuration file
#
# only parameter is the file to open for reading config info
#
#-----------------------------------------------------------------------
sub readConfig ($) {
    my ($conf_file_name) = @_;

    $main::MIN_WAKEUP = $main::cWAKEUP;

    # open file
    my $conf_file = new IO::File "$conf_file_name";
    if (not (defined ($conf_file))) {
	logMessage ("Cannot open config file, using factory defaults\n");
    }
#    open (FILE, "$main::index_file");

    # iterate on lines
    my $eofile = 0;
    my $db_count = 0;
    my $curr_line = "";
    my @curr_array = ();
    my $theTNS = "";
    my $theUser = "";
    my $thePass = "";
    my $theGroup = "";
    my $key = undef;
    $main::pref_groups = undef;
    

   $main::names{shown}{up} = 1;
   $main::names{shown}{db} = 1;

    while (!$eofile) {
	
	$eofile = !($curr_line = <$conf_file>);
	
	#
	# ignore comment lines and blank lines
	#
	while ((!$eofile) && (($curr_line =~ /^ *\#/) ||
               ($curr_line =~ /^ *$/))) {
#	    $eofile = !($curr_line = <FILE>);
	    $eofile = !($curr_line = <$conf_file>);
	}

	if ($curr_line) {

	    # remove the eol character
	    chop ($curr_line);


	    @curr_array = split (":", $curr_line);

	    if ($curr_array[0] =~ /^karma$/i) {

		# tnsname
		$theTNS = uc $curr_array[2];


		if (isValidTNS ($theTNS) == 1) {
		    $theUser = $curr_array[3];
		    $thePass = $curr_array[4];
		    
		    # connect to the db
		    $main::db_info{$theTNS}[$main::cDB_HANDLE] = DBI->connect 
			("DBI:Oracle:$theTNS", 
			 $theUser,
			 $thePass,
			 {RaiseError => 0,
			  PrintError => 0});
		    
		    # username
		    $main::db_info{$theTNS}[$main::cDB_USER] = $theUser;
		    
		    # password
		    $main::db_info{$theTNS}[$main::cDB_PASS] = $thePass;
		    
		    # karma refresh
		    $main::db_info{$theTNS}[$main::cDB_REFRESH] = $curr_array[4];
		    
		    # pref_group
		    $theGroup = $curr_array[1];
		    if (($theGroup =~ /^$/) ||
			($theGroup =~ /^\*$/) ||
			($theGroup =~ /^default$/i)) {
			
			$theGroup = $main::cDEF_GRP_NAME;
		    } else {
			$theGroup = $curr_array[1];
		    }
		    push (@{$main::pref_groups{$theGroup}}, $theTNS);
		    setPrefGroup ($theTNS, $theGroup);
#		    $main::db_info{$theTNS}[$main::cDB_PREFGROUP] = $theGroup;

		    
		    #
		    # log any errors
		    #
		    if (defined ($DBI::errstr)) {
			logMessageWTime ("readConfig - $DBI::errstr\n");
		    }

		} else {
		 
		    logMessageWTime ("Invalid TNS - $theTNS\n");
		}

	    } elsif ($curr_array[0] =~ /^refresh$/i) {
		$main::config_info{default}{up}[0] = $curr_array[1];
		$main::config_info{default}{up}[1] = $curr_array[2];
		$main::config_info{default}{up}[2] = 0;
	    } elsif (($curr_array[0] =~ /^redolog$/i) ||
		     ($curr_array[0] =~ /^os$/i) ||
		     ($curr_array[0] =~ /^rollback$/i) ||
		     ($curr_array[0] =~ /^tablespace$/i) ||
		     ($curr_array[0] =~ /^slowsql$/i) ||
		     ($curr_array[0] =~ /^alertlog$/i) ||
		     ($curr_array[0] =~ /^hitratios$/i) ||
		     ($curr_array[0] =~ /^fragmentation$/i) ||
		     ($curr_array[0] =~ /^extents$/i) ||
		     ($curr_array[0] =~ /^latch$/i) ||
		     ($curr_array[0] =~ /^repqueue$/i) ||
		     ($curr_array[0] =~ /^reperror$/i) ||
		     ($curr_array[0] =~ /^mts$/i)) {
	
		setConfig ("default",
			      $curr_array[0],
			      $curr_array[1],
			      $curr_array[2],
			      $curr_array[3]);
	    } elsif (($curr_array[0] =~ /^$main::cNOTIFY_ALRT$/i) ||
		     ($curr_array[0] =~ /^$main::cNOTIFY_WARN$/i)) {
		setConfigNotify ("default",
				   $curr_array[0],
				   $curr_array[1],
				   $curr_array[2]);
	    } elsif ($curr_array[0] =~ /^$main::cNOTIFY_EMAIL$/i) {
		setConfigEmail ("default",
				  $curr_array[1],
				  $curr_array[2]);
	    } elsif ($curr_array[0] =~ /^warn_blink$/i) {
		if (($curr_array[1] =~ /^true$/i) || 
		    ($curr_array[1] =~ /^yes$/i) ||
		    ($curr_array[1] =~ /^1$/i)) {
		    $main::USE_BLINK_WARNING=1;		    
		}
	    } elsif ($curr_array[0] =~ /^alert_blink$/i) {
		if (($curr_array[1] =~ /^true$/i) || 
		    ($curr_array[1] =~ /^yes$/i) ||
		    ($curr_array[1] =~ /^1$/i)) {
		    $main::USE_BLINK_ALERT=1;
		}
	    } elsif ($curr_array[0] =~ /^pref_group_sections$/i) {
		if (($curr_array[1] =~ /^true$/i) || 
		    ($curr_array[1] =~ /^yes$/i) ||
		    ($curr_array[1] =~ /^1$/i)) {
		    $main::USE_PREFGROUP_SECTIONS=1;
		}

	    } else {
		
		#
		# get prefgroup preferences
		# 
		# These are just like default preferences, except
		# they are preceded by a prefgroup name.
		#
		foreach $key (keys %main::pref_groups) {


		    #
		    # preferences are bound for a particular preference group
		    #
		    if ($curr_array[0] =~ /^$key$/i) {
			if (($curr_array[1] =~ /^redolog$/i) ||
			    ($curr_array[1] =~ /^os$/i) ||
			    ($curr_array[1] =~ /^rollback$/i) ||
			    ($curr_array[1] =~ /^tablespace$/i) ||
			    ($curr_array[1] =~ /^slowsql$/i) ||
			    ($curr_array[1] =~ /^alertlog$/i) ||
			    ($curr_array[1] =~ /^hitratios$/i) ||
			    ($curr_array[1] =~ /^fragmentation$/i) ||
			    ($curr_array[1] =~ /^extents$/i) ||
			    ($curr_array[1] =~ /^latch$/i) ||
			    ($curr_array[1] =~ /^repqueue$/i) ||
			    ($curr_array[1] =~ /^reperror$/i) ||
			    ($curr_array[1] =~ /^mts$/i)) {
			    setConfig ($key,
					$curr_array[1],
					$curr_array[2],
					$curr_array[3],
					$curr_array[4]);
			} elsif ($curr_array[1] =~ /^$main::cNOTIFY_EMAIL$/i) {
			    setConfigEmail ($key,
					      $curr_array[2],
					      $curr_array[3]);
			} elsif (($curr_array[1] =~ /^$main::cNOTIFY_ALRT$/i) ||
				 ($curr_array[1] =~ /^$main::cNOTIFY_WARN$/i)) {
			    setConfigNotify ($key,
					       $curr_array[1],
					       $curr_array[2],
					       $curr_array[3]);
			}

#			print "Nothing right now\n";
		    }
		}


	    }

	}

    }

    #
    # initialize everything to $main::cNO_STATUS;
    #
    # iterate on all configured services
    my $servKey = "";
    my $tnsKey = "";
    my $prefGroup = "";
    foreach $servKey (keys %{$main::config_info{default}}) {
#    foreach $servKey (keys %{$main::names{long}}) {
#	if ($main::names{shown} == 1) {
	    foreach $tnsKey (keys %main::db_info) {
		$prefGroup = getPrefGroup ($tnsKey);
#		$prefGroup = $main::db_info{$tnsKey}[$main::cDB_PREFGROUP];
		if (defined ($main::config_info{$prefGroup}{$servKey})) {
		    $main::stats{$servKey}{$tnsKey}[$main::cSTATUS] = $main::cNO_STATUS;
		} else {
		    $main::stats{$servKey}{$tnsKey}[$main::cSTATUS] = $main::cNO_SHOW;
		}
		$main::stats{$servKey}{$tnsKey}[$main::cUPD_TIME] = $main::startTime;
	    }
#	}
    }

    if ($conf_file) {
	$conf_file->close;
    }
    #close (FILE);

    checkForDBs ();
}

#---------------------------------------------------------------
#
# add the configuration information just read from the file
# to the right place in the config_info hash of hash of arrays
#
#---------------------------------------------------------------
sub setConfig ($$$$$) {
    my ($inPrefGroup, $inService, $inInterval, $inWarn, $inAlert) = @_;

    if ((defined ($inInterval)) &&
	($inInterval < $main::MIN_WAKEUP)) {
	$main::MIN_WAKEUP = $inInterval;
    }

   $main::names{shown}{$inService} = 1;

    #
    # set the interval based on passed in value, or factory  default
    # if passed value is null
    #
    if ((defined ($inInterval)) && ($inInterval =~ /\d*/)) {
	$main::config_info{$inPrefGroup}{$inService}[0] = $inInterval;
    } else {
	$main::config_info{$inPrefGroup}{$inService}[0] =
	    $main::config_info{factory}{$inService}[0];
    }

    # 
    # set the warn value based on passed in value, or based on
    # factory settings if passed value is null
    #
    if ((defined ($inWarn)) && ($inWarn =~ /\d*/)) {
	$main::config_info{$inPrefGroup}{$inService}[1] = $inWarn;
    } else {
	$main::config_info{$inPrefGroup}{$inService}[1] =
	    $main::config_info{factory}{$inService}[1];
    }

    # 
    # set alert value
    #
    if ((defined ($inAlert)) && ($inAlert =~ /\d*/)) {
	$main::config_info{$inPrefGroup}{$inService}[2] = $inAlert;
    } else {
	$main::config_info{$inPrefGroup}{$inService}[2] =
	    $main::config_info{factory}{$inService}[2];
    }

}


#---------------------------------------------------------------
#
# 
# still need to handle "@" in the email address...
#
#---------------------------------------------------------------
sub setConfigEmail ($$$) {
    my ($inPrefGroup, $inSize, $inEmails) = @_;

    if (not (defined ($inEmails))) {
	debugMessage ("setConfigEmail:$inPrefGroup - inEmails is undefined...\n", 2);
    }

    my $i = 0;
    my @emails = split ("," , $inEmails);
    my $count = scalar @emails;

    for ($i = 0; $i < $count; $i++) {
	push (@{$main::config_email{$inPrefGroup}{$inSize}}, $emails[$i]);
    }
}

#---------------------------------------------------------------
#
#
#
#---------------------------------------------------------------
sub showInfoPage ($$$$) {
    my ($inTableRef, $inType, $inTNS, $inMessage) = @_;

    debugMessage ("showInfoPage:$inTNS, $inType\n", 2);
    #
    # top of page
    #
    $main::INFO_FILE_NAME = "$main::KARMA_DOC_ROOT$main::PATH_DELIM" . 
	'info' . "$main::PATH_DELIM$inTNS.$main::files{info}{$inType}";

    debugMessage ("showInfoPage:$inTNS, IF:$main::INFO_FILE_NAME\n", 2);
    $main::info_file = new IO::File ">$main::INFO_FILE_NAME"
#	or die ("Can't open $main::INFO_FILE_NAME - $!\n");
	or logMessage_exit ("Can't open $main::INFO_FILE_NAME - $!\n");

    debugMessage ("showInfoPage:$inTNS, IF opened ok.\n", 2);

    if (not (defined ($main::info_file))) {
	logMessage ("Cannot open info page file: $main::INFO_FILE_NAME\n");
    } else {
#       open ($main::info_file, ">$filename");


	showInfoHead ($inTNS, $inType);

	debugMessage ("showInfoPage:$inTNS - passed showInfoHead\n",2);
	my $i = 0;
	my $j = 0;
	
	if (defined ($inTableRef)) {
	    #
	    # content of page (table data)
	    #
#    print $main::info_file ("<TABLE>");
	    print $main::info_file ("<table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">\n");
	    
	    
	    if (defined ($inTableRef->[0][0])) {
#	print $main::info_file ("<TR>\n");
		print $main::info_file ("<tr bgcolor=\"$main::cMAIN_TABLE_BG\" align=\"left\" valign=\"middle\">\n");
		
		$i = 0;
		#
		# title row
		#
		while (defined ($inTableRef->[0][$i])) {
		    print $main::info_file ("<TD <font face=\"Arial, helvetica, sans-serif\" color=\"$main::cKARMA_TEXT_COLOR\">$inTableRef->[0][$i]</font></TD>\n");
		    $i++;
		}
		print $main::info_file ("</TR>\n");
		$j = 1;
		
		while (defined ($inTableRef->[$j][0])) {
		    
		    print $main::info_file ("<TR align=\"left\" valign=\"middle\">\n");
		    $i = 0;
#	    while ($inTableRef[$j][$i]) {
		    while (defined ($inTableRef->[$j][$i])) {
			if ($i == 0) {
			    if ($inTableRef->[$j][$i] == $main::cWARNING_STATUS) {
				print $main::info_file ("<TD><IMG SRC=\"..$main::PATH_DELIM" . 'images' . "$main::PATH_DELIM" . 'yellow_status' . "\"></TD>\n"); 
			    } elsif ($inTableRef->[$j][$i] == $main::cALERT_STATUS) {
				print $main::info_file ("<TD height=\"30\"><IMG SRC=\"..$main::PATH_DELIM" . 'images' . "$main::PATH_DELIM" . 'red_status' . "\"></TD>\n"); 
			    } elsif ($inTableRef->[$j][$i] == $main::cOK_STATUS) {
				print $main::info_file ("<TD><IMG SRC=\"..$main::PATH_DELIM" . 'images' . "$main::PATH_DELIM" . 'green_status' . "\" ></TD>\n");  
			    } else {
				print $main::info_file ("<TD><IMG SRC=\"..$main::PATH_DELIM" . 'images' . "$main::PATH_DELIM" . 'purple_status' . "\"></TD>\n");  
			    }
			} else {
			    print $main::info_file ("<TD height=\"25\">\n");
			    print $main::info_file ("<font face=\"Arial, Helvetica, sans-serif\" color=\"$main::cKARMA_TEXT_COLOR\">\n");
			    print $main::info_file ("$inTableRef->[$j][$i]\n");
			    print $main::info_file ("</font>\n");
			    print $main::info_file ("</TD>\n");
			}
			$i++;
		    }
		    print $main::info_file ("</TR>\n");
		    
		    $j++;
		    
		}      
	    }
	    
	    print $main::info_file ("</TABLE>");
	    
	    if ($j == 1) {
		print $main::info_file ("No Data Found.\n");
	    }
	} else {
	    if (not (defined $inMessage)) {
		$inMessage = 'Unknown connectivity problem.';
	    }
	    print $main::info_file 
		"<font color=\"$main::cEMPHASIS_TEXT\"",
		"size=\"5\" face=\"Arial, Helvetica, sans-serif\">",
		"$inMessage" . "</font>\n";
	}
	
	
	showInfoFoot ($inType, $inTNS);
	debugMessage ("showInfoPage:$inTNS - passed showInfoFoot\n",2);	
	
	#   if (defined ($main::info_file)) {
	$main::info_file->close;
	#   }
    }

#    close ($main::info_file);

}

#-----------------------------------------------------------------------
#
# returns the time of day in minutes
#
#-----------------------------------------------------------------------
#sub getDayMinutes ($) {
#    ($inTime) = @_;


#    my $sec =  $min = $hour = $mday = $mon = 
#       $year = $wday = $yday = $isdst = 0;
#    my $dayMinutes = 0;

    #
    # gmtime is 4 hours forward... didn't know the correct way to handle
    # this...
    #
#    $inTime -= (4 * 60 * 60);
    
#    ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime ($inTime);

#    $dayMinutes = $hour * 60 + $min;

#    return $dayMinutes;
#}


#-----------------------------------------------------------------------
#
# prints the header of the "main" page
#
#-----------------------------------------------------------------------
sub showKarmaHeadMain () {
#    my ($main::index_file) = @_;


    print $main::index_file ("<html>\n");
    print $main::index_file ("<head>\n");
    print $main::index_file ("<title>main</title>\n");

    my $pageRefresh = $main::MIN_WAKEUP * 60;

    print $main::index_file 
	("<META HTTP-EQUIV=\"REFRESH\" CONTENT=\"$pageRefresh\">\n");

#    print $main::index_file ("<META HTTP-EQUIV=\"REFRESH\" CONTENT=\"60\">\n");

#    print $main::index_file ("<META HTTP-EQUIV=\"REFRESH\" CONTENT=\"$main::config_info{up}[$main::cUPD_TIME]\">\n");

#    print $main::index_file ("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n");
    print $main::index_file ("</head>\n\n");

    print $main::index_file ("<body bgcolor=\"$main::cINFO_BG_COLOR\" link=\"$main::cKARMA_LINK_COLOR\" vlink=\"$main::cKARMA_LINK_COLOR\" alink=\"$main::cKARMA_LINK_COLOR\">\n");

    print $main::index_file ("<table width=\"100\%\" border=\"0\" height=\"67\%\" cellpadding=\"10\" cellspacing=\"0\" bordercolordark=\"$main::cBORD_COL_DARK\" bordercolor=\"$main::cBORDER_COLOR\" >\n");



    print $main::index_file ("<tr bgcolor=\"$main::cHEAD_BG_COLOR\">\n"); 

    print $main::index_file ("<th align=\"left\" height=\"75\" ><img src=\"images" . "$main::PATH_DELIM" . "logo.jpg\" ></th>\n");
    print $main::index_file ("<th width=\"61\%\" height=\"50\"><font color=\"#FF9933\" face=\"Arial, Helvetica, sans-serif\" size=\"6\">Oracle Monitor</font></th>\n");
    print $main::index_file ("<th width=\"11\%\" height=\"50\" align=right><img src=\"images" . "$main::PATH_DELIM" . "sm_logo.jpg\"></th>\n");

    print $main::index_file ("</tr>\n");

#    print $main::index_file ("<tr bgcolor=\"$main::cBODY_BG_COLOR\" valign=\"middle\" align=\"center\">\n"); 
    print $main::index_file ("<tr bgcolor=\"$main::cINFO_BG_COLOR\" valign=\"middle\" align=\"center\">\n"); 
    print $main::index_file ("<td colspan=\"3\" height=\"250\">\n"); 

}

#-----------------------------------------------------------------------
#
# prints the footer of the main page
#
#-----------------------------------------------------------------------
sub showKarmaFootMain () {
#    my ($main::index_file) = @_;

#    print $main::index_file ("</TABLE>\n");
#    print $main::index_file ("</CENTER>\n");
#    print $main::index_file ("</TABLE>\n");
#    print $main::index_file ("</BODY></HTML>\n");


    my $timeStr = getCurrTimeString (undef);
    print $main::index_file ("</td>\n");
    print $main::index_file ("</tr>\n");
    print $main::index_file ("<tr bgcolor=\"$main::cINFO_BG_COLOR\">\n"); 
    print $main::index_file ("<td colspan=\"2\" height=\"2\" bgcolor=\"$main::cHEAD_BG_COLOR\" colspan=\"2\"><font color=\"$main::cTEXT_COLOR\" face=\"Arial, Helvetica, sans-serif\" size=\"5\">Last \n");
    print $main::index_file ("updated <font color=\"$main::cEMPHASIS_TEXT\">$timeStr</font></font></td>\n");

    print $main::index_file ("<td bgcolor=\"$main::cHEAD_BG_COLOR\" cellpadding=\"0\" cellspacing=\"0\" align=\"right\"><a href=\"help.html\"><img border=0 src=\"images" . "$main::PATH_DELIM" . "help.jpg\"></a></td>\n");

    print $main::index_file ("</tr>\n");
    print $main::index_file ("</table>\n");
    print $main::index_file ("</body>\n");
    print $main::index_file ("</html>\n");

}


#-----------------------------------------------------------------------
#
# prints the footer of an info page
#
#-----------------------------------------------------------------------
sub showInfoFoot ($$) {
    my ($inType, $inTNS) = @_;

#    print $main::info_file ("<BR><A HREF=\"..$main::PATH_DELIM" . 'help' . "$main::PATH_DELIM$main::files{help}{$inType}\"><IMG SRC=\".." . "$main::PATH_DELIM" . 'images' . "$main::PATH_DELIM" . "question_symbol\"></A>\n");

    #
    # bottom of the page
    #
#    print $main::info_file ("</BODY>\n");
#    print $main::info_file ("</HTML>\n");

    my $timeStr = getCurrTimeString (undef);

    print $main::info_file ("</td>\n");
    print $main::info_file ("</tr>\n");
    print $main::info_file ("<tr bgcolor=\"$main::cINFO_BG_COLOR\"> \n");
    print $main::info_file ("<td height=\"$main::cHEAD_HEIGHT\" bgcolor=\"$main::cHEAD_BG_COLOR\"\n");
    print $main::info_file ("width=\"178\%\" colspan=\"2\">\n");
    print $main::info_file ("<font color=\"$main::cTEXT_COLOR\" face=\"Arial, Helvetica, sans-serif\" size=\"5\">Info for\n");
    print $main::info_file ("<font color=\"$main::cEMPHASIS_TEXT\">\n");
    print $main::info_file ("$inTNS\n");
    print $main::info_file ("</font> Database last updated <font color=\"$main::cEMPHASIS_TEXT\">\n");
    print $main::info_file ("$timeStr</font></font></td>\n");
    print $main::info_file ("<td height=\"$main::cHEAD_HEIGHT\" bgcolor=\"$main::cHEAD_BG_COLOR\" width=\"$main::cHEAD_HEIGHT\">\n");
#    print $main::info_file ("<a href=\"generic_help.html\" target=\"_self\">\n");
    print $main::info_file ("<a href=\"..$main::PATH_DELIM" . 'help' . "$main::PATH_DELIM$main::files{help}{$inType}\" target=\"_self\">\n");
    print $main::info_file ("<img src=\"..$main::PATH_DELIM" . 'images' . "$main::PATH_DELIM" . "help.jpg\" width=\"$main::cHEAD_HEIGHT\" height=\"59\" align=\"right\" border=\"0\"></a></td>\n");
    print $main::info_file ("</tr>\n");
    print $main::info_file ("</table>\n");
    print $main::info_file ("</body>\n");
    print $main::info_file ("</html>\n");


}


#-----------------------------------------------------------------------
#
# prints the header of an info page
#
#-----------------------------------------------------------------------
sub showInfoHead ($$) {
    my ($inTNS, $inType) = @_;


    my $timeStr = getCurrTimeString (undef);
    #
    # old header stuff
    #


    print $main::info_file ("<html>\n");
    print $main::info_file ("<head>\n");
    print $main::info_file ("<title>$inTNS - $main::names{long}{$inType} Info</title>\n");
    print $main::info_file ("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n");
    print $main::info_file ("</head>\n");
    
    print $main::info_file ("<body bgcolor=\"$main::cINFO_BG_COLOR\">\n");
    
    print $main::info_file ("<table width=\"100\%\" border=\"0\" height=\"300\" cellpadding=\"5\" cellspacing=\"0\" bordercolordark=\"$main::cBORD_COL_DARK\" bordercolor=\"$main::cBORDER_COLOR\" >\n");



    print $main::info_file ("<tr bgcolor=\"$main::cHEAD_BG_COLOR\">\n"); 
    print $main::info_file ("<th align=\"left\" height=\"$main::cHEAD_HEIGHT\"><font color=\"$main::cTEXT_COLOR\" face=\"Arial, Helvetica, sans-serif\" size=\"7\"><img src=\"..$main::PATH_DELIM" . 'images' . "$main::PATH_DELIM" . "logo.jpg\" width=\"144\" height=\"36\">\n"); 
    
    print $main::info_file ("</font></th>\n");
    print $main::info_file ("<th height=\"$main::cHEAD_HEIGHT\"><font color=\"$main::cTEXT_COLOR\" face=\"Arial, Helvetica, sans-serif\" size=\"6\">\n");
    print $main::info_file ("$main::names{long}{$inType} Info\n");
    print $main::info_file ("</font></th>\n");
    print $main::info_file ("<th height=\"$main::cHEAD_HEIGHT\"><img src=\"../images/info.jpg\"\n");
    print $main::info_file ("width=\"$main::cHEAD_HEIGHT\" height=\"$main::cHEAD_HEIGHT\" align=\"right\"></th>\n");
    print $main::info_file ("</tr>\n");
    
#    print $main::info_file ("<tr bgcolor=\"$main::cBODY_BG_COLOR\">\n"); 
    print $main::info_file ("<tr bgcolor=\"$main::cINFO_BG_COLOR\">\n"); 

    print $main::info_file ("<td colspan=\"3\" height=\"150\" width=\"0\">\n"); 

}


#-----------------------------------------------------------------------
#
# sendEmail - Thanks to Dennis Taylor <dennis@funkplanet.com> for this code...
#
# mail notification, before calling this, check for Mail::Send;
#
# sendEmail( 'zot@foo.com', <<NEEN );
# Your database escaped from the institution and burned your house down.
# NEEN
#
#-----------------------------------------------------------------------
sub sendEmail ($$@) {
    my ($inRecipient, $inSubject, @inMessage) = @_;

    #
    # required only if you use this routine...
    # 
    require Mail::Send;
    my $msg = Mail::Send->new( To => $inRecipient,
                               Subject => $inSubject );
    my $fh = $msg->open( 'sendmail' );
    print $fh @inMessage;
    $fh->close;
}


#-----------------------------------------------------------------------
#
# normal kill signal handler
#
#-----------------------------------------------------------------------
sub catchTERM {
    logMessageWTime ("Received TERM signal, exiting karmad\n");
    exitKarma ();
}

#-----------------------------------------------------------------------
#
# HUP signal means reread config file
#
#-----------------------------------------------------------------------
sub catchHUP {
#    logMessage ("kill -HUP, rereading karma.conf file\n");
    logMessageWTime ("Rereading config file.\n");
    my $key = undef;

    # 
    # clean up the database handle structures
    #
    foreach $key (keys %main::db_info) {
	logMessage ("cleaning up DB:$key\n");
	if (defined ($main::db_info{$key}[$main::cDB_HANDLE])) {
	    $main::db_info{$key}[$main::cDB_HANDLE]->disconnect;
	    undef $main::db_info{$key};
	}
    }

    #
    # clean up the config info data structures
    #
    foreach $key (keys %main::config_info) {
	logMessage ("cleaning up $key\n");
	if (not ($key =~ /^factory$/)) {
	    undef %{$main::config_info{$key}};
	}
    }
    
    #
    # clean up the config notify data structures
    #
    foreach $key (keys %main::config_notify) {
	undef %{$main::config_notify{$key}};
    }

    #
    # clean up pref groups hash
    #
    foreach $key (keys %main::pref_groups) {
	undef $main::pref_groups{$key};
    }

    #
    # assume no columns are shown again
    #
    $main::names{shown}{redolog}       = 0;
    $main::names{shown}{rollback}      = 0;
    $main::names{shown}{slowsql}       = 0;
    $main::names{shown}{alertlog}      = 0;
    $main::names{shown}{hitratios}     = 0;
    $main::names{shown}{extents}       = 0;
    $main::names{shown}{latch}         = 0;
    $main::names{shown}{fragmentation} = 0;
    $main::names{shown}{mts}           = 0;
    $main::names{shown}{tablespace}    = 0;
    $main::names{shown}{os}            = 0;
    $main::names{shown}{repqueue}      = 0;
    $main::names{shown}{reperror}      = 0;

    readConfig ($main::CONF_FILE_NAME);
    $main::currTime = time ();

    #
    # do all checking as if karma just started
    #
    doDBChecks (1);
    

}


#-----------------------------------------------------------------------
#
# USR1 signal, return status via named pipe (fifo)
#
# status includes 
#   alert_notify services
#   warn_notify services
#   dbname: up/down
#
#-----------------------------------------------------------------------
sub showStatus {
#    logMessage ("kill -USR1, return status via named pipe\n");

    my $dbStatus = "";
    my $startTimeStr = getTimeString ($main::startTime);
    my $currTimeStr = getCurrTimeString (undef);
    my $timeStr = "";
    my $statusStr = "";
    my $thePrefGroup = "";
    my $servKey = "";
    my $tnsKey = "";

    open (FIFO, ">$main::KARMA_FIFO_NAME");
    print FIFO ("karmad started at $startTimeStr, pid:$main::KARMA_PID\n");
    if ($main::USE_EMAIL_NOTIFICATION == 1) {
	print FIFO ("Using EMAIL for notification.\n");
    } else {
	print FIFO ("Using LOGFILE for notification.\n");
    }
    foreach $tnsKey (keys %main::db_info) {

	$thePrefGroup = getPrefGroup ($tnsKey);
#	$thePrefGroup = $main::db_info{$tnsKey}[$main::cDB_PREFGROUP];
	$dbStatus = "DOWN";
	if (defined $main::db_info{$tnsKey}[$main::cDB_HANDLE]) {
	    $dbStatus = "UP";
	}
	print FIFO ("  DB:$tnsKey $dbStatus, Prefgroup:$thePrefGroup - services:\n");

#	foreach $servKey (keys %{$main::config_info{$thePrefGroup}}) {
	foreach $servKey (keys %{$main::names{long}}) {
#	    if (shouldShowService ($thePrefGroup, $servKey)) {
	    if ($main::names{shown}{$servKey} == 1) {
		# not correct.  getCurrTimeString doesn't take a time param

#		$timeStr = getCurrTimeString ($main::stats{$servKey}{$tnsKey}[$main::cUPD_TIME]);
		$timeStr = getTimeString ($main::stats{$servKey}{$tnsKey}[$main::cUPD_TIME]);
		$statusStr = getStatusStr ($main::stats{$servKey}{$tnsKey}[$main::cSTATUS]);
		print FIFO ("    $timeStr $statusStr\t$servKey\n");

		
	    }
	}

    }

    close (FIFO);
}

#-----------------------------------------------------------------------
#
# USR2 signal, refresh each services status
#
#
#-----------------------------------------------------------------------
sub refreshServices {
	$main::currTime = time ();
	logMessageWTime ("Refreshing service statuses for all dbs.\n");
	doDBChecks (1);
}


#-----------------------------------------------------------------------
#
# put all the clean exit stuff in here.
#
#-----------------------------------------------------------------------
sub exitKarma () {

    logMessageWTime ("Exiting karma.\n");

    $main::logfile->close;

    #
    # implicit cleanup is ok I think...
    #
    exit 1;
}



#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub doNotification ($$) {
    my ($inTNS, $inMinutes) = @_;
#    my @emails = undef;
    my @alertServices = undef;
    my @warnServices = undef;
    my $prefGroup = getPrefGroup ($inTNS);
    my $currStatus = $main::cNO_STATUS;
    my $i = 0;
    my $servCount = 0;
    my $send_msg = 0;
    my $notifyMinutes = 0;
    my $notifySize = 0;
    my @notifyServices = undef;
    my $doNotify = 0;

    debugMessage ("doNotification...\n", 2);

#    @emails = getNotifyEmails ($prefGroup, $);
    $doNotify = shouldNotify ($prefGroup);

    if ($doNotify == 1) {
	@alertServices = getNotifyServices ($prefGroup, $main::cNOTIFY_ALRT);

	if (defined (@alertServices)) {
#	    $notifySize = getNotifySize ($prefGroup, $main::cNOTIFY_ALRT);
	    $notifySize = getEmailSize ($prefGroup);
	    $notifyMinutes = 
		getNotifyMinutes ($prefGroup, $main::cNOTIFY_WARN);
	    $servCount = scalar @alertServices;
	    for ($i = 0; $i < $servCount; $i++) {
		$currStatus = getCurrStatus ($alertServices[$i], $inTNS);
		if ((((defined ($inMinutes)) && ($inMinutes == 0)) ||
		     ((defined ($notifyMinutes)) &&
		      ($notifyMinutes > 0) && 
		      ($inMinutes % $notifyMinutes == 0))) &&
		    ($currStatus == $main::cALERT_STATUS)) {
		    $send_msg = 1;
		    push (@notifyServices, $alertServices[$i]);
		}
	    }
	} else {
	    # should log message here
	}
	if ($send_msg == 1) {
	    sendNotification ($main::cNOTIFY_ALRT,
			       $inTNS,
			       @notifyServices);
	} else {
	    $send_msg = 0;
	    $notifySize = "full";
	    @warnServices = 
		getNotifyServices ($prefGroup, $main::cNOTIFY_WARN);

	    if (defined (@alertServices)) {
#		$notifySize = getNotifySize ($prefGroup, $main::cNOTIFY_WARN);
		$notifySize = getEmailSize ($prefGroup);
		$servCount = scalar @warnServices;
		for ($i = 0; $i < $servCount; $i++) {
		    $currStatus = getCurrStatus ($alertServices[$i], $inTNS);
		    if ((($inMinutes == 0) ||
			 ((defined ($notifyMinutes)) &&
			  ($notifyMinutes > 0) &&
			  ($inMinutes % $notifyMinutes == 0))) &&
			($currStatus == $main::cALERT_STATUS)) {
			$send_msg = 1;
			push (@notifyServices, $alertServices[$i]);
		    }
		}
	    } else {
		# should log message here
	    }
	    if ($send_msg == 1) {
		sendNotification ($main::cNOTIFY_WARN,
				   $inTNS,
				   @notifyServices);
	    }
	}

    }
}


#-----------------------------------------------------------------------
#
# sends email notification
# 
# one email gets sent out per database per email address...
#
# Want to add a check here to see if Mail::Send is installed, if
# not, log a message to the logfile, and return...
#
# Also, still need to add interval checking... don't mail unless the 
# specified interval has passed
#
#-----------------------------------------------------------------------
sub doNotification_old ($$) {
    my ($inTNS, $inMinutes) = @_;
    my $send_alert = 0;
    my $send_warn = 0;
    my $pref_group = getPrefGroup ($inTNS);
#    my $pref_group = $main::db_info{$inTNS}[$main::cDB_PREFGROUP];

    if (defined ($main::config_notify{$pref_group}{$main::cNOTIFY_ALRT}[0])) {

	#
	# send alert notification
	#
	
	my @alertServices = @{$main::config_notify{$pref_group}{$main::cNOTIFY_ALRT}[2]};
	my $alertSize = $main::config_notify{$pref_group}{$main::cNOTIFY_ALRT}[1];
	my @theServices = ();
	
	my $sent_message = 0;
	my $count = 0;
	my $i = 0;

	if (defined (@alertServices)) {
	    $count = scalar @alertServices;
	    for ($i = 0; $i < $count; $i++) {
		

#	print ("do_notify: P:$pref_group M:$inMinutes A:$main::config_notify{$pref_group}{$main::cNOTIFY_ALRT}[0]\n");
		if ((defined ($main::stats{$alertServices[$i]}{$inTNS}[$main::cSTATUS])) && 
		    (($inMinutes == 0) || ($inMinutes % $main::config_notify{$pref_group}{$main::cNOTIFY_ALRT}[0] == 0)) &&
		    ($main::stats{$alertServices[$i]}{$inTNS}[$main::cSTATUS] == $main::cALERT_STATUS)) {
		    $send_alert = 1;
		    push (@theServices, $alertServices[$i]);
		}
	    }
	    
	} else {
	    # should log message here
	}
	if ($send_alert == 1) {
#	    sendNotification ($main::cNOTIFY_ALRT, $alertSize, $inTNS, @theServices);
	    sendNotification ($main::cNOTIFY_ALRT, $inTNS, @theServices);
	}
    } 

    if (($send_alert == 0) && (defined ($main::config_notify{$pref_group}{$main::cNOTIFY_WARN}[0]))) {

	my @warnServices = @{$main::config_notify{$pref_group}{$main::cNOTIFY_WARN}[2]};
	my $warnSize = $main::config_notify{$pref_group}{$main::cNOTIFY_WARN}[1];
	my @theServices = ();
	my $count = 0;	    
	my $i = 0;

	if (defined (@warnServices)) {
	    $count = scalar @warnServices;
	    for ($i = 0; $i < $count; $i++) {
		
#		print ("do_notify: P:$pref_group M:$inMinutes W:$main::config_notify{$pref_group}{$main::cNOTIFY_WARN}[0]\n");
		if ((defined ($main::stats{$warnServices[$i]}{$inTNS}[$main::cSTATUS])) && 
		    (($inMinutes == 0) || ($inMinutes % $main::config_notify{$pref_group}{$main::cNOTIFY_WARN}[0] == 0)) &&
		    ($main::stats{$warnServices[$i]}{$inTNS}[$main::cSTATUS] == $main::cWARNING_STATUS)) {
		    $send_warn = 1;
		    push (@theServices, $warnServices[$i]);
		}
	    }
	    
	    if ($send_warn == 1) {
#		sendNotification ($main::cNOTIFY_WARN, $warnSize, $inTNS, @theServices);
		sendNotification ($main::cNOTIFY_WARN, $inTNS, @theServices);
	    }
	} else {
	    # should log a message here...
	}

    }
}

#-----------------------------------------------------------------------
#
# construct email message (full or short), and email it
# out to all the right people
#
#-----------------------------------------------------------------------
sub sendNotification ($$@) {
    my ($inType, $inTNS, @inServices) = @_;
    my $pref_group = getPrefGroup ($inTNS);
#    my $pref_group = $main::db_info{$inTNS}[$main::cDB_PREFGROUP];
    my $subject = "";
    my $email_count = 0;
    my @emails = undef;
    my $keySize = undef;

    foreach $keySize (keys %{$main::config_email{$pref_group}}) {

	@emails = getNotifyEmails ($pref_group, $keySize);

	#
	# make sure there are email addresses defined
	# if not, we should at least log a message that they've enabled
	# notification without specifying who to email messages to
	#
	if (defined @{$main::config_email{$pref_group}}) {
#	    $email_count = scalar @{$main::config_email{$pref_group}};
	    $email_count = scalar @emails
	}
	
	if ($email_count > 0) {
	    my $count = 0;
	    my $message = "";
	    my $i = 0;
	    
	    $message = "$inTNS:";
	    if ($inType =~ /^$main::cNOTIFY_WARN$/i) {
		$message .= "WARN:";
	    } else {
		$message .= "ALRT:";
	    }
	    $count = scalar @inServices;
	    for ($i = 0; $i < $count; $i++) {
		$message .= "$inServices[$i],";
	    }
	    
	    if (not ($keySize =~ /^$main::cSHORT_EMAIL$/i)) {
		
		$subject = $message;
		$message = "$inTNS database ";
		if ($inType =~ /^$main::cNOTIFY_WARN$/i) {
		    $message .= "WARNING";
		} else {
		    $message .= "**ALERT**";
		}
		
		$message .= " - The following services have problems:\n";
		$count = scalar @inServices;
		for ($i = 0; $i < $count; $i++) {
		    $message .= "\t$inServices[$i]\n";
		}
	    }
	    
	
	    #
	    # iterate on each email address in config_email hash of arrays
	    #
	    #show_config_email ();
	    my $email = "";
	    for ($i = 0; $i < $email_count; $i++ ) {
		$email = $emails[$i];
		if ($main::USE_EMAIL_NOTIFICATION == 1) {
		    sendEmail ($email, $subject, $message);
		}
		logMessage ("EMAILING $email - $message\n");
	    }
	}
    }
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub setConfigNotify ($$$$) {
    my ($inPrefGroup, $inType, $inInterval, $inServices) = @_;

#    my @services = split (",", $inServices);
    my @services = getServices ($inPrefGroup, $inServices);

    # current size of the array
#    my $i = scalar @{$main::config_notify{$inPrefGroup}{$inType}};

    #
    # array starts from zero sooo...
    #

#    push (@{$main::config_notify{$inPrefGroup}{$inType}}, [($inInterval, $inEmail, [@services])]);

    #
    # if it's a duplicate, replaces existing one... last
    # entry in config file wins
    #
    $main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_INT] = 
	$inInterval;
#    $main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_SIZE] = 
#	$inSize;
    $main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_SERV] =
	[@services];
   
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub show_notify_config () {

    my $array_len = 0;
    my $interval = 0;
    my $email = 0;
    my $size = 0;
    my @services = ();
    my $k = 0;
    my $j = 0;
    my $i = 0;
    my $pref_key = undef;
    my $type_key = undef;

    foreach $pref_key (keys %main::config_notify) {

	foreach $type_key (keys %{$main::config_notify{$pref_key}}) {
	    
	    $array_len = scalar @{$main::config_notify{$pref_key}{$type_key}};
#	    for ($i = 0; $i < $array_len; $i++ ) {
		
	    $interval = $main::config_notify{$pref_key}{$type_key}[0];
	    $size = $main::config_notify{$pref_key}{$type_key}[1];
#	    $email = $main::config_notify{$pref_key}{$type_key}[2];
	    @services = @{$main::config_notify{$pref_key}{$type_key}[2]};
	    print ("GP:$pref_key, TY:$type_key, SZ:$size IN:$interval EM:$email SERV:");
	    $j = scalar @services;
	    for ($k = 0; $k < $j; $k++) {
		print "$services[$k]|"
	    }
	    print ("\n");

#	    }
	}
    }
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub checkService ($$) {
    my ($inPrefGroup, $inService, $inTNS) = @_;

}

#-----------------------------------------------------------------------
#
# test routine to print the stats hash
#
#-----------------------------------------------------------------------
sub show_stats () {
#    my ($inStats) = @_;

    my $key = undef;
    foreach $key (keys %main::config_info) {
	print ("$key:$main::stats->{$key}, ");
    }
    print ("\n");

}

#-----------------------------------------------------------------------
#
# test routine to print out the pref groups hash
#
#-----------------------------------------------------------------------
sub show_pref_groups () {
 
    print ("show_pref_groups:");
    my $key = undef;
    foreach $key (keys %main::pref_groups) {
	print ("\"$key,\"");
    }
    print ("\n");
}

#-----------------------------------------------------------------------
#
# test routine to print the config_email hash
#
#-----------------------------------------------------------------------
sub show_config_email () {

    my $count = 0;
    my $i = 0;

    my $key = undef;
    my $sizeKey = undef;
    foreach $key (keys %main::config_email) {

	foreach $sizeKey (keys %{$main::config_email{$key}}) {
	    print ("GROUP: $key SIZE:$sizeKey\n");
	    $count = scalar @{$main::config_email{$key}{$sizeKey}};
	    for ($i = 0; $i < $count ; $i++) {
		print ("\t$main::config_email{$key}{$sizeKey}[$i]\n");
	    }
	}
    }
}

#-----------------------------------------------------------------------
#
# no config file was specified, but DBI_DSN, DBI_USER, and DBI_PASS 
# are set, so we'll try to connect to that db, and use all defaults
#
#-----------------------------------------------------------------------
sub setDefConfig () {

    my $theUser = $ENV{DBI_USER};
    my $thePass = $ENV{DBI_PASS};
    %main::pref_groups = [];
    my $theTNS = $ENV{DBI_DSN};
    $theTNS =~ s/.*://;

    logMessage ("Using default configuration, USER:$ENV{DBI_USER}\n");
    logMessage ("PASS:$ENV{DBI_PASS}, TNS:$theTNS\n");
    debugMessage ("setDefConfig: TNS:$theTNS\n", 1);

    # connect to the db
    $main::db_info{$theTNS}[$main::cDB_HANDLE] = DBI->connect 
	("dbi:Oracle:$theTNS", 
	 $theUser,
	 $thePass,
	 {RaiseError => 0,
	  PrintError => 0});

    # username
    $main::db_info{$theTNS}[$main::cDB_USER] = $theUser;
    
    # password
    $main::db_info{$theTNS}[$main::cDB_PASS] = $thePass;
    
    # karma refresh
    $main::db_info{$theTNS}[$main::cDB_REFRESH] = 1;

    # pref group
#    $main::db_info{$theTNS}[$main::cDB_PREFGROUP] = $main::cDEF_GRP_NAME;
    setPrefGroup ($theTNS, $main::cDEF_GRP_NAME);

    $main::pref_groups{$main::cDEF_GRP_NAME}[0] = $theTNS;


    #
    # log any errors
    #
    if (defined ($DBI::errstr)) {
	logMessageWTime ("$DBI::errstr\n");
    }

    setConfig ("default","up", undef, undef, undef);
    setConfig ("default","redolog", undef, undef, undef);
    setConfig ("default","rollback", undef, undef, undef);
    setConfig ("default","slowsql", undef, undef, undef);
    setConfig ("default","hitratios", undef, undef, undef);
    setConfig ("default","extents", undef, undef, undef);
    setConfig ("default","latch", undef, undef, undef);
    setConfig ("default","fragmentation", undef, undef, undef);
    setConfig ("default","tablespace", undef, undef, undef);

}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub shouldUpdateService ($$$) {
    my ($inPrefGroup, $inService, $inTime) = @_;


    debugMessage ("shouldUpdateService: $inPrefGroup, $inService, $inTime\n",2);
    #
    # using pref group prefs, if defined
    #
    if ((defined ($main::config_info{$inPrefGroup}{$inService}[$main::cUPD_TIME])) && 
	(($inTime == 0) ||
	 ($inTime % $main::config_info{$inPrefGroup}{$inService}[$main::cSTATUS] == 0))) {
	debugMessage ("Will update S:$inService T:$inTime I:$main::config_info{$inPrefGroup}{$inService}[$main::cSTATUS]\n", 2);
	return 1;
    }

    return 0;
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getServiceWarn ($$) {
    my ($inPrefGroup, $inService, $inTime) = @_;

    #
    # using pref group prefs, if defined
    #
    if (defined ($main::config_info{$inPrefGroup}{$inService}[$main::cUPD_TIME])) {
	return $main::config_info{$inPrefGroup}{$inService}[$main::cUPD_TIME];
    #
    # using default prefs
    #
    } else {
	return $main::config_info{default}{$inService}[$main::cUPD_TIME];
    }

}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getServiceAlert ($$) {
    my ($inPrefGroup, $inService) = @_;

    #
    # using pref group prefs, if defined
    #
    if (defined ($main::config_info{$inPrefGroup}{$inService}[2])) {
	return $main::config_info{$inPrefGroup}{$inService}[2];
    #
    # using default prefs
    #
    } else {
	return $main::config_info{default}{$inService}[2];
    }
}

#-----------------------------------------------------------------------
#
# call the appropriate status routine
#
#-----------------------------------------------------------------------
sub getStatus ($$$) {
    my ($inPrefGroup, $inService, $inTNS) = @_;
    
    my $serviceWarn = getServiceWarn ($inPrefGroup, $inService);
    my $serviceAlert = getServiceAlert ($inPrefGroup, $inService);
    
    my $theStatus = $main::cNO_STATUS;
    
    if ($inService =~ /^redolog$/) {
	$theStatus = getRedologStatus ($serviceWarn, 
				       $serviceAlert, $inTNS);
    } elsif ($inService =~ /^rollback$/) {
	$theStatus = getRollbackStatus ($serviceWarn, 
					$serviceAlert, $inTNS);
    } elsif ($inService =~ /^fragmentation$/) {
	$theStatus = getFragmentationStatus ($serviceWarn,
					     $serviceAlert, $inTNS);
    } elsif ($inService =~ /^extents$/) {
	$theStatus = getExtentsStatus ($serviceWarn,
				       $serviceAlert, $inTNS);
    } elsif ($inService =~ /^slowsql$/) {
	$theStatus = getSlowsqlStatus ($serviceWarn,
				       $serviceAlert, $inTNS);
    } elsif ($inService =~ /^os$/) {
	$theStatus = getOSStatus ($serviceWarn,
				       $serviceAlert, $inTNS);
    } elsif ($inService =~ /^alertlog$/) {
	$theStatus = getAlertlogStatus ($serviceWarn,
					$serviceAlert, $inTNS);
    } elsif ($inService =~ /^mts$/) {
	$theStatus = getMTSStatus ($serviceWarn,
				   $serviceAlert, $inTNS);
    } elsif ($inService =~ /^tablespace$/) {
	$theStatus = getTablespaceStatus ($serviceWarn,
					  $serviceAlert, $inTNS);
    } elsif ($inService =~ /^latch$/) {
	$theStatus = getLatchStatus ($serviceWarn,
				     $serviceAlert, $inTNS);
    } elsif ($inService =~ /^hitratios$/) {
	$theStatus = getHitratiosStatus ($serviceWarn,
					 $serviceAlert, $inTNS);
    } elsif ($inService =~ /^db$/) {
	$theStatus = getDbStatus ($inTNS);
    } elsif ($inService =~ /^up$/) {
	$theStatus = getUpStatus ($inTNS);
    } elsif ($inService =~ /^repqueue$/) {
	$theStatus = getRepqueueStatus ($serviceWarn,
                                        $serviceAlert, $inTNS);
    } elsif ($inService =~ /^reperror$/) {
	$theStatus = getReperrorStatus ($serviceWarn,
                                        $serviceAlert, $inTNS);
    }

    debugMessage ("getStatus:$inTNS, $inService, $theStatus\n",2);
    return $theStatus;

}



#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getRefresh ($) {
    my ($inTNS) = @_;
    my $thePrefGroup = getPrefGroup ($inTNS);
#    my $thePrefGroup = $main::db_info{$inTNS}[$main::cDB_PREFGROUP];
    
    return $main::config_info{$thePrefGroup}{up}[$main::cUPD_TIME];
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub shouldShowService ($$) {
    my ($inPrefGroup, $inService) = @_;

    return $main::names{shown}{$inService};
}

#    if ((defined ($main::config_info{$inPrefGroup}{$inService}[$main::cSTATUS])) && 
#	($main::config_info{$inPrefGroup}{$inService}[$main::cSTATUS] > 0)){
#	return 1;
#    }
#    return 0;
#}

#
# The header should show if this service is displayed for any of the
# monitored databases
#
sub shouldShowServiceHeader ($) {
    my ($inService) = @_;

    return $main::names{shown}{$inService};
}

#    my $retVal = 0;
#    my $thePrefGroup = undef;
#    my $tnsKey = undef;
#    foreach $tnsKey (keys %main::db_info) {
#	if (defined ($main::db_info{$tnsKey})) {
#	    $thePrefGroup = $main::db_info{$tnsKey}[$main::cDB_PREFGROUP];
#	}
#	if ((defined ($thePrefGroup)) && 
#	    (defined ($main::config_info{$thePrefGroup}{$inService})) &&
#	    (defined ($main::config_info{$thePrefGroup}{$inService}[$main::cSTATUS])) && 
#	    ($main::config_info{$thePrefGroup}{$inService}[$main::cSTATUS] > 0)){
#	    $retVal = 1;
#	}
#    }
#    return $retVal;
#}



#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub isValidTNS ($) {
    my ($inTNS) = @_;

    my $i = 0;
    my @tns_names = DBI->data_sources ("dbi:Oracle:");
    my $aTNS = "";
    my @source = ();

    while (defined ($tns_names[$i])) {
	@source = split (":", $tns_names[$i]);
	$aTNS = $source[2];
	if ($inTNS =~ /^$aTNS$/i) {
	    return 1;
	}
	$i++;
    }

    return 0;
}



#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub doDBChecks ($) {
    my ($forceUpdate) = @_;


    #
    # iterate on each database
    #
    my $dayMinutes = int (($main::currTime - $main::startTime) / 60);
    my $tnsKey = undef;

    foreach $tnsKey (keys %main::db_info) {
	getInfo ($forceUpdate, $tnsKey, $dayMinutes);

	#
	# handle notification here
	# 
#	doNotificationOld ($tnsKey, $dayMinutes);
	doNotification ($tnsKey, $dayMinutes);
    }

    debugMessage ("doDBChecks, generating index...\n", 2);
    
    #
    # need to open and close this file inside the doPage routine
    # and send the file handle as a param to the inside routines
    #  
    showIndexPage ($dayMinutes);
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub writePidFile () {

    my $pid_file = new IO::File ">$main::PID_FILE_NAME"
	or logMessageWTime ("Failed to write pid to file: $main::PID_FILE_NAME - $!\n");

    if (defined ($pid_file)) {
	print $pid_file ($$);
	$pid_file->close ();
    }

#    print $pid_file ($main::KARMA_PID);
#    logMessage ("Wrote to PID:$$ to file:$main::PID_FILE_NAME\n");
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getStatusStr ($) {
    my ($inStatus) = @_;

    my $retStr = "--";
    if ($inStatus == $main::cALERT_STATUS) {
	$retStr = "ALRT";
    } elsif ($inStatus == $main::cWARNING_STATUS) {
	$retStr = "WARN";
    } elsif ($inStatus == $main::cOK_STATUS) {
	$retStr = "OK";
    } elsif ($inStatus == $main::cNO_STATUS) {
	$retStr = "NO";
    }

    return $retStr;
}

#-----------------------------------------------------------------------
#
# given the services string from the config file found in a 
# notify_alert or notify_warning line, get the services which 
# require notification.  If there are none specified, or "*" is
# specified, all services for this group are monitored
#
#-----------------------------------------------------------------------
sub getServices ($$) {
    my ($inPrefGroup, $inServices) = @_;
    my @retServices = undef;
    my $key = undef;

    #
    # if not specified, or *, assume all services
    # use notification
    #
    if ((not defined ($inServices)) ||
	($inServices =~ /^\*.*$/)) {
	foreach $key (keys %{$main::config_info{$inPrefGroup}}) {
	    push (@retServices, $key);
	}
    } else {
	@retServices = split (",", $inServices);
    }

    return @retServices;
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getPrefGroup ($) {
    my ($inTNS) = @_;
    return $main::db_info{$inTNS}[$main::cDB_PREFGROUP];
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub setPrefGroup ($$) {
    my ($inTNS, $inGroup) = @_;
    $main::db_info{$inTNS}[$main::cDB_PREFGROUP] = $inGroup;
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getCurrStatus ($$) {
    my ($inService, $inTNS) = @_;
    if (defined $main::stats) {
	if (defined $main::stats{$inService}) {
	    if (defined @{$main::stats{$inService}{$inTNS}}) {
		return $main::stats{$inService}{$inTNS}[$main::cSTATUS];
	    }
	}
    } else {
	return $main::cNO_STATUS;
    }
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getEmailSize ($) {
    my ($inPrefGroup) = @_;
    return $main::cNOTIFY_MSG;
}

#sub getNotifySize ($$) {
#    my ($inPrefGroup, $inType) = @_;
#
#    if (defined (@{$main::config_notify{$inPrefGroup}{$inType}})) {
#	return $main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_SIZE];
#    } else {
#	return $main::cNOTIFY_MSG;
#    }
#}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getNotifyServices ($$) {
    my ($inPrefGroup, $inType) = @_;
    my $key = undef;

    if (defined (@{$main::config_notify{$inPrefGroup}{$inType}})) {
	return $main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_SERV];
    } else {
	my @services = undef;
	foreach $key (keys %{$main::names{long}}) {
	    push (@services, $key);
	}
	return @services;
    }
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getNotifyMinutes ($$) {
    my ($inPrefGroup, $inType) = @_;

    if (defined (@{$main::config_notify{$inPrefGroup}{$inType}})) {
	return $main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_INT];
    } else {
	return $main::cNOTIFY_WAKEUP;
    }
}


#-----------------------------------------------------------------------
#
# return the array of email addresses for a preference group
#
#-----------------------------------------------------------------------
sub getNotifyEmails ($$) {
    my ($inPrefGroup, $inSize) = @_;
    return $main::config_email{$inPrefGroup}{$inSize};
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub shouldNotify ($) {
    my ($inPrefGroup) = @_;
    my $count = 0;
    my $retNotify = 0;
    my $sizeKey = undef;
    
    foreach $sizeKey (keys %{$main::config_email{$inPrefGroup}}) {
	$count = scalar @{$main::config_email{$inPrefGroup}{$sizeKey}};
	if ($count > 0) {
	    $retNotify = 1;
	}
    }
    return $retNotify;
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub logMessageExit ($) {
    my ($message) = @_;

    logMessageWTime ($message);
    exitKarma ();
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub checkForDBs () {
    my $validTNS = 0;
    my $key = undef;
    foreach $key (keys %main::db_info) {
	if (defined $key) {
	    $validTNS = 1;
	    return;
	}
    }

    if ($validTNS == 0) {
	logMessageExit ("No valid databases found (check tnsnames.ora).\n");
    }
}


#
# use this to comment out sections of code
#


#sub getLogFileName () {
#    my $fileName = tryOption ($opt_l);
#    if (not (defined $fileName)) {
#	$fileName = "$main::KARMA_HOME$main::PATH_DELIM" . 'karma.log';
#    }
#    return $fileName;
#}

#sub tryOption ($) {
#    my ($inOption) = @_;
#    my $fileName = undef;
#    if (defined ($inOption)) {

#	if ($inOption =~ /^\/.*$/) {
#	    $fileName = $inOption;
#	} else {
#	    if ($inOption =~ /^\..*$/) {
#		$inOption =~ s/^\.//;
#	    }
#	    $fileName = $main::wd . $main::PATH_DELIM . $inOption;
#	}
#    }
#    return $fileName;
#}



#---------------------------------
#
# Plain Old Documentation (pod...)
#
#---------------------------------

=pod

=head1 NAME

Karma - karmad

=head1 DESCRIPTION

karmad is the main karma daemon.  It wakesup periodically to
check your database for various statistics, generating a new
karma.html each time.

=head1 SYNOPSIS

Generally you won't run karma by invoking karmad directly.  One
exception to this rule is if you want to turn on debugging with
C<-d>.  In most other cases, use karmactl to invoke this daemon.

=head1 NOTES

After you've edited your config file, invoke karmad for testing
and debugging as follows:

C<$ ./karmad -d 2 -c test.conf -l test.log>

Check test.log for details.

=cut

