php / php/php-src

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

Offen
#8,242 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

Bug Status: Needs Triage
Vorherrschende Sprache
C
Sterne
40.4k
Forks
8.1k
Ø Merge
2 T. 13 Std.
Gemergte PRs (30 T.)
96

Beschreibung

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

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Es sind keine Repository-Dateien oder Tests identifiziert. Beginnen Sie damit, die SSE-Trennung mit PHP 7.4.3 auf Ubuntu 20.04 zu reproduzieren, und lesen Sie anschließend die Einstiegspunkte proc_open(), proc_terminate() und register_shutdown_function() sowie den verknüpften Fehler im Zusammenhang mit Kindprozessen. Als erledigt gilt die Aufgabe, wenn der Kindprozess nach dem Schließen der Verbindung nicht weiterläuft und eine Regressionstestabdeckung für den gemeldeten Lebenszyklus vorhanden ist.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
bash, php
Bereich
backend, operating-systems
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Muss geklärt werden
Anfängerfreundlichkeit
25/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.