Search code examples
phpshellshell-execproc-open

PHP only able to kill process when run in cli


I have this php script "start_stream.php", which starts a process (ffmpeg) using proc_open() and then stays in a loop checking for a keep-alive signal.

Once the keep-alive signal is too old, the php script will terminate the process by its pID with this command:

$output = system('taskkill /F /PID '.$this->pID);
echo 'TASKKILL OUTPUT: ';
var_dump($output);

The output from this is always like this:

TASKKILL OUTPUT: string(55) "SUCCESS: The process with PID 2272 has been terminated."
    

BUT under some circumstances the process does not terminate, but keeps running. But I always get the SUCCESS message.

If I run the start_stream.php script from cmd (windows) with this command:

php start_stream.php --stream [STREAM-NAME] --url [STREAM-URL]

Then the process will terminate properly as expected.

But since this is a web-application, I need to call the start_stream.php script from another php script. I do it like this:

//Set the command for streaming
$command = 'php start_stream.php --stream '.$stream.' --url '.$url;
//Add the environment variables for shell execution
putenv('PATH=' . $_SERVER['PATH']);
//Set the working directory to streaming directory
chdir("..\\streaming");
    
//Start the streaming process
define('STDIN',fopen("php://stdin","r"));
define('STDOUT',fopen("stdout-".$stream,"w"));
$descriptorspec = [STDIN, STDOUT, STDOUT];
$process = proc_open($command, $descriptorspec, $pipes);

When I do this, I still get the SUCCESS output, stating that the process has terminated, but the process keeps running in the background.

It works to kill the process with this:

shell_exec('taskkill /IM "ffmpeg.exe" /F');

But I cannot use that, since it will kill all ffmpeg processes, and I need to run concurrent processes, killing only the relevant ones.

As I said earlier, the script runs and terminates as intended if I use the following command on the command line:

php start_stream.php --stream [STREAM-NAME] --url [STREAM-URL]

But if I use php.exe instead of php like so:

php.exe start_stream.php --stream [STREAM-NAME] --url [STREAM-URL]

I will have the same problem with the ffmpeg process not terminating, but still giving a success message.

I am also using proc_terminate() in the start_stream.php script like so:

proc_terminate($this->process);

But this also gives me a "process terminated" status response while the process does not stop.

The pID for the process is the pID for the cmd instance running the process, not for the ffmpeg process itself. When I call the termination of this process, the cmd process is killed in every instance. When calling the php process from cmd without using php.exe, the ffmpeg process is killed along with the cmd process. When using php.exe, only the cmd process is killed, but the ffmpeg process keeps running.

Is there a way to either ensure that the ffmpeg process is killed along with the cmd process, or maybe some way to get the pID of the ffmpeg process itself in the context that I need?


Solution

  • Although I was able to fix this, I am still not exactly sure why and how the execution conext changes between calling start_stream.php from cmd, and calling it from another php script.

    When running a command using php's proc_open(), the pID returned will by default be of the shell executing the command. So if we are starting a process via the shell, we are getting the pID of the shell executing the process, but not the pID for the child process itself.

    In some context, terminating the shell will also terminate the child process, but in other cases, the shell will be terminated, while the child process keeps running.

    If we pass the bypass_shell = TRUE to proc_open, php will return the pID of the child process and will not be creating a new shell process to contain it.

    The following example will return the destination process pID:

    $command = "ffmpeg do something";
    $descriptorspec = [STDIN, STDOUT, STDOUT];
    $otherOptions = ['bypass_shell' => TRUE];
    $this->process = proc_open( $command, $descriptorspec, [], NULL, NULL, $otherOptions);