0

I'm working on my own little shell program in C. When I run the child process as a background process, I would like to ignore the input from the user coming from the parent process. I am currently trying to pipe it then close stdin for the child, but the input still goes to the child.

    else // A process
    {
        pid_t child_pid;
        char lastArgument = args[currArgsIndex-1][0];
        if (lastArgument != '&'){ //Normal process
            if((child_pid = fork()) == 0) {
                execvp(filepath, args);
                exit(0);
            }
            else
            {
                while(wait(NULL) != child_pid);
            }
        }
        else { // Background
            args[currArgsIndex-1] = NULL; 
            int process_pipe[2];
            pipe(process_pipe); // Piping
            if((child_pid = fork()) == 0) {
                close(process_pipe[0]); // Ignore stdin for child
                execvp(filepath, args);
                exit(0);
            }
        }
    }

1 Answers1

0

You create a pipe and close the read end, but you never say that the pipe should be stdin.

It sounds like your intention was instead to 1. open the pipe only in the child, 2. close the write end so that no data can be read, 3. set the read end as stdin:

    else { // Background
        args[currArgsIndex-1] = NULL; 
        if((child_pid = fork()) == 0) {
            int process_pipe[2];
            pipe(process_pipe); // Piping
            dup2(process_pipe[0], 0); // Copy read end as stdin
            close(process_pipe[0]);   // Close FD that is now unused
            close(process_pipe[1]);   // Close write end so no data can be read
            execvp(filepath, args);
            perror("execvp failed");
            exit(1); // exit with error 
        }
    }

There's no point having a pipe though. You can more easily open /dev/null for reading and setting that as stdin. Alternatively, simply close stdin entirely (some programs will complain):

    else { // Background
        args[currArgsIndex-1] = NULL; 
        if((child_pid = fork()) == 0) {
            close(0); // Close stdin
            execvp(filepath, args);
            /* error handling */
    }

Be aware that real shells allow redirecting to backgrounded processes, in which case none of the above will work:

wc -l < myfile &

Real shells will in fact not close or redirect stdin at all, but will put the command in its own process group that's not controlling the terminal. The process will then receive a SIGTSTP when it tries to read from stdin, and you can then use fg to bring it to the foreground to start typing data.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Right now I was just trying to get the parent to not affect the child when run in the background. The next step would be redirection, which is why I wanted to use pipes. Would would be so kind to add an example with redirection? What would be the best logic if no redirection character is found when parsing? Just another conditional that simply does a ```close(0)``` like you described? – ProgrammingRookie Jan 30 '20 at 22:24
  • Redirection from files does not involve pipes. See [this question](https://askubuntu.com/questions/1120556/how-input-redirection-works) for a longer description of how redirection works. – that other guy Jan 30 '20 at 22:31
  • For either one of your answers, when running a python script on the background that takes input for testing I get ```IOError: [Errno 9] Bad file descriptor```. – ProgrammingRookie Jan 30 '20 at 23:08
  • This happens because `stdin` was entirely closed (as mentioned, some programs will complain about this). If you have correctly set `stdin` to the read end of a pipe whose write end was closed (or to `/dev/null`), then Python will not say this. I don't know what exactly you expect to happen though, since there is no data to be read. Are you hoping to see the same behavior as e.g. Bash where it's stopped and you can resume it with `fg` to enter data? – that other guy Jan 30 '20 at 23:18
  • You are correct. Maybe I've been going about it the wrong way. If I reattach to the child process I do wanna be able to pass input to it as well! If I do dup2 to either ```/dev/null``` or my own pipe I get ```EOFError: EOF when reading a line```. But yes, I actually don't want the child to not read input anymore. I just don't want the input I enter in the terminal that is running my shell to be sent to the child process if I try running it on the background. – ProgrammingRookie Jan 30 '20 at 23:44
  • There's an [Implementing a Shell](https://www.gnu.org/software/libc/manual/html_node/Launching-Jobs.html#Launching-Jobs) part of the GNU libc documentation that describes how Bash and other shells accomplish this – that other guy Jan 30 '20 at 23:53