¿Puede popen () hacer tubos bidireccionales como pipe () + fork ()?

Estoy implementando tuberías en un sistema de archivos simulado en C ++ (principalmente con C). Necesita ejecutar comandos en el shell del host pero realizar la propia tubería en el sistema de archivos simulado.

Podría lograr esto con las llamadas al sistema pipe() , fork() y system() , pero preferiría usar popen() (que maneja la creación de una tubería, bifurca un proceso y pasa un comando al shell) . Esto puede no ser posible porque (creo) necesito poder escribir desde el proceso principal de la tubería, leer en el extremo del proceso secundario, escribir la salida desde el elemento secundario y, finalmente, leer esa salida del elemento primario. La página de popen() para popen() en mi sistema dice que es posible una popen() bidireccional, pero mi código necesita ejecutarse en un sistema con una versión anterior que solo admite canalizaciones unidireccionales.

Con las llamadas separadas anteriores, puedo abrir / cerrar tuberías para lograr esto. ¿Es eso posible con popen() ?

Para un ejemplo trivial, ejecutar ls -l | grep .txt | grep cmds ls -l | grep .txt | grep cmds ls -l | grep .txt | grep cmds Necesito:

  • Abra un conducto y procese para ejecutar ls -l en el host; leer su salida de vuelta
  • Transmite la salida de ls -l a mi simulador
  • Abra una tubería y procese para ejecutar grep .txt en el host en la salida canalizada de ls -l
  • Transmite la salida de esta vuelta al simulador (pegado aquí)
  • Abra una tubería y procese para ejecutar grep cmds en el host en la salida canalizada de grep .txt
  • Transfiera la salida de esta al simulador e imprímala

hombre popen

Desde Mac OS X:

La función popen() ‘abre’ un proceso creando un conducto bidireccional, bifurcando e invocando el shell. Todos los flujos abiertos por llamadas de popen() anteriores en el proceso principal se cierran en el nuevo proceso hijo. Históricamente, popen() se implementó con una tubería unidireccional; por lo tanto, muchas implementaciones de popen() solo permiten que el argumento de modo especifique lectura o escritura, no ambas. Como ahora popen() se implementa utilizando un conducto bidireccional, el argumento de modo puede solicitar un flujo de datos bidireccional. El argumento de modo es un puntero a una cadena terminada en nulo que debe ser ‘r’ para leer, ‘w’ para escribir o ‘r +’ para leer y escribir.

    Parece que has respondido tu propia pregunta. Si su código necesita funcionar en un sistema anterior que no es compatible con popen abriendo popen bidireccionales, entonces no podrá usar popen (al menos no el que se suministra).

    La verdadera pregunta sería acerca de las capacidades exactas de los sistemas más antiguos en cuestión. En particular, ¿sus pipe admiten la creación de tuberías bidireccionales? Si tienen una pipe que puede crear una tubería bidireccional, pero popen que no, entonces escribiría la stream principal del código para usar popen con una tubería bidireccional, y suministraría una implementación de popen que puede usar una tubería bidireccional que se comstack en un usado donde sea necesario.

    Si necesita admitir sistemas lo suficientemente antiguos como para que la pipe solo admita tubos unidireccionales, entonces está prácticamente atascado con el uso de pipe , dup2 , dup2 , etc., por su cuenta. Probablemente aún termine esto en una función que funciona casi como una versión moderna de popen , pero en lugar de devolver un identificador de archivo, rellena una pequeña estructura con dos identificadores de archivo, uno para el stdin del niño y el otro para el del niño stdout .

    Sugeriría que escribas tu propia función para hacer la tubería / bifurcación / sistema para ti. Puede hacer que la función genere un proceso y devuelva los descriptores de archivos de lectura / escritura, como en …

     typedef void pfunc_t (int rfd, int wfd); pid_t pcreate(int fds[2], pfunc_t pfunc) { /* Spawn a process from pfunc, returning it's pid. The fds array passed will * be filled with two descriptors: fds[0] will read from the child process, * and fds[1] will write to it. * Similarly, the child process will receive a reading/writing fd set (in * that same order) as arguments. */ pid_t pid; int pipes[4]; /* Warning: I'm not handling possible errors in pipe/fork */ pipe(&pipes[0]); /* Parent read/child write pipe */ pipe(&pipes[2]); /* Child read/parent write pipe */ if ((pid = fork()) > 0) { /* Parent process */ fds[0] = pipes[0]; fds[1] = pipes[3]; close(pipes[1]); close(pipes[2]); return pid; } else { close(pipes[0]); close(pipes[3]); pfunc(pipes[2], pipes[1]); exit(0); } return -1; /* ? */ } 

    Puede agregar cualquier funcionalidad que necesite allí.

    POSIX estipula que la llamada a popen() no está diseñada para proporcionar comunicación bidireccional:

    El argumento de modo para popen () es una cadena que especifica el modo de E / S:

    1. Si el modo es r, cuando se inicia el proceso secundario, su descriptor de archivo STDOUT_FILENO será el final escribible del conducto y el descriptor de archivo fileno (flujo) en el proceso de llamada, donde stream es el puntero de flujo devuelto por popen (), será el final legible de la tubería.
    2. Si el modo es w, cuando se inicia el proceso secundario, su descriptor de archivo STDIN_FILENO será el extremo legible del conducto, y el descriptor de archivo fileno (flujo) en el proceso de llamada, donde stream es el puntero de flujo devuelto por popen (), ser el final escribible de la tubería.
    3. Si el modo es cualquier otro valor, el resultado no se especifica.

    Cualquier código portátil no hará suposiciones más allá de eso. El BSD popen() es similar a lo que describe su pregunta.

    Además, las tuberías son diferentes de las tomas y cada descriptor de archivo de tubería es unidireccional. Tendría que crear dos tuberías, una configurada para cada dirección.

    No es necesario crear dos tubos y perder un descriptor de archivo en cada proceso. Solo use un socket en su lugar. https://stackoverflow.com/a/25177958/894520

    En uno de los backends netresolve estoy hablando con un script y, por lo tanto, necesito escribir en su stdin y leer desde su stdout . La siguiente función ejecuta un comando con stdin y stdout redirigidos a un conducto. Puedes usarlo y adaptarlo a tu gusto.

     static bool start_subprocess(char *const command[], int *pid, int *infd, int *outfd) { int p1[2], p2[2]; if (!pid || !infd || !outfd) return false; if (pipe(p1) == -1) goto err_pipe1; if (pipe(p2) == -1) goto err_pipe2; if ((*pid = fork()) == -1) goto err_fork; if (*pid) { /* Parent process. */ *infd = p1[1]; *outfd = p2[0]; close(p1[0]); close(p2[1]); return true; } else { /* Child process. */ dup2(p1[0], 0); dup2(p2[1], 1); close(p1[0]); close(p1[1]); close(p2[0]); close(p2[1]); execvp(*command, command); /* Error occured. */ fprintf(stderr, "error running %s: %s", *command, strerror(errno)); abort(); } err_fork: close(p2[1]); close(p2[0]); err_pipe2: close(p1[1]); close(p1[0]); err_pipe1: return false; } 

    https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#L46

    (Utilicé el mismo código en popen lectura y escritura simultáneas )