enviar comandos a un proceso secundario a través de pipe/dup2 en C

Estoy tratando de escribir un programa de control remoto para omxplayer en mi rasperry Pi

Puedo hacer que omxplayer funcione correctamente en un proceso secundario, pero parece que no puedo hacer que las tuberías funcionen correctamente para enviar comandos al proceso secundario.

int fd_pipe[2];
pipe (fd_pipe);

while(1) {  // main accept() loop

    printf("server: got connection from %s\n", s);

    /* Attempt to fork and check for errors */
    if( (pid=fork()) == -1){
        fprintf(stderr,"Fork error. Exiting.\n");  /* something went wrong */
        exit(1);
    }

    if (pid==0) 
    { // this is the child process
        dup2(0, fd_pipe[0]);
        close(fd_pipe[1]);


        if(execl("/usr/bin/top","top",NULL) == -1){
            fprintf(stderr,"execl Error!");
            exit(1);
        }

        //try and send command here
        write(fd_pipe[0], "q", 1);

        exit(0);

    } else {

        close(new_fd);  // parent doesn't need this
        dup2(1, fd_pipe[1]);

        //try and send here too
        write(fd_pipe[0], "q", 1);
    }
}

Cuando estaba probando con top y ejecuté el programa, puedo ver que aparece la salida superior en la ventana del terminal y puedo ver el comando q en la ventana, pero parece que va al proceso principal en lugar del secundario. ¿Estoy haciendo algo mal con las tuberías o no es posible enviar comandos al proceso secundario generado?

Intenté cambiar la instrucción child dup2 para copiar de la tubería a stdin

        { // this is the child process
        dup2(fd_pipe[0], 0);

Pero luego top falla al comenzar con un mensaje de obtención de tty fallido

preguntado el 27 de julio de 12 a las 15:07

1 Respuestas

Para interactuar con un programa que espera poder manipular una terminal, debe usar un pseudo tty. Esto evitará la failed tty get error.

/*...*/
#include <pty.h>

void do_child () {
    if (execlp("top", "top", (const char *)0) < 0) {
        perror("exec top");
        exit(EXIT_FAILURE);
    }
    /* NOTREACHED */
}

void do_parent (int fd, pid_t p) {
    sleep(5);
    char r;
    write(fd, "q", 1);
    while (read(fd, &r, 1) > 0) { write(1, &r, 1); }
    waitpid(p, 0, 0);
    close(fd);
}

int main () {
    int fd;
    pid_t p = forkpty(&fd, 0, 0, 0);
    switch (p) {
    case 0:  do_child();
             /* NOTREACHED */
    case -1: perror("forkpty");
             exit(EXIT_FAILURE);
    default: break;
    }
    do_parent(fd, p);
    return 0;
}

Tenga en cuenta que forkpty no es POSIX, sino una interfaz que está disponible en versiones BSD y Linux de UNIX.

Puedes ejecutar top en modo por lotes, pero no puede usar un comando para salir. Tienes que matarlo.

void do_child () {
    if (execlp("top", "top", "-b", (const char *)0) < 0) {
        perror("exec top");
        exit(EXIT_FAILURE);
    }
    /* NOT REACHED */
}

void do_parent (pid_t p) {
    sleep(5);
    if (kill(p, SIGINT) < 0) {
        perror("kill");
        exit(EXIT_FAILURE);
    }
    waitpid(p, 0, 0);
}

int main () {
    pid_t p;
    switch ((p = fork())) {
    case 0:  do_child();
    case -1: perror("fork"); exit(EXIT_FAILURE);
    default: break;
    }
    do_parent(p);
    return 0;
}

Aunque la ejecución en modo por lotes le permitiría abrir el proceso con una llamada más simple (como popen como sugiere mux), a menos que esa llamada devuelva la identificación del proceso del niño, no podrá eliminarlo (sin ejecutar un pkill, o busque en la tabla de procesos para encontrar el proceso secundario correcto para eliminar).

Creo que tienes cierta confusión sobre cómo usar dup2. La página del manual utiliza los términos oldfd y newfd, y significa que oldfd se convertirá newfd. Para ilustrar, aquí hay un programa simple que redirige stdout y stderr a un archivo de registro, llama a una función y luego restaura stdout y stderr después.

void do_something () {
    fputs("Error message\n", stderr);
    puts("This is regular output.");
    fputs("Error message\n", stderr);
    puts("This is regular output.");
}

int main () {
    int fd = creat("/tmp/output.log", 0664);
    int outfd = dup(fileno(stdout));
    int errfd = dup(fileno(stderr));
    fflush(stdout);
    fflush(stderr);
    dup2(fd, fileno(stdout));
    dup2(fd, fileno(stderr));
    setlinebuf(stdout);

    do_something();

    fflush(stdout);
    fflush(stderr);
    dup2(outfd, fileno(stdout));
    dup2(errfd, fileno(stderr));
    close(outfd);
    close(errfd);

    fputs("Error to the screen\n", stderr);
    puts("Regular output to screen");
    fputs("Error to the screen\n", stderr);
    puts("Regular output to screen");

    return 0;
}

Como señaló mux, su programa está escribiendo en el extremo equivocado de la tubería. Él pipe El comando devuelve un par unidireccional de descriptores de archivo, donde lo que se escribe en fd_pipe[1] se puede leer desde fd_pipe[0].

Respondido 31 Jul 12, 15:07

Gracias, usuario315052 y @mux. Vaya, quiero decir que estaba escribiendo a fd_pipe[1]. Estoy en la cima como un ejemplo en lugar del requisito final. Estoy tratando de usarlo para ejecutar un reproductor multimedia llamado omxplayer en un proceso secundario y luego poder enviar comandos a ese proceso. Eliminé la escritura del proceso secundario y solo tengo write(fd_pipe[1], "q", 1); - Karl

@Karl: Entonces, ¿no intentaste usar la solución pseudo tty? ¿Por qué no? - jxh

Lo intento, sin embargo, tengo problemas para encontrar la biblioteca. Tengo pty.h instalado, pero cuando intento compilar obtengo una referencia indefinida a los errores `forkpty'. Así que estoy tratando de vincular en -l/usr/lib/libutils pero está informando: /usr/bin/ld: no se puede encontrar -l/usr/lib/libutils - Karl

@Karl: Añadir -lutil para vincular int libutils - jxh

urg, pensé que había intentado -lutil pero obviamente no. Se está compilando ahora. Sin embargo, el comando q todavía no parece estar registrándose. Puedo ver que se inicia el proceso superior (aunque ahora no se repite en la terminal principal, lo cual no es importante), pero no se detiene cuando escribo en el fd. se cerrará cuando cierre el fd, pero creo que se debe a que el pty está cerrado en lugar del comando. Lo confirmé enviando un comando diferente y aún se cierra al cerrar (fd). Intenté su ejemplo anterior y no estoy seguro de que funcione correctamente, ya que top no se inicia hasta después de que el padre durmiente: Karl

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.