#!/usr/local/bin/perl # EMU is an event manager processing events from "emsg". # VERSION 2.34 # Copyright 1999 # by Jarrix Systems Pty Ltd. All rights reserved. Some individual # files in this distribution may be covered # by other copyrights, as noted in their embedded comments. # # Redistribution and use in source and binary forms are permitted # provided that this entire copyright notice is duplicated in all such # copies, and that any documentation, announcements, and other # materials related to such distribution and use acknowledge that the # software was developed at Jarrix Systems Pty Ltd by Jarra and Anna # Voleynik. # # No charge, other than an "at-cost" distribution fee, may be charged # for copies, derivations, or distributions of this material without # the express written consent of the copyright holder. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR # PURPOSE. require 5.002; use Socket; use Carp; use Fcntl; use SDBM_File; # socket init, put here because of perl on Windows NT my $proto = getprotobyname('tcp'); socket(Server, PF_INET, SOCK_STREAM, $proto) or die "\nCannot create socket\n\n"; $port = $ARGV[0]; $confdir = $ENV{EMUBASE}; if ($confdir) { $conffile = "$confdir/conf/$port".".cfg"; } else { $conffile = "/usr/local/emu/conf/$port".".cfg"; } # global variables $DEBUG = 0; $curday = -1; # force log file creation later on $curday2 = 1; # force log file creation later on $message_changed = 0; # is 1 if an update_count > 1 and message text has changed $severity_changed = 0; # is 1 if an update_count > 1 and message severity has changed $class_changed = 0; # is 1 if an update_count > 1 and message class has changed $txt_written = 0; # is 1 when txt file has been written $name = ""; # the real name of the host sending the message. Remember that the sender host and user name can be changed. # message attributes $key = ""; $value = ""; $send_host = ""; $send_time = ""; $send_user = ""; $severity = 1; $first_timestamp = 1; $update_timestamp = 1; $update_count = 0; $class = ""; $ttl = ""; $savettl = ""; $lag = 0; #lag for messages of type count $password = ""; # password in the message $message = ""; $type = ""; $msgnum = 0; %ENV = {}; $mcount = 1; $discard_msg = 0; # communicates the main loop if message is to be discarded # config file options $input_script = ""; $output_script = ""; $delete_script = ""; $gpassword = ""; # correct password from the config file $dbfile = ""; $logdir = ""; $outdir = ""; # files $textfile = ""; $lockfile = ""; # socket my $paddr; ####################### # write_txt_file - write the db contents into a text file ####################### sub write_txt_file { my $wmsgnum,$wsend_time,$wsend_host,$wsend_user,$wttl,$wseverity,$wclass,$wmessage,$wtype,$wcomment,$wfirst_timestamp,$wupdate_timestamp,$wupdate_count; my $wkey,$wvalue; return 1 if ($discard_msg); # create lock file to let browsers know print "creating lock file\n" if ($DEBUG); open LOCK, ">$lockfile" or log_err("cannot create $lockfile for txt file access", 0); # print LOCK "\n"; close LOCK; print "writing text file\n" if ($DEBUG); open TEXT,">>$textfile" or log_err("Cannot open file $textfile", 0); truncate TEXT, 0; while (($wkey,$wvalue) = each %DB) { if ( $wkey eq MSGNUM ) { # skip msgnum counter next; } ($wmsgnum,$wsend_time,$wsend_host,$wsend_user,$wttl,$wseverity,$wtype,$wfirst_timestamp,$wupdate_timestamp,$wupdate_count) = unpack("A6A12A64A24A12A12A24A16A16A12",$DB{$wkey}); # print messages of type normal if ($wtype eq "normal") { # get the message ($wmessage,$wcomment,$wclass) = unpack("A256A256A128",$MDB{$wkey}); print TEXT $wmsgnum,"^","$wkey","^",$wsend_time,"^",$wsend_host,"^",$wsend_user,"^",$wttl,"^",$wseverity,"^",$wclass,"^",$wmessage,"^",$wtype,"^",$wcomment,"^",$wfirst_timestamp,"^",$wupdate_timestamp,"^",$wupdate_count,"\n"; } } close TEXT; #remove lock file print "removing lock file\n" if ($DEBUG); unlink ("$lockfile"); $txt_written = 1; } ############# # get_options - get options from config file ############# sub get_options { open OPT, "@_" or die "\nCannot open @_\n\n"; while() { if(/^\#/) { next; } if(/logdir\s+(.*)/) { $logdir = $1; print "logdir $logdir\n" if ($DEBUG); } if(/password\s+(.*)/) { $gpassword = $1; print "gpassword $gpassword\n" if ($DEBUG); } if(/output_script\s+(.*)/) { $output_script = $1; print "output_script $output_script\n" if ($DEBUG); } if(/delete_script\s+(.*)/) { $delete_script = $1; print "delete_script $delete_script\n" if ($DEBUG); } if(/input_script\s+(.*)/) { $input_script = $1; print "input_script $input_script\n" if ($DEBUG); } if(/dbfile\s+(.*)/) { $dbfile = $1; print "dbfile $dbfile\n" if ($DEBUG); } if(/outdir\s+(.*)/) { $outdir = $1; print "outdir $outdir\n" if ($DEBUG); } } close OPT; } ######################## # set_environ - set up environment variables ######################## sub set_environ { print "set_environ\n" if ($DEBUG); if ("$type" eq "delete") { $ENV{E_KEY} = $object; } else { $ENV{E_KEY} = $key; } $ENV{E_MSGNUM} = $msgnum; $ENV{E_TYPE} = $type; $ENV{E_TIME} = $send_time; $ENV{E_USER} = $send_user; $ENV{E_SEV} = $severity; $ENV{E_COUNT} = $update_count; $ENV{E_CLASS} = $class; $ENV{E_TTL} = $savettl; $ENV{E_HOST} = $send_host; $ENV{E_RHOST} = $name; $ENV{E_MSG} = $message; $ENV{E_CHANGED} = $message_changed; $ENV{E_SEV_CHANGED} = $severity_changed; $ENV{E_CLASS_CHANGED} = $class_changed; $ENV{E_COMMENT} = $comment; $ENV{E_TXT_WRITTEN} = $txt_written; } ######################## # log_err - log errors and warnings ######################## sub log_err { my $lmsg = $_[0]; my $ex = $_[1]; print "$lmsg\n" if ($DEBUG); ($sec,$min,$hour,$mday,$mon,$year) = localtime(); # get current time $mday2 = sprintf "%02d", $mday; $mon2 = sprintf "%02d", $mon + 1; $hour2 = sprintf "%02d", $hour; $min2 = sprintf "%02d", $min; $year = $year + 1900; open ELOG, ">>$errlogfile"; print ELOG "$year$mon2$mday2$hour2$min2 - $lmsg\n"; close ELOG; exit 1 if ($ex); } ####################### # do_log - put an entry in the log file ####################### sub do_log { my $logfile; my $sec = 0; my $min = 0; my $hour = 0; my $mday = 0; my $mon = 0; # (0-11) my $nmon = 0; # (1-12) my $nmon2 = 0; # (1-12) print "do log \n" if ($DEBUG); return 1 if ($discard_msg); set_environ; ($sec,$min,$hour,$mday,$mon,$year) = localtime(); # get current time $nmon = $mon + 1; if($mday != $curday) { close LOG if ($curday > 0);; $curday = $mday; $year = $year + 1900; $curday2 = sprintf "%02d", $curday; $nmon2 = sprintf "%02d", $nmon; $logfile = "$logdir/$year"."$nmon2"."$curday2.log"; open LOG, ">>$logfile" or die "\nLog file $logfile can not be created\n\n"; chmod 0640, $logfile; } select(LOG); $| = 1; # synchronous write for logs printf LOG "%02d%02d%02d%02d|%s|%s|%s|%s|%s|%s|%s|%s|%s\n",$mday,$mon+1,$hour,$min,$type,$send_time,$send_host,$send_user,$severity,$update_count,$class,$savettl,$message; select(STDOUT); $| = 0; } ############################ # do_input_script - run the input script ############################ sub do_input_script { my $ret; print "do_input_script\n" if ($DEBUG); if( -f $input_script ) { set_environ; $ret = system("$input_script"); $ret = $ret / 256; return $ret; } } ############################ # do_output_script - run the output script ############################ sub do_output_script { my $ret; print "do_output_script\n" if ($DEBUG); return 1 if ($discard_msg); if( -f $output_script ) { set_environ; $ret = system("$output_script &"); return $ret; } } ############################ # do_delete_script - run the delete script ############################ sub do_delete_script { my $ret; print "do_delete_script\n" if ($DEBUG); return 1 if ($discard_msg); if( -f $delete_script ) { set_environ; $ret = system("$delete_script &"); return $ret; } } ########################### # consolidate_nums - re-number messages starting with a 0 ########################### sub consolidate_nums { my $cmsgnum,$csend_time,$csend_host,$csend_user,$cttl,$cseverity,$cclass,$cmessage,$ctype,$ccomment,$cfirst_timestamp,$cupdate_timestamp,$cupdate_count; my $ckey, $cvalue; my $ccounter = 0; while (($ckey,$cvalue) = each %DB) { if ( $ckey eq "MSGNUM" ) { # skip msgnum counter next; } ($cmsgnum,$csend_time,$csend_host,$csend_user,$cttl,$cseverity,$ctype,$cfirst_timestamp,$cupdate_timestamp,$cupdate_count) = unpack("A6A12A64A24A12A12A24A16A16A12",$DB{$ckey}); print "old $cmsgnum \n" if ($DEBUG); $cmsgnum = $ccounter; print "new $cmsgnum \n" if ($DEBUG); $DB{$ckey} = pack("A6A12A64A24A12A12A24A16A16A12",$cmsgnum,$csend_time,$csend_host,$csend_user,$cttl,$cseverity,$ctype,$cfirst_timestamp,$cupdate_timestamp,$cupdate_count); $ccounter++; } return $ccounter; } ########################### # get_expiry - calculate ttl if not hh:mm ########################### sub get_expiry { my $gttl = $_[0]; SWITCH: { # seconds $gttl = $gttl + time(), last SWITCH if ($gttl =~ /^\d+s$/); $gttl = $gttl + time(), last SWITCH if ($gttl =~ /^\d+$/); # minutes $gttl = ($gttl*60) + time(), last SWITCH if ($gttl =~ /^\d+m$/); # hours $gttl = ($gttl*3600) + time(), last SWITCH if ($gttl =~ /^\d+h$/); # hh:mm $gttl = $gttl, last SWITCH if ($gttl =~ /^\d\d:\d\d/); $gttl = $gttl, last SWITCH if ($gttl =~ /^-1$/); $gttl = $gttl, last SWITCH if ($gttl =~ /^0$/); log_err("wrong ttl ($gttl) for ($send_host:$object)", 0); $discard_msg = 1; # discard the message } return $gttl; } ############################## # add_update - add or update a message ############################## sub add_update { my $umsgnum,$usend_time,$usend_host,$usend_user,$uttl,$useverity,$uclass,$umessage,$utype,$ucomment,$ufirst_timestamp,$uupdate_timestamp,$uupdate_count; # $key is global now print "add_update \n" if ($DEBUG); if (! $DB{$key}) { # doesn't exist in the database $mcount = 1; $msgnum = $DB{"MSGNUM"}; if($msgnum >= 9999) { $MCOUNT = &consolidate_nums; print "consolidate returned $MCOUNT\n" if ($DEBUG); $msgnum = $MCOUNT; print "this msgnum = $msgnum\n" if ($DEBUG); $MCOUNT++; $DB{"MSGNUM"} = $MCOUNT; print "next msgnum = $MCOUNT\n" if ($DEBUG); } else { $DB{"MSGNUM"}++; } $first_timestamp = time(); $update_timestamp = time(); $update_count = 1; $ttl = get_expiry($ttl); # get message expiry time return 1 if ($discard_msg); $DB{$key} = pack("A6A12A64A24A12A12A24A16A16A12",$msgnum,$send_time,$send_host,$send_user,$ttl,$severity,$type,$first_timestamp,$update_timestamp,$update_count); $MDB{$key} = pack("A256A256A128", $message,$comment,$class); write_txt_file; print "inserted $key in class $class\n" if ($DEBUG); } else { # update the entry # read the old entry ($umsgnum,$usend_time,$usend_host,$usend_user,$uttl,$useverity,$utype,$ufirst_timestamp,$uupdate_timestamp,$uupdate_count) = unpack("A6A12A64A24A12A12A24A16A16A12",$DB{$key}); ($umessage,$ucomment,$uclass) = unpack("A256A256A128",$MDB{$key}); $msgnum = $umsgnum; # set global variable # masked =>, do nothing unless extending a mask message life if ($utype eq "mask" && $type ne "mask") { print "message ($key) is masked out\n" if ($DEBUG); return 5; } # convert to secs or leave descriptive time (HH:MM) $uupdate_timestamp = time(); $uttl = get_expiry($ttl); # get message expiry time return 1 if ($discard_msg); $uupdate_count++; $update_count = $uupdate_count; # set global variable if ($utype =~ /count/ && $lag == $uupdate_count) { $utype = "normal"; $type = "normal"; $update_count = 1; # reset count to 1 $uupdate_count = 1; # reset count to 1 print "message ($key) reached a count of $lag\n" if ($DEBUG); } $message_changed = 1 if ($umessage ne $message); $umessage = $message; # change severity as it may have changed $severity_changed = 1 if ($useverity ne $severity); $useverity = $severity; # perharp the class may have changed as well $class_changed = 1 if ($uclass ne $class); $uclass = $class; # allow overwrite of message type if not "count" $utype = $type if ($type !~ /count/ ); $DB{$key} = pack("A6A12A64A24A12A12A24A16A16A12",$umsgnum,$usend_time,$send_host,$usend_user,$uttl,$useverity,$utype,$ufirst_timestamp,$uupdate_timestamp,$uupdate_count); $MDB{$key} = pack("A256A256A128",$umessage,$ucomment,$uclass); write_txt_file if ( $message_changed || $severity_changed || $class_changed || $update_count < 6 ); print "updated $key $uclass num - ",$umsgnum," count - ",$uupdate_count,"\n" if ($DEBUG); } } ########################### # do_delete - delete a message ########################### sub do_delete { my $dkey = $_[0]; my $dwrite = $_[1]; # 1 => write text file my $dfirst_timestamp,$dupdate_timestamp,$dsend_host,$dtype; return 1 if ($discard_msg); print "deleting $dkey ...\n" if ($DEBUG); # retrieve message attributes for the set_environ procedure ($msgnum,$send_time,$dsend_host,$send_user,$savettl,$severity,$dtype,$dfirst_timestamp,$dupdate_timestamp,$update_count) = unpack("A6A12A64A24A12A12A24A16A16A12",$DB{$dkey}); ($message,$comment,$class) = unpack("A256A256A128",$MDB{$dkey}); delete $DB{$dkey}; delete $MDB{$dkey}; if ($dwrite == 1) { write_txt_file; } } ########################### # do_wakeup - wake up a sleeping message ########################### sub do_wakeup { my $wkmsgnum,$wksend_time,$wksend_host,$wksend_user,$wkttl,$wkseverity,$wkclass,$wkmessage,$wktype,$wkcomment,$wkfirst_timestamp,$wkupdate_timestamp,$wkupdate_count; my $wkkey = $_[0]; my $wkwrite = $_[1]; # 1 => write text file my ($sec,$min,$hour,$mday,$mon) = localtime(); return 1 if ($discard_msg); print "do_wakeup \n" if ($DEBUG); ($wkmsgnum,$wksend_time,$wksend_host,$wksend_user,$wkttl,$wkseverity,$wktype,$wkfirst_timestamp,$wkupdate_timestamp,$wkupdate_count) = unpack("A6A12A64A24A12A12A24A16A16A12",$DB{$wkkey}); $wktype = "normal"; $wksend_time = sprintf "%02d/%02d %02d:%02d", $mday, $mon+1, $hour, $min; $wkttl = -1; $wkupdate_count = 1; $DB{$wkkey} = pack("A6A12A64A24A12A12A24A16A16A12",$wkmsgnum,$wksend_time,$wksend_host,$wksend_user,$wkttl,$wkseverity,$wktype,$wkfirst_timestamp,$wkupdate_timestamp,$wkupdate_count); ($wkmessage,$wkcomment,$wkclass) = unpack("A256A256A128",$MDB{$wkkey}); $send_time = $wksend_time; $send_user = $wksend_user; $severity = $wkseverity; $update_count = $wkupdate_count = 1; $class = $wkclass; $savettl = $wkttl; $send_host = $wksend_host; $message = $wkmessage; $comment = $wkcomment; $key = $wkkey; if ($wkwrite == 1) { write_txt_file; } } ########################### # do_comment - add a comment to an existing message ########################### sub do_comment { my $comsgnum,$cosend_time,$cosend_host,$cosend_user,$cottl,$coseverity,$coclass,$comessage,$cotype,$cocomment,$cofirst_timestamp,$coupdate_timestamp,$coupdate_count; my $cokey = $_[0]; my $cowrite = $_[1]; # 1 => write text file return 1 if ($discard_msg); print "do_comment \n" if ($DEBUG); ($comessage,$cocomment,$coclass) = unpack("A256A256A128",$MDB{$cokey}); $cocomment = $comment; $MDB{$cokey} = pack("A256A256A128",$comessage,$cocomment,$coclass); if ($cowrite == 1) { write_txt_file; } } ########################## # do_query - return a message type if it exists ########################## sub do_query { my $qmsgnum,$qsend_time,$qsend_host,$qsend_user,$qttl,$qseverity,$qclass,$qmessage,$qtype,$qcomment,$qfirst_timestamp,$qupdate_timestamp,$qupdate_qunt; my $qkey = $_[0]; my $qwrite = $_[1]; # 1 => write text file my $line; my $RET; # return code return 1 if ($discard_msg); print "do_query \n" if ($DEBUG); if($qkey eq "FILE" || $qkey eq "_FILE") { # for security $rest =~ s:\.\.::g; $rest =~ s:\/::g; $file = "$outdir"."/$rest"; if( ! -f $file) { print Client "none\n"; } else { open (QRY,"< $file") or log_err("Cannot open $file",0); while() { $line = $_; print Client "$line"; } close QRY; if($qkey eq "FILE") { unlink("$file"); $RET = 0; } else { $RET = 1; } } close Client; return $RET; } if(! $DB{$qkey}) { print Client "none\n"; close Client; return 0; } ($qmsgnum,$qsend_time,$qsend_host,$qsend_user,$qttl,$qseverity,$qtype,$qfirst_timestamp,$qupdate_timestamp,$qupdate_qunt) = unpack("A6A12A64A24A12A12A24A16A16A12",$DB{$qkey}); print Client "$qtype\n"; close Client; return 0; } ########################################################################## # main program ########################################################################## if($#ARGV != 0) { die "\nemu \n\n"; } get_options $conffile; # establish the message db file $mdbfile = $dbfile."m"; # open the key database tie(%DB, SDBM_File, "$dbfile" ,O_RDWR|O_CREAT,0660) or die "cannot tie the key database"; untie %DB; # open the message database tie(%MDB, SDBM_File, "$mdbfile" ,O_RDWR|O_CREAT,0660) or die "cannot tie the message database"; untie %MDB; # make sure message count is present if( ! $DB{"MSGNUM"}) { $DB{"MSGNUM"} = 0; } # establish the text file for browsers $textfile = $dbfile.".txt"; # establish the lock file for browsers $lockfile = $dbfile.".lck"; # establish the error log file $errlogfile = $logdir."/"."err.log"; # get the current process ID for later killing $pidfile = $logdir."/"."emu.pid"; open PID, ">$pidfile" or log_err("Cannot open file $pidfile", 0); print PID "$$\n"; close PID; # make sure the textfile exists if (! -f $textfile) { open TXT, ">$textfile" or log_err("Cannot open file $textfile", 0); close TXT; } #system("touch $textfile"); # Now the socket stuff setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l",1)) or die "\nCannot setsockopt\n\n"; my $sa = Socket::sockaddr_in($port, INADDR_ANY); bind(Server, $sa ) or die "\nCannot bind\n\n"; listen(Server, SOMAXCONN) or die "\nCannot listen\n\n"; print "EMU server started on port $port\n" if ($DEBUG); for (; $paddr= accept(Client,Server); close Client) { my ($port, $iaddr) = sockaddr_in($paddr); $name = gethostbyaddr($iaddr, AF_INET); # open the key database tie(%DB, SDBM_File, "$dbfile" ,O_RDWR|O_CREAT,0660) or die "cannot tie the key database"; # open the message database tie(%MDB, SDBM_File, "$mdbfile" ,O_RDWR|O_CREAT,0660) or die "cannot tie the message database"; while () { print "message received from $name\n" if ($DEBUG); $discard_msg = 0; $message_changed = 0; $severity_changed = 0; $class_changed = 0; $txt_written = 0; $update_count = 0; # get message attributes chop; ($send_time,$send_host,$send_user,$ttl,$severity,$password,$class,$message,$type) = unpack("A12A64A24A12A12A24A128A256A24",$_); print "$send_time,$send_host,$send_user,$ttl,$severity,$password,$class,$message,$type\n" if $DEBUG; # discard if password mismatch print "password check ... +$gpassword+ vs +$password+\n" if $DEBUG; last if ($gpassword ne $password); print "password OK\n" if $DEBUG; # save $ttl $savettl = $ttl; # form key # get the first word of message => object $message =~ /^([^ ]+)\s*(.*)$/; ($object,$rest) = ($1,$2); print "object = $object\n" if ($DEBUG); $key = "$send_host".":"."$object"; print "key = $key\n" if ($DEBUG); # check input script print "input_script check ...\n" if $DEBUG; last if (do_input_script > 0); print "input_script OK\n" if $DEBUG; # case the message type $comment = ""; SWITCH: { if ($type eq "comment") { $comment = $rest; do_comment ($object, 1); do_log; do_output_script; last SWITCH; } if ($type eq "normal") { $RET = add_update if ($ttl != 0); do_log; do_output_script if ($RET != 5); # if not masked out last SWITCH; } if ($type eq "delete") { do_log; do_delete ($object, 1); # message is the key do_delete_script; last SWITCH; } if ($type eq "suspend") { do_log; if ($object =~ /^\d+$/) { untie %DB; untie %MDB; sleep $object; tie(%DB, SDBM_File, "$dbfile" ,O_RDWR|O_CREAT,0660) or die "cannot tie the key database"; tie(%MDB, SDBM_File, "$mdbfile" ,O_RDWR|O_CREAT,0660) or die "cannot tie the message database"; } else { log_err("time ($object) for suspend from ($send_host)", 0); } last SWITCH; } if ($type eq "mask") { add_update; do_log; do_output_script; last SWITCH; } if ($type eq "sleep") { add_update; do_log; do_output_script; last SWITCH; } if ($type eq "event") { add_update; do_log; do_output_script; last SWITCH; } if ($type eq "query") { last SWITCH if (do_query($object, 1) == 1); # _FILE do_log; do_output_script; last SWITCH; } if ($type eq "wakeup") { do_log; do_wakeup ($object, 1); # wake up a sleep message, message is the key do_output_script; last SWITCH; } if ($type =~ /^count\s(\d+)$/) { $lag = $1; $RET = add_update; do_log; do_output_script if ($RET != 5); # not masked out last SWITCH; } #endif log_err("message type ($type) from ($send_host:$object) not supported or invalid", 0); } #_end switch } #_end while untie %DB; untie %MDB; } #_end of for