Python gracefully stop a script from Laravel/Php - php

I'm looking for a way to gracefully exit from a long running python script which I launch from my Laravel app.
My actual process to do so is:
From Laravel set a 'script_state' to 1 in a MySql table database
Launch the python script via shell_exec
The python scripts periodically check the 'script_state' by a MySql query. If it is changed to 0 (intentionally from my Laravel app) then it gracefully exit the script
Retrieving the pid from the shell_exec and then kill could have been an option but I actually wan't the script to stop and exit gracefully.
My actual config works but I'm looking for a better approach.

Retrieving the pid from the shell_exec and then kill could have been an option but I actually wan't the script to stop and exit gracefully.
This is probably your best bet. You can use SIGTERM to politely ask the script to exit:
The SIGTERM signal is a generic signal used to cause program termination. Unlike SIGKILL, this signal can be blocked, handled, and ignored. It is the normal way to politely ask a program to terminate.
This is effectively what happens when you click the close button in a GUI application.
In your Python code you can handle SIGTERM using the signal module with whatever cleanup logic you want, then exit:
import signal, os
def handler(signum, frame):
print('Signal handler called with signal', signum)
raise OSError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable the alarm
See also this Stack Overflow answer for a class that cleans up before exiting.

Related

How to stop SIGTERM and SIGKILL?

I need to run a huge process which will run for like 10+ minutes. I maxed the max_execution_time, but in my error logs I get a SIGTERM and then a SIGKILL.
I read a little about SIGTERM and SIGKILL that they come from the daemon, but i Didn't figure out how to stop it from happening. I just need to disable it for one night.
Rather than trying to ignore signals, you need to find who sends them and why. If you're starting php from the command line, no one will send that signal and your script time will have all the time.
But if you're actually starting this process as a response to an http request, it's probably the web server of FastCGI manager that limits the amount of time it waits for the script to finish. It also may simply kill the script because client connection (between user's browser and http server) has been terminated.
So the important question you should ask yourself - what's the source of that signal and how this timeout can be increased. Please also provide all details about how you start this script and what platform you're running.

Define a custom kill signal for PHP process

I am working on a long-running PHP script. The script runs from command line. I need to add a graceful shutdown capability. I tried to use the process control library to handle POSIX signals. Unfortunately, some third party libraries that I am using are also intercepting POSIX signals, and they handle the signal and my script is not informed. I tried using SIGTERM first. Then I tried using custom signals SIGUSR1 and SIGUSR2, same happened.
Is it possible to define/use a new signal that is not defined in POSIX?
If not, what else can I use to implement graceful shutdown?

PHP CLI: How do I intercept terminal window/tab closure?

I use pcntl_signal function to handle Ctrl+Z, Ctrl+C and exit at safe point from the application.
Can I do the same when user close window?
Try the hangup signal.
SIGHUP
You can use:
http://en.wikipedia.org/wiki/Unix_signal
To find and overview of when each signal will be sent.
There no UI specific a specific "tab/window" closed signal and you would have to hook into X11 to accomplish anything like that (certainly not recommended especially in PHP) but SIGHUP is probably what you need.
There is a SIGCHLD but I don't know about anything that indicates that a parent is terminating which is what happens when you close the tab or window.

How to prevent upstart from killing child processes to a daemon?

Situation
I have a daemon I wrote in PHP (not the best language for this, but work with me), and it is made to receive jobs from a queue and process them whenever a job needs to be done. For each new job, I use pcntl_fork() to fork the job off into a child process. Within this child process, I then use proc_open() to execute long-running system commands for audio transcoding, which returns directly to the child when finished. When the job is completely done, the child exits and is cleaned up by the parent process.
To keep this daemon always running, I use upstart. Here is my upstart configuration file:
description "Audio Transcoding Daemon"
start on startup
stop on shutdown
# kill signal SIGCHLD
kill timeout 1200 # Don't force kill the process until it runs over 20 minutes
respawn
exec audio-daemon.php
Goal
Because I want to use this daemon in a distributed environment, I want to be able to shutdown the server at any time without disrupting any running jobs. To do this, I have already implemented signal handlers using pcntl_signal() for SIGTERM, SIGHUP, and SIGINT on the parent process, which waits for all children to exit normally before exiting itself. The children also have signal handlers, but they are made to ignore all kill signals.
Problem
The problem is, according to the docs...
The signal specified by the kill signal stanza is sent to the process group of the main process. (such that all processes belonging to the jobs main process are killed). By default this signal is SIGTERM.
This is concerning because, in my child process, I run system commands through proc_open(), which spawns new child processes as well. So, whenever I run sudo stop audio-daemon, this sub-process (which happens to be sox) is killed immediately, and the job returns back with an error. Apparently, sox obeys SIGTERM and does what it's told...
Originally, I thought, "Fine. I'll just change kill signal to send something that is inherently ignored, and I'll just pick it up in the main process only." But according to the manual, there are only two signals that are ignored by default: SIGCHLD and SIGURG (and possibly SIGWINCH). But I'm afraid of getting false flags, since these can also be triggered other ways.
There are ways to create a custom signal using what the manual calls "Real-time Signals" but it also states...
The default action for an unhandled real-time signal is to terminate the receiving process.
So that doesn't help...
Can you think of any way that I can get upstart to keep all of my sub-processes open until they complete? I really don't want to go digging through sox's source code to modify its signal handlers, and while I could set SIGCHLD, SIGURG, or SIGWINCH as my upstart kill signal and pray nothing else sends them my way, I can't help but think there's a better way to do this... Any ideas?
Thanks for all your help! :)
Since I haven't received any other answers for how to do this a better way, this is what I ended up doing, and I hope it helps someone out there...
To stall shutdown/reboot of the system until the daemon is finished, I changed my start on and stop on in my upstart configuration. And to keep upstart from killing my children, I resorted to using SIGURG as my kill signal, which I then catch as a kill signal in my main daemon process only.
Here is my final upstart configuration:
description "Audio Transcoding Daemon"
start on runlevel [2345]
stop on starting rc RUNLEVEL=[016] # Block shutdown/reboot until the daemon ends
kill signal SIGURG # Kill the process group with SIGURG instead of SIGTERM so only the main process will pick it up (since SIGURG will be ignored by all children by default)
kill timeout 1200 # Don't force kill the process until it runs over 20 minutes
respawn
exec audio-daemon.php
Note that using stop on starting rc RUNLEVEL=[016] is necessary to stall shutdown/reboot. stop on runlevel [016] will not work.
Also note that if you use SIGURG in your application for any other reason, using it as a kill signal may cause problems. In my case, I wasn't, so this works fine as far as I can tell.
Ideally, it would be nice if the POSIX standard provided a user-defined signal like SIGUSR1 and SIGUSR2 that was ignored by default. But right now, it looks like it doesn't exist.
Feel free to chime in if you have a better answer, but for now, I hope this helps anyone else having this problem.
Disclaimer: I don't know any PHP
I solved a similar problem with my ruby process by setting a new group id for a launched subprocess. It looks like php has a similar facility.
you can start a new group (detaching from your audio-daemon.php) by settings it's group id to its process id
something like
$chldPid=pcntl_fork()
... << error checks etc
if ($chldPid){
...
posix_setpgid($chldPid, $chldPid)

Constantly Running Gearman Worker

I have a process I'd like to be able to run in the background by starting up a Gearman Client any time.
I've found success by opening up two SSH connections to my server, and in one starting the worker and in the other then running the client. This produces the desired output.
The problem is that, I'd like to have a worker constantly running in the background so I can just call up a client whenever I need to have the process done. But as soon as I close the terminal which has the worker PHP file running, a call to the client does not work - the worker seems to die.
Is there a way to have the worker run constantly in the background, so calling a new client will work without having to start up a new worker?
Thanks!
If you want a program to keep running even after its parent is dead (i.e. you've closed your terminal), you must invoke it with nohup :
nohup your-command &
Quoting the relevant Wikipedia page I linked to :
nohup is a POSIX command to ignore
the HUP (hangup) signal, enabling
the command to keep running after the
user who issues the command has logged
out.The HUP (hangup) signal is
by convention the way a terminal warns
depending processes of logout.
For another (possibly more) interesting solution, see the following article : Dæmonize Your PHP.
It points to Supervisord, which makes sures a process is still running, relaunching it if necessary.
Is there a way to have the worker run constantly in the background, so calling a new client will work without having to start up a new worker?
Supervisor!
The 2009 PHP Advent Calendar has a quick article on using Supervisor (and other tricks) to create constantly-running PHP scripts without having to deal with the daemonization process in PHP itself.

Categories