0

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).

user1016031
  • 123
  • 1
  • 7
  • 2
    On an (possible) unrelated note, all three standard descriptors are numbered `0`, `1` and `2`. So your loop could skip the condition if it just started from descriptor `3` instead of `0`. – Some programmer dude Sep 28 '22 at 10:34
  • 3
    When you say that "anything that was using STDIN no longer works", what do you mean by that? *How* doesn't it work? What happens when you attempt to do some input? What is supposed to happen? What is the command you attempt to execute? Can you please try to create a [mre] to show us? Also please take some time to refresh [the help pages](http://stackoverflow.com/help), take the SO [tour], read [ask], as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). – Some programmer dude Sep 28 '22 at 10:38
  • It's difficult to provide a MRE, but I can try and provide some more information. The execvp() process is a logger program that splits up logs from STDIN and outputs them to various other logs (a splitlogs type program) for webserver logs. When I take rfd out and use it in a pipe, no logs are written to disk, and it appears no data is being read from stdin. – user1016031 Sep 28 '22 at 11:08
  • Can you enable some debugging output from the program you `exec`? Like adding a set of `printf` calls at important points to see what's happening? Or perhaps even attach a debugger to be able to set breakpoints and step through functions? – Some programmer dude Sep 28 '22 at 11:13
  • 1
    Please [edit] your question and add all requested information or clarification to the question. Don't use comments for this purpose. Describe your use case: What are you trying to achieve with your set of programs? Where does the input for the "splitlogs type program" come from? Where does its output go? What exactly do you want to happen if the parent dies? – Bodo Sep 28 '22 at 13:21
  • I have been trying to do that. No biggie. I'll just try and do it a different way. – user1016031 Sep 28 '22 at 23:52

1 Answers1

0

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157