Home > Enterprise >  Best way to use pipes() within a fork with a fd passed as an arg?
Best way to use pipes() within a fork with a fd passed as an arg?

Time:09-29

I'm trying to Impliment a pipe trick for my function, but I'm kind of at a loss of how.

Here's some example code I'm working with - you'll get the idea as it is psuedo code.

int do_command_exec (fd_t rfd, command_t *command)
pid_t pid; 
fd_t fd;

pid = fork();
if (pid == 0) {
    dup2(rfd, STDIN_FILENO);
    for (fd = 0; fd < NOFILE; fd  ) {
        if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) {
           close(fd);
        }
    }
 }
        memset(cmd, 0, sizeof(cmd));
        memcpy(cmd, command->data, command->len);
        argv[0] = SHELL_CMD;
        argv[1] = "-c";
        argv[2] = cmd;
        argv[3] = NULL;
        execvp(argv[0], argv);
        exit(1);
else
.. parent... 

Now, when I try and implement it using rfd for a pipe, as you expect, my program does not function correctly with STDIN (ie: no logs)

    int pipes[2];
    pipe(pipes);
    pid = fork();
    if (pid == 0) /* Child */ 
    close(pipes[1]); 
    dup2(pipes[0], STDIN_FILENO); 
    for (fd = 0; fd < NOFILE; fd  ) {
        if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) {
           close(fd);
        }
    }
 }
        memset(cmd, 0, sizeof(cmd));
        memcpy(cmd, command->data, command->len);
        argv[0] = SHELL_CMD;
        argv[1] = "-c";
        argv[2] = cmd;
        argv[3] = NULL;
        execvp(argv[0], argv);
        exit(1);
else /* Parent */ 
close(pipes[0]);

Now the good news is, my program is now existing like I would like (ie: EOF is seen, and the children quit when the parent process dies for any reason, but anything that was using STDIN no longer works).

CodePudding user response:

Now, when I try and implement it using rfd for a pipe, as you expect, my program does not function correctly with STDIN (ie: no logs)

Each process inherits its standard input stream from its parent process. If you redirect the child process's standard input to be from the pipe, then its child, the program you ultimately want to control, will also have that pipe as its standard input. It will not receive input from the original program's standard input. The answer you are referencing does not anticipate that the child process being controlled will attempt to do that.

But there's good news: the shell can read from arbitrary file descriptors. It's merely a minor convenience that the example code presented in that answer uses its standard input to monitor the parent. Even better, it would be approximately as easy to do substantially the same thing directly, without involving the shell. In the same spirit as the pseudocode presented in the question, that would look something like this:

    // parent
    int pipe_fds[2];
    pipe(pipe_fds);
    pid_t monitor_pid = fork();

    // in parent
    ...

    // in child 1 (monitor)
    close(pipe_fds[1]);
    pid_t child_pid = fork();

    // in child 1 (monitor)
    char c;
    read(pipe_fds[0], &c, 1);
    kill(child_pid, SIG_TERM);
    exit(0);

    // in child 2 (child)
    close(pipe_fds[0]);
    execlp("/the/command", "the_command", "arg1", "arg2", (char *) NULL);
    _exit(1);

Of course, a real implementation needs to provide the flow control to make the "in parent", "in child 1", and "in child 2" comments correct, and it must implement proper checking of return values and error handling, among other things.

  •  Tags:  
  • c
  • Related