Question

I am trying to write a remote control program for omxplayer on my rasperry Pi

I can get omxplayer to run ok in a child process but I don't seem to be able to get the pipes working correctly to actually send commands to the child process.

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);
    }
}

When I was testing with top and ran the program, I can see the top output appear in the terminal window and I can see the q command in the window but it looks like its going to the parent process rather than the child. Am I doing something wrong with the pipes or is it not possible to send commands to the spawned child process?

I tried changing the child dup2 statement to copy from the pipe to stdin

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

But then top fails to start with a failed tty get message

Was it helpful?

Solution

To interact with a program that expects to be able to manipulate a terminal, you should use a pseudo tty. This will avoid the 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;
}

Note that forkpty is not POSIX, but an interface that is available on BSD and Linux flavors of UNIX.

You can execute top in a batch mode, but you can't use a command to quit. You have to kill it.

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;
}

Although running in batch mode would allow you to open the process with a simpler call (such as popen as mux suggests), unless that call returns the process id of the child, you won't be able to kill it (without executing a pkill, or dig through the process table to find the right child process to kill).

I think you have some confusion about how to use dup2. The manual page uses the terms oldfd and newfd, and it means that oldfd will become newfd. To illustrate, here is a simple program that redirects stdout and stderr to a log file, calls a function, and then restores stdout and stderr afterward.

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;
}

As mux pointed out, your program is writing to the wrong end of the pipe. The pipe command returns a unidirectional pair of file descriptors, where what ever is written to fd_pipe[1] can be read from fd_pipe[0].

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top