Execute function in php before SIGTERM - php

I am executing a PHP script through Windows Console. I have some ending functions that write results to a file. Anyway, sometimes I have to interrupt the execution (ctrl + c) and halt the script. I am interested in some way to write the current progress to file between keys stroke and actual sigterm. Is this possible ? I'd really need to be able to resume my script execution from last point the next time I run it. Thank you !

You can register a signal handler for SIGTERM:
function sig_handler($signo)
{
// Do something
}
pcntl_signal(SIGTERM, "sig_handler");
Your handler should then be executed when the signal is received.

Related

Commands return exit code 2 while process is terminating [duplicate]

I have one simple program that's using Qt Framework.
It uses QProcess to execute RAR and compress some files. In my program I am catching SIGINT and doing something in my code when it occurs:
signal(SIGINT, &unix_handler);
When SIGINT occurs, I check if RAR process is done, and if it isn't I will wait for it ... The problem is that (I think) RAR process also gets SIGINT that was meant for my program and it quits before it has compressed all files.
Is there a way to run RAR process so that it doesn't receive SIGINT when my program receives it?
Thanks
If you are generating the SIGINT with Ctrl+C on a Unix system, then the signal is being sent to the entire process group.
You need to use setpgid or setsid to put the child process into a different process group so that it will not receive the signals generated by the controlling terminal.
[Edit:]
Be sure to read the RATIONALE section of the setpgid page carefully. It is a little tricky to plug all of the potential race conditions here.
To guarantee 100% that no SIGINT will be delivered to your child process, you need to do something like this:
#define CHECK(x) if(!(x)) { perror(#x " failed"); abort(); /* or whatever */ }
/* Block SIGINT. */
sigset_t mask, omask;
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
CHECK(sigprocmask(SIG_BLOCK, &mask, &omask) == 0);
/* Spawn child. */
pid_t child_pid = fork();
CHECK(child_pid >= 0);
if (child_pid == 0) {
/* Child */
CHECK(setpgid(0, 0) == 0);
execl(...);
abort();
}
/* Parent */
if (setpgid(child_pid, child_pid) < 0 && errno != EACCES)
abort(); /* or whatever */
/* Unblock SIGINT */
CHECK(sigprocmask(SIG_SETMASK, &omask, NULL) == 0);
Strictly speaking, every one of these steps is necessary. You have to block the signal in case the user hits Ctrl+C right after the call to fork. You have to call setpgid in the child in case the execl happens before the parent has time to do anything. You have to call setpgid in the parent in case the parent runs and someone hits Ctrl+C before the child has time to do anything.
The sequence above is clumsy, but it does handle 100% of the race conditions.
What are you doing in your handler? There are only certain Qt functions that you can call safely from a unix signal handler. This page in the documentation identifies what ones they are.
The main problem is that the handler will execute outside of the main Qt event thread. That page also proposes a method to deal with this. I prefer getting the handler to "post" a custom event to the application and handle it that way. I posted an answer describing how to implement custom events here.
Just make the subprocess ignore SIGINT:
child_pid = fork();
if (child_pid == 0) {
/* child process */
signal(SIGINT, SIG_IGN);
execl(...);
}
man sigaction:
During an execve(2), the dispositions of handled signals are reset to the default;
the dispositions of ignored signals are left unchanged.

php signal not handled right away

I have a software written in PHP which can run for a long time, it's lauched via command line (not a web application). I wanted to make sure to call a function when the software exit, including when killed via ctrl+c. When killed by a signal, the shutdown functions (destructors and functions registered via register_shutdown_fucntion() ) are not called.
After some reading, I realized I had to add a handler on every relevant signal just to call "exit;". Doing so solved my problem and works.
My issue is that in some case, the signal is not handled right away. It can take seconds, minutes and in some case, is not handled at all. Just like if the handler code was never reached. I don't really know where to start to debug that. I've tried others signals (sigkill, sighup, etc), same behaviour.
My code is withing these brackets
declare(ticks=1)
{...}
I can't find any correlation between the time it get treated right away and the ones that it doesn't.
Any help would be appreciated.
The signal should be handled this way :
First, you have to make a signal handler like this :
function signalHandler($signo = null) {
$pid = posix_getpid();
switch ($signo) {
//case SIGKILL: // you can't override SIGKILL so it is useless
case SIGTERM:
case SIGINT:
// unexpected shut down
exit(3);
break;
case SIGCHLD:
case SIGHUP:
// just ignore it
break;
case 10:
// user signal 1 received. Process exited normally
exit(0);
break;
case 12:
// user signal 2 received. Precess exited with a catched error
exit(3);
break;
default:
// ignore other signals
}
}
then, in your main process, you have to tell PCNTL you are using this handler :
pcntl_signal(SIGTERM, "signalHandler");
pcntl_signal(SIGINT, "signalHandler");
// pcntl_signal(SIGKILL, "signalHandler"); // you can't override SIGKILL so it is useless
pcntl_signal(SIGCHLD, "signalHandler");
pcntl_signal(SIGHUP, "signalHandler");
pcntl_signal(10, "signalHandler");
pcntl_signal(12, "signalHandler");
I'm using this and seems to work for 200 process, 8 process running at the same time, and 10 sec for each process.
Here, when a process exits, he sends a signal 10 or 12 (for success or error) using posix_kill($pid, 10)
I think the problem here is that maybe you make a call to a function that has loop in it and every iteration of the loop takes a lot of time (for example, fetching data from database). In this case, when your program receives a stop signal, it adds this signal to stack, waits, until data will be fetched from database, and then calls signal handler function.
I'm also looking for a way to immediately call signal handler function. Maybe PHP just doesn't have this option.
UPDATE:
I think I found the solution. Try to pass false as the 3rd argument to function pcntl_signal() like this:
pcntl_signal(SIGINT, function () {
echo 'Do something here.';
}, false);

How do I catch a KILL or HUP or User Abort signal?

I have a script running on the background of my linux server and I would like to catch signals like reboot or anything that would kill this script and instead save any importante information before actually exiting.
I think most of what I need to catch is, SIGINT, SIGTERM, SIGHUP, SIGKILL.
How do catch any of these signals and have it execute an exit function otherwise keep executing whatever it was doing ?
pseudo perl code:
#!/usr/bin/perl
use stricts;
use warnings;
while (true)
{
#my happy code is running
#my happy code will sleep for a few until its breath is back to keep running.
}
#ops I have detected an evil force trying to kill me
#let's call the safe exit.
sub safe_exit()
{
# save stuff
exit(1);
}
pseudo php code:
<?php
while (1)
{
#my happy code is running
#my happy code will sleep for a few until its breath is back to keep running.
}
#ops I have detected an evil force trying to kill me
#let's call the safe exit.
function safe_exit()
{
# save stuff
exit(1);
}
?>
PHP uses pcntl_signal to register a signal handler, so something like this:
declare(ticks = 1);
function sig_handler($sig) {
switch($sig) {
case SIGINT:
# one branch for signal...
}
}
pcntl_signal(SIGINT, "sig_handler");
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
# Nothing for SIGKILL as it won't work and trying to will give you a warning.
Perl:
$SIG{$_} = \&safe_exit for qw( INT TERM HUP );
SIGKILL cannot be caught. It is not sent to the process.
%SIG is documented in perlvar. See also perlipc
For the perl version, see perldoc -q signal -- basically, set $SIG{signal} to a sub reference.

When does a PHP <5.3.0 daemon script receive signals?

I've got a PHP script in the works that is a job worker; its main task is to check a database table for new jobs, and if there are any, to act on them. But jobs will be coming in in bursts, with long gaps in between, so I devised a sleep cycle like:
while(true) {
if ($jobs = get_new_jobs()) {
// Act upon the jobs
} else {
// No new jobs now
sleep(30);
}
}
Good, but in some cases that means there might be a 30 second lag before a new job is acted upon. Since this is a daemon script, I figured I'd try the pcntl_signal hook to catch a SIGUSR1 signal to nudge the script to wake up, like:
$_isAwake = true;
function user_sig($signo) {
global $_isAwake;
daemon_log("Caught SIGUSR1");
$_isAwake = true;
}
pcntl_signal(SIGUSR1, 'user_sig');
while(true) {
if ($jobs = get_new_jobs()) {
// Act upon the jobs
} else {
// No new jobs now
daemon_log("No new jobs, sleeping...");
$_isAwake = false;
$ts = time();
while(time() < $ts+30) {
sleep(1);
if ($_isAwake) break; // Did a signal happen while we were sleeping? If so, stop sleeping
}
$_isAwake = true;
}
}
I broke the sleep(30) up into smaller sleep bits, in case a signal doesn't interrupt a sleep() command, thinking that this would cause at most a one-second delay, but in the log file, I'm seeing that the SIGUSR1 isn't being caught until after the full 30 seconds has passed (and maybe the outer while loop resets).
I found the pcntl_signal_dispatch command, but that's only for PHP 5.3 and higher. If I were using that version, I could stick a call to that command before the if ($_isAwake) call, but as it currently stands I'm on 5.2.13.
On what sort of situations is the signals queue interpreted in PHP versions without the means to explicitly call the queue parsing? Could I put in some other useless command in that sleep loop that would trigger a signal queue parse within there?
Fixed my own problem: The answer is the "ticks" declaration. I had, as part of the Daemon process startup done the declare(ticks=1); action, but it wasn't seeming to carry over to the main script (since that was inside a function, in an include file?. Adding a declare(ticks=1) line before the while(true) loop causes signals to come through immediately (i.e. the sleep(1) command causes a tick, so after waking up from sleep, signals are processed).

Can a PHP script abort itself or detect abortion?

PHP scripts can continue executing after the HTTP page request, so how do I finally stop it executing when I'm done with it?
Also, is there an event to detect when the OS is going to forcibly abort the script? or how do I keep an internal timer to predict max_execution_time?
exit()/die() will stop a php script.
To know when to stop the script, you'll just have to use microtime as a timer and save as a constant (or fetch from the php.ini file) the maximum execution time.
You can also look at the Connection handling information. Where we have things like connection_aborted() and connection_status()
But what is the problem you're trying to solve?
You can use register_shutdown_function to register a function to be called at the end of script execution. You can use this to detect when the script has been terminated and perform a certain set of actions.
To have a callback at the moment your request is shutting down, use register_shutdown_function( myfunction ).
Much like most POSIX environments, PHP also supports signal handlers. You can register your own handler for the SIGTERM event.
function my_abort_handler( $signo ) {
echo "Aborted";
}
pcntl_signal( SIGTERM, "my_abort_handler" );
You may want to take a look at pcntl-alarm which allows a script to send a signal to itself. Also contains some sample on how to catch the kill signals which can be send by the OS. And die() indeed.
Well, you could start a $start=microtime(true) which will return a timestamp. Then you can just keep checking microtime(true) and subtract that from your start time to get the number of seconds since executing.
But no, you can't "catch" the script as its terminating for the reason of the request being too long. You could try to do some last minute stuff in the shutdown handler, but I'm not sure if PHP will honor that.
It looks like there used to be a function that does exactly what you want, connection_timeout(), but it was deprecated and removed. Don't know if there is any kind of replacement for this, however.

Categories