10

Currently my solution is:

exec('php file.php >/dev/null 2>&1 &');

and in file.php

if (posix_getpid() != posix_getsid(getmypid()))
    posix_setsid();

is there any way I can do this just with exec?

Dalius
  • 726
  • 1
  • 8
  • 19

2 Answers2

18

No, detaching can't be done with exec() directly (nor shell_exec() or system(), at least not without changing the command, and using third-party tool which does detach).


If you have the pcntl extension installed, it would be:

function detached_exec($cmd) {
    $pid = pcntl_fork();
    switch($pid) {
         // fork errror
         case -1 : return false;

         // this code runs in child process
         case 0 :
             // obtain a new process group
             posix_setsid();
             // exec the command
             exec($cmd);
             break;

         // return the child pid in father
         default: 
             return $pid;
    }
}

Call it like this:

$pid = detached_exec($cmd);
if($pid === FALSE) {
    echo 'exec failed';
}

// ... do some work ...

// Optionally, kill child process (if no longer required).
posix_kill($pid, SIGINT);
waitpid($pid, $status);

echo 'Child exited with ' . $status;
Top-Master
  • 7,611
  • 5
  • 39
  • 71
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
  • I do not want to daemonize the process nor do I need to fork it. I just need to start another php process that wouldn't receive SIGINT when the one who created it exits. (I'm not really sure how a daemon is different from a process that is detached from the terminal?) – Dalius Oct 23 '13 at 23:18
  • Check my example again. Isn't it what you want? ;) The only difference to a daemon is that the does not run forever but will be killed on fathers exit. – hek2mgl Oct 23 '13 at 23:20
  • Basically yes, but try implementing that in a simple function that returns true or false. (It MUST NOT block). And I don't want the new process to be killed, it will exit itself. – Dalius Oct 23 '13 at 23:27
  • Yes it is :) Though I think it's an overkill to make a fork, so I think I'll stick with detaching the process when it starts. – Dalius Oct 23 '13 at 23:35
  • 1
    What do you expect `exec()` does under the hood? It will start a shell (that's why you can use output redirection in commands) You may replace the `exec()` in child with the actual php code of the child.. (can be a function or a class method, ....) then you'll save the additional shell and the php-cli call and it would be best for performance – hek2mgl Oct 23 '13 at 23:36
  • @hek2mgl Looking at the example in my answer, could you look into updating/deleting the first sentence in yours? Otherwise your solution looks very clean and feasible :) – Philzen Oct 24 '13 at 18:29
  • @Philzen Yours is a working solution but it is more a shell script than a PHP script. The work of detaching the process is not done by the `exec()` function, it is done by nohup. But in was questioned whether the `exec()` function can do it. However yours is a good workaround if the pcntl extension isn't installed – hek2mgl Oct 24 '13 at 19:43
  • "detaching the process is not done by the exec() function, it is done by nohup" - fair point you have there :) – Philzen Oct 24 '13 at 20:23
  • If parent process won't exit immediately after fork the child process should [fork one more time](http://stackoverflow.com/questions/881388/what-is-the-reason-for-performing-a-double-fork-when-creating-a-daemon) to prevent creating zombie. – vvolodko Oct 01 '14 at 13:53
  • @vvolodko Thanks for your hint! (and srry for my late reply) Will read through this and update my answer – hek2mgl Oct 03 '14 at 22:06
8

Provided your current user has sufficient permissions to do so this should be possible with exec and alike:

/*
/ Start your child (otherscript.php)
*/
function startMyScript() {
    exec('nohup php otherscript.php > nohup.out & > /dev/null');
}

/*
/ Kill the script (otherscript.php)
/ NB: only kills one process at the time, otherwise simply expand to 
/ loop over all complete exec() output rows
*/
function stopMyScript() {
    exec('ps a | grep otherscript.php | grep -v grep', $otherProcessInfo);
    $otherProcessInfo = array_filter(explode(' ', $otherProcessInfo[0]));
    $otherProcessId = $otherProcessInfo[0];
    exec("kill $otherProcessId");
}

// ensure child is killed when parent php script / process exits
register_shutdown_function('stopMyScript');

startMyScript();
Philzen
  • 3,945
  • 30
  • 46
  • 5
    Please do **not** do this ever again: http://stackoverflow.com/review/suggested-edits/3201462 This is absolutely not how Stack Overflow works. You are way, **way** out of line, crossing out somebody else's answer and pointing their answer to your own. – user229044 Oct 24 '13 at 00:47
  • Apologies - no pun intended. I was under the impression S.O. was very serious about not having incorrect information here, thus i merely suggested to remove hek2mgl's info that this couldn't be done with exec() function. – Philzen Oct 24 '13 at 18:19
  • 3
    Voting is the way we sort content here. – user229044 Oct 24 '13 at 18:23
  • 1
    This is a good workaround if the pcntl extension isn't available +1 – hek2mgl Oct 24 '13 at 19:44