kvz.io
Published on

Create Daemons in PHP

Authors
  • avatar
    Name
    Kevin van Zonneveld
    Twitter
    @kvz

Everyone knows PHP can be used to create websites. But it can also be used to create desktop applications and commandline tools. And now with a class called System_Daemon, you can even create daemons using nothing but PHP. And did I mention it was easy?

Update 4 Dec, 2012: Legacy Warning

This class was relevant in 2009, and may still be to some people, but if you want to daemonize a php script nowadays, a 5-line Ubuntu upstart script should suffice. Upstart will daemonize your script and can even respawn it if it goes down. Something that you'd otherwise have to use monit for. I have another article on daemonizing nodejs scripts with upstart, that can just as well be applied to PHP scripts.

If you're still convinced you need to do this with pure PHP, read on.

What Is a Daemon?

A daemon is a Linux program that run in the background, just like a 'Service' on Windows. It can perform all sorts of tasks that do not require direct user input. Apache is a daemon, so is MySQL. All you ever hear from them is found in somewhere in /var/log, yet they silently power over 40% of the Internet.

You reading this page, would not have been possible without them. So clearly: a daemon is a powerful thing, and can be bend to do a lot of different tasks.

Why PHP?

Most daemons are written in C. It's fast & robust. But if you are in a LAMP oriented company like me, and you need to create a lot of software in PHP anyway, it makes sense:

  • Reuse & connect existing code Think of database connections, classes that create customers from your CRM, etc.
  • Deliver new applications very fast PHP has a lot of build in functions that speed up development greatly.
  • Everyone knows PHP (right?) If you work in a small company: chances are there are more PHP programmers than there are C programmers. What if your C guy abandons ship? Admittedly it's a very pragmatic reason, but it's the same reason why Facebook is building HipHop.

Possible Use Cases

  • Website optimization If you're running a (large) website, jobs that do heavy lifting should be taken away from the user interface and scheduled to run on the machine separately.
  • Log parser Continually scan logfiles and import critical messages in your database.
  • SMS daemon Read a database queue, and let your little daemon interface with your SMS provider. If it fails, it can easily try again later!
  • Video converter (think Youtube) Scan a queue/directory for incoming video uploads. Make some system calls to ffmpeg to finally store them as Flash video files. Surely you don't want to convert video files right after the upload, blocking the user interface that long? No: the daemon will send the uploader a mail when the conversion is done, and proceed with the next scheduled upload

Deamons vs Cronjobs

Some people use cronjobs for the same Possible use cases. Crontab is fine but it only allows you to run a PHP file every minute or so.

  • What if the previous run hasn't finished yet? Overlap can seriously damage your data & cause significant load on your machines.
  • What if you can't afford to wait a minute for the cronjob to run? Maybe you need to trigger a process the moment a record is inserted?
  • What if you want to keep track of multiple 'runs' and store data in memory.
  • What if you need to keep your application listening (on a socket for example) Cronjobs are a bit rude for this, they may spin out of control and don't provide a

framework for logging, etc. Creating a daemon would offer more elegance & possibilities. Let's just say: there are very good reasons why a Linux OS isn't composed entirely of Cronjobs :)

How It Works Internally

(Nerd alert!) When a daemon program is started, it fires up a second child process, detaches it, and then the parent process dies. This is called forking. Because the parent process dies, it will give the console back and it will look like nothing has happened. But wait: the child process is still running. Even if you close your terminal, the child continues to run in memory, until it either stops, crashes or is killed.

In PHP: forking can be achieved by using the Process Control Extensions. Getting a good grip on it, may take some studying though.

System_Daemon

Because the Process Control Extensions' documentation is a bit rough, I decided to figure it out once, and then wrap my knowledge and the required code inside a PEAR class called: System_Daemon. And so now you can just:

<?php
require_once "System/Daemon.php";                 // Include the Class

System_Daemon::setOption("appName", "mydaemon");  // Minimum configuration
System_Daemon::start();                           // Spawn Deamon!
?>

And that's all there is to it. The code after that, will run in your server's background. So next, if you create a while(true) loop around that code, the code will run indefinitely. Remember to build in a sleep(5) to ease up on system resources.

Features & Characteristics

Here's a grab of System_Daemon's features:

  • Daemonize any PHP-CLI script
  • Simple syntax
  • Driver based Operating System detection
  • Unix only
  • Additional features for Debian / Ubuntu based systems like:
  • Automatically generate startup files (init.d)
  • Support for PEAR's Log package
  • Can run with PEAR (more elegance & functionality) or without PEAR (for standalone packages)
  • Default signal handlers, but optionally reroute signals to your own handlers.
  • Set options like max RAM usage

Download

You could download the package from PEAR, or, if you have PEAR installed on your system: just run this from a console:

$ pear install -f System_Daemon

You can also update it using that last command.

Beta

Though I have quite some daemons set up this way, it's officially still beta. So please report any bugs over at the PEAR page. Other comments may be posted here.

Complex Example

Ready to dig a little deeper? This example program is called 'logparser', it takes a look at a more complex use of System_Daemon. For instance, it introduces command line arguments like:

--no-daemon              # just run the program in the console this time
--write-initd            # writes a startup file

Read this source to get the picture. Don't forget the comments!

#!/usr/bin/php -q
<?php
/**
 * System_Daemon turns PHP-CLI scripts into daemons.
 *
 * PHP version 5
 *
 * @category  System
 * @package   System_Daemon
 * @author    Kevin <kevin@vanzonneveld.net>
 * @copyright 2008 Kevin van Zonneveld
 * @license   https://www.opensource.org/licenses/bsd-license.php
 * @link      https://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 arguments 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_STRICT);
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' => 'kevin@vanzonneveld.net',
    'sysMaxExecutionTime' => '0',
    'sysMaxInputTime' => '0',
    'sysMemoryLimit' => '1024M',
    'appRunAsGID' => 1000,
    'appRunAsUID' => 1000,
);

System_Daemon::setOptions($options);

// This program can also be run in the foreground 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(
            'successfully 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 actual 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();
?>

Console Action: Controlling the Daemon

Now that we've created an example daemon, it's time to fire it up! I'm going to assume the name of your daemon is logparser. This can be changed with the statement: System_Daemon::setOption('appName', 'logparser'). But the name of the daemon is very important because it is also used in filenames (like the logfile).

Execute

Just make your daemon script executable, and then execute it:

$ chmod a+x ./logparser.php
$ ./logparser.php

Check

Your daemon has no way of communicating through your console, so check for messages in:

$ tail /var/log/logparser.log

And see if it's still running:

$ ps uf -C logparser.php

Kill

Without the start/stop files (see below for howto), you need to:

$ killall -9 logparser.php

Autch.. Let's work on those start / stop files, right?

Start / Stop Files (Debian & Ubuntu Only)

Real daemons have an init.d file. Remember you can restart Apache with the following statement?

$ /etc/init.d/apache2 restart

That's a lot better than killing. So you should be able to control your own daemon like this as well:

$ /etc/init.d/logparser stop
$ /etc/init.d/logparser start

Well with System_Daemon you can write autostartup files using the writeAutoRun() method, look:

<?php
$path = System_Daemon::writeAutoRun();
?>

On success, this will return the path to the autostartup file: /etc/init.d/logparser, and you're good to go!

Run on Boot

Surely you want your daemon to run at system boot.. So on Debian & Ubuntu you could type:

$ update-rc.d logparser defaults

Done your daemon now starts every time your server boots. Cancel it with:

$ update-rc.d -f logparser remove

Logrotate

Igor Feghali shares with us a logrotate config file to keep your log files from growing too large. Just place a file in your /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
}

Obviously, replace the mydaemon occurrences with values corresponding to your environment. Thanks Igor!

Troubleshooting

Here are some issues you may encounter down the road.

  • 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 uncaught error to bring it to its 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 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.

I know I'm saying MySQL a lot, but you can obviously replace that with Oracle, MSSQL, PostgreSQL, etc.

Legacy Comments (281)

These comments were imported from the previous blog system (Disqus).

Anon
Anon·

I read your article and it did a great job of explaining daemons and such in an easy to learn way. However,

\"Everyone knows PHP (right?)\"

Everyone knows how to grunt and point too. Sure, I find it interesting that PHP can do this, but please, there is horrible PHP code littered throughout the web. Please do not bring that to a lower level :(

crackgerbal
crackgerbal·

Awesome blog! I went from not knowing what a daemon is to being able to write my own in php. great post!

Anon
Anon·

Oh, err, I didn\'t mean that YOUR code was the horrible PHP code that was littered throughout the web, I just meant in general ;D

simeon
simeon·

Your work to create the System_Daemon class is impressive. Unfortunately PHP is just not the best language to do this sort of thing. One recent article in my feeds noted that PHP leaks file descriptors (see http://gnuvince.wordpress.c.... This means that long running PHP processes eventually are unable to open files...

PHP\'s process model was is inherited from the cgi days and is built around loading, executing, and terminating (and don\'t get me wrong. There are definitely advantages to this process model for web development). Running a PHP script indefinitely is likely to push up against the edge cases in the runtime engine...

The alternative approach I\'ve taken when I need something more sophisticated than cron jobs is to make sure I have command line interfaces to my PHP business logic and write the daemon in python or even bash. The daemon can be extremely simple (listen on a port, tail a file, etc) and merely invokes the appropriate PHP script to handle all the business logic... Just my 3 cents.

Matt Baker
Matt Baker·

You use too many commas. Mostly in incorrect places.
http://owl.english.purdue.e...

Kev van Zonneveld
Kev van Zonneveld·

@ Anon: Yes (because it\'s so easy) there are a LOT of PHP coders, and obviously there are a lot of rookies as well. And some may even never get passed that point.

But to do any real damage you would need root access to a Linux box in a production environment.
If you do: I hope you\'re at least responsible enough to know your own coding limits and not run your hobby projects on your company\'s main server. If you\'re not: your company\'s box is probably already destroyed, anyway. along with your job :)

Kev van Zonneveld
Kev van Zonneveld·

@ crackgerbal: Thank you!

@ simeon: I agree PHP is not the best language for it, the article says something similar. But it\'s very well possible and even suitable in some cases.

From a design point-of-view you may be right about PHP. It probably wasn\'t meant to run that long, but:
- I haven\'t run into problems yet
- I believe PHP resource IDs are different from file descriptors

You do make in interesting point though: thank you.

Kev van Zonneveld
Kev van Zonneveld·

@ Matt Baker: Thank you for correcting me :)

I suppose I forgot to say the obligatory:
Sorry for my bad English, it\'s not my native language.

But really, thanks. I want to improve my English and I will definitely take a look at it.

wiwo
wiwo·

Thank you for your post. It is really interesting. I didn\'t know one can create deamon with php! Not yet tested but when I have the need I would know where to go :)

Timothy
Timothy·

Wow. That\'s awesome! Thank you!

marek
marek·

how to fix issue \"Connect to MySQL after you start() the daemon\" ?

Dustin
Dustin·

Yeah first post stumble - sorry for being dumb - i like this post.

Jason Moss
Jason Moss·

Glad to see other people recognize the use of PHP for non-web use. We\'ve been using it for some command-line based applications that work quite nicely. Development especially is nice, with the massive amount of built-in functionality and the outside resources available. I use PHP a ton, but I haven\'t done any web dev :P

Zach Blank
Zach Blank·

This looks amazing! I will be using it for some live data collection and visualization at a party in NYC this weekend. Thanks for sharing!

Douwe Kasemier
Douwe Kasemier·

Brilliant pear plugin. Using it for multithreading!

Kev van Zonneveld
Kev van Zonneveld·

@ all: thanks for the kind words

@ marek: That\'s not an issue.. Thats the solution TO the issue that you will lose the mysql connection if you don\'t.

Strawp
Strawp·

This looks great!

Quick stupid question though: In your example above, is everything from ::start(); to the end of the script the child process? Is the parent dead at this point?

If for example I wanted to fork a slow script as a child from the action page of a web app but keep the parent running so that it could finish up and redirect the user, how would that look?

Tom
Tom·

Really cool! Did you also know about php-gtk? You can use PHP to make desktop application as well. PHP is really a great language for it\'s convenience. I wouldn\'t go and make Photoshop with it...but for small applications -- most definitely -- especially if people are accepting things like AIR as \"applications\". Great work, thanks!

Kev van Zonneveld
Kev van Zonneveld·

@ Strawp: Yes that\'s not really what this class was designed for. Maybe you could look into PHP_Fork (http://pear.php.net/package... which I believe does something similar. Good luck!

Kev van Zonneveld
Kev van Zonneveld·

@ Tom: Yes, I haven\'t made anything with it yet, but it\'s very cool that\'s all possible :)

Najaf Ali
Najaf Ali·

Epic post! I was looking for a way to use a daemon to create an automated backup system and this is just what we needed!

Chris
Chris·

Did you know this has been published verbatim here: http://www.hurricanesoftwar...

Kev van Zonneveld
Kev van Zonneveld·

@ Chris: No I didn\'t. That\'s quite shameless. Most of the times they have the decency to at least add a small note as to who they got it from.

I\'ve left a comment at that URL.. Let\'s see how long it stays there ;)

Thanks a lot for letting me know Chris!

Centerax
Centerax·

Wow!

This is great stuff, in conjunction with Monit this is very powerful and easy to implement.

Great job!

Anon
Anon·

@Kevin

Hey, Anon here (the one you responded to). I seemed to stumble upon this post again~

Let me reemphasize that it is interesting that PHP can do this, but for obvious reasons it should not. PHP is an awesome language in which quick websites can be easily created due to it\'s ease of use and availability.

Other than than, to put it bluntly, it sucks. It is truly a horrible language. There is little point in debating that point here so please don\'t. (If you really want to debate, use a search engine to find essays about PHP and it\'s..erm..attributes.) (I am kind of cheating here; I go on to insult PHP and tell you not to argue :O)

I suppose when you have a hammer everything looks like a nail, but your three reasons to use PHP for creating daemons are not convincing at all.

\"Reuse & connect existing code\"
You should not be reinventing the wheel to begin with. If you must, there is Perl and CPAN.

\"Deliver new applications very fast\"
Yes, PHP has a lot of functions (too many), but Perl is better at creating applications quickly.

\"Everyone knows PHP (right?)\"
I already mentioned this point in my previous post, but I will do so here again (with a different approach). If your C programmer leaves, find a new C programmer. It is really that simple. If your Photoshopping person left would you resort to using Microsoft\'s paint? No, you would get a new Photoshopper. (Hypothetical situation is hypothetical, by the way.)

Kev van Zonneveld
Kev van Zonneveld·

@ Anon: Thanks for your comment. It is clear that you do not want to use PHP for this.

Let me just say: Nobody is forcing your hand here.

But I think your vision on this matter does not include factors like: time, resources, and company context.
For instance at my company, there is no (need for a) C programmer at all. There never was and there probably never will be. But we still have the need to automate a lot of thinks that are based on our PHP software.
Perl just doesn\'t play well with our LAMP environment. For instance we have many libraries & classes that the automating daemons need to interface with. And I was not planning to recreate & maintain all of our business-logic (no, this is not in CPAN unfortunately ;) in two different languages. I value DRY principles too much for that.

So I\'ve been running PHP daemons for years now (it took a while before I submitted to PEAR) and without any problems. And so for me it\'s safe to say: it solves more problems than it creates.

Eduardo Jordan
Eduardo Jordan·

Here we go again. PHP sucks? Is it horrible language?
I wonder why facebook, a multibillon dolar company, uses it!
Kevin, I gonna use this class and will give you feedback.
About the perl guy. FYI, PHP has syntax similar to Perl, just simpler to understand.

I guess that for Perl programmers:

my @lines = <FILE>;
while (<FILE>) { print $_; }

that is nice, and this horrible

$handle = fopen(\'datafile.csv\', \"r\");
while (!feof($handle)) {
$buffer = fgets($handle, 4096);
echo $buffer;
}
fclose($handle);

I don\'t think so, second one is easier to read and understand.

Kev van Zonneveld
Kev van Zonneveld·

@ Eduardo Jordan: Thanks!

jatrn
jatrn·

Hello,
First of all thank you very much for this lib, it is very usefull.
I got a small problem. I\'m able to start my daemon by \"/etc/init.d/IMM15 start\" but can not stop it, \"/etc/init.d/IMM15 stop\" doesn\'t work. There is nothing in the log and on the screen. See my code below.

Thanks for you help.

#!/usr/bin/php -q
<?php
require_once \"System/Daemon.php\";
System_Daemon::setOption(\"appName\",\"IMM15\");
System_Daemon::setOption(\"appDescription\",\"simple deamon by jatrn\");
System_Daemon::setOption(\"authorName\",\"xxx\");
System_Daemon::setOption(\"authorEmail\",\"xxx@yahoo.com\");

System_Daemon::start();
System_Daemon::writeAutoRun();

while (true) {
$t=1;
sleep(10); }
System_Daemon::stop();
?>

rhuel
rhuel·

Your code works and it rocks. Problem is my webhost has php without --enable-pcntl compiled. Can we go around this requirement?. I need it to run in background. Thanks.

Kev van Zonneveld
Kev van Zonneveld·

@ jatrn: Please upgrade to the latest version of System_Daemon, and try again. Now what does your logfile say?

@ rhuel: This is not going to work on shared-host-environments, because you need to be root to fire up the daemon. Only after you start it, it can become another user.
This is because root will have to setup some dirs like logdir & rundir, and chown it all.

Dani
Dani·

Great, I guess I will be able to get some of my php scripts out of their uggly cron configuration. When I needed to run a script every 15 seconds I had it run it 4 times in cron with different sleep times (0, 15, 30 and 45 secs) which is a very uggly thing to do (and \'dangerous\' if the script cannot handle concurrent execution).

Sometimes PHP can be the ideal choice to run such things. If you have some LAMP environment with PHP handling your data in the first place and you need some background process it is the best choice to use the same environment for that process instead of risking to corrupt your data because of compatibility issues like character-encoding.

ger
ger·

I have jabber (XMPP) php daemon running which sends news updates to the users who has subscribed to them.
The only thing I can say from my experience - php seems have memory leaks when works in infinitive loop. Even if he php code itself is well designed to pervent this. If you\'ll monitor the memory consumed by your php daemon - you will see it continuously grows in time.
The solution of this prob I use - juts kill / restart the daemon with cronjob from time to time.
Hope this will be useful for someone.

Kev van Zonneveld
Kev van Zonneveld·

@ ger: I am not able to reproduce that behavior. Maybe you can dump all get_defined_vars so make 100% sure nothing is increasing?

anon
anon·

http://dev.pedemont.com/sonic/

a php daemon, with plugin type arch.

Yılmaz Uğurlu
Yılmaz Uğurlu·

Hi Kevin, i tested and it\'s working great.
I have question about startup file. I\'m runnig as root my script, after the start i\'m checking log file, all script start\'s has this two line.
[CODE=\"php\"]
notice: Directory: \'/etc/init.d\' is not writable. Maybe run as root?
[Apr 11 12:23:56] warning: Unable to create startup file. [l:761]
[/CODE]

But i\'m running script as root, what i\'m doing wrong?

Kev van Zonneveld
Kev van Zonneveld·

@ Yılmaz Uğurlu: Thanks for testing. I\'ve improved the method for is_writable and hope that will help avoid your bug. The fix is in the OS.php file. The new version can be found here:
http://svn.plutonia.nl/proj...

Are you able to confirm this works? If so than I can release a new PEAR package for System_Daemon

Kev van Zonneveld
Kev van Zonneveld·

@ Yılmaz Uğurlu: On that link, click on Download in other formats: raw, and overwrite it in (probably) /usr/share/php/System/Daemon/

Yılmaz Uğurlu
Yılmaz Uğurlu·

Thanks Kevin. I tested,

First:
in OS.php line 297 and line 299 must be self::isWritable(...)
because i get this error fist:
[CODE=\"php\"]Fatal error: Call to undefined function is__writable() in /usr/share/php/System/Daemon/OS.php on line 297[/CODE]
and i changed these lines.

So, after i run my daemon just like: sudo ./myDaemon.php
It went well but, when i looked at the log file i showed these errors
[CODE=\"php\"]
[Apr 12 17:04:31] warning: [PHP Error] fopen(/etc/init.d/130827514849e1f4ef90c8b.tmp): failed to open stream: Permission denied [l:303]
[Apr 12 17:04:31] notice: Directory: \'/etc/init.d\' is not writable. Maybe run as root?
[Apr 12 17:04:31] warning: Unable to create startup file. [l:761]
[/CODE]

I have one question more, i didn\'t add the System_Daemon::stop(); line to end of my file.
Bu after 3 times loop my daemon is stopped. i guess i didn\'t understand how to write endless daemon, how can i do that?

Kev van Zonneveld
Kev van Zonneveld·

@ Yılmaz Uğurlu: Sorry my bad, fixed in SVN now as well.

It looks though as if you really really don\'t have permission to write anything to init.d

What if you \'su -\' first and then really run it as root instead of sudo-ing it?

About the 3 times thing. Are you using the logparser example? That shuts down because it\'s only a test.

If you just put a
[CODE=\"PHP\"]
while(true) {
// Your code here
sleep(5);
}
[/CODE]

It should run indefinitely. Unless you run into any problems like stated in the troubleshooting section

Yılmaz Uğurlu
Yılmaz Uğurlu·

Thank you, the daemon now runing indefinitely.
But i still didn\'t create startup script correctly. I tried mant things, i guess i have problem with my ubuntu box, whatever.

I wrote feed crawler daemon for test. It\'s fecthing and writing feed items to databse. I watched very long, and everyting went well. Cpu usage, memory usage, all my concerns are gone. Thank you so much this is really great application.

Janitha Karunaratne
Janitha Karunaratne·

Using PHP to do small quick tasks is ok, it\'s good for rapid development of websites, but it\'s quite unfit for purposes such as writing service scripts that run in the background.

PHP is meant to be started quickly and killed quickly (such as a page view) and that\'s what it\'s memory management is optimized for for the duration of the script... if you run it for prolonged periods of time, expect some seriously nasty memory usage and leaks.

Your reasoning for this is that everyone knows it and it\'s availability everywhere... this is where I have to disagree. That\'s like saying \"Hey, lets build this house using spoons and forks, because everyone knows how to use them and their everywhere!\". It\'s possible, but it\'s a very poor choice.

Just because it\'s the only thing a person knows or just because it exists everywhere doesn\'t make it the proper tool to use.

Saso
Saso·

Hi, Kevin, great article.
I am searching for IPC (Inter Process Communicating) for using with Ur Daemon.

I would like to change architecture of my app. to lighten frontend and have heavy duty daemons to do all the work.

What I find missing (from my point of standing) is automated limiting daemon loops, number of daemons and some mechanism for maintaining pre-forked processes, ready to use (similar as Apache2).
This is needed just due bad PHP-coding practices, to avoid leaked code eating all resources.
And I miss Remote Procedure Call, as stated above: It would be great to have daemoned API, which we \'just use\' in our PHP frontend.

Regards, Saso
ps: dont get me wrong, Ur work is just great, I just give U some new ideas.. :)
(aka. being lazy-constructive)

Kev van Zonneveld
Kev van Zonneveld·

@ Yılmaz Uğurlu: Great to hear!

@ Janitha Karunaratne: An ideal daemon in an ideal world would not be written in PHP, I make that clear in the article already. But you actually make it sound immoral :D

Don\'t be alarmed.. My house is built with good old bricks: not forks & spoons. And yes some of my daemons are created with PHP. Why? Because it works. Because I can use my existing classes and take away load from front-end processes built on the same technology.

I feel like using 4 different languages to solve 1 problem is really about creating 1 thing and 1 thing only: a maintenance hell.

If I am to redo my existing classes in C (or perl or python), I will have to duplicate my patches & improvements for the rest of my programming career...
To justify this decrease in productivity there would have to be some very convincing reasons for that. And your (funny but) dogmatic metaphor is not going to cut it.

I don\'t believe you\'re not basing your opinion on actual results.
I have PHP daemons running for over 2 years now without a problem. I understand you may get an unary feeling but I\'m pragmatic and this is actually working very well.

Anyway, nobody is forcing you to use this, so Happy compiling :)

@ Saso: You could work with a database queue, storing heavy jobs and having daemons eat them away using the same DB class as your frontend process.
Deamons are already limited to 1 per \'appName\', and for simple communication you could use POSIX signals. Does this help you out a bit?

Saso
Saso·

@Kevin:
Well, it doesn\'t help me much :)
Probably I will use Ur project and upgrade it with my ideas and send it back to U, for U to make world use of it :)
I would need sync and async communication between processes, so front-end and daemons can \'talk\' with each other. Self-observing would also be necessary, to avoid wild daemons eating resources.
I, for example, need this to make chat bot, which will be daemonized and frontend interfaces (IRC, PHP, what-ever..) will ise it for \'chat API\'.
This is great to avoid the overhead of creating and killing all the objects in heavy front end.
But I agree, for mass mailing, writing to DB is enough.
Regards, Saso

Kev van Zonneveld
Kev van Zonneveld·

@ Saso: Currently your daemon can make iterations without my class executing one line of code. So self monitoring would require my users to call for example:

[CODE=\"PHP\"]
while (true) {
System_Daemon::ping();
// your code
}
[/CODE]

That way I could implement self checks from within the ping method, depending on the configuration you specify.

It could be an interesting addition. As far as advanced communication goes. You could look into Net_Socket (also pear) and/or go for TCP/IP.

Another method would be a central memcached server or actual shared memory using shmop (but that\'s limited to 1 machine).

Non of these things and methods are standard in System_Daemon however. It merely provides the most fundamental brick for others to build upon.

But if you like to share what you\'ve come up with, give me a ping. I\'m not sure yet but maybe we could generalize it and add a little sauce on top of the daemon.

Tech Blog
Tech Blog·

Great post thanks, I learned something new about PHP today :)

James
James·

I noticed you added version 0.8 alpha to the PEAR repository today. This is some very promising stuff. Keep up the great work!

Mike Stackhouse
Mike Stackhouse·

Nice article - I\'ve tested your sample code on our FreeBSD 7 server.

We have the requirement to have a daemonized server process that would fork off children based on control records in a database. Does your class have functionality to fork off children, keep track of their process id\'s and kill them if need be?

Still trying to wrap my head arounc pcntl - I might be over-thinking the problem. Could it be as simple as implementing in a database reading loop, and when a new child is needed execute the fork at that time? There are some good examples on the web for managing child processes from that point forward - this one seems to make sense - http://www.van-steenbeek.ne....

Might you know of any examples that would have functionality such as we require?

Thank you very much!

Kev van Zonneveld
Kev van Zonneveld·

@ James: You noticed correctly :) Thanks. If you have any specific ideas on System_Daemons future feel welcome to submit them.

@ Mike Stackhouse: I\'ve found an unmaintained PEAR package: Fork. Maybe that can do what you want. I didn\'t have plans to make this a system_daemon feature as I\'ve always felt that it should be a simple and basic building block. What do you think?

James
James·

@Kevin & @Mike: There is some code from ~2008 that looks like it forks off children pretty elegantly: ( http://bipinb.com/making-ph... ). I have not actually executed the code, however, so don\'t know for sure.

To control those forked daemons, it seems possible to do the following: insert each daemon\'s PID into a MySQL table, and have each daemon update MySQL every several minutes informing you of what it\'s up to. You could then see what\'s going on with each daemon child via a browser interface, and kill the child if needed.

At least, in my head, that makes sense. But I haven\'t committed those thoughts to code.

Mike
Mike·

@James - that link to bipinb.com appears to be dead - nuts, I wanted to look at that.

@Kevin - I looked at that php_fork PEAR package. I\'m pretty new to PEAR, but I couldn\'t even get the basic.php to run - doesn\'t even throw an error I can diagnose.... I know PEAR is good on my server as I\'m able to execute your sample code fine.

Thanks very much for any help you can provide, but I don\'t want to be a bother...

Mike
Mike·

Update - I was able to get php_fork running on my server. I did not have the shared memory module installed for PHP - duhhh. I\'m going to continue testing that.

Thanks for your help!

James
James·

Mike, it\'s back up now. If it\'s down the next time you try, I\'ll post a link to a copy of that code that I just snagged for backup purposes.

mikesta
mikesta·

You did a good job, keep ahead!

vince
vince·

Hi Kevin,
I would like to start creating some funny daemons in php so I have installed a LAMP + php5-cli on an ubuntu 9.04 system.
But I have no support for pcntl yet! How can I do? Can you suggest me a guide?

vince.

Kev van Zonneveld
Kev van Zonneveld·

@ James: That could definitelty work although you would have to make a bridge between web interface + (root) cli, and personally I don\'t have use cases for something like that. As in: I can\'t think of scenarios where I would want to have to manually intervene or stop daemons. They should be designed to not require that I think.

@ Mike: Sorry I don\'t know PHP_Fork, I only know of it.

@ mikesta: thank you :)

@ vince: I\'ve tested up until Ubuntu Intrepid and those all work out of the box. Can you give me some exact output/errors?

vince
vince·

Hi Kevin,
thanks for the reply. I haven\'t yet installed the pear class because I think that I have to install first the pcntl support. But maybe it\'s unnecessary... Your pear class isn\'t based on php-pcntl functions?

Kev van Zonneveld
Kev van Zonneveld·

@ vince: Yes it uses pcntl, but it\'s probably already enabled cause Ubuntu should have this this by default. I think you can just install the class and let me know what happens?

Adam Charnock
Adam Charnock·

Hi Kevin,

This is really great work, thank you very much. We are going to be using it extensively in the backend of the next version of walltweet.org.

I did notice this:

\"On success, this will return the path to the autostartup file: /etc/init.d/logparser, and you\'re good to go!\"

... which isn\'t actually correct. It seems to return boolean, not a string. Just a minor point!

Thanks again :D

Adam Charnock

Kev van Zonneveld
Kev van Zonneveld·

@ Adam Charnock: It should a boolean when it already exists. And the string when it has created it for the first time. Can you confirm this?

jonathan
jonathan·

RE: Statcache will corrupt your data

is clearstatcache () broken?

I\'ve been using this successfully, but did I miss something?

Martijn
Martijn·

Thnx, for the article. have to say it works nice and easy.

I think it\'s better solution then using cronjobs. Only wondering, i have to do a daily job, processing books. (about 2.000.000 records) Maybe i missing something, how to do a daily update, depending the content of a certain folder?

I\'m istill depending on cron or can you give some hints to solve the problem?

regards Martijn

Kev van Zonneveld
Kev van Zonneveld·

@ jonathan: the iterate function actually uses clearstatcache() so you can also use that directly. As long as you use it.
But I\'d rather have people use my iterate() function so that I can implement additional fixes as I find more issues.

@ Martijn: You could also use cron combined with solo (http://timkay.com/solo/) to prevent overlap and do what you want. If you need more control / intelligence on the other hand, you may need to fire actions and keep track of time yourself.
I have once seen a full table-based cron implementation in PHP, I\'m sure you\'ll find it if that is what you need. Or maybe a simple
[CODE=\"PHP\"]
while(true) {
if (!$ranToday && date(\'Hi\') > 1700) {
execute();
}
}
[/CODE]

dulig
dulig·

Hey,
Thanks for this great article.
I think about using your daemon in my web application.
This might be a stupid question, but since the daemon can only be started out of command line, will it be possible, to do a shell_exec to start the daemon out of a web application?

Thanks in advance
dulig

Kev van Zonneveld
Kev van Zonneveld·

Typically you would configure a deamon to run at server boot, and have it accept jobs while it\'s running.

If you want to control a daemon\'s start / stop you typically would do that with a script inside /etc/init.d (os dependent, for some OSes, System_Daemon can generate such a script for you)

So, yes in theory you could then use a PHP script to control run the init.d script and control your daemon process. But I\'d hardly recommend it because of
- bad practice
- security risks involved

AkEdEv
AkEdEv·

Please check this.
I just run your example on php 5.3.0 and appears this line.

PHP Deprecated: Function eregi() is deprecated in /usr/share/pear/System/Daemon/Options.php on line 250

Kev van Zonneveld
Kev van Zonneveld·

@ AkEdEv: Excellent, thanks for reporting. I fixed it in svn:
http://trac.plutonia.nl/pro...

It will be included in the next package release.

If for follow ups on things like these you could use the official PEAR bug system, that would be awesome.

Bucanero
Bucanero·

Nice Job creating this !!!

I was wondering how to create \"multiple email generation daemons\", My idea is to have 10 (or more if we have bigger loads) of this all the time running and looking for emails to create and send.

The problem I was encountering is that if I try to run the same file again (starting another one from the console) I receive and error message. One option is to have files from to 1 to x (example daemon1.php ... deamon2.php) do you think this make sense ? Is this the way to go ?

Also running your example, I got 3 times on the console \" Who are you?\"

Thanks again for this

Bucanero

Kev van Zonneveld
Kev van Zonneveld·

@ Bucanero: I don\'t see where \'who are you\' is coming from at the moment. Considering your multiple instances question. Here\'s an idea:

Start your daemon with an argument like:
./yourdaemon.php a

and configure your appName at runtime to adhere the argument \'a\'. Now when you write an init.d file or pidfile, \'a\' will be used.
This way you can have as many daemons as you want with own logfiles, pidfiles & init.d files, but with 1 script.

Bucanero
Bucanero·

Kevin

Thanks for your reply

I figure out the \"Who are you?\", it was because on my box I did not have a user with uid 1000 or group with id 1000. As your example had

Another question, How can I know from php that my daemons are running ?

Running ps uf -C from the console I can see they are running, is there a way to have this info via php ? I need to write a monitoring script

Thanks again

Bucanero

Kev van Zonneveld
Kev van Zonneveld·

@ Bucanero: Ok, several ways. You can have them open a socket and see if it\'s still open. You can check the pidfiles (inside /var/run/) and see if the pids are still running (probably execute ps to do that), or send then POSIX signals. You can also write something to disk periodically and check that, or have a third party tool like monit monitor your process (which is probably the wisest)

Easy Daemons in PHP
Easy Daemons in PHP·

[...] This class and script makes it easy to turn a one-shot php script into a daemon that runs in the background on a timer [...]

Kev van Zonneveld
Kev van Zonneveld·

@ Easy Daemons in PHP: I\'m honored that someone build a wrapper around my wrapper : ) Haven\'t looked at the code yet, but if it does what you need: I\'m happy.

Sean Hess
Sean Hess·

@Kevin - The original motivation was to get the daemon\'s loop to run as a separate script, since writing a loop with System_Daemon was causing strange problems. Thanks for your article and work!

Peter
Peter·

Kevin,

I\'d like to use your class for as job scheduler daemon in PHP where I have the daemon run various jobs throughout different times of the day. I\'d like the daemon to simply spawn each job in a child process and go back to being a daemon without each job running within the daemon\'s process. How hard would this be to do without interfering with your class?

Thanks

How hard would it be to have some of the code running with the daemon

Kev van Zonneveld
Kev van Zonneveld·

@ Peter: Have a look at
http://brian.moonspot.net/p...

Jason
Jason·

I have installed all the required libraries and created a test which I start at the command line with php socket.php I have made a separate sub directory of the pid so that it lives in socket/socket.pid. The continue to get \"socket daemon was unable to write pid file\" although the script does delete the file. I recreate the file and I get the same error and the file is gone. Any thoughts on what I might do. I am running the script as root and have checked all ownership and permissions.

Kris
Kris·

I have a weird problem. I created a daemon based on the logger example. I ran the script using ./daemon.php, then killed it with the command killall -9 daemon.php (notice the 9 not g) the process was killed, but when I try to restart the script i now get:
Extension \'./daemon.php\' not present

I have no clue how to solve this... the script ran the first time.

Kris
Kris·

Apparently this was caused by a wrong EOL in my php script. Using dos2unix on the daemon.php file fixed the problem... took me 3hours to find this one :-/

Kev van Zonneveld
Kev van Zonneveld·

@ Jason: There were some bugfixes recently. What version did you try?

@ Kris: Autch.. Love your gravatar though :)

Remiko
Remiko·

Could I know the minimum file lists in standalone packages to let the daemon without PEAR?

Kev van Zonneveld
Kev van Zonneveld·

@ Remiko: This should prove to be enough:

[CODE=\"BASH\"]
./Daemon.php
./Daemon
./Daemon/Options.php
./Daemon/OS.php
./Daemon/OS
./Daemon/OS/Ubuntu.php
./Daemon/OS/Debian.php
./Daemon/OS/Fedora.php
./Daemon/OS/BSD.php
./Daemon/OS/RedHat.php
./Daemon/OS/Windows.php
./Daemon/OS/Linux.php
./Daemon/OS/Exception.php
./Daemon/Exception.php
[/CODE]

You could even also leave out the operating systems you\'re not planning on using.

But on the other hand: if you\'re going to need multiple files anyway, why not just add the entire dir. It will really only add a few kbs.

David
David·

Hi Kevin,

Good lib. Just wondering if you can give me some advice about spawning children from within the daemon. Essentially I want to start x instances of the daemon, that run (looking for jobs from a Gearman server) for the entire time the daemon is running.

This may not be the best approach. I imagine that spawning children AS work/jobs come into the daemon would be a more efficient, however I haven\'t been able to get that working with System_Daemon yet.

So far this is what I\'m doing (and seems to be working) and I\'m wondering if there are any problems with something like the following:

[code=\"php\"]
for($i=0; $i<3; $i++) {
$pid = pcntl_fork();
if ($pid == -1) {
exit(\'Could not fork children\');
} else if ($pid) {
System_Daemon::log(System_Daemon::LOG_INFO, \'Spawned child: \' . $pid);
} else {
while (!System_Daemon::isDying()) {
// do something like look for a job from gearman
$gmw->work();
System_Daemon::iterate(1);
}
System_Daemon::stop();
}
}
[/code]

I understand that potentially if the children exit they could become zombies, however I\'m not sure if that applies in this situation as I want the children to run for the life of the parent (daemon).

Any help / guidance would be greatly appreciated.

Joe
Joe·

First, this is fantastic!!! (Also, this is fantastic!)

In my case I\'m using this for a monitor that dips into a data store every ten seconds. The monitor launches a child process to munch on said data. Now, this process is best kept open for a prolonged period, only to be closed after 60 minutes of inactivity (although if we absolutely have to shut it down, that\'s fine too).

That\'s where the problem is, shutting it down. If I send TERM to the monitor, it won\'t go away until that child process shuts down. However, in the monitor, the last thing I do (once isDying() is true) is exit my loop and check for that child process. If it\'s around, we tell it to exit. I never get to that part though. It\' s as if isDying() isn\'t returning TRUE after sending TERM to the daemon. Hmm ...

Joe
Joe·

Ahh, I\'ve removed the child process from the equation. Even if I never run it, TERM just won\'t make this pup quit.

I did try setSigHandler for SIGTERM, and my function _does_ get called, but the loop keeps on going. I guess I could set runningOkay to TRUE but this isn\'t an error condition we\'re bailing out of. :-o

Clues welcome/appreciated!

Joe
Joe·

OK, I have to call System_Daemon::defaultSigHandler($sig) (passing in the signal that was passed to my own handler). Silly me. :)

Joe
Joe·

At last, I think I\'ve found the smoking gun:

First, I tried putting some cleanup immediately after the main loop. Even if I send SIGTERM to the main pid, I never seem to make it out of the loop to do any cleanup. Nothing gets logged.

Second, I set up my own handler for SIGTERM. In here, I look at the value of $child which points to our child process object (wherein there is a shutdown function). However, $child is null!

This could be a result of the process forking, and the handler is really occurring in the pre-daemon world. If that\'s so, how do I communicate to the other side to shut things down gracefully?

(Apologies for the multiple stream-of-consciousness posts! Maybe someone else is having the same issue though, so this might help.)

Kev van Zonneveld
Kev van Zonneveld·

@ David: You may want to find out and see if you can use PEAR\'s Fork class in combination with System_Daemon to help you with that.

@ Joe: Thanks for the comments, np. Would it be possible for you to share the code somewhere? maybe pastebin? I\'m having a hard time doing all this in my head. Give me something to test with and we can take it from there

Kees
Kees·

Hei David,

I\'m using our solution to create several daemons ( 20 + ) which communicate with each other thru spread (ipc), however, when creating more then 12 daemonson one server, it seems that the cpu usage dramaticly increases(measured thru \'top\' ), is that something you are aware of, or is this something php specific ?

wregen
wregen·

Hi, you have mentioned that one possible application of System_Daemon is : \"keep your application listening (on a socket for example)\".

Can you provide a simple example? This will help me a lot.

Thank you.

aeuglein
aeuglein·

I have to say I am also interested in the simple example \"wregen\" asked for! #91

jatrn
jatrn·

The log of my daemon is full of:
info: phpvoices daemon received signal: child.

Since my PHP script is a constant loop (with 1 second sleep), I\'m getting entry to the log every second. Is there any way to turn this notification off?

Thanks again for this package.
jatrn

Kev van Zonneveld
Kev van Zonneveld·

@ wregen & aeuglein: I don\'t have one yet, but there are plans to team up with System_Socket which is probably getting a revamp:
http://article.gmane.org/gm...
After that, life should be sweet for you guys

@ Kees: There has to be some logging or something otherwise growing going on in your code. Cause if you run System_Daemon without custom code, this does not happen.

@jatrn: I will change the log level to DEBUG, so that you can then choose to turn off debug logging, how does that sound?

jatrn
jatrn·

kevin: sounds good. just let me know how to do it :)
thanks a lot

Sun Location
Sun Location·

Thanks you very much for this !
I\'ve try the application but I have this error : MySQL has gone away

Kev van Zonneveld
Kev van Zonneveld·

@ Sun Location: The Troubleshooting section covers your problem

rana
rana·

here is my code

#!/usr/bin/php -q
<?php
require_once \"System/Daemon.php\"; // Include the Class

System_Daemon::setOption(\"appName\", \"ct\"); // Minimum configuration
System_Daemon::start(); // Spawn Deamon!

$path = System_Daemon::writeAutoRun();
while(1)
{
sleep(5);
}

?>

why i cant start/stop the daemon using sudo /ect/init.d/ct start or stop ??

i\'m using ubuntu 8.10 and daemon is running nicely using sudo ./daemon.php

jatrn
jatrn·

rana: I had this problem too...
Check line \"PIDFILE=/var/run/$NAME.pid\" (in /etc/init.d/whatever). I had to manually change it to /var/run/$NAME/$NAME.pid, verify location of pid file in /var/run

Kev van Zonneveld
Kev van Zonneveld·

@ rana & jatrn: System_Daemon looks at the option: \'appPidLocation\' the moment it writes the init.d file.
You can change it and write another file. be sure to use the latest version

David Heremans
David Heremans·

Hello,

I\'m very impressed with this article (in fact had it in my favourites to check out for a long time already...)

I decided to go give it a go now, but I can\'t get it to work.

It\'s giving me the following error:

[CODE=\"text\"]
root@server32 [/home/flexinb/public_html]# ./daemon.php
Extension \'./daemon.php\' not present.
root@server32 [/home/flexinb/public_html]#
[/CODE]

Any idea what might be causing this?

Kev van Zonneveld
Kev van Zonneveld·

What happens if you try
php ./daemon.php?

If it works, you need to chmod u+x daemon.php and maybe put /usr/bin/php in your /etc/shells and maybe put #!/usr/bin/php -q in the head of your script.

Urda
Urda·

Fantastic class! Fit my need for daemons in a project of mine at work. One question though, will we be seeing any more updates soon?

Peter
Peter·

Why does the System_Daemon log this information?

[CODE=\"text\"]
notice: starting My-Daemon daemon, output in: /path/to/log/file.log

emerg: My-Daemon daemon is still running. exiting [l:1010]

info: Not stopping My-Daemon, daemon was not running
[/CODE]

This only occurs when the same daemon script is called again, while a child process is still running. It seems non-nonsensical to me, since it acts like the parent was still running, and wants to kill it. Then right after that discovers the parent wasn\'t running and no longer wants to kill it.

Why is it doing this?

Ben
Ben·

I\'m encountering a problem when running a simple daemon. I\'ve included the code below. When I start the daemon with ./dtest.php, it runs and appears to fork properly. The problem is when I attempt to exit or logout of my terminal. It just hangs. If I kill dtest.php from a separate terminal, the first closes immediately. Any ideas what could be causing this?

[code=dtest.php]#!/usr/bin/php -q
<?php
require_once \"System/Daemon.php\";
System_Daemon::setOption(\"appName\", \"Daemon_test\");
// Overrule the signal handler with any function
System_Daemon::setSigHandler(SIGCONT, array(\"System_Daemon\",
\"defaultSigHandler\"));
System_Daemon::start();

while (!System_Daemon::isDying()) {
System_Daemon::iterate(3);
}

// Shut down the daemon nicely
// This is ignored if the class is actually running in the foreground
System_Daemon::stop();
?>[/code]

helpful guy
helpful guy·

service mydaemon status
wasnt working for me on fedora 9

I added the option
\"appPidLocation\" => \"/var/run/mydaemon.pid\"

and removed the arbitrary restriction from
/usr/share/pear/System/Daemon.php in _isValidPidLocation where it does an arbitrary check to force the pid file to a location which isnt checked by /etc/init.d/functions \'status\' function.

Anyway, now it works great :)

Ulrich
Ulrich·

Hi Kevin,

Congratulations for System_Daemon, great job !

I have encountered an issue, maybe stupid, but I can\'t find why.

I try to use System_Daemon inside my class system which used __autoload() to get the classes. I need to because of our environment

But if I call a class right after the require_once \"System/Daemon.php\", even if the deamon is not started, I can\'t call any of my classes. Is that something wrong (I mean, using Daemon and __autoload?)

Diana
Diana·

Hello

I would like to help me or guide me, my question is this I hear dmonio create a port and turn the information as received by the port to be stored in a database or text file. Any of you could guide me? Se los agradeceria enough, I thank you very much Greetings advance Diana

Urda
Urda·

How can I use System_Daemon::log inside of a function?

I get FATAL ERROR: CALL TO UNDEFINED FUNCTION if I try that. How can I get around this and still log to the system Daemon?

Warren Tucker
Warren Tucker·

I used the system daemon not so long ago, one thing that happened was the daemon started outputing to the screen bad file discriptor.

bertoli
bertoli·

I am getting an error on the log file when I try to get the \'init.d\' generated:

Directory: \'/etc/init.d\' is not writable. Maybe run as root?
Unable to create startup file. [l:835]
unable to write init.d script

my \'etc/init.d\' directory is:

drwxr-xr-x 2 root root 4.0K Feb 10 20:25 init.d

my script is root owned, as I did:

chown root myscrip.php

I also used: sudo to run the script but same error got and init.d is never generated...

any suggestion?

bertoli
bertoli·

umm... solved, it was in fact a permission issue.

I did a:

chmod a+w /etc/init.d

so the init.d file for the script was sucessfully written. Later changed back to:

chmod go-w /etc/init.d

to keep persions as before...

sameer
sameer·

really explained in max elaborate manner

Kev van Zonneveld
Kev van Zonneveld·

@ Urda: Well you can easily follow the github repository to see my commits.
Was there anything in particular you wanted to see comitted?

@ Peter: Does this happen during a restart or something?

@ Ben: What if you don\'t set the SIGCONT handler?

@ helpful guy: Thanks, and there is no way to have the different pid location? Cause yours creates problems on itself (related to permissions, it needs to be inside an owned subdirectory)

@ Ulrich: __autoload functions are stacked. So if System_Daemon\'s can\'t load your class, the autoload function that was registered before that is called. Maybe somehow our autoloads conflict. You could add debugging information to the Daemon.php file inside the static public function autoload($className) function, and in yours, to see what paths are traversed in order to include the classfiles.

@ Diana: You could have a look at node.js, seems perfect for what you are trying to achieve.

@ Urda: You should be able to do that, something is wrong. Do you have more debugging output?

@ Warren Trucker: Any chance you have more details on that, or maybe you can share your code somewhere?

@ bertoli: You should run it as root, not sudo, but thanks for sharing!

Kev van Zonneveld
Kev van Zonneveld·

@ Peter: I think I found & fixed what you ran into!
It\'s in this commit:
http://github.com/kvz/syste...
and will be part of the next release.
If you can\'t wait just could download the source files directly from github, but remember to download all of them as they are dependent & I changed some more things in them

nadir
nadir·

hi, i need your help. i am trying to create a daemon for my project and we are doing our php workks in lampp. so when i try to include mysql connections in my phpdaemon , its said as no socket found as i have stopped my default sql. i want to change my socket path from /var/run/mysql to /opt/lampp/var...
where should i do that??
pls help.... its quite urgent too..
(sorry i am a newbie,excuse my mistakes)

juan
juan·

Thank you for this.

I found it very useful!!

I\'ll try more and send you some feedback.

Kev van Zonneveld
Kev van Zonneveld·

@ nadir: If I understand you correctly, it looks like you don\'t include your mysql After you start the daemon. The troubleshooting section mentions this.

Changing socket paths of your Mysql server has Nothing to do whatsoever with my PEAR class and should be avoided if you are indeed a newbie.

David James
David James·

This package is fantastic. We are currently using it for a notification service (monitors activity in a log table and sends out alerts by subscription) and for a social media feed service (polling Gnip and bringing Twitter posts into the application).

One question I have, is there any way to signal the daemon to shutdown or a graceful way of shutting it down, instead of kill or Ctl-C (for no-daemon variety)?? I\'m thinking you could have a \"flag\" file that the daemon monitors, if the file is deleted then the daemon finishes up and exits. Any other ideas?

Kev van Zonneveld
Kev van Zonneveld·

@ David James: You\'ve given the answer yourself: Signals :)
Checkout the signal handling stuff.
Or just let system_daemon generate an init.d script and and let that send the POSIX signals for you.

Bucanero
Bucanero·

Hello Kevin, again I\'m having some issues deploying this on 2 new servers

I\'m getting the following error on the logs:

[Feb 27 19:03:32] info: Changed identify to \'daemon\':\'\'
[Feb 27 19:03:32] warning: [PHP Error] pcntl_signal(): Error assigning signal [l:1289]
[Feb 27 19:03:32] emerg: Unable to reroute signal handler: 0 [l:1293]

Any clues ?

Thanks

Bucanero

Kev van Zonneveld
Kev van Zonneveld·

@ Bucanero: Did a lot of fixes to signal handling in 10.1. Are you running the latest version?

Menderezz
Menderezz·

Hey Kevin, this Pear package is wonderfull, and it\'s exactly what I needed. However I have a question.
When the php script gets started as a daemon, and if it has the command to autocreate the script in /etc/init.d/app_name it\'s not showing the status of the daemon. For my example, it says that app_name is stopped.

The question is: is it possible and how, to make the php daemon report it\'s state, if it\'s working or not, and I always have to \"kill -9 PID\" the \"/etc/init.d/app_name stop\" command doesn\'t work and it times out.

Once again, this project is just perfect, I only need some help with mentioned things.

Cheers!

Bucanero
Bucanero·

Kevin, actually I downgrade to 0.9.2 and it works ...

I will like to stay up to date, how can I troubleshoot this on my dev servers, any clues what to look for first ?

And again, thanks for your great work

Bucanero

Gareth
Gareth·

I am currently having a problem where I create init.d files and then have my while loop do a check for isDying() but the daemon seems to exit as soon as an /etc/init./daemon_name stop is sent instead of when the script stops itself.

[code=\"php\"]
if (!System_Daemon::isInBackground() && !System_Daemon::isRunning())
{
$init_path = System_Daemon::writeAutoRun(false);

System_Daemon::start();

$sleep_time = 5;

while (!System_Daemon::isDying())
{
//App code here
}
System_Daemon::notice(\"Termination signal recieved, exiting\");
}
[/code]

Gareth
Gareth·

Just to add to this, essentially my daemon cannot just die. It needs to complete a cycle of processing it is busy with before it terminates. I attempted to set a sig handler but it just gave me an error of:

Can only overrule on of these signal handlers: \'Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, 1, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array, Array\'

My code now is :

[code=\"php]
require_once(\"System/Daemon.php\");

System_Daemon::setOption(\"appName\", \"name\");
System_Daemon::setOption(\"appDescription\", \"Description\");
System_Daemon::setOption(\"authorName\", \"Name\");
System_Daemon::setOption(\"authorEmail\", \"mail@mail.com\");

System_Daemon::setSigHandler(SIGKILL, array(\"System_Daemon\",\"defaultSigHandler\"));

if (!System_Daemon::isInBackground() && !System_Daemon::isRunning())
{
$init_path = System_Daemon::writeAutoRun(true);

System_Daemon::start();

$sleep_time = 5;

while (!System_Daemon::isDying())
{
//Code to run per loop
}
System_Daemon::notice(\"Termination signal recieved, exiting\");
}
[/code]

Ulrich
Ulrich·

hi Kevin,

a little feedback for my issue (#107)

It works !

the problem come from the face I use __autoload and not spl_autoload_functions(). So my autoload function is ignored after I include the Daemon file

thanks for your help.

Edmund
Edmund·

Can this be used as a multi-threaded daemon?

I need to query a DB and each record that is returned needs to be processed individually but timing is important so I need the ability to asynchronously process each record.

If this is possible, are there any function calls specifically for this purpose?

Thanks in advance for any info on this topic.

Kev van Zonneveld
Kev van Zonneveld·

@ Menderezz: Did you try overriding the signal handlers? Cause that would be the way to fly. You can use the kill command to send a POSIX signal to the application, and write a function that performs your status task when it\'s received.

About the init.d file not working, that\'s weird. If you\'re sure it\'s not your code and you\'re running the latest version of System_Daemon you may file a bug report against PEAR and we\'ll take it from there.

@ Gareth: Yes it will do that automatically, if you want any other behavior you can overrule the signal handlers to perform any task you like.

Ah ok. About your Array,Array error; this is in the latest version? If so, please file a bug report at PEAR

@ Bucanero: Agreed, you should be able to upgrade. To troubleshoot, it would help a lot to echo which signal it is trying to bind, that doesn\'t work on your system.

@ Ulrich: Awesome!

@ Edmund: There\'s nothing like that in System_Daemon at the moment. It just does a straight forward fork of itself and that\'s it. You may want to look at PEAR\'s Fork class, or use the pcntl functions to build it yourself. If you have clear ideas on how to implement this in System_Daemon I\'d be happy to review any patches you may (or may not) build.

Gareth
Gareth·

Thanks for the response. But as per the example I gave I add a sig handler to attempt to catch the termination signal bit ti doesn\'t seem to work:

[code=\"php\"]
System_Daemon::setSigHandler(SIGKILL, array(\"System_Daemon\",\"defaultSigHandler\"));
[/code]

I have also used various different constants to try and catch the correct signal but it just seems to ignore them all.

Austin B
Austin B·

@Gareth: Updated your bug with some info check it out.

@Bucanero: I got the same error and found the cause not sure how to fix but commenting it out seemed to work. Check out the bugs listing on the Pear project page.

Kev van Zonneveld
Kev van Zonneveld·

@ Austin B, Bucanero, Gareth:

Hey guys: \"Version 0.10.2 of System_Daemon has been successfully released, and its promotion cycle has started.\"

I hope this fixes all of your issues. Please come back to me if it doesn\'t, and thanks for your time reporting this!

Nathan
Nathan·

Googled \"PHP daemon\", and this was the first result. Couldn\'t be happier. Thanks for the help!

Gareth
Gareth·

Funny thing is even using SIGTERM or any other signal constant I can think of in the signal handler option it still does not allow my own script to control its termination and just kills the daemon outright :(

Menderezz
Menderezz·

Hey Kevin, thanks for the reply, the /etc/init.d/app_name stop/start.... actually works, the problem I was having was that my code wasn\'t checking this:
[CODE=\"php\"]if (!System_Daemon::isDying())[/CODE]

it is a socket application that awaits connections and that\'s why it was timing out when I tried to stop, so I made a checkup in the script so before it starts to listen again, it will check if it\'s time to sleep :D

All I have to do now is to make this code report it\'s status, so I can at least use MONIT and restart it, if it stops for some reason.

Kev van Zonneveld
Kev van Zonneveld·

@ Nathan: Thanks : )

@ Gareth: Looking at the code samples you\'ve provided so far, I only see you routing signals to the default handler, instead of your own. Did you have a look at the logparser example? It shows you how to route signals to your own functions.

@ Menderezz: Monit can look at pid file & check if the socket is open. I don\'t know but maybe that\'s already enough for your purpose? Check this sample config:
[CODE=\"text\"]
check process mysql with pidfile /var/run/mysqld/mysqld.pid
start program = \"/etc/init.d/mysql start\"
stop program = \"/etc/init.d/mysql stop\"
if failed host 127.0.0.1 port 3306 type tcp then restart
[/CODE]

Gareth
Gareth·

The new logparser.php for version 0.10.2 does not even have a call to the setSigHandler method in it. The previous version had exactly the same as I had, ie array(\'System_Daemon\',\'defaultSigHandler\');

Kev van Zonneveld
Kev van Zonneveld·

@ Gareth: Hah! sorry my mistake. I obviously meant the signals.php example:
http://github.com/kvz/syste...

Menderezz
Menderezz·

Thanks Kevin, your example config was very helpfull, I just want to notify that upgrading System_Daemon on RHEL 5 is not possible, it gives some error, saying that it\'s a BETA, non-stable release etc.

Here is the actual shell output:

pear upgrade System_Daemon

Failed to download pear/System_Daemon within preferred state \"stable\", latest release is version 0.10.2, stability \"beta\", use \"channel://pear.php.net/Syst...\" to install
Cannot initialize \'System_Daemon\', invalid or missing package file
Package \"System_Daemon\" is not valid
upgrade failed

Regards

Kev van Zonneveld
Kev van Zonneveld·

@ Menderezz: Hey. Thanks : ) That is actually true for all PEAR packages that haven\'t reached stable yet.
To circumvent you can either use the force argument -f, or append the state like: System_Daemon-beta so the installer knows that you know what you are doing : )

Jock0
Jock0·

Hi Kevin,
Thank you for this helpful guide. I am trying to run a socket listener as a daemon, but get the following lines in the log file:
[Mar 23 16:30:00] notice: Starting socketdaemon daemon, output in: \'/var/log/socketdaemon.log\'
[Mar 23 16:30:00] info: Changed identify to \'root\':\'root\'
[Mar 23 16:30:00] warning: [PHP Error] pcntl_signal(): Error assigning signal [l:1304]
[Mar 23 16:30:00] emerg: Unable to reroute signal handler: 0 [l:1308]
[Mar 23 16:30:03] notice: Starting socketdaemon daemon, output in: \'/var/log/socketdaemon.log\'
[Mar 23 16:30:03] info: Changed identify to \'root\':\'root\'
[Mar 23 16:30:03] warning: [PHP Error] pcntl_signal(): Error assigning signal [l:1304]
[Mar 23 16:30:03] emerg: Unable to reroute signal handler: 0 [l:1308]

What is the problem, and how could it be fixed?

dave
dave·

Is there an example of setting up logging with log rotation? The default facility appears to have the logs growing without end...

Kev van Zonneveld
Kev van Zonneveld·

@ dave: On ubuntu there\'s a standard logrotate facility as - I\'m sure - there is on other systems. You can just configure that system to keep system_daemon\'s log rotated as well.

@ Jocko: While I cannot reproduce this, this has been reported as a bug:
http://pear.php.net/bugs/bu...

And this is my proposed fix:
http://github.com/kvz/syste...

Haguey
Haguey·

I\'m having a bit of trouble starting and stopping my daemons.

when I issue ps ax I see my daemon running as:

/usr/bin/php -q /home/haguey/hagueyd.php

The problem is that when I try to use /etc/init.d/haguey to start and stop the damon it never gets the correct process name.

it\'s trying to do TERM hagueyd and not TERM /usr/bin/php -q /home/haguey/hagueyd.php

Any ideas what I could be doing wrong?

thanks in advance!!

Adi
Adi·

I just cldnt get it to work...when i tr ps -e or ps iaux..i dotn see the process der...i cant even get the init script up..im running the latest version..and after trying for 23 hours..i thot ill throw in the towel publicly here..:(

Kev van Zonneveld
Kev van Zonneveld·

@ Haguey: Is it possible you appended the \'d\' to hagueyd later on? So after you wrote the init.d files?

@ Adi: Too bad.. If you share code we might be able to actually establish what\'s going wrong and maybe fix bugs whether on your or my part.

haguey
haguey·

@Kevin,

Thanks for you reply, it turns out the problem is that procfs on debian stores truncated process names...

so that if the daemon process name goes over 16 characters, start-stop-daemon will not find it using the -name option...

check /proc/<pid>/stat to see if the processname is truncated. I guess other people unable to stop their daemon might have this problem.

Rsk
Rsk·

For long days i was dependent on Cron for the daemon tasks. Now this system daemon looks really great. I have one issue when trying out.
I used the example script and added the php code as mentioned. And made it as a daemon and restarted the machine. On booting time the system waits for this process to execute , it keeps waiting over there until i remove it from the init.d.
Am i doing it right ?

Manuel de Ruiter
Manuel de Ruiter·

Hello Kevin,

It\'s been a while since I visited you\'re tech-blog, but I\'m facing the problem that the \'daemon\' that I create consumes to much CPU.

I\'ve got a VPS with Debian Lenny installed and default the load of that machine is ~0.15 (5 minutes). But when I activate the daemon the load of the machine goes up to 100% CPU usage. (Got one VCPU clocked at 1.8Ghz and have 2048MB RAM). The weird part is that it doesn\'t consume that much RAM (0.1% to 0.2%).

Here a little example of the output: [CODE=\"text\"]lighttpd 5781 48.1 0.2 51676 5696 pts/1 R 19:45 12:09 php -q /var/www/path/to/location/daemon.inc.php[/CODE]

I\'m running the daemon under the lighttpd user cause that area is chrooted. Let me describe what I\'ve done so far.

- Used the iterate and raised it up to 60s
- Checked my codes for leaks
- Modified the daemon code to minimum
- Made a blank daemon (only with the while)
- Set options manually
- Changed System_Daemon version

Example: http://pastebin.com/3HytDKpp

Let me know!

Manuel de Ruiter
Manuel de Ruiter·

Sorry for the double-post but I forget to add the $i++ add the bottom of the pastebin url. For the \'full\' example please check: http://pastebin.com/hDA4udCA

Kev van Zonneveld
Kev van Zonneveld·

@ haguey: Thanks for sharing

@ Rsk: And this does not happen when you run the init.d file it from the console?

@ Manuel de Ruiter: I don\'t see a sleep, let a alone a System_Daemon::iterate in your while loop. That will definitely exhaust your system\'s resources

Manuel de Ruiter
Manuel de Ruiter·

Thank you Kevin for your reply on this problem. I\'ve just did some checks with the iterate function and it makes a huge difference.

Without: 99.9% CPU is in use and with 0.0% CPU is in use. Also did some checks with sleep and usleep and now it works like a charm. Thank you Kevin!

Kev van Zonneveld
Kev van Zonneveld·

@ Manuel de Ruiter: Good!

Brian René Jensen
Brian René Jensen·

Hi Kevin.

I just wanted to thank you.

Great post - Very useful to me.

I\'m great at PHP, but bash doesn\'t really apeal to me. This way, I can write a ton of code to monitor my Ubuntu server with email statistics.

Thanks allot.
Bwyan

Petri
Petri·

I was having problems with writing the /etc/init.d/ startup file untill I realized the UserId and UserGroup this daemon wants to run. File permissions were fine to beginwith. CentoOS just does not have user Id nor group values 1000 by default.

By default:
\'appRunAsGID\' => 1000,
\'appRunAsUID\' => 1000,

In CentOS, I had to change these to:
\'appRunAsGID\' => 0,
\'appRunAsUID\' => 0,

root has id and group = 0.

Now, this could potentially be a security risk if the daemon runs as root, therefore I suggest using the 0 values only when creating the startup script, and then use some other user values in actual run.

Kev van Zonneveld
Kev van Zonneveld·

@ Petri: System_Daemon defaults to root. You must have been using code that explicitly defines

[CODE=\"PHP\"]
array(
\'appRunAsGID\' => 1000,
\'appRunAsUID\' => 1000,
);
[/CODE]

removing that should make them 0 as well.

Daniele Palumbo
Daniele Palumbo·

Hi,

i wish to know the best way to execute System_Daemon from web (exec? directly via browser?).

with system() seems not to fork, going into timeout after a while (php timeout).

Also, i HAVE to set \"runAppAsGID\" and \"...UID\", but i am starting it from web...

thanks,
s.

Student Brands
Student Brands·

Thanks for the great information. I hope to be back soon. I have book marked it keep posting. Thanks again.

steve
steve·

@Kevin. Thanks for the info, I also had the permission issue with Fedora.

I modified the script to with the following:

[CODE=\"php\"]
$gid = 1000;
$uid = 1000;

if ($runmode[\'write-initd\']) {
$gid = 0;
$uid = 0;
}

$options = array(
\'appName\' => \'tgsendmail\',
\'appDir\' => dirname(__FILE__),
\'appDescription\' => \'Parses vsftpd logfiles and stores them in MySQL\',
\'authorName\' => \'Steve\',
\'authorEmail\' => \'\',
\'sysMaxExecutionTime\' => \'0\',
\'sysMaxInputTime\' => \'0\',
\'sysMemoryLimit\' => \'1024M\',
\'appRunAsGID\' => $gid,
\'appRunAsUID\' => $uid,
);
...
...
while (!System_Daemon::isDying() && $runningOkay && !$runmode[\'write-initd\']) {
$runningOkay = true;
...
...
[/CODE]

ilovebonnie.net
ilovebonnie.net·

Hello!

Thanks much for the great PEAR class. I found this to be incredibly useful and well-written. Unfortunately, I had a hard time figuring out what options I needed to set in order to get permissions issues dealt with. Particularly, I found it hard to find information about how to set the location of the log file or the pid file that you wanted the daemon to use.

I wanted to come here to let you know I wrote a little blog post about how users can set those options in case they are having similar problems due to permissions:
http://www.ilovebonnie.net/...

Thanks again!

-ilovebonnie.net

Mike
Mike·

I get the following error when trying to start my daemon:

<CODE=\"text\">PHP Warning: PHP Startup: Unable to load dynamic library \'usr/lib/php5/20060613/pcntl.so\' - /usr/lib/php5/20060613/pcntl.so: cannot open shared object file: No such file or directory in Unknown on line 0</CODE>

The pcntl.so file does not appear to exist in that directory. Any idea why it is trying to call this file? And, why it doesn\'t exist there?

Tom Dexter
Tom Dexter·

Thanks for an awesome class! It\'ll be a life saver for me. In testing today I noticed that this line in Daemon.php is doing a fileowner() function call that should be a filegroup() call:

[CODE=\"php\"] // Change File GID
$doGid = (fileowner($filePath) != $gid ? $gid : false);[/CODE]

Thanks again!

Kev van Zonneveld
Kev van Zonneveld·

@ Daniele Palumbo: Deamons should run at all times using init.d/upstart scripts.

@ Mike: pcntl extensions are essential for the forking to work. You need PHP to support that.

@ Tom Dexter: Good catch, awesome! Fixed it here: http://github.com/kvz/syste... and will be part of next release cycle.

Utahcon
Utahcon·

I have setup a daemon using your PEAR class, and I have to say that was mighty handy.

I did a --write-initd and all went well.

start works just fine

but when I try to stop the script with
[CODE=\"text\"]
sudo /etc/init.d/myDaemon stop
[/CODE]

it seems to change reporting from the log to STDOUT and my Daemon keeps running.

Ideas?

Kev van Zonneveld
Kev van Zonneveld·

@ Utahcon: Doesn\'t ring any bells. What version was this?

Tom Dexter
Tom Dexter·

Thanks for the filegroup() fix! I have a suggestion (one I\'d even help with if you want to email me). I\'m not sure if it would cause problems or not. The pcntl_signal php function has a third parameter (restart_syscalls) which defaults to true. If you want to be able to call pcntl_wait or pcntl_waitpid to wait for a spawned child and still be able to catch a signal for which you\'ve assigned a function (that is, interrupting the pcntl_wait), that third parameter needs to be set to false or the pcntl_wait immediately resumes when the signal is received and won\'t call the handler function until the child exits (if in fact it ever does).

System_Daemon::setSigHandler currently has no way to use that option. In the project I\'m working on I was able to work around that by not using pcntl_wait, but rather relying on a SIGCHLD handler and a loop. It\'d be a nice feature to allow for that. Again, I\'d be willing to help with that if you want to email me. Thanks!

ppafford
ppafford·

Getting this error: --enable-pcntl
Running on CentOS 5, how do I fix this?

I have the script (No running as daemon) and is triggered by incoming SOAP request which I want to kick off the daemon to process this.

Tom Dexter
Tom Dexter·

One question: I see that SIGCHLD is one of the signals you\'ve implemented as a string. Are there php platforms that don\'t support that?

By the way...I tried implementing an option to override that restart_syscalls parameter of pcntl_signal in System_Daemon as I discussed previously. The change wasn\'t difficult, but for reasons I can\'t figure out, it doesn\'t behave as it does in scripts not using System_Daemon (even scripts forked as children of a daemon). Here\'s the behavior I wanted:

If my daemon gets a TERM signal while it\'s waiting for a child process with pcntl_wait or pcntl_waitpid, I want to immediately cancel the wait call and jump to my TERM signal handler which can kill the child process. This is exactly how it works in other scripts (again, even is child scripts spawned from a script running System_Daemon).

For some reason however in the script using System_Daemon, when the wait call is interrupted by the receipt of the TERM signal, the script continues past the pcntl_wait call through the current iteration loop. Worse yet, in the code past that wait call, calls to pcntl_wifexited() and pcntl_wexitstatus() using the status from the wait all act as though the child ended normally when it actually hasn\'t exited at all!...totally perplexing.

I finally gave up...that one has me totally stumped.

chris
chris·

@Kevin

First many kudos to your work, we\'ve been playing around with some hand-written process controll mechanism here..

i\'m experiencing the same behaviour as @Utahcon.. i\'m using Ubuntu 8.04 and latest Version of System_Daemon from pear (0.10.3)

When i call
[CODE=\"console\"]
/etc/init.d/myscript restart
[/CODE]

a 2nd instance is created, and so on.. the script also switches from background mode into non-background, according to the logfile..

i can reproduce this behaviour with your example snipped above, just removed the 3-run-limitation.

the script runs with user/group \"0/0\", got +x for execution.. and is started with root privileges..

hope this gives you an idea how to track this down.. :)

chris
chris·

i\'m curious... looks like i just figured out what might cause my problems.. i\'ve had called my Skript \"messageQueueBroker.php\", when i tried your example again i named it \"logparser.php\" and all works fine..

are there any known problems with filelength? The appName in both cases was \"logparser\", just the filename/length differs..

.: Pampa :.
.: Pampa :.·

Nice tut Kev!

I came here looking for PHP daemons (actually written in pure PHP code) and found a really easy to understand tut!

Hardly I\'ve learned your troubleshoot tips, but good to let the newbies know.

Congrats :)

utahcon
utahcon·

@chris @kevin

I am sorry I didn\'t come back to report but I found the same thing as @chris. The init.d script that was written on Ubuntu was bad as it called the wrong name, or something (I don\'t recall now).

I had to puttz with the init.d to make it work properly.

Kev van Zonneveld
Kev van Zonneveld·

@ chris & Utahcon: Found your problem. You are not supposed to use caps in unix daemon names.
I want to build protection against this but I\'m gonna need some code samples cause by the looks of it, Options.php already has it:

[CODE=\"PHP\"]
protected function strIsUnix( $str )
{
return preg_match(\'/^[a-z0-9_]+$/\', $str);
}
[/CODE]

So I\'m wondering how exactly you got passed that..

@ Tom Dexter: I\'m having a hard time trying to grasp your problem without being able to look at & play with code. Have any?
You could also fork http://github.com/kvz/syste..., make the required changes, and send me a pull request.
That would work very well, actually.

Alessandro Desantis
Alessandro Desantis·

Do you know if it is possible to disable echoing with this class?

gazbond
gazbond·

System_Daemon does not work on OSX! Anyone confirm this?

frank
frank·

ist there anway to prevent multiple instances on running?im on an debian etch and for some reasons i had two instances running which messed up everthing!

help appreciated

fra*

Thom
Thom·

Thanks for putting this together. I had my first daemon up and running in an hour.

Great work!

Greg J
Greg J·

hey, this is great stuff and very quick and easy to implement, however, I cannot seem to get past the following error when I make the daemon an executable and try to start with /etc/init.d/hb_daemon start. the error I get is:

Extension '/var/www/hb_daemon.php' not present.

Forum posts I have found say that php cannot be found in the path however I can confirm this.
php indeed resides in /usr/bin and that path is specified in the init.d file that is created from the $path = System_Daemon::writeAutoRun(); command.

The daemon starts fine when I use the command php hb_daemon.php from the folder where the daemon is located. I found the earlier post from december and tried all the things you suggested but the problem remains.

What happens if you try
php ./daemon.php?
- it runs as expected

If it works, you need to chmod u+x daemon.php
- did this

and maybe put /usr/bin/php in your /etc/shells
- did this

and maybe put #!/usr/bin/php -q in the head of your script.
- already there

The script is simple, it just counts to 5 at 1 second intervals then the daemon stops.

I am running the apt-get installation of php on ubuntu 10.04. PHP 5.3.2-1ubuntu4.5 with Suhosin-Patch (cli) (built: Sep 17 2010 13:41:55)

Any help would be appreciated

Thanks in advance

Greg J

Greg J
Greg J·

Im back. I have resolved the problem mentioned in my previous post from 3rd October (see post below) so I thought it might be useful to record what causes this issue and how to fix it.

for the record, the file runs fine with the following command

php myfile.php

its only when I attempt to run it as an executable with the following command that I get the problem

./myfile.php

gives me this error

Extension '/var/www/hb_daemon.php' not present.

THE PROBLEM
The problem is that the script I had originated from a windows environment. I thought by deleting a line then recreating it would remove the characters at the end that was causing the problem but the end line characters appear to be built into the file somehow. I have been using gedit in ubuntu 10.04 (lucid) to edit in linux. I tried several other editors but couldnt seem to shake the problem. there seems to be a mysterious ^M at the end of every line. this appeared when I tried using vi but was invisible in all other editors.

THE FIX
you need to run dos2unix over the file.

this program was not installed in my distro so I ran the following command to install it.

sudo apt-get install tofrodos

after that, it still didnt run, said file not found, which is apparently due to a bug so the workaround is to run the following commands from the /usr/bin directory

sudo ln -s fromdos dos2unix
sudo ln -s todos unix2dos

once that was done, I simply ran

dos2unix myfile.php

once done i can run a executable php file from the command line by typing

./myfile.php

happy days!

good luck

Greg J

Kev van Zonneveld
Kev van Zonneveld·

@ gazbond: Haven't tested it on OSX. But I'll soon get an iMac so I can see if I can make it work. In the mean time you could verify if you have the pcntl extensions in your PHP?

@ Alessandro Desantis: Use ob_start() or maybe even use classkit to overrule echo (dirty)

@ frank: System_Daemon init.d files have protection against this. Should not be possible if you use them

@ Thom: Thanks :)

@ Greg J: Wow. Thanks for reporting the fix!

robert
robert·

Hi.

many thanks for your great work.

Unfortunately I have an issue regarding using of the init.d

This code appears in the console if i try to use the written init.d file

Unable to start /var/daemon/bookingd_test/scriptd.php: Exec format error (Exec format error)

Do I miss something important??

System: Debian lenny
br
robert

Kev van Zonneveld
Kev van Zonneveld·

@ robert: Need a little more details to be of any help.

Tom
Tom·

Kevin,

Thanks for the awesome tutorial, just what I need!

I am looking to create a daemon in php which will handle commands send to it. However when it is sent a command, i would like it to spawn a kind of child daemon to process that command, otherwise the main daemon would be bogged down doing one command and not be able to handle subsequent commands. Is this possible with this package?

How would I go about doing it?!

Thanks again.

KevBurnsJr
KevBurnsJr·

Great article. I found this post after stumbling across a PHP port of Resque (a Redis-based queue implementation originally written in Ruby) that lets you run queue workers as PHP daemons. I'd always thought it was possible, but never thought that anyone would be crazy enough to try it.

Have you ever actually deployed a PHP daemon as part of a production system?

Do you know anyone who has?

Kev van Zonneveld
Kev van Zonneveld·

@ Tom: Like I mentioned in the comments before, I've found an unmaintained PEAR package: Fork. Maybe that can do what you want. I didn't have plans to make this a system_daemon feature as I've always felt that it should be a simple and basic building block.

@ KevBurnsJr: I'm using it in production yes, other folks told me they have as well. However there seem to be quite some edge cases in which a deamon stops working (use echo 1 time, have a dependency that tracks some kind of log/history in an array, thus eventually becoming too large for memory). Because there are plenty other examples and it's hard to cover all of them at first try, you should use monit to restart your daemon & notify you whenever that happens.
There are currently no known bugs that will cause it in PHP or System_Daemon itself, but anything put in your execution loop can pretty easily kill your daemon.

Kev van Zonneveld
Kev van Zonneveld·

@ KevBurnsJr: Wow, I mention there's no memory leaks, 15 minutes later someone reports a memory leak : )
But I fixed it as well:
http://pear.php.net/bugs/bu...
So be sure to upgrade when I push the new release (or get it from github if you can't wait)

Tom Dexter
Tom Dexter·

Hi Kevin. I've been having an occasional occurrence in my daemon that I can't figure out. I occasionally get the "Process was not daemonized yet, just halting current process" when stopping the service when it absolutely was daemonized. I've traced it to the isRunning function failing the file_exists check on the pid file, but the pid file certainly should be there at that point. I'm running this under CentOS (I HATE their init scripts by the way), and am stopping the daemon with their killproc -p <pidfile> syntax. I've verified that the init script would not try to delete the pid file until after the daemon has stopped. Any ideas what might cause that?

Tom Dexter
Tom Dexter·

Regarding my last post: I'm trying to understand the rational behind the requirement that the pid file be in a directory unique to the appName. In my case I have one master script that I use for two different monitor services based on a name passed to it. My pid files and log files (via Pear Log) are in fact specific to the proper monitor based on that name it receives, but they are using the same appName with both pid files under that directory. I can easily change it to make the appName and the subdirectory unique, but I'm not understanding how/if my current setup could cause any conflict.

Tom
Tom·

Kevin,

Once again thanks for the awesome work. Just having some troubles getting the daemon to run as root. I am running a clean ubuntu install, with your example daemon above. I have chmod'd the file as you say above, but everytime I run it it says changing identity to tom:tom. I have tried chown'ing the file too, not sure if that would help the situation?

Thanks.

Brian
Brian·

@Tom

You probably have these two options set.

"appRunAsUID"=>array(
"type"=>"number/0-65000",
"default"=>0,
"punch"=>"The user id under which to run the process",
"example"=>"1000",
"detail"=>"Defaults to root which is insecure!",
"required"=>true),

"appRunAsGID"=>array(
"type"=>"number/0-65000",
"default"=>0,
"punch"=>"The group id under which to run the process",
"example"=>"1000",
"detail"=>"Defaults to root which is insecure!",
"required"=>true),

Look in your /etc/passwd file and compare the uid and gid for root

Tom
Tom·

@Brian

Many thanks for the reply!

I was under the impression that the UID and GID of 1000 was for the root user, my mistake!

Anyway, my passwd file says that root is 0:0 but after entering these into the daemon option I get the following errors:

[Dec 02 20:45:59] err: Unable to change group of file '/var/run/daemon' to 0 [l:1425]
[Dec 02 20:45:59] crit: Unable to change identity [l:1281]
[Dec 02 20:45:59] emerg: Cannot continue after this [l:1283]

Am I overlooking something else now?

Thanks for your patience!

Tom

Kyle
Kyle·

I found when running the /etc/init.d/$file stop, that my command was not really being stopped.

To fix this I had to shorten the name of my appName

'appName' => 'jque1',
from
'appName' => 'jobqueue_processor1',

and then this worked. It seems that /proc/$ids/status truncates thus causing start-stop-daemon to fail to shutdown as the --name does not match

Joerg
Joerg·

Hi,
first of all: This class is awesome. Thanks a lot!

But now my question, which is not 100% related to your code:
I try to create a php-Daemon, which parses an logfile and saves each line to a database-table.
The logfile is actively written to and also will be rotated every night.
Do you got such a environment build up successfully with php and System_Daemon? I searched several days in the internet, but the only thing I found was a CPAN-module for perl called File::Tail, which does exactly what I need but in the wrong language. All php-code I found just implemented something like "return the last x lines of messages in a logfile" or weren't aware of logrotate :-(

Thanks in advance

Morten
Morten·

Thank you very much for a great class. :-)

We're running a daemon with it and using monit to make sure it's always running.

Part of the function is calling an external service - but since this has some downtime during the night, we have a cronjob killing the daemon at that time and then monit makes sure it gets started again. This seems to work just fine.

But - now and then we've experienced that more than one thread of our daemon is runing!

How can this be? How can we prevent it from happening?

Thanks for any help.

Kind regards and a happy new year to all.

Stefan
Stefan·

Hey there, absolute great class you built here!!

I have one slight problem:
OS = gentoo

when i call System_Daemon::writeAutoRun() i get an error:
No autoRunTemplatePath found
when i add this variable to Linux.php in OS-folder i get:
No autoRunTemplateReplace found
when i add this variable to Linux.php i get:
notice: No PROPERTIES found in autoRun template

i'm stuck here, please help me out
thanks in advance!!

Wilson
Wilson·

Oh this is exactly what I need because I am thinking of adding a module in the admin section of my site which will automate the adding of entries to the database on a daily basis pulled from a pre-entered pool of data. I can accomplish the same thing with Cron but I hate having too much external dependencies since I always aim when coding to have my code easily portable from one server to another without too much re-configuring.

Kumar Chetan Sharma
Kumar Chetan Sharma·

now is the time to convert all cron jobs to daemons, thanks :-)

Anish Sharma
Anish Sharma·

Very good information on CRON vs. Daemons. Will try it for sure!

Shane Harter
Shane Harter·

I recently open-sourced a Daemon platform I wrote that we've used for a long time in Production.

https://github.com/shanehar...

I'm posting this because the commenter before me was talking about switching a Cron to a daemon. My library is similar to yours in many ways but a feature this has that readers here might find useful is a built-in timer. Suppose you wanted to run some code every second. Or maybe 5 times a second.

Also, I built a pretty nifty auto-restart feature to side-step the (very correct) memory issues you wrote about.

Shane

Kev van Zonneveld
Kev van Zonneveld·

@ Morten: Please make sure you're running the latest version (1.0) and don't use caps in your daemon names.

Kev van Zonneveld
Kev van Zonneveld·

@ Tom: You should change:

'appRunAsGID' => $gid,
'appRunAsUID' => $uid,

Kev van Zonneveld
Kev van Zonneveld·

@ Stefan: I don't support Gentoo, but if you want you can write a Gentoo file and contribute it?

Andy
Andy·

One observation I've made: The file that is invoked by init.d must be less than 16 characters total, including file extension.

If the file is not less than 16 characters, the daemon will not stop. When you issue a restart command, a new, simultaneous process will be spun up.

Doesn't work: mydaemonfile.php
Works: mydaemonfil.php

Cheers,
Andy

Andre Agassi
Andre Agassi·

__autoload isn't called in daemon code. Please provide reason why not

Kev van Zonneveld
Kev van Zonneveld·

@ Andy: System_Daemon now reports when you try setting long appnames: https://github.com/kvz/syst...

@ Andre Agassi: If your code has an existing __autoload function then this function must be explicitly registered on the __autoload stack. This is because spl_autoload_register() will effectively replace the engine cache for the __autoload function by either spl_autoload() or spl_autoload_call().
From: http://www.php.net/manual/e...

Andy
Andy·

Hey Kevin, thanks for your great work on this!

It would be nice if System_Daemon::start() would return a success flag, so that one could continue/abort based upon whether or not the daemon actually started.

Robert
Robert·

sounds very interesting, wish I was a php programmer. Any interest in contract work?

Dan
Dan·

I have set up the init.d script and it is working great, when I use "service app_name stop" to stop the daemon, how can I use System_Daemon::isDying() to trigger an action? I want to write to the database one last time before it shuts down. I have an if statement in the while loop but it is not firing when I shut it down.

btw - this is an awesome class

Thanks

Dan

Tom Dexter
Tom Dexter·

@Dan: To perform an update of some sort on shutdown just override the default signal handler such as:

System_Daemon::setSigHandler(SIGTERM, 'your_function');

In that function (which gets passed the signal number like any signal handler) perform your update and then call System_Daemon::stop();

Tom Dexter
Tom Dexter·

Just a word of caution to others writing daemons: Many of you may have discovered, as I did, that in order for your daemon to background properly (especially when being started from other processes like gui service managers) you need to close STDOUT and STDERR with for example fclose(STDOUT), otherwise programs attempting to start your service can hang.

I found out the hard way that this can have the nasty side affect of having some other unwanted resource becoming the default file descriptor for STDOUT (2) or STDERR (3). In my case STDOUT became the file handle for my MySQL unix socket! As soon as I accidentally left a print statement in my script it started mysteriously closed the connection causing "MySQL has gone away" errors!

To prevent this, early in your script before opening any logs or database connections, close ALL three of STDIN, STDOUT, and STDERR and open global file handles to three things to take their place, in order (using a dummy for STDIN, and some log file for the others) such as:

fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
$GLOBALS['STDIN'] = fopen('/dev/null', 'r');
$GLOBALS['STDOUT'] = fopen('/var/log/default.log', 'a+b');
$GLOBALS['STDERR'] = fopen('/var/log/default.log', 'a+b');

That will cause any erroneous output to write harmlessly to those logs. Note that the names of those globals are irrelevant, the important thing is the ORDER they're in, which causes them to get the file descriptors 1, 2, and 3 respectively.

Be glad you didn't have to figure this out :D

Tom

Adolphe
Adolphe·

Hi Everyone,
i use systemdaemon to create a server that listens for incoming connections and hit a function to query a MS SQL database.
When not running in background, mssql_connect works perfectly, everything seems good. However in background mode mssql does not work.

How can you help me on this ?

Tom
Tom·

Hi Kevin, (or anyone for that matter!)

Still using and loving your pear package!

Trying to use it on Fedora 14 at the moment, after playing with it on Ubuntu.

When I go to run the daemon I get the following error:

PHP Fatal error: Uncaught System_Daemon_Exception: PHP is compiled without --enable-posix directive in /var/www/testproject/system/system.php on line 85
#0 /var/www/testproject/system/system.php(85): System_Daemon::start()
#1 {main}
thrown in /usr/share/pear/System/Daemon.php on line 552
<font color=#ff0000>
Fatal error: Uncaught System_Daemon_Exception: PHP is compiled without --enable-posix directive in /var/www/testproject/system/system.php on line 85
#0 /var/www/testproject/system/system.php(85): System_Daemon::start()
#1 {main}
thrown in /usr/share/pear/System/Daemon.php on line 552
</font>

Will I just have to re-compile php without the -enable-posix argument?

Thanks in advance for your help!!

Tom

Jim McNeely
Jim McNeely·

I'm having a lot of trouble making this work with PEAR. "which pear' returned nothing until I figured out it no longer comes with Mac OS X 10.6. I finally figured this out, I think, and it returns this:

err: [PHP User Error] PEAR_Exception not found?! [l:519]

Which upon examination shows it is looking for some PEAR exception classes; I see these buried in the PEAR sub file Exception.php, but it still isn't working. I would really love to use this, any help would be hugely appreciated.

Tom
Tom·

Ooops! Just did: yum install php-process.

That enables the posix functions apparently.

Thanks anyway!

Kev van Zonneveld
Kev van Zonneveld·

@ Andy: you can also use isInBackground() for that.

@ Robert: Nope, pretty busy as it is, thanks : )

@ Tom Dexter: Thanks for chiming in.

@ Jim McNeely: Maybe you can try installing pear with homebrew on OSX

Kev van Zonneveld
Kev van Zonneveld·

@ Adolphe: Your problem is already covered in the trouble shooting section.

Jason
Jason·

Great script!

How can you detect if an argument has been passed to the daemon whilst it is running?

start dameon...
./daemon.php

then a command is executed on the server...
./daemon.php --blah

[code="php"]
while (!System_Daemon::isDying()) {

// detect if blah has been called
}
[/code]

Kev van Zonneveld
Kev van Zonneveld·

@ Jason: arguments are handled by the wrapper script, not by the daemon class itself. So that's something you code yourself or take from the examples

Jason
Jason·

@Kevin: What I mean is there a way to communicate with the daemon i.e. send it a command whilst it's running which it can detect and perform some specific code if present?

We want the daemon to run a mysql query but only when we tell it to, we don't want the daemon doing anything else the rest of the time (but obviously still keep running in the background).

So the daemon can't check to do anything, it can only take in input.

I know it can read in arguments through $argv but this would only work initially when you start up the daemon. It has to somehow dynamically retrieve this. Maybe using php://stdin or something but I don't know the best way.

Any help would be appreciated on this!

Kev van Zonneveld
Kev van Zonneveld·

@ Jason: As the article explains, never use stdin/out/err as it will crash your daemon the moment it wants to use it & your terminal session is gone.

You can open a tcp port, or for very rudimentary communication, can use custom POSIX signals. Or work with a jobs table or filebased queue, etc.

Kev van Zonneveld
Kev van Zonneveld·

@ Jason: As the article explains, never use stdin/out/err as it will crash your daemon the moment it wants to use it & your terminal session is gone.

You can open a tcp port, or for very rudimentary communication, can use custom POSIX signals. Or work with a jobs table or filebased queue, etc.

Jason
Jason·

@Kevin: Thanks, I have found out how to send out custom signals. Looking through the list of signals SIGUSR1 and SIGUSR2 are good to use, however we would probably need more than 2 different signals to differentiate between queries to run.

Could we technically send any signal as long as we set it using setSigHandler? Or would certain signals have an effect on the daemon (like SIGKILL)?

Ed
Ed·

Useful, thanks for the work you've done on this project. In order to create the startup script, does the file need to be in /etc/init.d/ ?

My script is chmod a+x, owned and grouped to root, but it's still not writing. Here's /var/log

[May 02 16:15:30] notice: Starting acibind daemon, output in: '/var/log/acibind.log'
[May 02 16:15:30] info: Changed identify to '':''
[May 02 16:15:30] notice: No data dir found in either: or
[May 02 16:15:30] warning: Unable to create startup file [l:1134]
[May 02 16:15:30] notice: unable to write init.d script
[May 02 16:15:30] err: Not receing data from ZM. [l:183]

Any help is much appreciated!

Thanks,
Ed

Simon
Simon·

Many thanks for this. I'm only having one small issue: I'd like to have the init.d script report a FAIL if the daemon isn't started due to it being unable to connect to the database. However, System_Daemon::stop doesn't let you return an error, and just using an exit(1) leaves the pid file around and appears to be ignored by the init script, so it looks like the daemon started OK. Is there a way to do this?

Adam
Adam·

Thanks man for both info & download

fforex
fforex·

great article, i found this trying to make a cron job run at frequencies less than 1 minute(in seconds). as i can't found anything useful i was able to get into this article searching google. i installed the required stuff easy with my Slackware and tried the simple example with the infinite loop. works perfect for my needs, i really appreciate the article. thanks.

Alex
Alex·

Hello Kevin, thanx for your excellent library

I am facing a problem when writing to the init.d folder. I'm getting the following error:

[CODE="text"]
[May 12 03:49:03] notice: No data dir found in either: or
[May 12 03:49:03] warning: Unable to create startup file [l:1134]
[May 12 03:49:03] notice: unable to write init.d script
[/CODE]

I have the next permissions:

[CODE="text"]
-rwxrwxrwx 1 root root 3501 2011-05-12 04:00 session_cleaner.php /* My executable php file */
drwxrwxrwx 3 root root 4096 2011-05-11 20:28 System /* your lib */
drwxrwxrwx 2 root root 4096 2011-05-09 22:47 init.d
[/CODE]

And finally my php code (session_cleaner.php)
[CODE="php"]
<?php

$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();
}

// Include Class
error_reporting(E_ALL);
require_once 'lib/System/Daemon.php';

// Setup
$options = array(
'appName' => 'session_cleaner',
'appDir' => dirname(__FILE__),
'appDescription' => 'Cleans up the /Indexing/storage/ directory from expired session files.',
'authorName' => 'Alexandros Biratsis',
'authorEmail' => 'abiratsis@gmail.com',
'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
);
}
}

// This variable gives your own code the ability to breakdown the daemon:
$runningOkay = true;

// While checks on 2 things in this case:
// - That the Daemon Class hasn't reported it's dying
// - That your own code has been running Okay
while (!System_Daemon::isDying() && $runningOkay) {
// 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
);

$runningOkay = true;
try{

$handle = opendir('storage/');
if ($handle) {

while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
System_Daemon::info($file.' found!');
}
}
closedir($handle);

}
else{
System_Daemon::err("Unable to open {$handle} directory!");
$runningOkay = false;
}
}
catch(Exception $e){
$runningOkay = false;
System_Daemon::err($e->getMessage());
}

if (!$runningOkay) {
System_Daemon::err('{appName} produced an error, so this will be my last run');
}

// Relax the system by sleeping for 10 sec
System_Daemon::iterate(10);
}

// Shut down the daemon nicely
// This is ignored if the class is actually running in the foreground
System_Daemon::stop();
?>
[/CODE]

Do you have any idea why I can't write to init.d folder?

Thanx a lot
Alex

kirill
kirill·

Regarding the script in the "Complex Example" section - here you start the daemon with System_Daemon::start() before you process --write-initd parameter. As process changes its identity upon the start - this causes problem "Unable to create startup file". To overcome that you could either move write-initd part up in the code or call script with [CODE="text"]--no-daemon --write-initd[/CODE] params.
Also, at ubuntu I have problems stopping / restarting the daemon with the generated init.d script. I got error:
[CODE="text"]warning: this system is not able to track process names
longer than 15 characters, please use --exec instead of --name[/CODE]
But even if I replace --name with --exec in the control script - I get another error - daemon does not stop but rather changes its mode to 'non-daemon' and keeps running. In this case it outputs log records to stdout, but can not be stopped with CTRL+C 0_o

James Gillmore
James Gillmore·

@kirill, i had the same issue and solved it by simply changing the name of the php file so it's name was less than 15 characters. note: .php is included in the character count.

Ismail
Ismail·

Thanks for such a nice article....

astaza
astaza·

wow it's what i looking for, but about echo,
you say echo will error if it's work in this mothed, but if it's run in cronjob will output errors?

thanks

Pinoy
Pinoy·

This is exactly what I'm looking for. Thank you!

Jocko
Jocko·

I have managed to get this up and running on one of my servers, but I am struggling with the second. I get this response in the shell: sh: /export: not found
sh: /export: not found

What is the problem here?

Jean
Jean·

Hey Kevin, great work on the Deamon class.
From what I've learned during my time as Perl developer, you might want to consider closing STD* handles before forking though (e.g. fclose(STDIN)). That should prevent errors when echo is called within the daemonized part of the script and it helps realiably detaching from the console.

Nemanja
Nemanja·

Brilliant, precise, informative, descriptive, accurate!

Thank you very much for this exquisite article!

yuki
yuki·

I want to ask about running php as a daemon to accept and inject file to port. I'm using postfix to build email server and i want to do some phrasing (to find url in incoming email) to incoming mail using php script. So the scenario is:

incoming mail-->postfix-->php phrasing-->postfix-->destination.

Postfix will receive incoming mail, then inject it to port, for example port 10033, then script php will receive and phrase it, after that, script php will inject it back to other port (e.g port 10034).

So how do i configure php so the script will be able to receive then do phrasing and inject to port?

many thanks for your answer.

Andrew Blake
Andrew Blake·

Hoping it works - looks good. Was googling for php daemon. Will be trying it today and will comment back. I have a hungry bit of code generating emails with swiftmail containing pdf attachment made by webkittoppdf. The emails queue in a table. Currently have been exec'ing background task to call my mail_queue.php but no matter what I try to ensure this is single instance I get race conditions problems that cause the odd delay or duplication. The answer is to turn it into a daemon.

Cheers for the post.

Andrew Blake
Andrew Blake·

Fantastic! Seems to be working perfectly. Will make small donation now - wish I could make it bigger - but not rich!

My linux Knowledge is very poor so will note the only minor thing I noticed here rather than trying to figure out how in PEAR. For some reason the kill command didn't work for me (though I never investigated why), and then once running trying to just run it again this time with the --write-initd argument wouldn't generate the init.d file. I had thought it might kill the daemon and restart. I just rebooted, and ran again with --write-inid and worked perfectly.

Thankyou

Andrew Blake
Andrew Blake·

Would make a small donation if I could find a "Make Donation" button somewhere? Apologies, it is probably here somewhere, I will look again in the future when I get a chance.

Cheers
Andrew

Gene
Gene·

This works great except for one simple problem. I cannot run my php by typing "./file.php"

I have to run it by typing "php ./file.php"

This wouldn't be a problem except for the startup script is trying to run it as "./file.php"

Anyone have an idea how I can edit the command the startup script uses, or update my server to not need the "php" in front of the command? Thank you for your help!

Gene
Gene·

Nevermind...got it...You need to put the path to PHP as the first line inside of your PHP file. so:
#!/usr/local/bin/php
<?php
....
?>

JW
JW·

interesting class you got. I'm running into a problem where I can't seem to get the init.d script to stop the daemon properly and I wind up having to kill -9 the process. Let me explain what I'm trying to do with your script.

I'm trying to build out a simplified rsyslog parser. I have a number of hosts on my network that write to my centralized syslog server. In addition to raw file logging, I have a named pipe that the syslog will output to as well. What I'm trying to accomplish is writing a script that will read the named pipe and check each line for predefined patterns for problems. If a line matches, it will log the problem line and notify somebody about the issue.

I'm trying to the script read in a configuration file/settings from either a flat file or mySQL DB table. These configurations will contain the REGEXP and notification settings, so they need to be updated on the fly and reread in if possible without having to fully restart the parsing script.

I'm in the initial stages of getting all this put together and here is some basic code that I put after "// Here comes your own actual code" in your example:

[CODE="php"]
$queue = array();
$pipefile = '/var/spool/rsyslog/rsys_fifo';
$pipe = fopen($pipefile,'r+');

if(!$pipe) die('unable to open the named pipe');
stream_set_blocking($pipe,false);
while(1) {
while($input = trim(fgets($pipe))) {
stream_set_blocking($pipe,false);
$queue[] = $input;
}
$job = current($queue);
$jobkey = key($queue);
if($job) {
if(preg_match("/MATCHTHISLINE/i",$job)){
mail_alert($job); // test function
}
next($queue);
unset($job,$queue[$jobkey]);
} else {
stream_set_blocking($pipe,true);
}
}
[/CODE]

I can start the daemon just fine using the init script, but I can't stop it. It just times out. Trying to do a kill -0 on the PID does not do anything and I have to do a kill -9 to stop it.

Andrew
Andrew·

Great stuff..
We use it for our daemon with CentOS. It never dies even for a minutes in months.
One thing that bugging me is memory usage for the daemon itself. I tested the daemon to run a simple task and it consume > 200MB. Have any idea about this?

Our daemon only process two lines:
while (!System_Daemon::isDying())
{
Proc_Close (Proc_Open ("/usr/bin/php /opt/web/anl/isat/engine/MTBillSendChildProc.php& > /dev/null", Array (), $fsx));

System_Daemon::iterate(2);
}

Adam
Adam·

The script works great, apart from writing the initd. I get an error - unable to forge startup script for non-existing daemon_filepath. I am running on CentOS.

Claudius Coenen
Claudius Coenen·

Hi and thanks for the Daemon-Class!

I ran into a minor problem with stopping my daemon, and added System_Daemon::isRunning() to my while-loop.

I thought i mentioned it here, in case anyone else has the same problem. When i stopped my daemon with /etc/init.d/awesomedaemon stop, the log-messages switched from "daemon" to "non-daemon", but the service continued to run. Adding the check for "isRunning" solved that problem.

Now it exits just fine!

Peter
Peter·

This is great!
I'm using it for a project at the moment.
But i wonder, does "sysMaxExecutionTime" actually do anything?
If i set it to 10 as test it doesn't kill the daemon after 10 seconds/minutes.

Which brings me to the next question, is there an option or command that allows me to kill the daemon if it runs too long? Let say after 10 minutes if it's still running : kill it.
I can do a date() with execution and compare, when it's higher and execute System_Daemon::stop() for example. But i just wondered if there's something better than that.

Adam
Adam·

Hi, I'm trying to implement a daemon process to replace an hourly cron job.

The issue I'm currently running into, is that even though I specify an integer value to System_Daemon::iterate, the process continues to run, and loop (incrementing the count variable each time).

[CODE="php"]
$thisMinute = intval(date('i'));
$napSeconds = intval( ( (60 - $thisMinute) * 60) + 60);

if ( $napSeconds > 0 ) {

//echo "napSeconds " . $napSeconds ." is an Integer greater than 0\n";
System_Daemon::iterate($napSeconds);

} else {
//echo "napseconds value is less than 0, override the value to iterate to be 60 seconds. \n";
System_Daemon::iterate(60);

}

$cnt++;

[/CODE]

What's occurring is that even though there is a non-negative, greater than zero integer passed to System_Daemon::iterate, the process keeps going and going, incrementing the count of the variable $cnt and seemingly ignoring the iterate call.

Is there anything else I need to be aware of or perhaps a method in addition to iterate here -

again, my goal is to replace an hourly cron with a daemon process.

Thanks in advance,
Adam

Evgeniy
Evgeniy·

Thank you for the example. !
I have a problem with running the daemon.
I do so:
[CODE="text"]
$ /usr/bin/php -q /var/www/dt_parser.php
In the logs get:
[Oct 19 11:36:51] notice: Starting simple daemon, output in: '/var/log/simple.log'
[Oct 19 11:36:51] emerg: simple daemon is still running. Exiting [l:1262]
[Oct 19 11:36:51] info: Process was not daemonized yet, just halting current process
[/CODE]

I close the terminal, and after re-connection can not detect the process.

I do not see the process in the list of server processes.
What am I doing wrong?
Im using Ubuntu
Thank you!

Jochem Stoel
Jochem Stoel·

Hello, thank you for this. This is making my world a little better. I use this for several things at the moment.

I came across a 'problem' with arguments given from the commandline. I will post the problem and my solution here in case it helps somebody.

Let's call your script daemon.php for the sake of it, you run the script by doing this:

[code="text"]./daemon.php[/code]

You can give arguments to your daemon by doing his:

[code="text"]./daemon.php --string hello --time 24[/code]

The array $argv then contains:
[code="text"]
./daemon.php
--string
hello
--time
24
[/code]

This daemon thing 'explodes' these arguments on a whitespace before they are passed to the script.

This means when you do this:
[code="text"]./daemon.php --string hello sweet world --url ww.google.nl[/code]

It will give you the following array values:
[code="text"]
./daemon.php
--string
hello
sweet
world
--url
www.google.nl
[/code]

This was not very convenient for me, because I wanted to pass a string that contains whitespaces to my daemon.

The following PHP function turns the above array into this:
[code="text"]
string = 'hello sweet world'
url = 'www.google.nl'
[/code]

[code="php"]
<?php
function make_arguments_usable($argv) {

$i = 0;
$runmode_current = array();

foreach ($argv as $k=>$arg) {

if (substr($arg, 0, 2) == '--' && $i > 0) {

$runmode_current['name'] = substr($arg, 2, 16);
$runmode_current['i'] = $i;

if (isset($argv[$i+1])) {

if (substr($argv[$i+1], 0, 2) != '--') {

$store_runmode[substr($arg, 2)] = $argv[$i+1];
} else {

$store_runmode[substr($arg, 2)] = true;
}
} else {

$store_runmode[substr($arg, 2)] = true;
}
} else if (isset($runmode_current['i']) && $i != $runmode_current['i']+1 && $i > 0){

$store_runmode[$runmode_current['name']] .= ' '.$argv[$i];
}

$i++;
}

return $store_runmode;
}
?>
[/code]

In this function, I purposefully do not use the first string that contains the scriptname.

If you want to set the runmode in your daemon, you can use this function like this:
[code="php"]
$arguments = make_arguments_usable($argv); // $argv is reserved

foreach ($arguments as $k=>$v) {

$runmode[$k] = $v; // $runmode['some'] = 'thing cool';
}
[/code]

I hope this can be useful for somebody.

Iser Epstein
Iser Epstein·

I run on Centos 6.0 and got the "--enable-pcntl" error.
Should I have to install other PHP Cli ?
If so,how to do that ? where can I get it from ?

Roberto
Roberto·

Gracias por el articulo del PHP Deamons, es mas que lo que necesitaba, quiero hacerte una donacion via paypal, pero donde ?

Saludos !!!

Hello Great job related to the PHP Deamons, I want to donate via paypal but where ?

Best Regards !

Barney
Barney·

Wow, this is great! i looked at writing a daemon in C but couldn;t find alot of clear, reliable guides.

This has saved me a pile of work!

One problem though, on ubuntu (and i assume all debian systems) when i use the --write-initd parameter, i get a message in the log file that it wasn't possible to write to the /etc/init.d directory, and that i should try running as root.

unfortunately i am already running as root!!!

any ideas?

Carl
Carl·

!! Attention CentOS users!!

Do the following if you want your daemon to run at system boot.

FYI: The following assumes that your script is called "logparser"

1.) Use your PHP script to write the autostartup files using the writeAutoRun() method.
!! Make sure "appRunAsGID" and "appRunAsUID" is set to 0 !!

2.) Add your script to the list of system services with:
[CODE="text"]
# chkconfig --add logparser
[/CODE]

3.) Don't forget to enable your service:
[CODE="text"]
# chkconfig logparser on
[/CODE]

That should be all there is to it :)

Carl
Carl·

...just wanted to add something to my previous comment...

If you get the "bash: chkconfig: command not found" and you already have it installed, just get it's location and run it from there:

[CODE=text"]
# whereis chkconfig
[/CODE]

I had to use:
[CODE=text"]
# /sbin/chkconfig --add myScript
[/CODE]

Big Jim
Big Jim·

This runs great until I try to run it via a shell_exec call in a script like this:

[CODE="php"]
shell_exec("/etc/init.d/logparser start");
[/CODE]

It bizarrely loops through the script that makes the above call and gives this output 136 times:

[CODE="text"]
X-Powered-By: PHP/5.3.8

Content-type: text/html

[/CODE]

Followed by this 136 times:

[CODE="text"]
Starting logparser: [ OK ]
[/CODE]

It doesn't actually start the daemon though. I'm running this on CentOS w/ the following small modification for writing the start script:

[CODE="php"]
$gid = 545; // I've changed these, but they match the uid/gid to the script owner
$uid = 547;

if ($runmode['write-initd']) {
$gid = 0;
$uid = 0;
}

// Setup
$options = array(
'appName' => 'logparser',
'appDir' => dirname(__FILE__),
'appDescription' => 'Parses logfiles and stores them in MySQL',
'authorName' => 'Kevin van Zonneveld',
'authorEmail' => 'kevin@vanzonneveld.net',
'sysMaxExecutionTime' => '0',
'sysMaxInputTime' => '0',
'sysMemoryLimit' => '1024M',
'appRunAsGID' => $gid,
'appRunAsUID' => $uid,
);
[/CODE]

I've also noticed that /etc/init.d/logparser status (and restart) don't work. I'm guessing something is wrong w/ the init.d start script, but I can't figure it out. Any ideas?

Thanks

Ivang
Ivang·

You're the f*cking crack of the php standalone applications!
I was looking a way to avoid the (ethernal) use of cron and your post is the most useful article I found for months.

It probably gives some minor problem (like the previous comments) but it does not care, your little script for demonizing a php script is the best contribution to php since the MVC frameworks!!

Thank you very much

Achim
Achim·

Another big benefit of a daemon vs. a cron job, specially in PHP world, is the caching:

The APC op-code cache is basically useless in PHP-CLI environment. If a PHP will be called regularly via cron, the op-code will be compiled every time again, even APC don't persist the cache over several processes.
But in case of a daemon, the op-code will be compiled only once and used for each "loop" again and again - even if APC is not used :-)

Benji
Benji·

Man alive, that's genius !

Thanks for sharing, you are a maven.

Tom
Tom·

For some reason iterate() was not waiting the proper amount of time. I still called it to clear the statcache but I also ended up using sleep() too. I said iterate(14400) and it instead continued in about 25 minutes (a few seconds off each loop). But this is really nice and really helped me a lot.

It's a LOT better than cron. I'm calling a command line script which calls a php file. Writing a script to call that and trigger on cron just was a nightmare because the script had to execute from a specific directory. So this was much better because you don't have to worry about the directory change being lost due to sub shells, etc.

Two other things worth mentioning are Gearman and Clockwork. Gearman is not going to be something you really want to loop and use like a daemon, but it's related and is nice. Clockwork is a replacement for cron basically. It's Ruby, but you should still be able to use PHP to control it. I haven't looked into it yet because it just took a few more minutes to setup than this and this worked perfect...But may be worth a look.

Pawel
Pawel·

Hi, Thanks for nice piece of software:)
Generally works fine, however I have some problem with my daemon.
Daemon was supposed to run some instructions every 3 seconds, but thanks to logs, I noticed that sometimes it freezes for around 1 hour.

So logs:
...
[Jan 12 08:52:59] info: running
[Jan 12 08:53:02] info: running
[Jan 12 08:53:05] info: running
[Jan 12 10:06:26] info: running //waits over 1 hours instead of 3 seconds

The pause is done with: System_Daemon::iterate(3)

Wondering if anyone had similar problem, or has an idea what might be causing it ?

Thanks

ouiea
ouiea·

Hi there, many thanks for this article!
I'm using Amazon's Linux (don't ask why) and I've been having the "No autoRunTemplateReplace found" when trying to create the init.d script.
In the end, I've ended editing the Daemon.php file
(located here in my install: /usr/share/pear/System ) and forcing RedHat as my OS:
search:
[CODE="php"]
self::$_osObj = System_Daemon_OS::factory();
[/CODE]
repace with:
[CODE="php"]
self::$_osObj = System_Daemon_OS::factory('RedHat');
[/CODE]
I could write the init.d file after that.

BTW, it would be great to have a bigger window for the comments, I feel like peeping through a keyhole ;)

Thanks!
H

Gilmar de Paula Fiocca
Gilmar de Paula Fiocca·

Hi,

Is there any way to avoid stopping the service ?
I need consistency when getting out of the script.
Like making sure complete execution of entire process was done.

Thanks,
Gilmar.

Seth Caldwell
Seth Caldwell·

Shouldn't you make use of nohup to avoid any echo problems? Or auto pipe output to the log instead of std?

milad
milad·

hi all
what can i do for write daemon in init.d opensuse 12.2?
i run daemon with this option but it say:
"unable to write init.d script" , "no autoRunTemplatePath found"
array(
'appName' => 'queue_daemon',
'appDir' => dirname(__FILE__),
'appDescription' => 'queue daemon',
'authorName' => 'me',
'authorEmail' => 'me',
'sysMaxExecutionTime' => '0',
'sysMaxInputTime' => '0',
'sysMemoryLimit' => '1024M',
'appRunAsGID' => 0,
'appRunAsUID' => 0,
'usePEAR'=>false
)

meddyjackson
meddyjackson·

This is the type of blog I've been hoping to find: I wanted to thank you for this great read!<a href="http://www.xwallpaperz.com"> High Definition Wallpaper</a>

Xaver
Xaver·

Cool article, bro. I did it on my site with dropr =)

Micah
Micah·

This is awesome stuff! I just happened to be wondering about this and came upon your article! This is going to come in very handy for a web site I'm in the process of building.

Thanks for the info, tutorial!!

Aaron
Aaron·

Trying to add the PHP mail() function to a working daemon, but all processing seems to stop after the mail command is executed. Any ideas what could be wrong. Nothing is jumping out at my in any log files.

Quakz
Quakz·

I cannot figure out how to make it reconnect to mysql again if it disconnects, can someone show me an example of this?

Greg J
Greg J·

Hi,

Been running these daemons for a couple of years now. they work very well indeed. However i want to get a couple of daemons talking to one another rather than the excessive polling that we are currently doing. Sockets seem to be the way to go however I have having trouble finding a decent working solution. i have one solution at the moment but there is a delay of 60 seconds every time. I get the right answer so I think its timing out somewhere. Anyway, does anyone have any code where they have 2 daemons talking to one another they would like to share!

thanks in advance

William
William·

Just wanted to thank Greg J for that find on fixing the extension not present error.

Nick
Nick·

@Greg J
I use ZeroMQ right now. It is brilliant! Fast and so easy. I use 5 daemons communicating to each other. By default a daemon waits for a message and then starts to work, it only takes milliseconds. And User communicating through ZeroMQ with the daemons. And daemons are publishing messages to subscribing users ;)
Untill now(alpha) i just run the daemons in the console for tests. So i will try kvz's class now. Thanks a lot for your work!

Greg J
Greg J·

@Nick
Hey thanks for that. I did look over that briefly but when I saw it required an install I think I moved on pretty quickly, I was just looking for a script. We are looking at developing something for 'raspberry pi' boards so I was looking for something that didnt require installing additional components. From what I have found so far though it seems to be hard to find! I will download it and have a play, it looks ideal!

As all my comms need to happen on the one PC I have something very suitable working with named pipes which will do for the moment but I do want to extend the comms to other PCs so ZeroMQ looks to be well placed to do that.

Pune Escorts
Pune Escorts·

Deamons

Van phong luat su
Van phong luat su·

I like this post

Mark
Mark·

Is there a way to specify what user this runs under? When I run a php daemon, it runs as user '997' and it can't modify/delete some files I would like it to.

Mark
Mark·

Never mind - found it:

'appRunAsGID' => 1000,
'appRunAsUID' => 1000,

BdN3504
BdN3504·

Great class! i'd like to use it to make a piece of php code be executed on a server to which i don't have shell access, i can only upload my code via ftp. I will try to get one person from the support team of the hoster to run the script from cli, but i'm not sure if they are willing to do it. Is there a way to trigger the execution of the parent process as a non root user? whenever i try to start the script from an external client e.g. a browser, i get loads of error messages telling me that all the SIG variables are missing...

There is one comment i'd like to make on the code: I think it's more appropriate to do [CODE="php"]'Changed identity to %s:%s',
$user['name'],
$group['name'][/CODE] instead of [CODE="php"]'Changed identity to %s:%s',
$group['name'],
$user['name'][/CODE] so that the logs read Changed identity to user:group. This portion of code is found on the lines 1548-1550 in the System/Daemon.php.
https://github.com/kvz/syst...
(I'd also have it say identiTy instead of identiFy).This notation is also used for the unix chown command. Is there a reason why you did it the other way round?

Klaas
Klaas·

Hi, great class!

After forking, the child process seems to be unable to fopen / fwrite a file. The script runs as root, and the target directory is writeable and owned by root as well.

When using the no-daemon option, the writing works perfectly. Does anybody have an idea how to fix this?

Klaas
Klaas·

I found it:

I was running the script as root, however the child process runs as:

[CODE="php"]
'appRunAsGID' => 1000,
'appRunAsUID' => 1000,
[/CODE]

I had to change it to:
[CODE="php"]
'appRunAsGID' => 0,
'appRunAsUID' => 0,
[/CODE]

Thanks again for this awesome script!

sunil
sunil·

its all blog are very intresting and benificial to developer.