asosat
asosat

Reputation: 39

Process Forks Shell, Then Entire Program Is Stopped?

I have a program copied from the man page for pipe(2), modified so that the child forks off a shell, and the parent reads from stdin, sending the commands to the child shell to be executed. This works for the first command, but after that command is run, I get a message that the program has stopped and I'm returned to the top-level shell.

Why does that happen, and how can I stop it?

signal(7) says:

   SIGTTIN      P1990      Stop    Terminal input for background process

But it's not supposed to be a background process, so why is it stopping?

What follows is a terminal session with the full source code, the result of gcc -Wall, and trying to run the program. As you can see, I give the command "date", then I'm sent back to the invoking shell, then the child shell executes "date" and the output appears, then an "exit" appears. "fg" gets me back into the pipe_shell program, "uptime" is sent to the shell and run, then I can do an "echo", and I get sent back to the top-level shell and "exit" appears again.

sh-5.0$ cat pipe_shell.c
/* pipe_shell.c - learn about pipes
 *
 * Modified from the man page for pipe(2).
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int   pipefd[2];
    pid_t cpid;

    if (argc != 1) {
        fprintf(stderr, "Usage: %s\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (cpid == 0) { // Child reads from pipe

        close(pipefd[1]);              // Close unused write end of pipe
        close(STDIN_FILENO);           // shut off standard input
        dup2(pipefd[0], STDIN_FILENO); // pipe output is standard input
        close(pipefd[0]);              // close pipe output

        execl("/bin/bash", "bash",
              "--norc",
              "-i",
              (char *) 0);

        perror("exec");

    } else { // Parent writes to pipe
        close(pipefd[0]);    // Close unused read end

        char line[256];      // we take input from keyboard
        while ( fgets(line, 255, stdin) != NULL ) {
            write(pipefd[1], line, strlen(line));
        }

        close(pipefd[1]);    // Reader will see EOF
        wait(NULL);          // Wait for child
        exit(EXIT_SUCCESS);
    }
}
sh-5.0$ gcc -Wall pipe_shell.c -o pipe_shell
sh-5.0$ ./pipe_shell
bash-5.0$ date

date
[1]+  Stopped(SIGTTIN)        ./pipe_shell
sh-5.0$ Wed 10 Jun 2020 12:16:28 PM EDT
bash-5.0$ exit
There are stopped jobs.
sh-5.0$ fg
./pipe_shell
uptime
uptime
 12:16:52 up 154 days, 23:32,  5 users,  load average: 0.04, 0.05, 0.07
bash-5.0$ echo "foo"

[1]+  Stopped(SIGTTIN)        ./pipe_shell
echo "foo"
sh-5.0$ foo
bash-5.0$ exit
exit

Upvotes: 1

Views: 402

Answers (1)

Milag
Milag

Reputation: 1986

Try running your program without the -i option for bash, though you won't see a prompt.

Oversimplified, SIGTTIN issues relate to read(), a control tty, and process groups. For details, a decent starting point is google.com with search terms: linux SIGTTIN; there are some articles that delve into read / tty / pgrp.

To run an interactive shell similar to your small program and possibly resolve an issue with your large program, you could make use of master/slave pseudo devices. There's a related lib service forkpty(). Also, there's a UNIX/linux utility named script that records an interactive shell session to a regular file; the source code for script is a good example of reader + writer processes, master/slave pseudo devices, and an interactive shell.

Upvotes: 1

Related Questions