php / php/php-src

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

Ouverte
#8,242 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Bug Status: Needs Triage
Langage dominant
C
Étoiles
40.4k
Forks
8.2k
Merge moyen
2 j 13 h
PR mergées (30 j)
96

Description

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

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Aucun fichier du dépôt ni aucun test n’est identifié. Commencez par reproduire la déconnexion SSE avec PHP 7.4.3 sur Ubuntu 20.04, puis lisez les points d’entrée proc_open(), proc_terminate() et register_shutdown_function(), ainsi que le bug lié au processus enfant indiqué. Le travail est terminé lorsque le processus enfant ne continue pas à s’exécuter après la fermeture de la connexion et qu’une couverture de régression est ajoutée pour le cycle de vie signalé.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
bash, php
Domaine
backend, operating-systems
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
À l'abandon
Clarté
À clarifier
Accessibilité débutants
25/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.