new file mode 100644
@@ -0,0 +1,530 @@
+#!/usr/bin/perl
+
+############################################################################
+# #
+# Send log and status emails for IPFire #
+# #
+# This 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 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This 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 IPFire; if not, write to the Free Software #
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #
+# #
+# Copyright (C) 2018 - 2019 The IPFire Team #
+# #
+############################################################################
+
+use strict;
+use warnings;
+
+use lib "/usr/lib/statusmail";
+
+package StatusMail;
+
+use base qw/EncryptedMail/;
+
+############################################################################
+# Constants
+############################################################################
+
+use constant { SEC => 0,
+ MIN => 1,
+ HOUR => 2,
+ MDAY => 3,
+ MON => 4,
+ YEAR => 5,
+ WDAY => 6,
+ YDAY => 7,
+ ISDST => 8,
+ MONSTR => 9 };
+
+use constant MONTHS => qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
+
+use constant LOGNAME => '/var/log/messages';
+
+############################################################################
+# Configuration variables
+############################################################################
+
+my @monthnames = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',
+ 'Sep', 'Oct', 'Nov', 'Dec');
+my %months;
+
+############################################################################
+# Variables
+############################################################################
+
+my %address_lookup_cache;
+
+############################################################################
+# Function prototypes
+############################################################################
+
+sub calculate_period( $$ );
+sub get_period_start();
+sub get_period_end();
+sub get_number_weeks();
+sub cache( $;$ );
+sub lookup_ip_address( $$ );
+sub set_host_name( $$$ );
+sub split_string( $$$ );
+
+############################################################################
+# Initialisation code
+############################################################################
+
+
+foreach (my $monindex = 0 ; $monindex < MONTHS ; $monindex++)
+{
+ $months{(MONTHS)[$monindex]} = $monindex;
+}
+
+#------------------------------------------------------------------------------
+# sub new
+#
+# Class constructor
+#------------------------------------------------------------------------------
+
+sub new
+{
+ my $invocant = shift;
+
+ my $class = ref($invocant) || $invocant;
+
+ my $self = $class->SUPER::new( @_ );
+
+ $self->{last_time} = 0;
+ $self->{last_mon} = 0;
+ $self->{last_day} = 0;
+ $self->{last_hour} = 0;
+
+ bless( $self, $class );
+
+ return $self;
+}
+
+#------------------------------------------------------------------------------
+# sub calculate_period( value, unit )
+#
+# Calculates the limits of the period covered by the message
+#
+# Parameters:
+# value Number of units
+# unit Unit of time
+#------------------------------------------------------------------------------
+
+sub calculate_period( $$ )
+{
+ my ( $self, $value, $unit ) = @_;
+
+ use Time::Local;
+
+ my $start_time = 0;
+ my @start_time = ();
+ my $end_time = 0;
+ my @end_time = ();
+ my $weeks_covered = 0;
+
+ @end_time = localtime();
+
+ $end_time[SEC] = 0;
+ $end_time[MIN] = 0;
+
+ $end_time = timelocal( @end_time );
+
+ if ($unit eq 'months')
+ {
+ # Go back the specified number of months
+
+ @start_time = @end_time;
+
+ $start_time[MON] -= $value;
+ if ($start_time[MON] < 0 )
+ {
+ $start_time[MON] += 12;
+ $start_time[YEAR]--;
+ }
+
+ $start_time = timelocal( @start_time );
+ }
+ else
+ {
+ my $hours = $value;
+
+ # Go back the specified number of hours, days or weeks
+
+ $hours *= 24 if ($unit eq 'days');
+ $hours *= 24 * 7 if ($unit eq 'weeks');
+
+ $start_time = timelocal( @end_time ) - ($hours * 3600);
+ @start_time = localtime( $start_time );
+ }
+
+ # Adjust end to end of previous hour rather than start of current hour
+
+ $end_time--;
+ @end_time = localtime( $end_time );
+
+ # Add the alphabetic month to the end of the time lists
+
+ push @start_time, $monthnames[ $start_time[MON] ];
+ push @end_time, $monthnames[ $end_time[MON] ];
+
+ # Calculate how many archive files have to be read
+
+ my $week_start = $start_time - ($start_time[WDAY] * 86400) - ($start_time[HOUR] * 3600) + 3600;
+ $weeks_covered = int( (time() - $week_start) / (86400 * 7) );
+
+ $self->{'start_time_array'} = \@start_time;
+ $self->{'start_time'} = $start_time;
+ $self->{'end_time_array'} = \@end_time;
+ $self->{'end_time'} = $end_time;
+ $self->{'weeks_covered'} = $weeks_covered;
+ $self->{'period'} = "$value$unit";
+ $self->{'period'} =~ s/s$//;
+ $self->{'total_days'} = ($end_time - $start_time) / 86400;
+}
+
+
+#------------------------------------------------------------------------------
+# sub get_period()
+#
+# Returns the period covered by a report.
+#------------------------------------------------------------------------------
+
+sub get_period()
+{
+ my $self = shift;
+
+ return $self->{'period'};
+}
+
+
+#------------------------------------------------------------------------------
+# sub get_period_start()
+#
+# Returns the start of the period covered by a report.
+#------------------------------------------------------------------------------
+
+sub get_period_start()
+{
+ my $self = shift;
+
+ return wantarray ? @{$self->{'start_time_array'}} : $self->{'start_time'};
+}
+
+
+#------------------------------------------------------------------------------
+# sub get_period_end()
+#
+# Returns the end of the period covered by a report.
+#------------------------------------------------------------------------------
+
+sub get_period_end()
+{
+ my $self = shift;
+
+ return wantarray ? @{$self->{'end_time_array'}} : $self->{'end_time'};
+}
+
+
+#------------------------------------------------------------------------------
+# sub get_number_weeks()
+#
+# Returns the number of complete weeks covered by a report.
+#------------------------------------------------------------------------------
+
+sub get_number_weeks()
+{
+ my $self = shift;
+
+ return $self->{'weeks_covered'};
+}
+
+
+#------------------------------------------------------------------------------
+# sub cache( name [, item] )
+#
+# Either caches an item or returns the cached item.
+#
+# Parameters:
+# Name name of item
+# Item item to be cached (optional)
+#
+# Returns:
+# Cached item if no item specified, undef otherwise
+#------------------------------------------------------------------------------
+
+my %cache;
+
+sub cache( $;$ )
+{
+ my ($self, $name, $item) = @_;
+
+ if ($item)
+ {
+ $cache{$name} = $item;
+ }
+ else
+ {
+ return $cache{$name};
+ }
+
+ return undef;
+}
+
+
+#------------------------------------------------------------------------------
+# sub clear_cache()
+#
+# Clears any cached values.
+#------------------------------------------------------------------------------
+
+sub clear_cache()
+{
+ %cache = ();
+}
+
+
+#------------------------------------------------------------------------------
+# sub get_message_log_line()
+#
+# Gets the next line from the message log.
+# Will cache log entries if the period covered is short.
+#------------------------------------------------------------------------------
+
+sub get_message_log_line
+{
+ my $self = shift;
+ my $line;
+
+ if (exists $self->{logindex})
+ {
+ # Reading from the cache
+
+ if ($self->{logindex} < @{ $self->{logcache} })
+ {
+ return $self->{logcache}[$self->{logindex}++];
+ }
+ else
+ {
+ # End of cache - reset to start again on next call
+
+ $self->{logindex} = 0;
+ return undef;
+ }
+ }
+
+ $self->{logfile} = $self->{'weeks_covered'} if (not exists $self->{logfile} or $self->{logfile} < 0);
+
+ LINE:
+ while (1)
+ {
+ if (not exists $self->{fh} or (exists $self->{fh} and eof $self->{fh}))
+ {
+ # Reading from a file and need to open a file
+
+ FILE:
+ while ($self->{logfile} >= 0)
+ {
+ my $name = $self->{logfile} < 1 ? LOGNAME : LOGNAME . '.' . $self->{logfile};
+ $self->{logfile}--;
+
+ if (-r $name)
+ {
+ # Not compressed
+
+ open $self->{fh}, '<', $name or die "Can't open $name: $!";
+ $self->{year} = (localtime( (stat(_))[9] ))[YEAR];
+ last FILE;
+ }
+ elsif (-r "$name.gz")
+ {
+ # Compressed
+
+ open $self->{fh}, "gzip -dc $name.gz |" or next;
+ $self->{year} = (localtime( (stat(_))[9] ))[YEAR];
+ last FILE;
+ }
+
+ # Not found - go back for next file
+ }
+
+ if ($self->{logfile} < -1)
+ {
+ # No further files - reset to start again on next call
+
+ delete $self->{fh};
+ return undef;
+ }
+ }
+
+ if (exists $self->{fh})
+ {
+ # Reading from a file
+
+ $line = readline $self->{fh};
+
+ if (eof $self->{fh})
+ {
+ if ($self->{logfile} < 0)
+ {
+ # No further files - reset to start again on next call
+
+ delete $self->{fh};
+ return undef;
+ }
+ # Go back for next file
+
+ close $self->{fh};
+ next LINE;
+ }
+
+ my ($mon, $day, $hour) = unpack 'Lsxs', $line;
+
+ if ($mon != $self->{last_mon} or $day != $self->{last_day} or $hour != $self->{last_hour})
+ {
+ # Hour, day or month changed. Convert to unix time so we can work out
+ # whether the message time falls between the limits we're interested in.
+ # This is complicated by the lack of a year in the logged information,
+ # so assume the current year, and adjust if necessary.
+
+ my @time;
+
+ $time[YEAR] = $self->{year};
+
+ ($time[MON], $time[MDAY], $time[HOUR], $time[MIN], $time[SEC]) = split /[\s:]+/, $line;
+ $time[MON] = $months{$time[MON]};
+
+ $self->{time} = timelocal( @time );
+
+ if ($self->{time} > time())
+ {
+ # We can't have times in the future, so this must be the previous year.
+
+ $self->{year}--;
+ $time[YEAR]--;
+ $self->{time} = timelocal( @time );
+ $self->{last_time} = $self->{time};
+ }
+ elsif ($self->{time} < $self->{last_time})
+ {
+ # Time should be increasing, so we must have gone over a year boundary.
+
+ $self->{year}++;
+ $time[YEAR]++;
+ $self->{time} = timelocal( @time );
+ $self->{last_time} = $self->{time};
+ }
+
+ ($self->{last_mon}, $self->{last_day}, $self->{last_hour}) = ($mon, $day, $hour);
+ }
+
+ # Check to see if we're within the specified limits.
+ # Note that the minutes and seconds may be incorrect, but since we only deal
+ # in hour boundaries this doesn't matter.
+
+ next LINE if ($self->{time} < $self->{start_time});
+
+ if ($self->{time} > $self->{end_time})
+ {
+ # After end time - reset to start again on next call
+
+ close $self->{fh};
+ delete $self->{fh};
+ $self->{logfile} = $self->{'weeks_covered'};
+
+ return undef;
+ }
+
+ # Cache the entry if the time covered is less than two days
+
+ push @{$self->{logcache}}, $line if ($self->{'total_days'} <= 2);
+
+ return $line;
+ }
+ }
+
+ return $line;
+}
+
+
+#------------------------------------------------------------------------------
+# sub lookup_ip_address( string )
+#
+# Converts an IP Address to a URL
+#------------------------------------------------------------------------------
+
+sub lookup_ip_address( $$ )
+{
+ my ($self, $address) = @_;
+
+ use Socket;
+
+ return $address_lookup_cache{$address} if (exists $address_lookup_cache{$address});
+
+ my $name = gethostbyaddr( inet_aton( $address ), AF_INET ) || "";
+
+ $address_lookup_cache{$address} = $name;
+
+ return $name;
+}
+
+
+#------------------------------------------------------------------------------
+# sub set_host_name( address, name )
+#
+# Records the mapping from an IP address to a name
+#------------------------------------------------------------------------------
+
+sub set_host_name( $$$ )
+{
+ my ($self, $address, $name) = @_;
+
+ return unless ($address and $name);
+ return if ($address eq $name);
+
+ if (exists $address_lookup_cache{$address})
+ {
+ $address_lookup_cache{$address} = "" if ($address_lookup_cache{$address} ne $name);
+ }
+ else
+ {
+ $address_lookup_cache{$address} = $name;
+ }
+}
+
+
+#------------------------------------------------------------------------------
+# sub spilt_string( string, size )
+#
+# Splits a string into multiple lf separated lines
+#------------------------------------------------------------------------------
+
+sub split_string( $$$ )
+{
+ my ($self, $string, $size) = @_;
+
+ my $out = '';
+
+ while (length $string > $size)
+ {
+ $string =~ s/(.{$size,}?)\s+//;
+ last unless ($1);
+ $out .= $1 . "\n";
+ }
+
+ $out .= $string;
+
+ return $out;
+}
+
+1;
new file mode 100755
@@ -0,0 +1,422 @@
+#!/usr/bin/perl
+
+############################################################################
+# #
+# Send log and status emails for IPFire #
+# #
+# This 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 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This 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 IPFire; if not, write to the Free Software #
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #
+# #
+# Copyright (C) 2018 - 2019 The IPFire Team #
+# #
+############################################################################
+# Main script for statusmail. #
+# #
+# Usually called by fcron when it will check to see if any schedules are #
+# due in which case the schedule will be executed. If the schedule #
+# produces any output it is sent as an email to the recipients given in #
+# the schedule. Emails are always signed using GPG and will be encrypted #
+# if an encryption keys is available for the user. #
+# #
+# Can also be run with the name of a schedule as an argument in which case #
+# the schedule is executed immediately regrardless of whether it is due or #
+# not. #
+# #
+# If run from a terminal additional debugging will be turned on and log #
+# messages will be output to the terminal. #
+############################################################################
+
+use strict;
+use warnings;
+
+use Sys::Syslog qw(:standard :macros);
+
+use lib "/usr/lib/statusmail";
+
+require "/var/ipfire/general-functions.pl";
+require "${General::swroot}/lang.pl";
+
+use StatusMail;
+
+############################################################################
+# Configuration variables
+#
+# These variables give the locations of various files used by this script
+############################################################################
+
+my $lib_dir = "/usr/lib/statusmail";
+my $plugin_dir = "$lib_dir/plugins";
+my $stylesheet = "$lib_dir/stylesheet.css";
+my $mainsettings = "${General::swroot}/main/settings";
+my $mailsettings = "${General::swroot}/dma/mail.conf";
+my $contactsettings = "${General::swroot}/statusmail/contact_settings";
+my $schedulesettings = "${General::swroot}/statusmail/schedule_settings";
+my $debug = 0;
+
+############################################################################
+# Function prototypes
+############################################################################
+
+# Used by plugins
+
+sub add_mail_item( % );
+
+# Local functions
+
+sub send_email( $ );
+sub execute_schedule( $$ );
+sub abort( $ );
+sub log_message( $$ );
+sub debug( $$ );
+
+############################################################################
+# Variables
+############################################################################
+
+my %mainsettings = ();
+my %sections = ();
+my $contacts = {};
+my $schedules = {};
+my %mailsettings = ();
+
+
+############################################################################
+# Main function
+############################################################################
+
+openlog( "statusmail", "nofatal", LOG_USER);
+log_message LOG_INFO, "Starting log and status email processing";
+
+# Check for existence of settings files
+
+exit unless (-r $contactsettings);
+exit unless (-e $mailsettings);
+exit unless (-r $schedulesettings);
+
+# Read settings
+
+General::readhash($mailsettings, \%mailsettings);
+General::readhash($mainsettings, \%mainsettings);
+
+unless ($mailsettings{'USEMAIL'} eq 'on')
+{
+ log_message LOG_WARNING, "Email disabled";
+ exit;
+};
+
+eval qx|/bin/cat $contactsettings| if (-r $contactsettings);
+eval qx|/bin/cat $schedulesettings| if (-r $schedulesettings);
+
+# Scan for plugins
+
+opendir DIR, $plugin_dir or abort "Can't open Plug-in directory $plugin_dir: $!";
+
+foreach my $file (readdir DIR)
+{
+ next unless ($file =~ m/\.pm$/);
+
+ debug 1, "Initialising plugin $file";
+
+ require "$plugin_dir/$file";
+}
+
+# Check command line parameters
+
+if (@ARGV)
+{
+ # Command line parameters provided - try to execute the named schedule.
+
+ my ($schedule) = $ARGV[0];
+
+ if (exists $$schedules{$schedule})
+ {
+ execute_schedule( $schedule, $$schedules{$schedule} );
+ }
+ else
+ {
+ print "Schedule '$schedule' not found\n";
+ }
+
+ closelog;
+ exit;
+}
+
+# Look for a due schedule
+
+my (undef, undef, $hour, $mday, undef, undef, $wday, undef, undef) = localtime;
+
+$hour = 1 << $hour;
+$wday = 1 << $wday;
+$mday = 1 << $mday;
+
+foreach my $schedule (keys %$schedules)
+{
+ next unless ($$schedules{$schedule}{'enable'} eq 'on'); # Must be enabled
+
+ next unless ($$schedules{$schedule}{'mday'} & $mday or # Must be due today
+ $$schedules{$schedule}{'wday'} & $wday);
+
+ next unless ($$schedules{$schedule}{'hours'} & $hour); # Must be due this hour
+
+ debug 1, "Schedule $schedule due";
+
+ execute_schedule( $schedule, $$schedules{$schedule} );
+}
+
+closelog;
+
+exit;
+
+#------------------------------------------------------------------------------
+# sub execute_schedule( name, schedule )
+#
+# Executes the specified schedule as long as at least one of the contacts is
+# enabled.
+#
+# Parameters:
+# name name of Schedule
+# schedule reference of Schedule hash to be executed
+#------------------------------------------------------------------------------
+
+sub execute_schedule( $$ )
+{
+ my ($name, $schedule) = @_;
+ my @contacts;
+ my $status = 0;
+
+ # Check that at least one of the contacts is enabled
+
+ foreach my $contact (split '\|', $$schedule{'email'})
+ {
+ push @contacts, $contact if (exists $$contacts{$contact} and $$contacts{$contact}{'enable'} eq 'on');
+ }
+
+ if (not @contacts)
+ {
+ debug 1, "No enabled contacts";
+ return;
+ }
+
+ log_message LOG_INFO, "Executing status mail schedule $name";
+
+ # Look for a theme stylesheet
+
+ my $theme_stylesheet = "$lib_dir/$mainsettings{'THEME'}.css";
+ $stylesheet = $theme_stylesheet if (-r $theme_stylesheet);
+
+ # Create message
+
+ my $message = new StatusMail( 'format' => $$schedule{'format'},
+ 'subject' => $$schedule{'subject'},
+ 'to' => [ @contacts ],
+ 'sender' => $mailsettings{'SENDER'},
+ 'max_lines_per_item' => $$schedule{'lines'},
+ 'stylesheet' => $stylesheet );
+
+ if (not $message)
+ {
+ log_message LOG_WARNING, "Failed to create message object: $!";
+ return;
+ }
+
+ $message->calculate_period( $$schedule{'period-value'}, $$schedule{'period-unit'} );
+
+ $message->add_text( "$Lang::tr{'statusmail period from'} " . localtime( $message->get_period_start ) .
+ " $Lang::tr{'statusmail period to'} " . localtime( $message->get_period_end ) . "\n" );
+
+ # Loop through the various log items
+
+ foreach my $section ( sort keys %sections )
+ {
+ debug 3, "Section $section";
+ $message->add_section( $section );
+
+ foreach my $subsection ( sort keys %{ $sections{$section} } )
+ {
+ debug 3, "Subsection $subsection";
+ $message->add_subsection( $subsection );
+
+ foreach my $item ( sort keys %{ $sections{$section}{$subsection} } )
+ {
+ debug 3, "Item $item";
+
+ # Is the item enabled?
+
+ my $key = $sections{$section}{$subsection}{$item}{'ident'};
+
+ next unless (exists $$schedule{"enable_$key"} and $$schedule{"enable_$key"} eq 'on');
+ next unless ($sections{$section}{$subsection}{$item}{'format'} eq 'both' or
+ $sections{$section}{$subsection}{$item}{'format'} eq $$schedule{'format'});
+
+ # Yes. Call the function to get it's content - with option if necessary
+
+ debug 2, "Process item $section :: $subsection :: $item";
+
+ $message->add_title( $item );
+
+ my $function = $sections{$section}{$subsection}{$item}{'function'};
+
+ if (exists $$schedule{"value_$key"})
+ {
+ $status += &$function( $message, $$schedule{"value_$key"} );
+ }
+ else
+ {
+ $status += &$function( $message );
+ }
+ }
+
+ $message->clear_cache;
+ }
+ }
+
+ # End the Message
+
+ if ($status > 0)
+ {
+ debug 1, "Send mail message";
+ $message->send;
+ }
+}
+
+
+#------------------------------------------------------------------------------
+# sub add_mail_item( params )
+#
+# Adds a possible status item to the section and subsection specified. This
+# function is called from the BEGIN block of the plugin.
+#
+# Any errors cause the item to be ignored without raising an error.
+#
+# Parameters:
+# params hash containing details of the item to be added:
+# section name of the section containing this item
+# subsection name of the subsection containing this item
+# item name of the item
+# function function called to add item to message
+# format available formats for the item 'html', 'text' or 'both'
+# option hash specifying option parameter (optional)
+#
+# option can specify either a selection or an integer. For a selection it
+# contains:
+# type must be 'option'
+# values array of strings representing the possible options
+#
+# For an integer option contains:
+# type must be 'integer'
+# min minimum valid value of parameter
+# max maximum valid value of parameter
+#------------------------------------------------------------------------------
+
+sub add_mail_item( % )
+{
+ my %params = @_;
+
+ # Check for all required parameters
+
+ return unless (exists $params{'section'} and
+ exists $params{'subsection'} and
+ exists $params{'item'} and
+ exists $params{'function'} );
+
+ # Check the option
+
+ if ($params{'option'})
+ {
+ return unless (ref $params{'option'} eq 'HASH');
+
+ if ($params{'option'}{'type'} eq 'select')
+ {
+ return unless (ref $params{'option'}{'values'} eq 'ARRAY' and @{ $params{'option'}{'values'} } > 1);
+ }
+ elsif ($params{'option'}{'type'} eq 'integer')
+ {
+ return unless (exists $params{'option'}{'min'} and
+ exists $params{'option'}{'max'} and
+ $params{'option'}{'min'} < $params{'option'}{'max'});
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ $params{'format'} = 'both' unless (exists $params{'format'});
+
+ # Record that the option exists
+
+ $sections{$params{'section'}}{$params{'subsection'}}{$params{'item'}} = { 'function' => $params{'function'},
+ 'format' => $params{'format'},
+ 'ident' => $params{'ident'} };
+}
+
+
+#------------------------------------------------------------------------------
+# sub abort( message )
+#
+# Aborts the update run, printing out an error message.
+#
+# Parameters:
+# message Message to be printed
+#------------------------------------------------------------------------------
+
+sub abort( $ )
+{
+my ($message) = @_;
+
+ log_message( LOG_ERR, $message );
+ croak $message;
+}
+
+
+#------------------------------------------------------------------------------
+# sub log_message( level, message )
+#
+# Logs a message to the system log. If the script is run from the terminal
+# then the message is also printed locally.
+#
+# Parameters:
+# level Severity of message
+# message Message to be logged
+#------------------------------------------------------------------------------
+
+sub log_message( $$ )
+{
+ my ($level, $message) = @_;
+
+ print "($level) $message\n" if (-t STDIN);
+ syslog( $level, $message );
+}
+
+
+#------------------------------------------------------------------------------
+# sub debug( level, message )
+#
+# Optionally logs a debug message
+#
+# Parameters:
+# level Debug level
+# message Message to be logged
+#------------------------------------------------------------------------------
+
+sub debug( $$ )
+{
+ my ($level, $message) = @_;
+
+ if (($level <= $debug) or
+ ($level == 1 and -t STDIN))
+ {
+ log_message LOG_DEBUG, $message;
+ }
+}