$SIG{'TERM'} = $SIG{'HUP'} = 'goodbye';
my $MAILER = "/usr/sbin/sendmail";
my $WRITE = "/usr/bin/write";
# in_range($range, $number)
# returns 1 if $number is inside $range, 0 if not
foreach my $f (split(/,/, $range)) {
my ($low,$high) = split(/-/, $f);
return 1 if ($low <= $num and $num <= $high);
# inside_time_window($days,$hours)
# returns 1 if inside window, 0 if outside window
my($days, $hours) = split(/:/, $range);
my ($hr, $wday) = (localtime(time))[2,6];
if (($days eq '*' or in_range($days, $wday))
and ($hours eq '*' or in_range($hours, $hr))) {
print "\n*** swatch-3.0.1 (pid:6826) started at " . `/bin/date` . "\n";
my @ranges = split(/:/, $dot_loc);
foreach my $range (0..$#ranges) {
if ($ranges[$range] != -1) {
my ($begin, $end) = split(/-/, $ranges[$range]);
$dot[$range] = substr($message, $begin, ($end - $begin + 1));
my ($date_loc, $time_loc) = ("-1:0-2:4-5", "7-8:10-11:13-14");
# Returns an array of year, month, day, hours, minutes, and seconds.
my $year_today = (Today())[0];
my ($y, $m, $d) = parse_dot($string, $date_loc);
my ($hrs, $mins, $secs) = parse_dot($string, $time_loc);
if (length($y) eq 0) { $y = (Today())[0] };
return ($y, $months{$m}, $d, $hrs, $mins, $secs);
$delta = sprintf("%d:%.2d:%.2d", $delta[1], $delta[2], $delta[3]);
$delta = sprintf("$delta[0] day%s %d:%.2d:%.2d", $delta[0] > 1 ? 's' : '',
$delta[1], $delta[2], $delta[3]);
return "$count $msg regular expressions in $delta";
return "$count in $delta: $msg";
# Stores message information in
# {<truncated message>|<pattern>} => {
# dhms => [ array ], # days,hours,minutes,seconds
CUT_MARKS => [ "0:16" ], # not used yet
my @ymdhms = YMDHMS($msg);
my @min_dhms_delta = split(/(\s+|:)/, $opts{'MIN_DELTA'});
foreach my $i (0..$#min_dhms_delta) {
# strip out unwanted element
splice (@min_dhms_delta, $i, 1) if ($min_dhms_delta[$i] eq ":");
$key =~ s/\[\d+\]/[PID]/;
while ($#min_dhms_delta < 3) {
unshift(@min_dhms_delta, 0); # make sure that the dhms array is full
if (exists $Msg_Rec{$key} and defined $Msg_Rec{$key}->{ymdhms}) {
$Msg_Rec{$key}->{count}++;
if ($ymdhms[1] > $Msg_Rec{$key}->{ymdhms}[1]) { $ymdhms[0]--; }
my @delta_dhms = Delta_DHMS(@{$Msg_Rec{$key}->{ymdhms}}, @ymdhms);
foreach my $i (0..$#min_dhms_delta) {
$passed = 0 if ($delta_dhms[$i] < $min_dhms_delta[$i]);
last unless ($delta_dhms[$i] == $min_dhms_delta[$i]);
$new = new_msg($use, $key, $Msg_Rec{$key}->{count}, @delta_dhms);
$Msg_Rec{$key}->{ymdhms} = [ @ymdhms ];
$Msg_Rec{$key}->{count} = 1;
$rec->{ymdhms} = [ @ymdhms ];
"yellow" => "\033[33;1m",
"magenta" => "\033[35;1m",
"black_h" => "\033[40;1m",
"green_h" => "\033[42;1m",
"yellow_h" => "\033[43;1m",
"blue_h" => "\033[44;1m",
"magenta_h" => "\033[45;1m",
"cyan_h" => "\033[46;1m",
"white_h" => "\033[47;1m",
"underscore" => "\033[4m",
return if (exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'}));
if ($args{'MODES'}[0] eq 'random') {
my @mode_names = keys %text_modes;
print $text_modes{$mode_names[rand $#mode_names]};
foreach my $mode (@{$args{'MODES'}}) {
print $text_modes{$mode};
print $text_modes{'normal'};
# ring_bell(args) -- send x number of control-G characters to the output.
my $sun_terminal = (`uname -s` eq 'SunOS\n');
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
my $bells = $args{'RINGS'};
for ( ; $bells > 0 ; $bells-- ) {
sleep 1 if $sun_terminal; # SunOS needed this. Not sure about Solaris though
# exec_command(args) -- fork and execute a command
if (exists $args{'COMMAND'}) {
$command = $args{'COMMAND'};
warn "$0: No command was specified in exec action.\n";
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
} elsif (defined $exec_pid) {
} elsif ($! =~ /No more processes/) {
# EAGAIN, supposedly recoverable fork error
warn "$0: Can't fork to exec $command: $!\n";
my $current_command_name;
# send_message_to_pipe -- send text to a pipe.
# usage: &send_message_to_pipe($program_to_pipe_to_including_the_vertical_bar_symbol,
# $message_to_send_to_the_pipe);
sub send_message_to_pipe {
if (exists $args{'COMMAND'}) {
$command = $args{'COMMAND'};
warn "$0: No command was specified in pipe action.\n";
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
# open a new pipe if necessary
if ( !$pipe_is_open or $current_command_name ne $command ) {
# first close an open pipe
close(PIPE) if $pipe_is_open;
or warn "$0: cannot open pipe to $command: $!\n" && return;
$current_command_name = $command;
print PIPE "$args{'MESSAGE'}";
if (not exists $args{'KEEP_OPEN'}) {
close(PIPE) if $pipe_is_open;
# close_pipe_if_open -- used at the end of a script to close a pipe
# usage: &close_pipe_if_open();
# send_email -- send some mail using $MAILER.
# usage: &send_email($addresses_to_mail_to);
my $login = (getpwuid($<))[0];
'SUBJECT' => 'Message from Swatch',
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
my $addresses = $args{'ADDRESSES'};
warn "ERROR: $0 cannot find a mail delivery program\n";
open(MAIL, "| $MAILER $addresses")
or warn "$0: cannot open pipe to $MAILER: $!\n" && return;
print MAIL "To: $addresses\n";
print MAIL "Subject: $args{SUBJECT}\n\n";
print MAIL "$args{'MESSAGE'}\n";
# write_message -- use $WRITE to send a message logged on users.
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
warn "ERROR: $0 cannot find the write(1) program\n";
if (exists($args{'USERS'})) {
foreach my $user (split(/:/, $args{'USERS'})) {
send_message_to_pipe(COMMAND => "$WRITE $user 2>/dev/null",
MESSAGE => "$args{'MESSAGE'}\n");
my $Filename = '/var/log/ulog/syslogemu.log';
my $File = File::Tail->new(name=>$Filename, maxinterval => 1, interval => 1);
die "/usr/local/bin/swatch: cannot read input \"$Filename\": $!\n";
LOOP: while (defined($_=$File->read)) {
# quote all special shell chars
$sanitized_ =~ s/([;&\(\)\|\^><\$`'\\])/\\$1/g;
my @sanitized_ = split(/\s+/, $sanitized_);
if (/INVALID|REPEATED|INCOMPLETE:LOGIN/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
ring_bell('RINGS' => "3", );
echo('MESSAGE' => "$_", );
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
if (/ipfw:.*Deny ICMP:8.0/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/drip.wav &", );
if (/ipfw:.*Deny TCP .*:6000/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/camera.wav &", );
if (/ipfw:.*Deny UDP .*:513/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/flush.wav &", );
if (/ipfw:.*Deny TCP .*:21/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/vault.wav &", );
if (/PROTO=TCP .*DPT=80/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/tcp80.mp3 &", );
if (/PROTO=TCP .*DPT=23/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/tcp23.mp3 &", );
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/udp137.mp3 &", );
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/ping.mp3 &", );
echo('MESSAGE' => "$_", );
#use strict;
# @(#) First Edit: Bas
# @(#) Last Edit: Fash
use POSIX ":sys_wait_h";
use vars qw(%Msg_Rec);
$SIG{'TERM'} = $SIG{'HUP'} = 'goodbye';
$SIG{'CHLD'} = 'IGNORE';
## Constants
my $BELL = "";
my $MAILER = "/usr/sbin/sendmail";
my $WRITE = "/usr/bin/write";
$/ = "
";
autoflush STDOUT;
sub goodbye {
$| = 0;
close_pipe_if_open();
exit(0);
}
#
# in_range($range, $number)
# returns 1 if $number is inside $range, 0 if not
#
sub in_range {
my $range = shift;
my $num = shift;
foreach my $f (split(/,/, $range)) {
if ($f =~ /-/) {
my ($low,$high) = split(/-/, $f);
return 1 if ($low <= $num and $num <= $high);
} elsif ($f == $num) {
return 1;
}
}
return 0;
}
#
# inside_time_window($days,$hours)
# returns 1 if inside window, 0 if outside window
#
sub inside_time_window {
my $range = shift;
my($days, $hours) = split(/:/, $range);
my ($hr, $wday) = (localtime(time))[2,6];
if (($days eq '*' or in_range($days, $wday))
and ($hours eq '*' or in_range($hours, $hr))) {
return 1;
} else {
return 0;
}
}
print "\n*** swatch-3.0.1 (pid:6826) started at " . `/bin/date` . "\n";
use Date::Calc qw(:all);
sub parse_dot {
my $message = shift;
my $dot_loc = shift;
my @dot = ();
my @ranges = split(/:/, $dot_loc);
foreach my $range (0..$#ranges) {
if ($ranges[$range] != -1) {
my ($begin, $end) = split(/-/, $ranges[$range]);
$dot[$range] = substr($message, $begin, ($end - $begin + 1));
}
}
return @dot;
}
my ($date_loc, $time_loc) = ("-1:0-2:4-5", "7-8:10-11:13-14");
my %months = (
Jan => 1,
Feb => 2,
Mar => 3,
Apr => 4,
May => 5,
Jun => 6,
Jul => 7,
Aug => 8,
Sep => 9,
Oct => 10,
Nov => 11,
Dec => 12
);
# Returns an array of year, month, day, hours, minutes, and seconds.
#
sub YMDHMS {
my $string = shift;
my $year_today = (Today())[0];
my ($y, $m, $d) = parse_dot($string, $date_loc);
my ($hrs, $mins, $secs) = parse_dot($string, $time_loc);
if (length($y) eq 0) { $y = (Today())[0] };
return ($y, $months{$m}, $d, $hrs, $mins, $secs);
}
sub new_msg {
my $use = shift;
my $msg = shift;
my $count = shift;
my @delta = @_;
my $delta;
if ($delta[0] == 0) {
$delta = sprintf("%d:%.2d:%.2d", $delta[1], $delta[2], $delta[3]);
} else {
$delta = sprintf("$delta[0] day%s %d:%.2d:%.2d", $delta[0] > 1 ? 's' : '',
$delta[1], $delta[2], $delta[3]);
}
if ($use eq 'regex') {
return "$count $msg regular expressions in $delta";
} else {
return "$count in $delta: $msg";
}
}
#
# Stores message information in
# $Msg_Rec = (
# {<truncated message>|<pattern>} => {
# dhms => [ array ], # days,hours,minutes,seconds
# count => integer,
sub throttle {
my %opts = (
KEY => $_,
CUT_MARKS => [ "0:16" ], # not used yet
USE => 'message',
@_
);
my $msg = $opts{'KEY'};
my $use = $opts{'USE'};
my @ymdhms = YMDHMS($msg);
my $key;
my @min_dhms_delta = split(/(\s+|:)/, $opts{'MIN_DELTA'});
foreach my $i (0..$#min_dhms_delta) {
# strip out unwanted element
splice (@min_dhms_delta, $i, 1) if ($min_dhms_delta[$i] eq ":");
}
if ($use eq 'regex') {
$key = $opts{'REGEX'};
} else {
$key = substr($msg, 16);
$key =~ s/\[\d+\]/[PID]/;
}
while ($#min_dhms_delta < 3) {
unshift(@min_dhms_delta, 0); # make sure that the dhms array is full
}
if (exists $Msg_Rec{$key} and defined $Msg_Rec{$key}->{ymdhms}) {
my $passed = 1;
$Msg_Rec{$key}->{count}++;
if ($ymdhms[1] > $Msg_Rec{$key}->{ymdhms}[1]) { $ymdhms[0]--; }
my @delta_dhms = Delta_DHMS(@{$Msg_Rec{$key}->{ymdhms}}, @ymdhms);
foreach my $i (0..$#min_dhms_delta) {
$passed = 0 if ($delta_dhms[$i] < $min_dhms_delta[$i]);
last unless ($delta_dhms[$i] == $min_dhms_delta[$i]);
}
if ($passed) {
my $new = '';
$new = new_msg($use, $key, $Msg_Rec{$key}->{count}, @delta_dhms);
$Msg_Rec{$key}->{ymdhms} = [ @ymdhms ];
$Msg_Rec{$key}->{count} = 1;
return $new;
} else {
return '';
}
} else {
my $rec;
$rec->{ymdhms} = [ @ymdhms ];
$Msg_Rec{$key} = $rec;
return $msg;
}
}
##
## ACTION SUBROUTINES
##
my %text_modes = (
"black" => "\033[30;1m",
"red" => "\033[31;1m",
"green" => "\033[32;1m",
"yellow" => "\033[33;1m",
"blue" => "\033[34;1m",
"magenta" => "\033[35;1m",
"cyan" => "\033[36;1m",
"white" => "\033[37;1m",
"black_h" => "\033[40;1m",
"red_h" => "\033[41;1m",
"green_h" => "\033[42;1m",
"yellow_h" => "\033[43;1m",
"blue_h" => "\033[44;1m",
"magenta_h" => "\033[45;1m",
"cyan_h" => "\033[46;1m",
"white_h" => "\033[47;1m",
"bold" => "\033[1m",
"blink" => "\033[5m",
"inverse" => "\033[7m",
"normal" => "\033[0m",
"underscore" => "\033[4m",
);
sub echo {
my %args = (
'MODES' => [ 'normal' ],
@_
);
return if (exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'}));
if ($args{'MODES'}[0] eq 'random') {
my @mode_names = keys %text_modes;
print $text_modes{$mode_names[rand $#mode_names]};
} else {
foreach my $mode (@{$args{'MODES'}}) {
print $text_modes{$mode};
}
}
print $args{'MESSAGE'};
print $text_modes{'normal'};
print "\n";
}
#
# ring_bell(args) -- send x number of control-G characters to the output.
#
sub ring_bell {
my %args = (
'RINGS' => 1,
@_
);
my $sun_terminal = (`uname -s` eq 'SunOS\n');
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
my $bells = $args{'RINGS'};
for ( ; $bells > 0 ; $bells-- ) {
print $BELL;
sleep 1 if $sun_terminal; # SunOS needed this. Not sure about Solaris though
}
}
#
# exec_command(args) -- fork and execute a command
#
sub exec_command {
my %args = (@_);
my $exec_pid;
my $command;
if (exists $args{'COMMAND'}) {
$command = $args{'COMMAND'};
} else {
warn "$0: No command was specified in exec action.\n";
return;
}
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
EXECFORK: {
if ($exec_pid = fork) {
waitpid(-1, WNOHANG);
return;
} elsif (defined $exec_pid) {
exec($command);
} elsif ($! =~ /No more processes/) {
# EAGAIN, supposedly recoverable fork error
sleep 5;
redo EXECFORK;
} else {
warn "$0: Can't fork to exec $command: $!\n";
}
}
return;
}
{
my $pipe_is_open;
my $current_command_name;
#
# send_message_to_pipe -- send text to a pipe.
#
# usage: &send_message_to_pipe($program_to_pipe_to_including_the_vertical_bar_symbol,
# $message_to_send_to_the_pipe);
#
sub send_message_to_pipe {
my %args = (@_);
my $command;
if (exists $args{'COMMAND'}) {
$command = $args{'COMMAND'};
} else {
warn "$0: No command was specified in pipe action.\n";
return;
}
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
# open a new pipe if necessary
if ( !$pipe_is_open or $current_command_name ne $command ) {
# first close an open pipe
close(PIPE) if $pipe_is_open;
$pipe_is_open = 0;
open(PIPE, "| $command")
or warn "$0: cannot open pipe to $command: $!\n" && return;
PIPE->autoflush(1);
$pipe_is_open = 1;
$current_command_name = $command;
}
# send the text
print PIPE "$args{'MESSAGE'}";
if (not exists $args{'KEEP_OPEN'}) {
close(PIPE) if $pipe_is_open;
$pipe_is_open = 0;
}
}
#
# close_pipe_if_open -- used at the end of a script to close a pipe
# opened by &pipe_it().
#
# usage: &close_pipe_if_open();
#
sub close_pipe_if_open {
if ($pipe_is_open) {
close(PIPE);
}
}
}
#
# send_email -- send some mail using $MAILER.
#
# usage: &send_email($addresses_to_mail_to);
#
sub send_email {
my $login = (getpwuid($<))[0];
my %args = (
'ADDRESSES' => $login,
'SUBJECT' => 'Message from Swatch',
@_
);
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
my $addresses = $args{'ADDRESSES'};
$addresses =~ s/:/,/g;
if ($MAILER eq '') {
warn "ERROR: $0 cannot find a mail delivery program\n";
return;
}
open(MAIL, "| $MAILER $addresses")
or warn "$0: cannot open pipe to $MAILER: $!\n" && return;
print MAIL "To: $addresses\n";
print MAIL "Subject: $args{SUBJECT}\n\n";
print MAIL "$args{'MESSAGE'}\n";
close(MAIL);
}
#
# write_message -- use $WRITE to send a message logged on users.
#
sub write_message {
my %args = (@_);
return if exists($args{'WHEN'}) and not inside_time_window($args{'WHEN'});
if ($WRITE eq '') {
warn "ERROR: $0 cannot find the write(1) program\n";
return;
}
if (exists($args{'USERS'})) {
foreach my $user (split(/:/, $args{'USERS'})) {
send_message_to_pipe(COMMAND => "$WRITE $user 2>/dev/null",
MESSAGE => "$args{'MESSAGE'}\n");
}
}
}
use File::Tail;
my $Filename = '/var/log/ulog/syslogemu.log';
my $File = File::Tail->new(name=>$Filename, maxinterval => 1, interval => 1);
if (not defined $File) {
die "/usr/local/bin/swatch: cannot read input \"$Filename\": $!\n";
}
LOOP: while (defined($_=$File->read)) {
chomp;
my $sanitized_ = $_;
@_ = split;
# quote all special shell chars
$sanitized_ =~ s/([;&\(\)\|\^><\$`'\\])/\\$1/g;
my @sanitized_ = split(/\s+/, $sanitized_);
if (/INVALID|REPEATED|INCOMPLETE:LOGIN/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
ring_bell('RINGS' => "3", );
next;
}
if (/(panic|halt)/) {
echo('MESSAGE' => "$_", );
ring_bell();
next;
}
if (/Regents/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
ring_bell();
next;
}
if (/ipfw:.*Deny ICMP:8.0/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/drip.wav &", );
next;
}
if (/ipfw:.*Deny TCP .*:6000/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/camera.wav &", );
next;
}
if (/ipfw:.*Deny UDP .*:513/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/flush.wav &", );
next;
}
if (/ipfw:.*Deny TCP .*:21/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 -q /root/wavs/vault.wav &", );
next;
}
if (/PROTO=TCP .*DPT=80/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/tcp80.mp3 &", );
next;
}
if (/PROTO=TCP .*DPT=23/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/tcp23.mp3 &", );
next;
}
if (/UDP .*=1300007/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/udp137.mp3 &", );
next;
}
if (/PROTO=ICMP/) {
echo('MODES' => [ "bold", ], 'MESSAGE' => "$_", );
exec_command('COMMAND' => "mpg123 /root/wavs/ping.mp3 &", );
next;
}
if (/.*/) {
echo('MESSAGE' => "$_", );
next;
}
}