Search code examples
phpprocessexecparent-childdaemon

Really daemonize a command when using exec()?


From PHP pages of my apache server, I run some commands using a line like :

exec("{$command} >> /tmp/test.log 2>&1 & echo -n \$!");

You can see an explaination of the arguments here.

But I don't understand something : if I restart or stop my apache server, my command dies too.

root@web2:/sx/temp# ps ax | grep 0ff | grep -v grep
15957 ?        S      0:38 /usr/bin/php /sx/site_web_php/fr_FR/app/console task:exec /sx/temp/task_inventaire/ 0ff79bf690dcfdf788fff26c259882e2d07426df 10800
root@web2:/sx/temp#  /etc/init.d/apache2 restart
Restarting web server: apache2 ... waiting ..
root@web2:/sx/temp# ps ax | grep 0ff | grep -v grep
root@web2:/sx/temp# 

After some researches, I read some things about parent pids, but using a & inside my command-line, I thought I was really detaching my child process from his parent.

I am using apache2 with libapache2-mod-php5 and apache2-mpm-prefork.

How can I really detach my children programs from apache?

edit

You can reproduce it on a Linux/Mac this way :

a) create a executed_script.php file that contains :

<?php
sleep(10);

b) create a execute_from_http.php file that contains :

<?php
exec("php executed_script.php > /tmp/test.log 2>&1 & echo -n \$!");

c) run http://localhost/path/execute_from_http.php

d) on a terminal, run the command :

ps axjf | grep execute | grep -v grep ; sudo /etc/init.d/apache2 restart ; ps axjf | grep execute | grep -v grep

If you run the command during the 10 secs of the execute_from_http.php script, you'll get the output :

php@beast:/var/www/xxx/$ ps axjf | grep execute | grep -v grep ; sudo /etc/init.d/apache2 restart ; ps axjf | grep execute | grep -v grep
    1  5257  5245  5245 ?           -1 S       33   0:00 php executed_script.php
 * Restarting web server apache2
 ... waiting    ...done.
php@beast:/var/www/xxx/$

As you can see, the ps command outputs only once, this tells you that the executed script died when apache restarted.


Solution

  • First note, that the '&' in your example is just a boolean AND that concats the command and the echo. If you want to start the command in background, meaning that exec will return immediately, use the & at the very end of the command line:

    exec("{$command} >> /tmp/test.log 2>&1 & echo -n \$! &");
    

    If you want the process running after apache has finished you'll have to daemonize the process using pcntl_fork()

    Here comes an example:

    $pid = pcntl_fork();
    
    switch($pid) {
        case -1 : die ('Error while forking');
    
        case 0:  // daemon code
             posix_setsid(); // create new process group
             exec("{$command} >> /tmp/test.log 2>&1 & echo -n \$!");
             break;
        default: 
             echo 'daemon started';
             break;
    
    }
    

    Now there is no code in the starting PHP scripts that handles the return value of exec nor its output. So the current process can finish before exec has finished. The worker process will be owned by init after this.


    Also you can have a look at the PEAR package System_Daemon. This can help to daemonize a script.