Created
January 15, 2013 21:52
-
-
Save amad/4542455 to your computer and use it in GitHub Desktop.
PHP Daemon
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/php -q | |
<?php | |
/** | |
* System_Daemon turns PHP-CLI scripts into daemons. | |
* | |
* PHP version 5 | |
* | |
* @category System | |
* @package System_Daemon | |
* @author Kevin <[email protected]> | |
* @copyright 2008 Kevin van Zonneveld | |
* @license http://www.opensource.org/licenses/bsd-license.php | |
* @link http://github.com/kvz/system_daemon | |
*/ | |
/** | |
* System_Daemon Example Code | |
* | |
* If you run this code successfully, a daemon will be spawned | |
* but unless have already generated the init.d script, you have | |
* no real way of killing it yet. | |
* | |
* In this case wait 3 runs, which is the maximum for this example. | |
* | |
* | |
* In panic situations, you can always kill you daemon by typing | |
* | |
* killall -9 logparser.php | |
* OR: | |
* killall -9 php | |
* | |
*/ | |
// Allowed arguments & their defaults | |
$runmode = array( | |
'no-daemon' => false, | |
'help' => false, | |
'write-initd' => false, | |
); | |
// Scan command line attributes for allowed arguments | |
foreach ($argv as $k=>$arg) { | |
if (substr($arg, 0, 2) == '--' && isset($runmode[substr($arg, 2)])) { | |
$runmode[substr($arg, 2)] = true; | |
} | |
} | |
// Help mode. Shows allowed argumentents and quit directly | |
if ($runmode['help'] == true) { | |
echo 'Usage: '.$argv[0].' [runmode]' . "\n"; | |
echo 'Available runmodes:' . "\n"; | |
foreach ($runmode as $runmod=>$val) { | |
echo ' --'.$runmod . "\n"; | |
} | |
die(); | |
} | |
// Make it possible to test in source directory | |
// This is for PEAR developers only | |
ini_set('include_path', ini_get('include_path').':..'); | |
// Include Class | |
error_reporting(E_ALL); | |
require_once 'System/Daemon.php'; | |
// Setup | |
$options = array( | |
'appName' => 'logparser', | |
'appDir' => dirname(__FILE__), | |
'appDescription' => 'Parses vsftpd logfiles and stores them in MySQL', | |
'authorName' => 'Kevin van Zonneveld', | |
'authorEmail' => '[email protected]', | |
'sysMaxExecutionTime' => '0', | |
'sysMaxInputTime' => '0', | |
'sysMemoryLimit' => '1024M', | |
'appRunAsGID' => 1000, | |
'appRunAsUID' => 1000, | |
); | |
System_Daemon::setOptions($options); | |
// This program can also be run in the forground with runmode --no-daemon | |
if (!$runmode['no-daemon']) { | |
// Spawn Daemon | |
System_Daemon::start(); | |
} | |
// With the runmode --write-initd, this program can automatically write a | |
// system startup file called: 'init.d' | |
// This will make sure your daemon will be started on reboot | |
if (!$runmode['write-initd']) { | |
System_Daemon::info('not writing an init.d script this time'); | |
} else { | |
if (($initd_location = System_Daemon::writeAutoRun()) === false) { | |
System_Daemon::notice('unable to write init.d script'); | |
} else { | |
System_Daemon::info( | |
'sucessfully written startup script: %s', | |
$initd_location | |
); | |
} | |
} | |
// Run your code | |
// Here comes your own actual code | |
// This variable gives your own code the ability to breakdown the daemon: | |
$runningOkay = true; | |
// This variable keeps track of how many 'runs' or 'loops' your daemon has | |
// done so far. For example purposes, we're quitting on 3. | |
$cnt = 1; | |
// While checks on 3 things in this case: | |
// - That the Daemon Class hasn't reported it's dying | |
// - That your own code has been running Okay | |
// - That we're not executing more than 3 runs | |
while (!System_Daemon::isDying() && $runningOkay && $cnt <=3) { | |
// What mode are we in? | |
$mode = '"'.(System_Daemon::isInBackground() ? '' : 'non-' ). | |
'daemon" mode'; | |
// Log something using the Daemon class's logging facility | |
// Depending on runmode it will either end up: | |
// - In the /var/log/logparser.log | |
// - On screen (in case we're not a daemon yet) | |
System_Daemon::info('{appName} running in %s %s/3', | |
$mode, | |
$cnt | |
); | |
// In the actuall logparser program, You could replace 'true' | |
// With e.g. a parseLog('vsftpd') function, and have it return | |
// either true on success, or false on failure. | |
$runningOkay = true; | |
//$runningOkay = parseLog('vsftpd'); | |
// Should your parseLog('vsftpd') return false, then | |
// the daemon is automatically shut down. | |
// An extra log entry would be nice, we're using level 3, | |
// which is critical. | |
// Level 4 would be fatal and shuts down the daemon immediately, | |
// which in this case is handled by the while condition. | |
if (!$runningOkay) { | |
System_Daemon::err('parseLog() produced an error, '. | |
'so this will be my last run'); | |
} | |
// Relax the system by sleeping for a little bit | |
// iterate also clears statcache | |
System_Daemon::iterate(2); | |
$cnt++; | |
} | |
// Shut down the daemon nicely | |
// This is ignored if the class is actually running in the foreground | |
System_Daemon::stop(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
http://kvz.io/blog/2009/01/09/create-daemons-in-php/ | |
pear install -f System_Daemon | |
--no-daemon # just run the program in the console this time | |
--write-initd # writes a startup file | |
tail /var/log/logparser.log | |
ps uf -C logparser.php | |
killall -9 logparser.php | |
/etc/init.d/logparser stop | |
/etc/init.d/logparser start | |
$path = System_Daemon::writeAutoRun(); | |
update-rc.d logparser defaults | |
update-rc.d -f logparser remove | |
/etc/logrotate.d/ | |
>> | |
/var/log/mydaemon.log { | |
rotate 15 | |
compress | |
missingok | |
notifempty | |
sharedscripts | |
size 5M | |
create 644 mydaemon_user mydaemon_group | |
postrotate | |
/bin/kill -HUP `cat /var/run/mydaemon/mydaemond.pid 2>/dev/null` 2> /dev/null || true | |
endscript | |
} | |
Don’t use echo() Echo writes to the STDOUT of your current session. If you logout, that will cause fatal errors and the daemon to die. Use System_Daemon::log() instead. | |
Connect to MySQL after you start() the daemon. Otherwise only the parent process will have a MySQL connection, and since that dies.. It’s lost and you will get a ‘MySQL has gone away’ error. | |
Error handling Good error handling is imperative. Daemons are often mission critical applications and you don’t want an uncatched error to bring it to it’s knees. | |
Reconnect to MySQL A connection may be interrupted. Think about network downtime or lock-ups when your database server makes backups. Whatever the cause: You don’t want your daemon to die for this, let it try again later. | |
PHP error handler As of 0.6.3, System_Daemon forwards all PHP errors to the log() method, so keep an eye on your logfile. This behavior can be controlled using the logPhpErrors (true||false) option. | |
Monit Monit is a standalone program that can kickstart any daemon, based on your parameters. Should your daemon fail, monit will mail you and try to restart it. | |
Watch that memory Some classes keep a history of executed commands, sent mails, queries, whatever. They were designed without knowing they would ever be used in a daemonized environment. Cause daemons run indefinitely this ‘history’ will expand indefinitely. Since unfortunately your server’s RAM is not infinite, you will run into problems at some point. This makes it’s very important to address these memory ‘leaks’ when building daemons. | |
Statcache will corrupt your data If you do a file_exists(), PHP remembers the results to ease on your disk until the process end. That’s ok but since the Daemon process does not end, PHP will not be able to give you up to date information. As of 0.8.0 you should call System_Daemon::iterate(2) instead of e.g. sleep(2), this will sleep & clear the cache and give you fresh & valid data. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment