interactive control of a program using php

2019-02-18 23:23发布

问题:

I would like to run a C program on a remote computer using php. The final goal is to control the program using a web browser on a phone or any other computer.

My C program is acquiring data from different sensors during a few tens of minutes. It runs from command line in linux and I can turn it off by pressing 'q' key on the computer keyboard. The main thread is something like this :

int main(){

    printf("Program is running... Press 'q' <enter> to quit\n");

    fflush(stdout);

    //create one thread per sensor

        while (getchar() != 'q'){
        }

    //ask the threads to terminate  

    return(0);
}

Each thread perform some printf to give the status of each sensor. I would like to monitor these value on my phone and have a button to terminate the remote program.

I can sucessfully monitor the values using system(), open() or proc_open(). It problem is the getchar in the main program. It hang the php script...

<?php

    if(isset($_POST['start'])){

        $descriptorspec = array(
           0 => array("pipe", "r"),  // // stdin est un pipe où le processus va lire
           1 => array("pipe", "w"),  // stdout est un pipe où le processus va écrire
           2 => array("file", "/tmp/error-output.txt", "a") // stderr est un fichier
        );

        $cwd = '/tmp';
        $env = array();

        $process = proc_open('/home/tristan/www/a.out', $descriptorspec, $pipes, $cwd, $env);

        if (is_resource($process)) {

            fwrite($pipes[0], 'q');

            fclose($pipes[0]);

            echo stream_get_contents($pipes[1]);
            fclose($pipes[1]);

            $return_value = proc_close($process);

            echo "La commande a retourné $return_value\n";
        }
    }
?>

Using fwrite($pipes[0], 'q'); works good but the php script hangs if I use fwrite($pipes[0], ''); in order to keep the program running...

EDIT: I invertigated the buffering issue $process = proc_open('stdbuf -i0 /home/tristan/www/a.out', $descriptorspec, $pipes, $cwd, $env); without success...

Does anyone have a clue on how to both monitor values and send command to the program in an interactive way ?

Thanks for you help !

回答1:

There were two problems with the PHP code above which let your program hang:

  • stream_get_contents() will hang forever, waiting for an EOL which never appears as the C code runs in an endless loop. You'll have to use stream_set_blocking() to prevent stream_get_contents() from blocking

  • proc_close() also waits forever unless the process has terminated. Which does never happen because of the endless loop. You'll have to terminate it explicitly using proc_terminate() (which makes not much sense at all imo)

I've prepared an example how the code would not hang, but I need to say, that your code makes not much sense. I would code the C program as a UNIX daemon which listens to a UNIX domain socket. Then you can design a simple text-based communication protocol for your commands. Then connect to the socket in PHP. But maybe I'm wrong as I don't know your application scenario in detail.

However here comes your fixed code:

$descriptorspec = array(
   0 => array("pipe", "r"),  // // stdin est un pipe où le processus va lire
   1 => array("pipe", "w"),  // stdout est un pipe où le processus va écrire
   2 => array("file", "/tmp/error-output.txt", "a") // stderr est un fichier
);

$cwd = '/tmp';
$env = array();

$process = proc_open(__DIR__ . '/a.out', $descriptorspec, $pipes, $cwd, $env);

if (is_resource($process)) {

    fwrite($pipes[0], ' ');
    fclose($pipes[0]);

    // set stream to unblocking mode
    stream_set_blocking($pipes[1], 0);
    // now the following line will not wait forever
    // for an EOF
    echo stream_get_contents($pipes[1]);

    fclose($pipes[1]);

    // proc_close will wait until the process has finished - forever in this case
    // if you want to terminate the process. You'll have to terminate it explicitely
    proc_terminate($process);
    $return_value = proc_close($process);

    // if you want  to have a meaningful return value
    // you'll have to implement a handler for SIGTERM 
    // in your C program
    echo "La commande a retourné $return_value\n";
}


回答2:

I finally got what I wanted using pipes within the system() function :

<?php

if(isset($_POST['start'])){

    system('> /tmp/progOut');
    system('mkfifo /tmp/progIn');

    $proc = popen('./a.out < /tmp/progIn > /tmp/progOut &', 'r');
}
?>