Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery

Timed event within a script

by bajangerry (Sexton)
on Mar 07, 2017 at 21:32 UTC ( [id://1183887] : perlquestion . print w/replies, xml ) Need Help??

bajangerry has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks!

I need some help with trying to find a way to have a single script complete two functions at the same time and then compile this into an executable... if that is at all possible.

The first point of the script is to constantly monitor a IP address and port for data and when any data is presented on that port number the script parses it and sends an alert if it finds the word "Emergency". This portion of the project is working fine at the moment.

The second function is for the script to send an "I'm alive" email every 30 mins or so but I can't seem to figure out how I can do this within the same script as the above even with subroutines in place. Currently I just have a simple sleep subroutine within the main script but it is not getting to that because I believe the main script is not allowing it to get that far.

My code (ugly as it is) is below. Any help appreciated!

#!/usr/bin/perl use strict; use IO::Socket; use POSIX; use warnings; use MIME::Lite; use Config::Simple; use Win32(); sub logMonitor{ # Reading configuration parameters from the config.ini file my $cfg = new Config::Simple(); $cfg->read('config.ini'); my $HOST = $cfg->param("pbx"); my $COMPANY = $cfg->param("company"); my $COMMUNITY = $cfg->param("community"); my $PORT = $cfg->param("port"); for(;; ){ my $sock = new IO::Socket::INET(PeerAddr => $HOST, PeerPort => + $PORT,Proto => "tcp",) or die "Cannot connect to PBX at address: $HOST port: $PORT: $ +!"; while (<$sock>) { s/^\0+//; # Remove leading null characters print $_; chomp ($_); if ($_ =~m"Emergency Call From Extension") { filePrint(); } } } } #End of monitor subroutine sub filePrint{ #write data to file with date and time stamp my $data = $_; print $data; my $file = strftime '%Y-%m-%d_%H-%M-%S.txt', localtime; if (-f $file){ open (my $fh,'>>', $file); print $fh "$_"; close $file; } else { open (my $fh,'>', $file); print $fh "$_"; close $file; } }# End of filePrint routine sub keepAlive{ while () { sleep 20; print " It's alive!!!"; } } #print disclaimer and wait for "y" to continue or exit my $disclaimer = "disclaimer.txt"; open(my $fh, '<:encoding(UTF-8)', $disclaimer) or die "Could not open file '$disclaimer' $!"; while (my $row = <$fh>) { chomp $row; Win32::MsgBox($row); } print "\nDo you accept this disclaimer and continue at your own risk?( +Y/N)?"; # first question yes/no my $input = <STDIN>; chomp $input; if ($input =~ m/^[Y]$/i){ # Start of processing logMonitor(); #Open the log monitoring subroutine keepAlive(); #start the alive email message }

Replies are listed 'Best First'.
Re: Timed event within a script
by stevieb (Canon) on Mar 07, 2017 at 21:51 UTC

    If you don't need to share any variables or anything, my Async::Event::Interval can help you. It forks off a separate process and performs work outside of the main process based on a callback subroutine within the script (you can name this sub whatever you want). Here's a basic example. The 1 in the call to new() is how often the event fires, in seconds (floating point seconds work), so for 30 minutes, you'd put 1800. The callback sub is the work you want the event to do. I've tested it on Strawberry perl v5.24.1, and the same version on Linux.

    use warnings; use strict; use Async::Event::Interval; my $event = Async::Event::Interval->new(1, \&callback); $event->start; my $c = 1; while (1){ print "$c: in the main process...\n"; $c++; sleep 10; } sub callback { print "we're alive!\n"; }


    1: in the main process... we're alive! we're alive! we're alive! we're alive! we're alive! we're alive! we're alive! we're alive! we're alive! we're alive! 2: in the main process... we're alive! we're alive!

      Thanks for you response, I will give this a try and see if it gives me what I want<\p>

Re: Timed event within a script
by johngg (Canon) on Mar 07, 2017 at 23:04 UTC

    Perhaps you could use alarm (but see this for portability issues) to trigger a $SIG{ ALRM } callback that sends the "I'm alive" message.

    Update: Corrected mis-matched brackets in text.



Re: Timed event within a script
by hippo (Bishop) on Mar 08, 2017 at 09:36 UTC

    To answer your question as stated, I would look first at an event loop, which in my preference would be AnyEvent. You might particularly consider the section in the tutorial headed Timers and other event sources to implement your 30-minute schedule.

    The second function is for the script to send an "I'm alive" email every 30 mins or so

    That, however is the intriguing part. I cannot think why you would want this - it sounds much more likely that you'd want to be alerted every 30 minutes or so if it wasn't alive - ie. in normal operation it's quiet, but when it is not running you receive an "I'm dead" email.

    Either way the trivial solution is not a Perl one but an O/S one: a cron job to run every 30 minutes which checks the process is running and sends your email if it is (or is not, as preferred).

      Thanks, I will have a look at AnyEvent and I agree with your comment about the alert email if the script isn't running but that is not what the user requested.

      I also wish it was a Linux machine so I can use cron but the script will be running in Windows and the request was for a "simple, single program" solution to use their words.<\p>

        I also wish it was a Linux machine so I can use cron

        FYI, Windows does have a chron "equivalent". It is called Task Scheduler. It has both a Windows and a command line interface. Something like "run program X every 30 minutes" is possible. I've used Task Scheduler to periodically run a Perl script before without problems.

        Update with a few comments and considerations: I wouldn't necessarily interpret the user request for "one program" as a hard and fast design implementation limitation. It appears that the main point appears to be something reliable. Simple often, but not always yields reliable.

        For the "wait for emergency" function, you could have a simple loop that does blocking I/O on that port while waiting for the "Emergency" message. If you get messages fairly regularly that are not "emergencies", blocking I/O would fine fine.

        For the once per 30 minute "watchdog on the monitor for emergency" function. In applications that I've worked on, more than the "process is running" is often required. The thing to be verified is: "is the thing that is monitoring for an emergency" really doing actual work(processing messages looking for an emergency)? You could have the main monitoring function do something that is observable by the "watchdog" process. Perhaps simply incrementing the name of a zero length file for every message it looks at? If the watchdog function determines that no messages are being processed by the monitoring function, it would conclude that something is amiss in addition to making sure that the monitor process is actually "running", i.e. in the process table.

Re: Timed event within a script
by huck (Prior) on Mar 07, 2017 at 23:56 UTC
Re: Timed event within a script
by madtoperl (Hermit) on Mar 08, 2017 at 10:03 UTC

    Based on my understanding on reading your problem, you are trying to do two things with a single script.

    I may go for,
    For first point - Schedule a cron job for every minute and do the monitoring stuff using perl script (you may select monitor option while running the script by passing argument as monitor)
    For second point - Schedule a cron job for every 30 minutes and send alert email using perl script(you may select alert option while running the same script by passing argument as alert)

    For last one "how to convert it into exe" - Please use PP you can look into one of the recent post by marto here
    Re: How to convert bunch of perl file + other dependent Input files Into a single executable