#!/usr/bin/perl -w

eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
    if 0; # not running under some shell


#------------------------------------------------------------------
# 
# Karma Copyright (C) 2000  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.
#
#------------------------------------------------------------------

if (not (-e ($ENV{ORACLE_HOME}))) {
    print ("Please set ORACLE_HOME to a valid directory and try again.\n");
    print ("  ORACLE_HOME='$ENV{ORACLE_HOME}' - invalid directory\n");
    exit 1;
}
#$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 1;
    }
}

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 1;
    }
}

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 ();
}

$main::DEBUG_LEVEL = 0;
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::MAX_FRAG_OBJS = 100;

$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::PAGE_WAKEUP = $main::cWAKEUP;
$main::NOTIFY_WAKEUP = $main::cWAKEUP;

#
# default notify minutes
#
$main::NOTIFY_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 1;
#}

#@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";


#
# titles
#
@{$main::titles{redolog}}    = ('Level', 'Group#', 'Sequence#', 'Timestamp');
@{$main::titles{rollback}}   = ('Level', 'Name', 'Status', 'Gets', 'Waits',
				'Hitratio');
@{$main::titles{slowsql}}    = ('Level', 'Disk I/O', 'Query Text');
@{$main::titles{alertlog}}   = ('Level', 'Facility', 'Error', 'Time',
				'Error Text');
@{$main::titles{hitratios}}  = ('Level', 'Consistent Gets', 'DB Block Gets',
				'Physical Reads', 'Hit Ratio');
@{$main::titles{extents}}    = ('Level', 'Name', 'Owner', 'Current', 'Max');
@{$main::titles{latch}}      = ('Level', 'Name', 'Gets', 'Misses', 'Hitratio');
@{$main::titles{fragmentation}} = ('Level', 'Type', 'Name', 'Initial',
				'Next', '% Increase');
@{$main::titles{mts}}        = ('Level', 'Name', 'Busy', 'Idle', '% Busy');
@{$main::titles{tablespace}} = ('Level', 'Name', 'Total (K)', 'Used (K)',
				'% Used', 'Auto');
@{$main::titles{os}}         = ('Level', '1 Min', '5 Min', '15 Min',
			       '% Idle', 'Time');
@{$main::titles{up}}         = ('Level', 'Statistic', 'Value');
@{$main::titles{db}}         = ('Level', 'Parameter', 'Value');
@{$main::titles{repqueue}}   = ('Level', 'Def Tran ID', 'Order', 'Timestamp');
@{$main::titles{reperror}}   = ('Level', 'Def Tran ID', 'Origin',
				'Destination', 'Timestamp', 'Error');



#
# 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}       = [(30,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 30 minutes... 
# setting the thresholds is as yet undefined
#
$main::config_info{factory}{fragmentation} = [(30,0,0)];

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

# check every minute, if greater than 95% alert, if greater than 85%
# send warning
#
$main::config_info{factory}{tablespace}    = [(5,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 1 minutes, refresh
# html page (with tag) every 60 seconds by default
#
$main::config_info{factory}{up}            = [(1,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 doNotifyCheck ();
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)) &&
    ($main::opt_c =~ /^\//)) {

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

} elsif ((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\n",
	"exists and is readable, otherwise\n",
	"specify another file with -c. \n";
    exit 1;
}

#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) && 
    ($main::DEBUG_LEVEL == 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) {
#    print ("default config\n");
    setDefConfig ();
} else {
#    print ("read config\n");
    readConfig ($main::CONF_FILE_NAME);
}

#
# now that we've checked the config file, make sure DOC_ROOT is set
# otherwise we exit...
#
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 1;
}

#
# 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';

#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);

debugDataSources (2);


#
# 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::PAGE_WAKEUP) {
	    doDBChecks ($main::cNO_FORCE);
	    $lastTime = $main::currTime;
	    $diffTime = 0;
	}

	#
	# as weird as it may seem, notify does it's own
	# wakeup checking.  That's because it was originally
	# being called from inside doDBChecks, and I don't 
	# really have time to fix it now.  Suffice it to say
	# it won't do anything unless it's time... we hope. :-)
	#
	doNotifyCheck ();
	
	#
	# I *could* calculate the greatest common multiple
	# between the NOTIFY_WAKEUP, and PAGE_WAKEUP, but
	# for now I'm just going to wakeup every minute...
	# it's only a slight bit of overhead
	#
        #sleep ($main::PAGE_WAKEUP * 60 - $diffTime);
	sleep (60);
    }
}


#-----------------------------------------------------------------------
#
# 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 = undef;
    my $theMessage = undef;

    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.gif';
	} else {
	    $theImage = 'red_dia.jpg';
	}
	$theMessage = 'ALRT';
    } elsif ($theStatus == $main::cWARNING_STATUS) {
	if ($main::USE_BLINK_WARNING == 1) {
	    $theImage = 'blink_yellow_status.gif';
	} else {
	    $theImage = 'yellow_dia.jpg';
	}
	$theMessage = 'WARN';
    } elsif ($theStatus == $main::cNO_STATUS) {
	$theImage = 'purple_status.jpg';
	$theMessage = 'NR';
    } elsif ($theStatus == $main::cOK_STATUS) {
	$theImage = 'green_status.jpg';
	$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]} = @{$main::titles{repqueue}};

	    #("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]} = 
		@{$main::titles{reperror}};

		#("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]} = @{$main::titles{redolog}};
		#("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]} = @{$main::titles{rollback}};
	    #("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]} = @{$main::titles{latch}};
	    #("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]} = @{$main::titles{tablespace}};
	    #("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 ($autoextend =~ /^YES$/) {
		    debugMessage ("YES - $curr_row->[0]:$autoextend\n",2);
		    $currStatus = $main::cOK_STATUS;
		} else {
		    debugMessage ("NO - $curr_row->[0]:$autoextend\n",2);
		    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::cOK_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]} = @{$main::titles{slowsql}};
	    #("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]} = @{$main::titles{alertlog}};
	    #("Level",
	    #"Facility",
	    #"Error",
	    #"Time",
	    #"Error Text");
	
	$alertlogRowCount = 1;
	
	if (defined ($row_ref->[0])) {
	    
	    if ($sth) {
		$sth->finish;
	    }


	    $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->
		prepare ('SELECT to_char(timestamp, \'J\'), to_char(timestamp, \'SSSSS\'), frequency FROM karma_agent');
	    $rv = $sth->execute;
	    $row_ref = $sth->fetchrow_arrayref;
	    my $notRunning = undef;
	    my $alert_days = undef;
	    my $alert_seconds = undef;
	    my $alert_frequency = undef;
	    if (defined $row_ref) {
		$alert_days = $row_ref->[0];
		$alert_seconds = $row_ref->[1] + ($alert_days * 86400);
		$alert_frequency = $row_ref->[2] * 60;
	    }
	    $sth = $main::db_info{$inTNS}[$main::cDB_HANDLE]->
		prepare ('SELECT to_char(sysdate, \'J\'), to_char(sysdate, \'SSSSS\') FROM dual');
	    $rv = $sth->execute;
	    $row_ref = $sth->fetchrow_arrayref;
	    my $curr_days = undef;
	    my $curr_seconds = undef;
	    if (defined $row_ref) {
		$curr_days = $row_ref->[0];
		$curr_seconds = $row_ref->[1] + ($curr_days * 86400);
	    }

	    if (($curr_seconds - $alert_frequency) > $alert_seconds) {
		$notRunning = 1;
	    }

	    if (defined $notRunning) {

		@{$alertlogTableRef->[$alertlogRowCount]} = 
		    ($main::cALERT_STATUS, 
		     'KAR',
		     $alert_frequency,
		     $alert_seconds,
		     'karmagentd does not seem to be running.');

	    } else {
	    
#	$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;
		}
	    }
	} else {
	    @{$alertlogTableRef->[$alertlogRowCount]} = 
		($main::cNO_STATUS, 
		 'KAR',
		 '0',
		 '0',
		 'karma_alertlog_errors table not created.');
	}
    }

    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]} = @{$main::titles{hitratios}};
	#("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]} = @{$main::titles{fragmentation}};
	    #("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;
	    my $j = 0;
	    while (($array_ref->[0]) && ($j < $main::MAX_FRAG_OBJS)) {
		
		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;
		}
		
		if (($currStatus == $main::cWARNING_STATUS) ||
		    ($currStatus == $main::cALERT_STATUS)) {
		    @{$fragmentationTableRef->[$fragmentationRowCount]} = 
			($currStatus, 
			 $array_ref->[2],
			 "$array_ref->[0].$array_ref->[1]",
			 $array_ref->[4],
			 $array_ref->[5],
			 $array_ref->[6]);
		    
		    $fragmentationRowCount++;
		    $j++;

		}
		$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]} = @{$main::titles{os}};
	#("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]} = @{$main::titles{mts}};
	#("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]} = @{$main::titles{up}};
	    #("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]} = @{$main::titles{db}};
	    #("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]} = @{$main::titles{extents}};
    #("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;
	my $num_extents = $extents_row->[2];
	my $max_extents = $extents_row->[1];

	while (defined ($extents_row->[0])) {
	    
	    #
	    # looks like we're at alert status...
	    #
	    if ($num_extents >=
		($max_extents - $extents_threshold_alert)) {
		$currStatus = $main::cALERT_STATUS;
		
		#
		# warn status... don't set if we're already at main::cALERT_STATUS
		#
	    } elsif ($num_extents >= 
		     ($max_extents - $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],
		     $num_extents,
		     $max_extents);
		
		
		$extentsRowCount++;
	    }
	    $extents_row = $sth->fetchrow_arrayref;
	}
	
	if ($sth) {
	    $sth->finish;
	}
    }

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

}


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

#    print ("What the hello - - - \n");

    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$/)) {
		if (defined (@{$main::config_info{$thePrefGroup}{$key}})) {
		    $main::stats{$key}{$inTNS}[$main::cSTATUS] =
			$main::cNO_STATUS;
		} else {
		    $main::stats{$key}{$inTNS}[$main::cSTATUS] =
			$main::cNO_SHOW;
		}
	    }
	}
    }

    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 0;

}


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

    $main::PAGE_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;
    my $j = 0;
    while (!$eofile) {
	
#	print ("reading line $j\n");
	$j++;
	$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)) {
		#my @myarray = $curr_array[2];
		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] =~ /^false$/i) || 
		    ($curr_array[1] =~ /^no$/i) ||
		    ($curr_array[1] =~ /^0$/i)) {
		    $main::USE_BLINK_WARNING=0;		    
		}
	    } elsif ($curr_array[0] =~ /^alert_blink$/i) {
		if (($curr_array[1] =~ /^false$/i) || 
		    ($curr_array[1] =~ /^no$/i) ||
		    ($curr_array[1] =~ /^0$/i)) {
		    $main::USE_BLINK_ALERT=0;
		}
	    } 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;
		}

	    } elsif ($curr_array[0] =~ /^doc_root$/i) {
		    $main::KARMA_DOC_ROOT=$curr_array[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)) {
			    #my @myarray = $curr_array[3];
			    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 ();

#    print ("We're before debugNotify...\n");

    debugNotify (2);

#    if ($main::DEBUG_LEVEL >= 3) {
	debugConfig ();
#    }
}

#---------------------------------------------------------------
#
# 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) = @_;

   $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];
    }

    my $interval = $main::config_info{$inPrefGroup}{$inService}[0];
    if ($interval < $main::PAGE_WAKEUP) {
	$main::PAGE_WAKEUP = $interval;
    }


}


#---------------------------------------------------------------
#
# 
# 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);
    }

    if (($inSize =~ /^$main::cFULL_EMAIL$/) || 
	($inSize =~ /^$main::cSHORT_EMAIL$/)) {
	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 logMessageExit ("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.gif' . "\"></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.gif' . "\"></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.jpg' . "\" ></TD>\n");  
			    } else {
				print $main::info_file ("<TD><IMG SRC=\"..$main::PATH_DELIM" . 'images' . "$main::PATH_DELIM" . 'purple_status.jpg' . "\"></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::PAGE_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 warn, and alert threshold values
    #
    print $main::info_file ("<tr><td>\n");
    showWarnAlert ($inTNS, $inType);
    print $main::info_file ("</td></tr>\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};
    }

    #
    # clean up email configuration hash
    #
    foreach $key (keys %main::config_email) {
	undef $main::config_email{$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);
    

}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getUptimeString ($) {
    my ($startTime) = @_;
    my $numDays = undef;
    my $numHours = '00';
    my $numMins = '00';
    my $uptimeString = undef;
    my $uptimeSecs = $main::currTime - $startTime;
    my $remain = undef;

    #
    # compute days
    #
    if ($uptimeSecs > 86400) {
	$remain = $uptimeSecs % 86400;
	$numDays = ($uptimeSecs - $remain) / 86400;
	$uptimeSecs = $remain;
    }
    
    #
    # compute hours
    #
    if ($uptimeSecs > 3600) {
	$remain = $uptimeSecs % 3600;
	$numHours = ($uptimeSecs - $remain) / 3600;
	$numHours = padNum ($numHours, 2);
	$uptimeSecs = $remain;
    }

    #
    # compute minutes
    #
    if ($uptimeSecs > 60) {
	$remain = $uptimeSecs % 60;
	$numMins = ($uptimeSecs - $remain) / 60;
	$numMins = padNum ($numMins, 2);
	$uptimeSecs = $remain;
    }
    
    $uptimeString = 'uptime: ';
    if (defined $numDays) {
	$uptimeString .= "$numDays days, ";
    }
    if (defined $numHours) {
	$uptimeString .= "$numHours:";
    }
    if (defined $numMins) {
	$uptimeString .= "$numMins";
    }

    return $uptimeString;
}


#-----------------------------------------------------------------------
#
# 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 $uptimeString = getUptimeString ($main::startTime);
    my $currTimeStr = getCurrTimeString (undef);
    my $timeStr = '';
    my $statusStr = '';
    my $thePrefGroup = '';
    my $servKey = '';
    my $tnsKey = '';

    open (FIFO, ">$main::KARMA_FIFO_NAME");
    print FIFO ("\n");
    print FIFO ("karmad started at $startTimeStr, $uptimeString, 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");
#    }

    print FIFO ("\n");
    #
    # new stuff
    # show configuration information in status report
    #
    print FIFO ("-- CONFIGURATION --\n");
    print FIFO ("WORKING DIR:$main::wd\n");
    print FIFO ("   LOG FILE:$main::LOG_FILE_NAME\n");
    print FIFO ("CONFIG FILE:$main::CONF_FILE_NAME\n");
    print FIFO (" INDEX FILE:$main::INDEX_FILE_NAME\n");
    print FIFO ("   PID FILE:$main::PID_FILE_NAME\n");
    print FIFO ("DOCROOT DIR:$main::KARMA_DOC_ROOT\n");
    print FIFO (" KARMA HOME:$main::KARMA_HOME\n");
    print FIFO ("\n");
    print FIFO ("-- STATUS OF MONITORED DBs --\n");


    foreach $tnsKey (keys %main::db_info) {

	$thePrefGroup = getPrefGroup ($tnsKey);



	# print the DB:TNSNAME UP/DOWN, prefgroup:group line
	#
#	$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\n");


	# short email addresses for this prefgroup (if any)
	#
	if (defined $main::config_email{$thePrefGroup}{$main::cSHORT_EMAIL}[0]) {
	    print FIFO ('     SHORT email notification to: ');
	    my $aStr = 'TEST - SHORT';#getArrayString
		#(@{$main::config_email{$thePrefGroup}{$main::cSHORT_EMAIL}});
	    print FIFO $aStr;
	    print FIFO ("\n");
	}

	# full email addresses for this prefgroup (if any)
	#
	if (defined $main::config_email{$thePrefGroup}{$main::cFULL_EMAIL}[0]) {
	    print FIFO ('     FULL email notification to: ');
	    my $aStr = 'TEST - FULL';#getArrayString
		#(@{$main::config_email{$thePrefGroup}{$main::cFULL_EMAIL}});
	    print FIFO $aStr;
	    print FIFO ("\n");
	}

	# warning services, and frequency for this prefgroup (if any) 
	#
	if (defined $main::config_notify{$thePrefGroup}{$main::cNOTIFY_WARN}[0]) {
	    print FIFO ('     WARNINGs sent every ');
	    #print FIFO $main::config_notify{$thePrefGroup}{$main::cNOTIFY_WARN}[$main::cNOTIFY_INT];
	    print FIFO (' minutes for: ');
	    #print FIFO @{$main::config_notify{$thePrefGroup}{$main::cNOTIFY_WARN}[$main::cNOTIFY_SERV]};
	    print FIFO ("\n");
	}

	# alert services, and frequency for this prefgroup (if any)
	#
	if (defined $main::config_notify{$thePrefGroup}{$main::cNOTIFY_ALRT}[0]) {
	    print FIFO ('     ALERTs sent every ');
	    #print FIFO $main::config_notify{$thePrefGroup}{$main::cNOTIFY_ALRT}[$main::cNOTIFY_INT];
	    print FIFO (' minutes for: ');
	    #print FIFO @{$main::config_notify{$thePrefGroup}{$main::cNOTIFY_ALRT}[$main::cNOTIFY_SERV]};
	    print FIFO ("\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");

		
	    }
	}

    }

    print FIFO ("\n");
    close (FIFO);
}


#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
sub getArrayString (@) {
    my (@inArray) = @_;
    my $retString = undef;
    my $j = 0;
    while (defined $inArray[$j]) {
	$retString .= $inArray[$j] . ',';
    }
    return $retString;
}


#-----------------------------------------------------------------------
#
# 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 0;
}



#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
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;
    my $j = 0;

    debugMessage ("doNotification PG:$prefGroup...\n", 2);

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

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

	foreach $notifySize (keys %{$main::config_email{$prefGroup}}) {

	    $j = 0;
	    $notifyServices = undef;
	    if (defined ($alertServices)) {
		$notifyMinutes = 
		    getNotifyMinutes ($prefGroup, $main::cNOTIFY_ALRT);
#	    $servCount = scalar $alertServices;
		
		debugMessage ("doNotification: YES PG:$prefGroup, S:$notifySize M:$notifyMinutes\n", 2);
		
		$i = 0;
		while (defined $alertServices->[$i]) {
#	    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]);
			if ($notifySize =~ /^$main::cSHORT_EMAIL$/) {
			    $notifyServices->[$j] =
				$main::names{short}{$alertServices->[$i]};
			} else {
			    $notifyServices->[$j] = $alertServices->[$i];
			}
			$j++;
		    }
		    $i++;
		}
	    } else {
		# should log message here
	    }
	    if ($send_msg == 1) {
		sendNotification ($main::cNOTIFY_ALRT,
				  $inTNS,
				  $notifySize,
				  $notifyServices);
	    } else {
		$notifyServices = undef;
		$send_msg = 0;
		$warnServices = 
		    getNotifyServices ($prefGroup, $main::cNOTIFY_WARN);
		
		if (defined ($warnServices)) {
		    #$servCount = scalar @warnServices;
		    
		    $i = 0;
		    $j = 0;
		    while (defined $warnServices->[$i]) {
#		for ($i = 0; $i < $servCount; $i++) {
			$currStatus = getCurrStatus ($warnServices->[$i], $inTNS);
			if ((($inMinutes == 0) ||
			     ((defined ($notifyMinutes)) &&
			      ($notifyMinutes > 0) &&
			      ($inMinutes % $notifyMinutes == 0))) &&
			    ($currStatus == $main::cWARNING_STATUS)) {
			    $send_msg = 1;
			    #push (@notifyServices, $warnServices->[$i]);
			    if ($notifySize =~ /^$main::cSHORT_EMAIL$/) {
				$notifyServices->[$j] =
				    $main::names{short}{$warnServices->[$i]};
			    } else {
				$notifyServices->[$j] = $warnServices->[$i];
			    }
			    $j++;
			}
			$i++;
		    }
		} else {
		    # should log message here
		}
		if ($send_msg == 1) {
		    sendNotification ($main::cNOTIFY_WARN,
				      $inTNS,
				      $notifySize,
				      $notifyServices);
		}
	    }    
	}
    }
}


#-----------------------------------------------------------------------
#
# construct email message (full or short), and email it
# out to all the right people
#
#-----------------------------------------------------------------------
sub sendNotification ($$$$) {
    my ($inType, $inTNS, $inSize, $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, $inSize);

	#
	# 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 @emails) {
#	    $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++) {
	    $i = 0;
	    while (defined $inServices->[$i]) {
		if (defined $inServices->[$i]) {
		    $message .= "$inServices->[$i],";
		}
		$i++;
	    }
	    
	    if (not ($inSize =~ /^$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++) {
		$i = 0;
		while (defined $inServices->[$i]) {
		    if (defined $inServices->[$i]) {
			$message .= "\t$inServices->[$i]\n";
		    }
		    $i++;
		}
	    }
	    
	
	    #
	    # 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) {
		    if ((defined $inType) && (defined $inTNS)) {
			$i = 0;
			while (defined $inServices->[$i]) {
			    debugMessage ("sendNotification - T:$inType TNS:$inTNS S:$inServices->[$i]\n", 2);
			    $i++;
			}
		    }
		    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);
    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;

    my $i = 0;
    while (defined $services->[$i]) {
	push (@{$main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_SERV]}, $services->[$i]);
	$i++;
    }
#    $main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_SERV] =
#	$services;
   
    $i = 0;
    while (defined $services->[$i]) {
	$main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_SERV][$i]
	    = $services->[$i];
	$i++;
    }
}

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

    my $array_len = 0;
    my $interval = 0;
    my $email = 0;
    my $size = 0;
    my @services = undef;
    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 doNotifyCheck () {

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

    foreach $tnsKey (keys %main::db_info) {
	doNotification ($tnsKey, $dayMinutes);
    }

    debugMessage ("doNotifyChecks...\n", 2);
}

#-----------------------------------------------------------------------
#
#
#
#-----------------------------------------------------------------------
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);
	# this happens in main now
#	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;
    my $i = undef;
    #
    # if not specified, or *, assume all services
    # use notification
    #
    if ((not defined ($inServices)) ||
	($inServices =~ /^\*.*$/)) {
	$i = 0;
	foreach $key (keys %{$main::config_info{$inPrefGroup}}) {
#	    push (@retServices, $key);
	    $retServices->[$i] = $key;
	    $i++;
	}
    } else {
	my (@myarray) = split (/,/, $inServices);
	$i = 0;
	while (defined $myarray[$i]) {
	    $retServices->[$i] = $myarray[$i];
	    $i++;
	}
#	$retServices = \@myarray;
    }

    $i = 0;
    while ((defined $retServices) && (defined $retServices->[$i])) {
	debugMessage ("getServices - $retServices->[$i]\n", 2);
	$i++;
    }
    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{$inService}{$inTNS}}) {
	return $main::stats{$inService}{$inTNS}[$main::cSTATUS];
    } else {
	return $main::cNO_STATUS;
    }
}

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

#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;
    my $services = undef;
    my $i = 0;

    if (defined (@{$main::config_notify{$inPrefGroup}{$inType}})) {

	while ($main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_SERV][$i]) {
	    $services->[$i] = $main::config_notify{$inPrefGroup}{$inType}[$main::cNOTIFY_SERV][$i];
	    $i++;
	}
    } else {

	foreach $key (keys %{$main::names{long}}) {
	    $services->[$i] = $key;
	    $i++;
	}
    }

    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::NOTIFY_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}}) {
	debugMessage ("shouldNotify PG:$inPrefGroup, SK:$sizeKey\n", 2);
	$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");
    }
}



#-----------------------------------------------------------------------
#
# debugNotify
# 
#-----------------------------------------------------------------------
sub debugNotify ($) {
    my ($inLevel) = @_;
    my $prefGroup = undef;
    my $sizeKey = undef;
    my $i = 0;

#    print ("What the hello...\n");
    debugMessage ("--CONFIG_EMAIL--\n", $inLevel);
    foreach $prefGroup (keys %main::config_email) {
	debugMessage ("PREFGROUP:$prefGroup\n", $inLevel);
	foreach $sizeKey (keys %{$main::config_email{$prefGroup}}) {
	    debugMessage ("\tSIZE:$sizeKey\n", $inLevel);
	    $i = 0;
	    while ($main::config_email{$prefGroup}{$sizeKey}[$i]) {
		debugMessage ("\t\tEMAIL:$main::config_email{$prefGroup}{$sizeKey}[$i]\n", $inLevel);
		$i++;
	    }
	}
    }

    debugMessage ("--CONFIG_NOTIFY--\n", $inLevel);
    foreach $prefGroup (keys %main::config_notify) {
	debugMessage ("PREFGROUP:$prefGroup\n", $inLevel);
	foreach $sizeKey (keys %{$main::config_notify{$prefGroup}}) {
	    debugMessage ("\tSIZE:$sizeKey\n", $inLevel);
	    debugMessage ("\t\tINT:$main::config_notify{$prefGroup}{$sizeKey}[$main::cNOTIFY_INT]\n", 2);
	    $i = 0;
	    while ($main::config_notify{$prefGroup}{$sizeKey}[$main::cNOTIFY_SERV][$i]) {
		debugMessage ("\t\tSERVICE:$main::config_notify{$prefGroup}{$sizeKey}[$main::cNOTIFY_SERV][$i]\n", $inLevel);
		$i++;
	    }
	}
    }


}


sub debugDataSources ($) {
    my ($inLevel) = @_;
    my @data_sources = DBI->data_sources('Oracle');
    my $i = 0;
    if (defined $data_sources[$i]) {
	debugMessage ("Valid Data Sources:\n", $inLevel);
    }
    while (defined $data_sources[$i]) {
	debugMessage ("-->$data_sources[$i]\n", $inLevel);
	$i++;
    }
    if ($i == 0) {
	debugMessage ("No valid Data Sources, perhaps ORACLE_HOME is not set.\n", $inLevel);
    }
}

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

    my $prefGroup = getPrefGroup ($inTNS);
    my $serviceWarn = getServiceWarn ($prefGroup, $inService);
    my $serviceAlert = getServiceAlert ($prefGroup, $inService);

    my $warnImage = undef;
    my $alertImage = undef;

    if ($main::USE_BLINK_ALERT == 1) {
	$alertImage = 'blink_red_status.gif';
    } else {
	$alertImage = 'red_dia.jpg';
    }

    if ($main::USE_BLINK_WARNING == 1) {
	$warnImage = 'blink_yellow_status.gif';
    } else {
	$warnImage = 'yellow_dia.jpg';
    }

    print $main::info_file ("<table>\n");
    print $main::info_file ("<tr>\n");
    print $main::info_file ("<td><img src=\"../images/$warnImage\"></td>\n");
    print $main::info_file ("<td>warning</td>\n");
    print $main::info_file ("<td>$serviceWarn</td>\n");
    print $main::info_file ("</tr>\n");

    print $main::info_file ("<tr>\n");
    print $main::info_file ("<td><img src=\"../images/$alertImage\"></td>\n");
    print $main::info_file ("<td>alert</td>\n");
    print $main::info_file ("<td>$serviceAlert</td>\n");
    print $main::info_file ("</tr>\n");

    print $main::info_file ("</table>\n");
}


#-----------------------------------------------------------------------
#
# debugConfig
# config_info{type}{service}(x,y,z)
#-----------------------------------------------------------------------
sub debugConfig () {

    my $typeKey = undef;
    my $servKey = undef;
    my $x = undef;
    my $y = undef;
    my $z = undef;

    debugMessage ("debugConfig - ************************\n", 2);

    foreach $typeKey (keys %main::config_info) {
	debugMessage ("debugConfig - $typeKey\n", 2);
	foreach $servKey (keys %{$main::config_info{$typeKey}}) {
	    $x = $main::config_info{$typeKey}{$servKey}[0];
	    $y = $main::config_info{$typeKey}{$servKey}[1];
	    $z = $main::config_info{$typeKey}{$servKey}[2];
	    debugMessage ("debugConfig -   $servKey:\t$x,$y,$z\n", 2);
	}
	debugMessage ("debugConfig - \n",2);
    }

    debugMessage ("debugConfig - MIN WAKEUP:$main::PAGE_WAKEUP\n", 2);

}



#
# 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 wakes up periodically to
check your database for various statistics, generating a new
karma.html and various info/*.html files each time.

=head1 SYNOPSIS

Generally you won't run karma by invoking karmad directly, but
will use the karmactl control utility instead.  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.  Use the C<-h> option for additional
help on options.

=cut

