How to catch signal in Perl and don't stop process - perl

I am trying to catch signal , SIGUSR2 in my case , I am creating subroutine to handle signal using next code
$SIG{USR2} =\&handle_usr2;
sub handle_usr2 {
open HELLO, ">hello" or die "die" ;
print HELLO "SAYHELLO";
close HELLO;
}
In this example I am catching signal and print some text to file. In this example signal really enters handle subroutine , it writes to file BUT after that process is killed.
So it kills process anyway what signal I am trapping.
BUT intresting thing is that if to set handler to 'IGNORE'
$SIG{USR2} = 'IGNORE';
it really ignores signal and doesn't kill process, how can I handle signal and don't kill process.

What does the rest of you code look like?
Because that should work fine, with one caveat (well two - you do potentially issue a 'die' within your handler). Kill will interrupt certain system calls, like 'sleep', and your code will jump past it.
IGNORE works a little differently - your code will discard the signal without processing it.

Related

ForkManager SIGINT only kills current process in fork

I want to have all child processes die when I kill a perl process that is using ForkManager. In the code below, if I run it and hit ctrl+c while the sleep line is running, the sleep process is killed, but the print lines are then all simultaneously executed before the script ends. Ideally, I'd like an interrupt to immediately stop all execution. What can I do?
#!/usr/bin/perl -w
use Parallel::ForkManager;
main {
my $fork1 = new Parallel::ForkManager(8);
while (1) {
$fork1->start and next;
system("sleep 15s");
print "Still going!"
$fork1->finish;
}
fork1->wait_all_children;
}
According to perldoc system, system actually ignores both SIGINT and SIGQUIT:
Since SIGINT and SIGQUIT are ignored during the execution of system,
if you expect your program to terminate on receipt of these signals
you will need to arrange to do so yourself based on the return value.
So if you want your processes to stop executing if you SIGINT during the system call, you need to implement that logic yourself:
#!/usr/bin/perl -w
use Parallel::ForkManager;
main {
my $fork1 = new Parallel::ForkManager(8);
while (1) {
$fork1->start and next;
print "Sleeping...";
system("sleep 15s") == 0 or exit($?);
print "Still going!";
$fork1->finish;
}
fork1->wait_all_children;
}
OR the more reasonable approach is the use the Perl built-in sleep:
#!/usr/bin/perl -w
use Parallel::ForkManager;
main {
my $fork1 = new Parallel::ForkManager(8);
while (1) {
$fork1->start and next;
print "Sleeping...";
sleep 15;
print "Still going!";
$fork1->finish;
}
fork1->wait_all_children;
}
First off - using system means you might have something strange happen, because ... then you're allowing whatever you're calling to do stuff to handle signals by itself.
That may be your problem.
However otherwise, what you can do with perl is configure signal handlers - what to do if a signal is recieved by this process. By default - signals are either set to 'exit' or 'ignore'.
You can see what this is currently via print Dumper \%SIG;
However the simplest solution to you problem I think, would be to set a handler to trap SIGINT and then send a kill to your current process group.
The behavior of kill when a PROCESS number is zero or negative depends on the operating system. For example, on POSIX-conforming systems, zero will signal the current process group, -1 will signal all processes, and any other negative PROCESS number will act as a negative signal number and kill the entire process group specified.
$SIG{'INT'} = sub {
kill ( 'TERM', -$$ );
};

perl: can end block be called when program is 'kill'

BEGIN {
while (1) {
print "hi\n";
}
}
END {
print "end is called\n";
}
in shell:
kill <pid>
OUTPUT:
hi
hi
hi
hi
hi
hi
hi
hi
hi
Terminated
The end block didnt get called when i killed it via kill or ctrl-c.
Is there something equivalent that will always get called before program exits
Ctrl C sends a SIGINT to your program. You can 'catch' this with a signal handler by setting the appropriate entry in %SIG. I would note - I don't see why you're using BEGIN that way. BEGIN is a special code block that's called at compile time - at the very first opportunity. That means it's triggered when you run perl -c to validate your code, and as such is really a bad idea to set as an infinite loop. See: perlmod
E.g.
#!/usr/bin/perl
use strict;
use warnings;
$SIG{'INT'} = \&handle_kill;
my $finished = 0;
sub handle_kill {
print "Caught a kill signal\n";
$finished++;
}
while ( not $finished ) {
print "Not finished yet\n";
sleep 1;
}
END {
print "end is called\n";
}
But there's a drawback - some signals you can't trap in this way. See perlipc for more details.
Some signals can be neither trapped nor ignored, such as the KILL and STOP (but not the TSTP) signals. Note that ignoring signals makes them disappear. If you only want them blocked temporarily without them getting lost you'll have to use POSIX' sigprocmask.
By default if you send a kill, then it'll send a SIGTERM. So you may want to override this handler too. However it's typically considered bad to do anything other than exit gracefully with a SIGTERM - it's more acceptable to 'do something' and resume when trapping SIGHUP (Hangup) and SIGINT.
You should note that Perl does 'safe signals' though - and so some system calls won't be interrupted, perl will wait for it to return before processing the signal. That's because bad things can happen if you abort certain operations (like close on a file where you're flushing data might leave it corrupt). Usually that's not a problem, but it's something to be aware of.
put the proper signal handler in your code:
$SIG{INT} = sub { die "Caught a sigint $!" };
the control-c sends the SIGINT signal to the script, who is catched by this handler

Polling mechanism in Perl

I have written a Perl script which executes a task as per the schedule.
Sometimes the task runs for 3 hours, and sometimes for 9 hours.
The status of the task changes to COMPLETE after the task is done.
We have an internal CLI command which displays the status of the task. I'd like to implement a polling mechanism whereby the script checks the status of the task periodically and returns PASS once the status changes to COMPLETE.
One option is to run the command in a loop and check the status periodically. I was wondering if there are any other better or more reliable ways to check the status periodically. Perhaps implementing a scheduler or something on those lines.
Does anyone have any suggestions?
If you want to check on the status of the Perl program before it ends, you could do it with signals. Note that the following code is very simplistic and there are a lot of things to be careful about with signals and Perl, particularly older versions of Perl.
First, define a signal handler function for the signal you want to use. If you are referencing any variables that might be used elsewhere in the program, make sure those variables are in scope. The following example used local to dynamically scope $i so that it will be available in the USR1 signal hander:
$SIG{USR1} = sub { print STDOUT "In USR1 signal handler. \$i is $i\n"; };
my $j;
for (local $i = 1; $i < 1000000000; $i++) {
$j = $i*2;
}
Then, whenever you want to query the running process for status, get the PID and send it a SIGUSR1 signal. You could do this from inside another program, but the following example shows it from command-line:
mb:~ doug$ kill -SIGUSR1 8492
With the signal handler shown above, when the program receives the signal it will output to its STDOUT something like this:
In USR1 signal handler. $i is 162181697
After Perl executes the signal handler it will go back to what it was doing before it was interrupted.

signal a perl process from an independent perl process to trigger code in handler

I am using perl v14 on windows. I have 2 simple files:
$SIG{'INT'} = sub {
print "got SIGINT\n";
#some useful code to be executed
#on reception of signal
};
$SIG{'ALRM'} = sub {
print "got SIGALRM\n";
};
print "my pid: ",$$,"\n";
while(1)
{
print "part 1\n";
sleep(3);
print "part 2\n\n";
sleep(3);
}
the above file starts and waits to be killed having given its pid.
The second file simply kills the first perl process using its pid(set manually).
$pid = xxxx; #this is the manually entered pid for I process
print "will attempt to kill process: $pid\n";
kill INT, $pid;
What I run the first perl script and press Ctrl-C, the handler works as expected but using the second file I can't get the same result. I have also tried with other signals like ALRM,HUP,TERM,FPE but no success. All I want to do is to execute the code in the signal handler.
I found something called INT2 signal for win32.
Thanks in advance.
Windows does let you use signals only within the same thread. So signaling different processes will not work.
Instead of signals you could use other methods of interprocess communication like sockets, pipes or files.
From perlwin32:
Signal handling may not behave as on Unix platforms (where it doesn't
exactly "behave", either :). For instance, calling die() or exit()
from signal handlers will cause an exception, since most
implementations of signal() on Windows are severely crippled. Thus,
signals may work only for simple things like setting a flag variable
in the handler. Using signals under this port should currently be
considered unsupported.

Killing an application started using system() in Perl

I am trying to run an application inside a Perl script using system(). The application I'm running gets stuck sometimes (it enters some kind of infinite loop). Is there a way I can know if this application is stuck and kill it to continue with the Perl script?
I'm trying to do something like this:
start testapp.exe;
if(stuck with testapp.exe) {
kill testapp.exe;
}
Determining if "it is stuck in infinite loop" is called Halting Problem and is undecidable.
If you want to kill it, you will have to fork the application using fork and then kill it from the other fork, if it is going for too long.
You can determine if the proccess is going for too long by this
use POSIX ":sys_wait_h";
waitpid($pid, WNOHANG)>0 #waitpid returns 0 if it still running
at least, according to this manual page
I am not sure how well it works on various systems, you can try it out.
Not a direct answer, but I can recommend using forks module if you want to fork with ease, but it works only on UNIX systems (not windows).
OK, more helping code :) It works in UNIX, according to perlfork perldoc, it should work on Windows exactly the same way.
use warnings;
use strict;
use POSIX ":sys_wait_h";
my $exited_cleanly; #to this variable I will save the info about exiting
my $pid = fork;
if (!$pid) {
system("anything_long.exe"); #your long program
} else {
sleep 10; #wait 10 seconds (can be longer)
my $result = waitpid(-1, WNOHANG); #here will be the result
if ($result==0) { #system is still running
$exited_cleanly = 0; #I already know I had to kill it
kill('TERM', $pid); #kill it with TERM ("cleaner") first
sleep(1); #wait a bit if it ends
my $result_term = waitpid(-1, WNOHANG);
#did it end?
if ($result_term == 0) { #if it still didnt...
kill('KILL', $pid); #kill it with full force!
}
} else {
$exited_cleanly = 1; #it exited cleanly
}
}
#you can now say something to the user, for example
if (!$exited_cleanly) {...}
system("start testapp")
is short for
system("cmd", "/c", "start testapp")
Perl just knows about cmd; it doesn't know anything about start, much less about testapp. system is not the tool you want. That's the first problem.
The second problem is that you haven't defined what it means to be "stuck". If you want to monitor a program, it needs a heartbeat. A heartbeat is a periodic activity that can be externally examined. It can be writing to a pipe. It can be changing a file. Anything.
The monitoring program listens for this heartbeat, and presumes the program is dead if the heart stops beating, so to speak.
"Killing" is done using signals in unix, but it's done using TerminateProcess in Windows. The third problem is that Perl core does not give you access to that function.
The solution to the first and third problem is Win32::Process. It allows you to launch a process in the background, and it also allows you to terminate it.
Creating a heartbeat is up to you.
Here is one way you can handle the problem if you know that testapp should not take more than N seconds to do its thing, then you can use a timeout to kill the app by way of IPC::Run.
In the example below there is a timeout of 1 second which kills the sleep 10 command that takes too long (longer than the timeout of 1 second). If this doesn't do what you want, then you should provide more information about how you can detect that testapp.exe is "stuck".
#!/usr/bin/env perl
use IPC::Run qw( run timeout );
eval { # if (stuck with testapp.exe for more than N seconds)
#cmd = ('sleep', '10'); # this could be testapp.exe instead of sleep
run \#cmd, \$in, \$out, \$err, timeout( 1 ) or die "test"; # start testapp.exe
print "do stuff if cmd succeeds\n";
};
print "more stuff to do afterwards whether or not command fails or succeeds\n";
You can't determine that the application is stuck if you execute it like that, because the system statement won't return until the application terminates.
So, at least, you need to start the test application so it can run asynchronously from the Perl script that is to monitor it.
Having resolved that part of the problem, you have to establish a mechanism that will allow the monitoring Perl script to determine that the application is stuck. That is a non-trivial exercise, and likely system dependent, unless you adopt a simple expedient such as requiring the application to write a heart-beat indication somewhere, and the Perl script monitors for the heart-beat. For example (not necessarily a good example), the application could write the current time into a file identified by its PID, and the Perl script could monitor the file to see if the heart-beat is sufficiently recent. Of course, this assumes that the 'infinite loop' doesn't include code that writes to the heart-beat file.