Search code examples
shellforkc99

Background process in my own shell program to ignore stdin


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

Solution

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