From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#!/usr/bin/perl -w
#
# Copyright (C) 2002-2016 National Marrow Donor Program. All rights reserved.
#
# For a description of this program, please refer to the POD documentation
# embedded at the bottom of the file (e.g. perldoc ecs_pid_chk).
use strict;
my $user = '';
if ( ! &GetOptions ( "user=s" => \$user)) {
print "\n$ARGV[0]: wrong parameter, use user=... !!!";
exit 1;
}
# load OS specific modules at compile time, in BEGIN block
BEGIN
{
if( $^O =~ /MSWin32/ )
{
# modules used only under Win32
eval "require File::Spec::Functions qw(catfile)";
eval "require Win32";
eval "require Win32::PerfLib";
eval "require EMDIS::ECS::Config";
}
else
{
# define subroutines referenced below
# (to avoid compile errors when not running under Win32)
package Win32::PerfLib;
eval "sub GetCounterNames { return undef; }";
eval "sub new { return undef; }";
package main;
}
}
# program now behaves differently under UNIX vs. Win32 ...
if( $^O =~ /MSWin32/ )
{
# Load ecs.cfg
my $cfg_file = 'ecs.cfg';
my $cfg = new EMDIS::ECS::Config($cfg_file);
print "Unable to load ECS configuration ($cfg_file): $cfg"
unless ref $cfg;
# Fill hash with current process list
my $tasks = get_remote_process_list(hostname());
# Check Process
check_PIDs_win32($cfg, $tasks, 'ecs_scan_mail');
check_PIDs_win32($cfg, $tasks, 'ecs_chk_com');
exit;
}
else
{
# Script is intended to be executed by a cron job.
# Accordingly, no output is generated if dameons are running.
# If daemons not running, cron sends email containing program output.
# check whether ECS daemons are running
my $errmsg = '';
$errmsg .= check_PIDs('ecs_chk_com');
$errmsg .= check_PIDs('ecs_scan_mail');
if($errmsg)
{
# print error message if daemon(s) not currently running
print STDERR "$errmsg\n";
exit -1;
}
exit 0;
}
exit 0;
# retrieve array of PID numbers for specified program
sub get_PIDs
{
my $progname = shift;
my $PS;
if ( $^O =~ /linux/i ) {
if ( $user eq '') {
$PS = '/bin/ps -fA'; # this works under Linux
}
else {
$PS = "/bin/ps -f -U $user";
}
}
else {
$PS = '/usr/bin/ps -fA'; # this works under Solaris
if($user ne '')
{
$PS = "/usr/bin/ps -fU $user";
}
}
# magical mystical one-liner to get PIDs
return map { /\s+(\d+)\s+/ ? $1 : () } grep /(\S+|^)$progname/, qx($PS);
}
# return error message unless specified program is running
sub check_PIDs
{
my $progname = shift;
my @pids = get_PIDs($progname);
if($#pids < 0)
{
return "\nERROR: $progname is currently not running on " .
hostname() . ".\n";
}
elsif ( $#pids > 0 )
{
return "\nERROR: more than one $progname is still running on "
. hostname() . ".\n";
}
return '';
}
# print error message unless specified program is running
sub check_PIDs_win32
{
my $cfg = shift;
my $tasks = shift;
my $progname = shift;
if(open PIDFILE,catfile($cfg->ECS_DAT_DIR, $progname . ".pid"))
{
my $pid = <PIDFILE>;
$pid =~ s/\s+//g;
if (defined $tasks->{$pid} and $tasks->{$pid} =~ /perl/ )
{
print "$progname is already running (pid $pid).\n";
}
else
{
print "$progname is NOT running\n";
}
close PIDFILE;
if (defined ($ARGV[0]) and $ARGV[0] == $pid )
{
unlink catfile($cfg->ECS_DAT_DIR, $progname . ".pid");
print "Killing $progname " . $ARGV[0] . "\n";
kill 9, $ARGV[0];
}
}
else
{
print "$progname is NOT running\n";
}
}
#Get Win32 process list (only for Windows 2000 or newer)
sub get_remote_process_list{
my $server = $_[0];
my %rtasks;
my %counter;
Win32::PerfLib::GetCounterNames($server, \%counter);
my %r_counter = map { $counter{$_} => $_ } keys %counter;
# retrieve the id for process object
my $process_obj = $r_counter{Process};
# retrieve the id for the process ID counter
my $process_id = $r_counter{'ID Process'};
# create connection to $server
my $perflib = new Win32::PerfLib($server);
if (! defined($perflib)) {die "$server error"};
my $proc_ref = {};
# get the performance data for the process object
$perflib->GetObjectList($process_obj, $proc_ref);
$perflib->Close();
my $instance_ref = $proc_ref->{Objects}->{$process_obj}->{Instances};
foreach my $p (sort keys %{$instance_ref})
{
my $counter_ref = $instance_ref->{$p}->{Counters};
foreach my $i (keys %{$counter_ref})
{
if($counter_ref->{$i}->{CounterNameTitleIndex} == $process_id)
{
$rtasks{$counter_ref->{$i}->{Counter}} =
$instance_ref->{$p}->{Name};
}
}
}
return \%rtasks;
}
__END__
# embedded POD documentation
# for more info: man perlpod
=head1 NAME
ecs_pid_chk - check whether ECS daemons are running
=head1 SYNOPSIS
ecs_pid_chk
=head1 DESCRIPTION
This program checks whether the ECS daemons are still running.
It is intended to be run periodically via a scheduling system such
as cron.
=head1 RETURN VALUE
Returns a non-zero exit code if a problem with the ECS daemons is detected.
=head1 BUGS
Possibly.
=head1 SEE ALSO
ecs_chk_com, ecs_scan_mail
=head1 AUTHOR
Joel Schneider <jschneid@nmdp.org>
=head1 COPYRIGHT AND LICENSE
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Copyright (C) 2002-2016 National Marrow Donor Program. All rights reserved.
See LICENSE file for license details.
=head1 HISTORY
ECS, the EMDIS Communication System, was originally designed and
implemented by the ZKRD (http://www.zkrd.de/). This Perl implementation
of ECS was developed by the National Marrow Donor Program
2004-03-12
Canadian Blood Services - Tony Wai
Rewritten to support MS Windows support for Windows 2000 and Windows XP
2007-08-01
ZKRD - emdisadm@zkrd.de
Added optional path for the ps command on linux systems.
Added optional parameter to select a user. This fixes the problem of a wrong
positive result in case of more than one running instance of perl ECS.
Modified the regular expressions in a way, that only the running daemons will
be detected. Otherwise you get an ERROR if you try to stop the daemons e.g.
while you work on the daemons source with an editor.
Added an additional exit code if there is more than one instance of a daemon.