cpipepopenintel-edison

How to write and read at the same file using "popen" in C


I'm using Intel Edison and SensorTag. In order to get temperature data via BLE, there are a bunch of commands. When I define popen as:

popen(command,"w"); 

code works fine most of the times. (Crashes other times due to delay issues I assume as I don't control the responses.)

However, when I want to control the command/console responses (such as step into next line when bluetooth connection is established and if not try to connect again etc.), I cannot read the responses. My "data" variable is not changed.

I also tried other modes of "popen" but they give run-time errors.

Here is the code I'm using:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int endsWith (char* base, char* str) {
    int blen = strlen(base);
    int slen = strlen(str);
    return (blen >= slen) && (0 == strcmp(base + blen - slen, str));
}

FILE* get_popen(char* command, int close, int block) {
    FILE *pf;
    char data[512];

    // Setup our pipe for reading and execute our command.
    pf = popen(command,"w");

    // Error handling

    if (block == 1) {

        // Get the data from the process execution
        char* result;
        do {
            result=fgets(data, 512 , stderr);
            if (result != NULL) {
                  printf("Data is [%s]\n", data);
            }
        } while (result != NULL);

        // the data is now in 'data'
    }
    if (close != 0) {
        if (pclose(pf) != 0)
            fprintf(stderr," Error: Failed to close command stream \n");
    }

    return pf;
}

FILE* command_cont_exe(FILE* pf, char* command, int close, int block) {
    char data[512];

    // Error handling
    if (pf == NULL) {
        // print error
        return NULL;
    }

    fwrite(command, 1, strlen(command), pf);
    fwrite("\r\n", 1, 2, pf);

    if (block == 1) {

        // Get the data from the process execution
        char* result;
        do {
            result=fgets(data, 512 , stderr);
            if (result != NULL) {
                  printf("Data is [%s]\n", data);
            }
        } while (result != NULL);//
    }
    // the data is now in 'data'

    if (close != 0) {
            if (pclose(pf) != 0)
                fprintf(stderr," Error: Failed to close command stream \n");
    }

    return pf;
}


int main()
{
    char command[50];

    sprintf(command, "rfkill unblock bluetooth");
    get_popen(command, 1, 0);
    printf("Working...(rfkill)\n");
    sleep(2);

    sprintf(command, "bluetoothctl 2>&1");
    FILE* pf = get_popen(command, 0, 1);
    printf("Working...(BT CTRL)\n");
    sleep(3);

    sprintf(command, "agent KeyboardDisplay");
    command_cont_exe(pf, command, 0, 1);
    printf("Working...(Agent)\n");
    sleep(3);
    //Main continues...

Solution

  • You cannot do this with popen, but can build a program using fork, exec and pipe. The last opens two file descriptors, which are related: the parent's connection to a pipe, and the child's connection. To make a two-way connection to a child process, you must use two calls to pipe.

    The file-descriptors opened by pipe are not buffered, so you would use read and write to communicate with the child (rather than fgets and fprintf).

    For examples and discussion, see