php / php/php-src

SSE terminate the script & proc_open leaves child's running in background

Abierto
#8,242 1 comentario 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Bug Status: Needs Triage
Lenguaje dominante
C
Estrellas
40.4k
Forks
8.2k
Merge medio
2 d 13 h
PR fusionados (30 d)
96

Descripción

Description

This code is simply a combination of Mozilla Server-Sent Events Example and PHP proc_open() Example, which make the browser receive the output of a command in real-time.

The first issue is proc_terminate() leaves children running, 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

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

No se han identificado archivos del repositorio ni pruebas. Empieza reproduciendo la desconexión de SSE con PHP 7.4.3 en Ubuntu 20.04; después, lee los puntos de entrada proc_open(), proc_terminate() y register_shutdown_function(), así como el error relacionado con el proceso hijo enlazado. Se considera terminado cuando el proceso hijo no sigue ejecutándose después de que se cierre la conexión y existe cobertura de regresión para el ciclo de vida indicado.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
bash, php
Área
backend, operating-systems
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Necesita aclaración
Aptitud para principiantes
25/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.