So I am trying to create a basic terminal, and my problem is this: if I use the following code to execute most "normal" terminal commands (e.g. ls
, cat
, etc), there are no issues. It sets the process group and works perfectly. However, when I try to execute a command such as vim .
, it appears that the process immediately stops. If I remove the call to setpgrp()
, the command works as expected (and executes with the pgid
of the controlling terminal).
Here is my code:
pid_t normal_cmd(char **argv, int bg) {
int pid = fork(), status;
if(pid < 0)
unix_error("Could not fork child process");
else if(!pid) { //child (this is the problematic area)
setpgrp();
status = execvp(argv[0], argv);
if(status < 0)
unix_error("Could not exec child process");
}
else { //parent
if(!bg) {
addjob(jobs, pid, FG);
pause(); //waitpid() is in SIGCHLD handler
struct job_t *cj = getjobpid(jobs, pid);
if(cj && cj->state != ST)
deletejob(jobs, pid);
}
else
addjob(jobs, pid, BG);
}
return pid;
}
Any idea why changing the process group would cause vim to fail here?
vim
will try to read from the controlling terminal, and because its process group is not the foreground process group, it will receive a SIGTTIN signal which suspends it. ls
and other "normal" commands aren't suspended because they don't read from stdin (which is the terminal in this case).
The setpgrp() call has the effect of creating a new process group with the calling process as its leader -- and the new process group is not the foreground process group on the terminal until you make it so with tcsetpgrp() or ioctl(TIOCSPGRP).
You can read more about job control here. Especially this, which explains why a program in the background receives a SIGTTIN if it tries to read from the the tty, but not a SIGTTOU if it tries to write to it.