Reputation: 39
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
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