3

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 !

Tristan
  • 31
  • 5
  • 1
    The question is interesting but vague. Show a little bit more C code and show the PHP code – hek2mgl Jul 04 '13 at 10:53
  • Yes. I respect people who still program in C. Its a powerful programming language with which we can actually access registers. Hope you succeed –  Jul 04 '13 at 10:56
  • 2
    If you structure your program to run as a daemon and provide a command line tool to access it, then you'll probably find it a lot simpler to do. Check out how linux does its services: `$ service {service-name} restart`, `$ service {service-name} stop` and `$ service {service-name} start` (http://www.cyberciti.biz/tips/how-to-controlling-access-to-linux-services.html) – Tom Jul 04 '13 at 11:01
  • 1
    You should do as Tom mentioned; you can check this out for how to create a daemon (http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html) – Vannen Jul 04 '13 at 11:06
  • Perhaps [this might help](http://stackoverflow.com/questions/10102952/proc-open-interaction) – Elias Van Ootegem Jul 04 '13 at 11:12
  • Just a suggestion: If the only interaction that you need is to retrieve info and allow terminate the program on request then I would suggest you have your C code write the output to a file, or better a database (MySQL, SQLite) and just use a SIGTERM if you wish to shut it down. – Sergey L. Jul 04 '13 at 11:23
  • @hek2mgl, I edited my post to add the php script. The c program is too big I cannot post it here but the simple code provided does the job. – Tristan Jul 04 '13 at 11:40
  • @Tom and Vannen: Thanks guys I will investigate this – Tristan Jul 04 '13 at 11:42
  • @Tristan I would do it like Tom suggested. I'm working on a similar project at the moment. I use a daemon which listens to a UNIX domain socket. The php client communicates with the daemon over this socket – hek2mgl Jul 04 '13 at 11:45
  • @Tristan While I think the daemon solution is best, your code should work too. I guess what you are seeing is a buffering problem with the underlying libc. stdin will be buffered. try to call the program like `stdbuf -i0 a.out` or use `flush()` on the pipe descriptor – hek2mgl Jul 04 '13 at 11:47
  • +1 for `fflush(stdout)` ;) But I see it isn't working anyway. – hek2mgl Jul 04 '13 at 11:59

2 Answers2

2

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";
}
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
0

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');
}
?>
Tristan
  • 31
  • 5