view release on metacpan or search on metacpan
t/data/AA_01.msg
t/data/AA_01_32_47.msg
t/data/AA_02.msg
t/data/AA_03.doc
t/data/AA_meta.msg
t/data/AAA_01.msg
t/data/non_ecs.msg
t/data/non_ecs_2.msg
t/ecs.t
t/filebackedmessage.t
t/lockedhash.t
t/message.t
t/setup
web_status/README
web_status/ecs_status
web_status/ecs_status.pl
web_status/images/ecs_logo.ico
web_status/images/led_horiz_black.png
web_status/images/led_horiz_blue.png
web_status/images/led_horiz_green.png
web_status/images/led_horiz_grey.png
lib/EMDIS/ECS.pm view on Meta::CPAN
}
return "EMDIS::ECS::send_ecs_message(): HUB_SND is incorrect: $hub_snd"
unless $hub_snd eq $ECS_CFG->THIS_NODE;
$node_id = $hub_rcv unless $node_id;
return "EMDIS::ECS::send_ecs_message(): node_id ($node_id) and FML " .
"HUB_RCV ($hub_rcv) do not match"
unless $node_id eq $hub_rcv;
}
# look up specified node in node_tbl
my $was_locked = $node_tbl->LOCK;
if(not $was_locked) {
$node_tbl->lock() # lock node_tbl if needed
or return "EMDIS::ECS::send_ecs_message(): unable to lock node_tbl: " .
$node_tbl->ERROR;
}
my $node = $node_tbl->read($node_id);
if(not $node) {
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "EMDIS::ECS::send_ecs_message(): node not found: " . $node_id;
}
if(not $node->{addr}) {
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "EMDIS::ECS::send_ecs_message(): addr not defined for node: $node_id";
}
if($seq_num =~ /auto/i) {
# automatically get next sequence number
$node->{out_seq}++;
$seq_num = $node->{out_seq};
}
my $subject = $cfg->MAIL_MRK . ':' . $cfg->THIS_NODE;
$subject .= ":$seq_num" if $seq_num;
lib/EMDIS/ECS.pm view on Meta::CPAN
@_);
}
if ( ! $err ) {
# update node last_out, possibly out_seq
$node->{last_out} = time();
$err = $node_tbl->ERROR
unless $node_tbl->write($node_id, $node);
}
$node_tbl->unlock() # unlock node_tbl
unless $was_locked;
return $err;
}
# ----------------------------------------------------------------------
# Send email message. Takes four or more arguments: the recipient,
# subject line, custom headers (hash ref), and body lines to be emailed.
# Returns empty string if successful or error message if error encountered.
sub send_email {
my $recipient = shift;
lib/EMDIS/ECS/FileBackedMessage.pm view on Meta::CPAN
$this->{filename} = $filename;
my $file_handle;
return "Unable to open input file $filename: $!"
unless open $file_handle, "+< $filename";
$this->{file_handle} = $file_handle;
binmode $file_handle;
# get exclusive lock (with retry loop)
# protects against reading a file while another process is writing it
my $locked = '';
for my $retry (1..5)
{
$locked = flock $file_handle, LOCK_EX | LOCK_NB;
last if $locked;
}
if(!$locked)
{
$err = "Unable to lock input file $filename: $!";
close $file_handle;
return $err;
}
my $email_headers = '';
my $data_offset = 0;
# attempt to read email headers only if sender_node_id not yet defined
lib/EMDIS/ECS/FileBackedMessage.pm view on Meta::CPAN
return "send_this_message(): ECS has not been configured."
unless ecs_is_configured();
my $cfg = $ECS_CFG;
my $node_tbl = $ECS_NODE_TBL;
my $err = '';
return "send_this_message(): Missing \$rcv_node_id!"
unless defined $rcv_node_id and $rcv_node_id;
# lock node_tbl, look up $rcv_node_id
my $was_locked = $node_tbl->LOCK;
if(not $was_locked)
{
$node_tbl->lock() # lock node_tbl
or return "send_this_message(): unable to lock node_tbl: " .
$node_tbl->ERROR;
}
my $node = $node_tbl->read($rcv_node_id);
if(not $node)
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): node not found: $rcv_node_id";
}
if(not $node->{addr})
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): addr not defined for node: $rcv_node_id";
}
# compute or assign message seq_num
my $seq_num = '';
if($is_re_send and not $this->{is_document})
{
# sanity checks
if(not defined $this->{seq_num})
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): seq_num not defined for RE_SEND";
}
if($this->{seq_num} > $node->{out_seq})
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): seq_num for RE_SEND (" .
$this->{seq_num} . ") is greater than out_seq for node " .
"$rcv_node_id (" . $node->{out_seq} . ")!";
}
$seq_num = $this->{seq_num};
}
elsif($is_re_send and $this->{is_document})
{
# sanity checks
if(not defined $this->{seq_num})
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): seq_num not defined for DOC_RE_SEND";
}
if($this->{seq_num} > $node->{doc_out_seq})
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): seq_num for DOC_RE_SEND (" .
$this->{seq_num} . ") is greater than doc_out_seq for node " .
"$rcv_node_id (" . $node->{doc_out_seq} . ")!";
}
$seq_num = $this->{seq_num};
}
elsif($this->{is_document})
{
# automatically get next (doc) sequence number
$node->{doc_out_seq}++;
$seq_num = $node->{doc_out_seq};
}
elsif(not $this->{is_meta_message})
{
# only allow $part_num to be specified if this is a RE_SEND request
if($part_num)
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): part_num specified ($part_num), for " .
"non- RE_SEND request!";
}
# automatically get next (msg) sequence number
$node->{out_seq}++;
$seq_num = $node->{out_seq};
}
# compute message part size
my $msg_part_size = $cfg->MSG_PART_SIZE_DFLT;
if(defined $node->{msg_part_size} and $node->{msg_part_size} > 0)
{
$msg_part_size = $node->{msg_part_size};
}
# compute data size
my $file_size = (stat $this->{file_handle})[7];
my $data_size = $file_size - $this->{data_offset};
if($data_size <= 0)
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): data_size is <= 0 ($data_size)!";
}
# for document, force num_parts = 1
if($this->{is_document})
{
$msg_part_size = $data_size;
}
# compute num_parts
my $num_parts = int($data_size / $msg_part_size);
$num_parts++ if ($data_size % $msg_part_size) > 0;
# num_parts should be 1 for meta message
if($this->{is_meta_message} and $num_parts > 1)
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): num_parts cannot be > 1 for meta message!";
}
# $part_num cannot be greater than $num_parts
if(defined $part_num and $part_num and $part_num > $num_parts)
{
$node_tbl->unlock() unless $was_locked; # unlock node_tbl if needed
return "send_this_message(): part_num ($part_num) cannot be greater " .
"than num_parts ($num_parts)!";
}
# compute base subject
my $subject = $cfg->MAIL_MRK . ':' . $cfg->THIS_NODE;
$subject .= ":$seq_num" if $seq_num;
$subject .= ":DOC" if $this->{is_document};
if($is_re_send)
lib/EMDIS/ECS/FileBackedMessage.pm view on Meta::CPAN
print $fh "From: " . $cfg->SMTP_FROM . "\n\n";
# copy data to $fh
$err = "Unable to position file pointer for file $this->{filename}" .
" to position $this->{data_offset}: $!"
unless seek $this->{file_handle}, $this->{data_offset}, 0;
my $buffer;
while(1)
{
if($err)
{
$node_tbl->unlock() unless $was_locked; # unlock if needed
close $fh;
unlink $filename;
return $err;
}
my $bytecount = read $this->{file_handle}, $buffer, 65536;
if(not defined $bytecount)
{
$err = "send_this_message(): Problem reading input file " .
"$this->{filename}: $!";
lib/EMDIS/ECS/FileBackedMessage.pm view on Meta::CPAN
}
if(not $err)
{
# update node last_out, possibly out_seq
$node->{last_out} = time();
$err = $node_tbl->ERROR
unless $node_tbl->write($rcv_node_id, $node);
}
$node_tbl->unlock() # unlock node_tbl if needed
unless $was_locked;
return $err;
}
1;
__DATA__
# embedded POD documentation
# for more info: man perlpod
lib/EMDIS/ECS/LockedHash.pm view on Meta::CPAN
sub ERROR {
my $this = shift;
my $err = shift;
if(defined $err) {
$this->{ERROR} = $err;
}
return $this->{ERROR};
}
# ----------------------------------------------------------------------
# set/get locked status indicator
sub LOCK {
my $this = shift;
my $status = shift;
if(defined $status) {
$this->{LOCK} = $status;
}
return $this->{LOCK};
}
# ----------------------------------------------------------------------
lib/EMDIS/ECS/LockedHash.pm view on Meta::CPAN
}
# ----------------------------------------------------------------------
# Obtain (advisory) lock and tie internal hash to db file.
sub lock {
my $this = shift;
my $lock_type = shift;
my $oldlock = $this->LOCK;
$lock_type = LOCK_EX unless $lock_type; # default = LOCK_EX
$this->ERROR(''); # reset error status
return 1 if $oldlock == $lock_type; # already locked
my $locked = 0;
my $attempt = 0;
while(!$locked and $attempt++ < 5) {
sleep 2 if $attempt > 1;
$this->ERROR(''); # reset error status
$locked = $this->_lock($lock_type);
}
if(!$locked) {
$this->ERROR("EMDIS::ECS::LockedHash::lock() failed: " . $this->ERROR);
return '';
}
if(!$this->TIED and !$this->_tie()) {
$this->ERROR("EMDIS::ECS::LockedHash::lock() failed: " . $this->ERROR);
return '';
}
return 1; # successful
}
lib/EMDIS/ECS/LockedHash.pm view on Meta::CPAN
sub _lock_win32 {
my $this = shift;
my $lock_type = shift;
$lock_type = LOCK_EX unless defined $lock_type;
my $result = 1;
# attempt to obtain lock, with time limit
# (uses polling method to obtain lock -- somewhat more crude than
# the unix method, which uses blocking with SIGALRM to enforce timeout)
my $timeoutCount = 0;
my $locked;
while (!($locked = flock($this->{FH_LOCK}, $lock_type | LOCK_NB)) and
($timeoutCount++ <= $this->{lock_timeout})) {
sleep 1;
}
if(!$locked) {
$this->ERROR("EMDIS::ECS::LockedHash::_lock_win32() failed: $@");
$this->LOCK(0); # reset status indicator
$result = '';
}
$this->LOCK($lock_type) # set status indicator
if $result;
return $result; # successful
}
# ----------------------------------------------------------------------
lib/EMDIS/ECS/Message.pm view on Meta::CPAN
$filename = $arg1;
}
# read encrypted file
my $newmsg = read_from_file($filename);
return $newmsg unless ref $newmsg; # check for error
return "not an ECS message" unless $newmsg->is_ecs_message or $newmsg->is_document;
# read relevant node info from node_tbl
my $node_tbl = $main::ECS_NODE_TBL;
my $was_locked = $node_tbl->LOCK;
if(not $was_locked) {
$node_tbl->lock() # lock node_tbl
or return "unable to lock node_tbl: " .
$node_tbl->ERROR;
}
my $node = $node_tbl->read($newmsg->sender);
if(not $was_locked) {
$node_tbl->unlock(); # unlock node_tbl
}
if(not $node) {
return "node not found: " . $newmsg->sender;
}
# decrypt message into temp file
my $decr_filename = "$filename.asc";
for ($node->{encr_typ}) {
/PGP2/i and do {
script/ecs_scan_mail view on Meta::CPAN
}
my $err = $msg->inspect_fml();
if($err)
{
log_error("process_maildrop(): unable to inspect FML in " .
"$filename: $err");
last; # don't continue
}
# retrieve node status from node_tbl
my $was_locked = $ECS_NODE_TBL->LOCK;
if(not $was_locked)
{
# lock ECS_NODE_TBL
if(not $ECS_NODE_TBL->lock())
{
log_error("process_maildrop(): unable to lock ECS_NODE_TBL: " .
$ECS_NODE_TBL->ERROR);
last;
}
}
my $node = $ECS_NODE_TBL->read($msg->hub_rcv);
$ECS_NODE_TBL->unlock() unless $was_locked;
if (not defined $node) {
log_error("process_maildrop(): cannot read node '" . $msg->hub_rcv . "'");
next;
}
# don't process the message, if the receiving node is disabled
if (not ( (exists $node->{node_disabled}) and
is_yes($node->{node_disabled}) ) )
{
script/ecs_scan_mail view on Meta::CPAN
push @nodelist, $node_id;
}
}
# iterate through nodes and
# check whether any of the messages are able to be processed
NODE:
for my $node_id (@nodelist)
{
# retrieve node status from node_tbl
my $was_locked = $ECS_NODE_TBL->LOCK;
if(not $was_locked)
{
# lock ECS_NODE_TBL
if(not $ECS_NODE_TBL->lock())
{
log_error("process_store(): unable to lock ECS_NODE_TBL: " .
$ECS_NODE_TBL->ERROR);
last NODE;
}
}
my $node = $ECS_NODE_TBL->read($node_id);
$ECS_NODE_TBL->unlock() unless $was_locked;
# don't try to process if node not found
if(not ref $node)
{
log_error(
"process_store(): unable to retrieve node $node_id status.");
next NODE;
}
# is node marked as disabled?
if((exists $node->{node_disabled}) and
is_yes($node->{node_disabled}))
script/ecs_scan_mail view on Meta::CPAN
}
$q_doc_gap_time = time();
}
}
else
{
$q_doc_gap_seq = $seq_num;
$q_doc_gap_time = time();
}
# update q_doc_gap_seq and q_doc_gap_time in node_tbl
my $was_locked = $ECS_NODE_TBL->LOCK;
if(not $was_locked)
{
if(not $ECS_NODE_TBL->lock())
{
log_error("process_store(): unable to lock " .
"ECS_NODE_TBL: " . $ECS_NODE_TBL->ERROR);
}
}
if($ECS_NODE_TBL->LOCK)
{
my $node = $ECS_NODE_TBL->read($node_id);
$node->{q_doc_gap_seq} = $q_doc_gap_seq;
$node->{q_doc_gap_time} = $q_doc_gap_time;
$ECS_NODE_TBL->write($node_id, $node);
$ECS_NODE_TBL->unlock() unless $was_locked;
}
last NODE_DOC_MSG_PART;
}
# sanity check - docnumparts should always be 1
if($nodes->{$node_id}->{docnumparts}->{$seq_num} != 1) {
log_error("process_store(): unexpected error, document parts > 1 " .
"for $node_id:$seq_num:DOC");
last NODE_DOC_MSG_PART;
}
script/ecs_scan_mail view on Meta::CPAN
}
$q_gap_time = time();
}
}
else
{
$q_gap_seq = $seq_num;
$q_gap_time = time();
}
# update q_gap_seq and q_gap time in node_tbl
my $was_locked = $ECS_NODE_TBL->LOCK;
if(not $was_locked)
{
if(not $ECS_NODE_TBL->lock())
{
log_error("process_store(): unable to lock " .
"ECS_NODE_TBL: " . $ECS_NODE_TBL->ERROR);
}
}
if($ECS_NODE_TBL->LOCK)
{
my $node = $ECS_NODE_TBL->read($node_id);
$node->{q_gap_seq} = $q_gap_seq;
$node->{q_gap_time} = $q_gap_time;
$ECS_NODE_TBL->write($node_id, $node);
$ECS_NODE_TBL->unlock() unless $was_locked;
}
last NODE_MSG_PART;
}
# assemble message part file name array, and
# determine whether all message parts are present
my @msg_part_filenames = ();
my @missing_parts = ();
for my $pn (1..$nodes->{$node_id}->{numparts}->{$seq_num})
{
script/ecs_scan_mail view on Meta::CPAN
}
$q_gap_time = time();
}
}
else
{
$q_gap_seq = $seq_num;
$q_gap_time = time();
}
# update q_gap_seq and q_gap time in node_tbl
my $was_locked = $ECS_NODE_TBL->LOCK;
if(not $was_locked)
{
if(not $ECS_NODE_TBL->lock())
{
log_error("process_store(): unable to lock " .
"ECS_NODE_TBL: " . $ECS_NODE_TBL->ERROR);
}
}
if($ECS_NODE_TBL->LOCK)
{
my $node = $ECS_NODE_TBL->read($node_id);
$node->{q_gap_seq} = $q_gap_seq;
$node->{q_gap_time} = $q_gap_time;
$ECS_NODE_TBL->write($node_id, $node);
$ECS_NODE_TBL->unlock() unless $was_locked;
}
last NODE_MSG_PART;
}
# process message
my $filename = $msgpart->{$seq_part_num};
print "$DEBUG_LABEL processing \"store\" file: $filename\n"
if $ECS_CFG->{ECS_DEBUG} > 0;
my $msg = EMDIS::ECS::Message::read_from_file($filename);
script/ecs_scan_mail view on Meta::CPAN
{
my $msg = shift;
my $filename = shift;
my $decrypt = shift;
my $err = '';
print "$DEBUG_LABEL process_document(\$msg, $filename, $decrypt)\n"
if $ECS_CFG->ECS_DEBUG > 0;
# look up node in ECS_NODE_TBL
my $was_locked = $ECS_NODE_TBL->LOCK;
if(not $was_locked) {
# lock ECS_NODE_TBL
return "process_document(): unable to lock ECS_NODE_TBL: " .
$ECS_NODE_TBL->ERROR
unless $ECS_NODE_TBL->lock();
}
# store information about document currently being processed
my $this_node = $ECS_NODE_TBL->read($ECS_CFG->THIS_NODE);
$this_node->{proc_node} = $msg->sender;
$this_node->{proc_seq} = $msg->seq_num;
$this_node->{proc_file} = $filename;
$ECS_NODE_TBL->write($ECS_CFG->THIS_NODE, $this_node);
# retrieve node info
my $node = $ECS_NODE_TBL->read($msg->sender);
$ECS_NODE_TBL->unlock() unless $was_locked; # release node_tbl lock
if(not $node) {
# don't process message from unknown node
$err = "process_document(): $err; " . EOL()
if $err;
$err .= "process_document(): document from unknown node: " .
$msg->sender;
return $err;
}
# don't process "duplicate" document (seq_num too low)
script/ecs_scan_mail view on Meta::CPAN
# remove temp file
unlink $doc_fname;
return $err if $err ne '';
unlink $filename; # remove input file after successful processing
# message was processed
# if needed, update $node->{doc_in_seq}
if(not $was_locked) {
$ECS_NODE_TBL->lock() # lock ECS_NODE_TBL if needed
or return "process_document(): unable to (write) lock " .
"ECS_NODE_TBL: " . $ECS_NODE_TBL->ERROR;
}
$node = $ECS_NODE_TBL->read($msg->sender);
$err = $ECS_NODE_TBL->ERROR;
if((not $err) and (ref $node))
{
$node->{doc_in_seq}++;
if($msg->seq_num == $node->{doc_in_seq})
script/ecs_scan_mail view on Meta::CPAN
}
else
{
$node->{doc_in_seq_ack} = $node->{doc_in_seq};
}
}
$ECS_NODE_TBL->write($msg->sender,$node);
$err = $ECS_NODE_TBL->ERROR;
}
}
$ECS_NODE_TBL->unlock() unless $was_locked; # release node_tbl lock
return "process_document(): $err" if $err;
return '';
}
# ----------------------------------------------------------------------
# Process specified meta-message.
# returns error message, if any
sub process_meta_message
{
script/ecs_scan_mail view on Meta::CPAN
my $msg_part_filenames = shift;
my $err = '';
my @msgs = ();
my $child_pid;
print "$DEBUG_LABEL process_message(\$msg, $filename, $decrypt, (" .
join(', ', @$msg_part_filenames) . "))\n"
if $ECS_CFG->ECS_DEBUG > 0;
# look up node in ECS_NODE_TBL
my $was_locked = $ECS_NODE_TBL->LOCK;
if(not $was_locked) {
# lock ECS_NODE_TBL
return "process_message(): unable to lock ECS_NODE_TBL: " .
$ECS_NODE_TBL->ERROR
unless $ECS_NODE_TBL->lock();
}
# store information about message currently being processed
my $this_node = $ECS_NODE_TBL->read($ECS_CFG->THIS_NODE);
$this_node->{proc_node} = $msg->sender;
$this_node->{proc_seq} = $msg->seq_num;
$this_node->{proc_file} = $filename;
$ECS_NODE_TBL->write($ECS_CFG->THIS_NODE, $this_node);
# retrieve node info
my $node = $ECS_NODE_TBL->read($msg->sender);
$ECS_NODE_TBL->unlock() unless $was_locked; # release node_tbl lock
if(not $node) {
# don't process message from unknown node
$err = "process_message(): $err; " . EOL()
if $err;
$err .= "process_message(): message from unknown node: " .
$msg->sender;
return $err;
}
# don't process "duplicate" message (seq_num too low)
script/ecs_scan_mail view on Meta::CPAN
chmod $EMDIS::ECS::FILEMODE, $from_filename;
}
# remove temp file
unlink $payload_filename;
return $err if $err ne '';
# message was processed
# if needed, update $node->{in_seq}
if(not $was_locked) {
$ECS_NODE_TBL->lock() # lock ECS_NODE_TBL if needed
or return "process_message(): unable to (write) lock " .
"ECS_NODE_TBL: " . $ECS_NODE_TBL->ERROR;
}
$node = $ECS_NODE_TBL->read($msg->sender);
$err = $ECS_NODE_TBL->ERROR;
if((not $err) and (ref $node))
{
$node->{in_seq}++;
if($msg->seq_num == $node->{in_seq})
script/ecs_scan_mail view on Meta::CPAN
}
else
{
$node->{in_seq_ack} = $node->{in_seq};
}
}
$ECS_NODE_TBL->write($msg->sender,$node);
$err = $ECS_NODE_TBL->ERROR;
}
}
$ECS_NODE_TBL->unlock() unless $was_locked; # release node_tbl lock
return "process_message(): $err" if $err;
return '';
}
# ----------------------------------------------------------------------
# Re-read configuration when SIGHUP received.
sub sighup_handler
{
$reload_config = 1;