summaryrefslogtreecommitdiff
path: root/includes/daemonize/daemonize.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/daemonize/daemonize.php')
-rw-r--r--includes/daemonize/daemonize.php205
1 files changed, 205 insertions, 0 deletions
diff --git a/includes/daemonize/daemonize.php b/includes/daemonize/daemonize.php
new file mode 100644
index 0000000..1f51708
--- /dev/null
+++ b/includes/daemonize/daemonize.php
@@ -0,0 +1,205 @@
+<?php
+/* This script daemonizes a PHP CLI script.
+
+Based on code from Dewi Morgan, http://www.thudgame.com/node/254
+Modified by bitweaver.org - spider@viovio.com, et al
+
+This code is explicitly released onto the public domain by myself,
+Dewi Morgan, the author. As such, it is not subject to copyright.
+You may do with it as you will. Credit, while appreciated, need not
+be given. This notice does not need to be included.
+
+Call from cron every minute as:
+* * * * * /path/to/bin/php /path/to/script/daemonize.php /path/to/script/daemon.php 2>&1 >> /path/to/logfile
+Could be trivially modified to take the script and other globals
+as arguments. But that seems a bit pointless to me.
+*/
+
+function usage_die( $pError ) {
+ die( "ERROR: $pError\nUsage: /path/to/bin/php /path/to/script/daemonize.php /path/to/script/daemon.php [--debug] [--nohup] [--pidfile=/path/to/lock/daemon.pid]\n\n" );
+}
+
+$PHP = $_SERVER['_']; # Remember to change the #! line above, too.
+if( empty( $argv[1] ) || !file_exists( $argv[1] ) ) {
+ usage_die( 'daemon script not found' );
+}
+$daemonScript = $argv[1];
+
+$debug = FALSE;
+if( !empty( $argv[2] ) ) {
+ // convert any remaining arguments into local variables
+ for( $i=2; $i < count( $argv ); $i++ ) {
+ $arg = preg_replace( '/^[-]*/', '', $argv[$i] );
+ @list( $name, $val ) = @split( '=', $arg );
+ ${$name} = (!empty( $val ) ? $val : TRUE);
+ }
+}
+
+
+if( empty( $pidfile ) ) {
+ $pidfile = '/var/lock/php-' . basename( $daemonScript,'.php' ).'.pid';
+}
+
+# Must contain any of nohup, perl, daemonise.pl, is_up.php and this script
+# That you intend to use.
+# Get the following values from "kill -l" or /usr/include/linux/signal.h
+# Should be correct for most unixes.
+$SIGTERM = 15;
+$SIGKILL = 9;
+
+# If we've self-bootstrapped, then load the bot and run with it.
+if ( !empty( $nohup ) ) {
+ to_log("arg detected, bootstrapping daemon through require.");
+ require( $daemonScript );
+ to_log("killing daemon child.");
+ exit(0);
+}
+
+# If we're already started, then don't bother running.
+if( empty( $nohup ) ) {
+ $test = 0;
+ $lines = array(0, 0);
+ $pid_data = "unread";
+
+ #to_log("Testing for pre-existing... $pidfile"); # Can get spammy.
+ if (file_exists($pidfile)) {
+ $pid_data = file_get_contents($pidfile);
+ $lines = explode("\n", $pid_data);
+ if (!empty($lines[0]) && is_numeric($lines[0]) && !empty($lines[1]) && is_numeric($lines[1])) {
+ # Kill hung processes.
+ if ($lines[1] + 300 < time()) { // If it's an OLD pidfile...
+ to_log("$pidfile OLD pidfile found from $lines[1] - ".date('d/M/Y:H:i:s O',$lines[1]).": $pid_data");
+ zero_file($pidfile);
+
+ # If found, kill. If it won't die, kill it harder. Then give up.
+ # In practice, it can take a bit longer than 20 seconds to die, but it
+ # tries again in a minute's time, and works.
+ if (is_alive($lines[0])) {
+ to_log("killing and waiting a few secs...");
+ posix_kill($lines[0], $SIGTERM);
+ # Loop for 10 seconds, or until it dies.
+ for ($i=0; $i<20 && is_alive($lines[0]); $i++) {
+ sleep(1);
+ if ($i == 10) { # After ten seconds, kill it harder.
+ to_log("Failed to kill it. Killing it harder!");
+ posix_kill($lines[0], $SIGKILL);
+ }
+ }
+ if (is_alive($lines[0])) {
+ to_log("Failed to kill it. Giving up.");
+ exit(0);
+ }
+ }
+ } else {
+ #to_log("Young pidfile found."); # Can get spammy.
+ }
+ if (is_alive($lines[0])) {
+// to_log( "DAEMON $lines[0] : $daemonScript running. Last seen at ".date('d/M/Y:H:i:s O', $lines[1] ) );
+ exit(0);
+ } else {
+ to_log("$pidfile pidfile $lines[0] found, but process is dead.");
+ }
+ }
+ else {
+ to_log("$pidfile Bad format pidfile found, zeroing, restarting...");
+ zero_file($pidfile);
+ }
+ }
+ else {
+ to_log("pidfile not found: $pidfile");
+ }
+ to_log("NOT found running.");
+}
+
+# If pcntl_fork() doesn't exist, we need to load ourselves in the background, then die.
+if (!function_exists("pcntl_fork")) {
+ to_log("no pcntl, calling self with nohup and param");
+ system("nohup $PHP $argv[1] -nohup &", $return);
+ if ($return == 0) { exit(0); }
+ # If there was a problem, we have one more ace in the hole - perl!
+ to_log("nohup failed with return $return, calling self with perl");
+ system("perl ".dirname( $argv[0] )."daemonize.pl &", $return);
+ to_log("killing daemon parent with return $return");
+ exit(0);
+}
+
+# If we've got the right functions to play with, all the above becomes moot.
+to_log("we have pcntl! Doing it all ourselves!");
+
+# Replace file handles
+$fh_unused = array(STDIN, STDOUT);
+
+ob_implicit_flush();
+
+# Daemon Rule 1) Fork and exit the parent.
+$pid = pcntl_fork();
+if ($pid == -1) {
+ die("could not fork");
+}
+else if ($pid) {
+ exit(); # Kill the parent
+}
+
+# Daemon Rule 2) become session leader, pg leader, no term
+$session_id = posix_setsid();
+if (!$session_id) {
+ die("Could not detach from terminal.");
+}
+
+# Daemon Rule 3) cd to /
+if (!chdir('/')) { die("Could not cd to rootfs"); }
+
+# Daemon Rule 4) set file creation mask to 0
+$oldmask = umask(00);
+
+# Daemon Rule 5) Close unneeded file handles
+foreach ($fh_unused as $fh) {
+ if (!fclose($fh)) { die("Unable to close $fh"); }
+}
+
+# Daemon Rule 6) Set up signal handlers where necessary.
+pcntl_signal(SIGTERM, "sig_handler");
+pcntl_signal(SIGHUP, "sig_handler");
+
+
+function sig_handler($signo) {
+ switch ($signo) {
+ case SIGTERM:
+ # handle shutdown tasks
+ die("Caught thud SIGTERM");
+ break;
+ case SIGHUP:
+ # handle restart tasks
+ die("Caught thud SIGHUP");
+ break;
+ default:
+ # handle all other signals
+ }
+}
+
+# Meat of the daemon goes here.
+to_log("requiring $daemonScript");
+chdir( dirname( $daemonScript ) );
+require_once( $daemonScript );
+
+exit(0);
+
+
+# Access functions.
+function is_alive($pid) {
+ $output = array();
+ exec( dirname( __FILE__ )."/is_up.sh $pid", $output );
+ $result = $output[0];
+ return (0 < $result);
+}
+
+function to_log($string) {
+ error_log( date( 'd/M/Y:H:i:s O' )." - $string");
+}
+
+function zero_file($pidfile) {
+ $fp = fopen($pidfile, "w");
+ fwrite($fp, ""); # Zero the pidfile.
+ fclose($fp);
+}
+?>