[php-src] Issue #8242: SSE terminate the script & proc_open leaves child's running in background

From: Date: Wed, 23 Mar 2022 11:34:11 +0000
Subject: [php-src] Issue #8242: SSE terminate the script & proc_open leaves child's running in background
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-240463@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/8242 Author: hassandraga ### Description This code is simply a combination of [Mozilla Server-Sent Events Example](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) and [PHP proc_open() Example](https://www.php.net/manual/en/function.proc-open.php), which make the browser receive the output of a command in real-time. The first issue is [proc_terminate() leaves children running](https://bugs.php.net/bug.php?id=39992), which is already open since 2006, but people suggested some workarounds (_workarounds 1, 2, 4_). The second issue which is why I'm open this, is the PHP script get killed as soon as the SSE connection get closed, which mean _kill_process($process)_ never reached, while in PHP 5.6 the script still alive and terminate normally. I fix it by register_shutdown_function() (_workarounds 3_). ``` <?php // -------------------------------------------- set_time_limit(0); ini_set('auto_detect_line_endings', 1); ini_set('max_execution_time', '0'); date_default_timezone_set("America/Halifax"); ob_end_clean(); gc_enable(); header("Content-Type: text/event-stream"); header("Cache-Control: no-store"); header('Access-Control-Allow-Credentials: true'); header("Access-Control-Allow-Origin: *"); header('Access-Control-Expose-Headers: X-Events'); // ------------------------------------------- $process; $descriptorspec = array( 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 1 => array("pipe", "w"), // stdout is a pipe that the child will write to 2 => array("pipe", "w") // stderr is a pipe that the child will write to ); $env = '/home'; $pipes; $process; // ------------------------------------------- function kill_process() { global $process; $status = proc_get_status($process); if($status['running'] == true) { // Close all pipes that are still open fclose($pipes[1]); //stdout fclose($pipes[2]); //stderr // Get the parent pid of the process we want to kill $ppid = $status['pid']; // Use ps to get all the children of this process, and kill them $pids = preg_split('/\s+/', ps -o pid --no-heading --ppid $ppid); foreach($pids as $pid) { if(is_numeric($pid)) { posix_kill($pid, 9); // SIGKILL } } } // Workaround 1 posix_kill($ppid, 9); // Close The Process proc_close($process); // Workaround 2 proc_terminate($process); } function SendEventToBrowser($event, $msg) { echo ("event: $msg\n".PHP_EOL); echo ("data: $msg\n".PHP_EOL); while (ob_get_level() > 0) ob_end_flush(); flush(); } function SendToBrowser($msg) { echo ("data: $msg\n".PHP_EOL); while (ob_get_level() > 0) ob_end_flush(); flush(); } // --------------------------------------------- // Workaround 3 register_shutdown_function('kill_process'); // Workaround 4 (exec) $process = proc_open("exec /bin/bash /home/MyScript.sh", $descriptorspec, $pipes, $env, array()); if(is_resource($process)) { SendToBrowser('Shell Script Started'); while ($s = fgets($pipes[1])) { SendToBrowser($s); // break the loop if the client // aborted the connection if (connection_aborted()){ break; } } kill_process($process); // Never Reached } SendToBrowser('Shell Script Closed'); ?> ``` ### PHP Version PHP 7.4.3 (cli) (built: Mar 2 2022 15:36:52) ( NTS ) ### Operating System Ubuntu 20.04.4 LTS

« previous php.bugs (#240463) next »