php / php/php-src

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

Aperta
#8,242 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Bug Status: Needs Triage
Lingua principale
C
Stelle
40.4k
Fork
8.1k
Merge medio
2g 13h
PR unite (30g)
96

Descrizione

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

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Non sono stati identificati file del repository né test. Inizia riproducendo la disconnessione SSE con PHP 7.4.3 su Ubuntu 20.04, poi leggi gli entry point proc_open(), proc_terminate() e register_shutdown_function() e il bug collegato relativo al processo figlio. Il lavoro è completato quando il processo figlio non rimane in esecuzione dopo la chiusura della connessione ed è presente una copertura di regressione per il ciclo di vita segnalato.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
bash, php
Ambito
backend, operating-systems
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Ferma
Chiarezza
Da chiarire
Idoneità per principianti
25/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.