1

I was trying to build an interactive shell which runs within the ZSH shell. Any command would execute in it's own process group (thus I use tcsetpgrp call to make it the foreground process group, and once the command is executed the the interactive shell would become the foreground process group). However I observe that during the execution of my code I get a SIGTTOU signal, which to the best of my knowledge is delivered when a background process attempts to write to the terminal. With respect to the below example if someone could explain the reason for the aforementioned signal it would be really helpful.

Minimal reproducible example

#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
    for(;;){
        printf(">Network shell\n");   
        int par = getpid();
        int child = fork();
        if(child == 0){
            int c2 = fork();
            if(c2 == 0){
                char * args[2];
                args[0]="/bin/ls";
                args[1] = NULL;
                execv(args[0], args);
            }
            else{
                wait(NULL);
                tcsetpgrp(STDIN_FILENO, par); //done to bring previous process group back to foreground
                tcsetpgrp(STDOUT_FILENO, par);
            }


        }
        else{
            setpgid(child, child);
            tcsetpgrp(STDIN_FILENO, child);
            tcsetpgrp(STDOUT_FILENO, child);

            wait(NULL);
        }


    }

}

Here is the output I see on my terminal: enter image description here

1 Answers1

0

Your /bin/ls writes to stdout, and since its stdout is not redirected, it writes to the terminal.

user1934428
  • 19,864
  • 7
  • 42
  • 87
  • `/bin/ls` writes to stdout but currently the process executing the command is part of the foreground process group hence I thought it shouldn't be raising the SIGTTOU signal. – ANIRUDH BUVANESH Mar 15 '21 at 08:34
  • After the "first" fork, you are in the child process of the original "main" process, and after the second fork, you are in the child process of that former process. You have two processes (the two `else` blocks, i.e. the original parent, and the first child), which both want to take over the terminal with `tcsetpgrp`. – user1934428 Mar 15 '21 at 08:54
  • @ANIRUDHBUVANESH : As for the signal, I think that the `else` block belonging to the `c2` case is running as child process of the original main process, and therefore in the background. But when a background process is doing a `tcsetpgrp`, this causes a SIGTTOU to be sent to the other background processes. – user1934428 Mar 15 '21 at 08:56
  • The `tcsetpgrp` after the first fork makes the newly created process group as foreground and the `tcsetpgrp` after the second fork bringd back the original process group to the foreground. Are you hinting towards sone synchronization issue? Could you please elaborate why you think that the `else` block in c2 is running as a child process of main process, isn't it running in it running as a group leader in the new process group – ANIRUDH BUVANESH Mar 15 '21 at 08:59
  • After the first fork, child is 0 in the child process; the `else` block is still the main process. Hence, the c2 fork itself is therefore running inside the first child process and contains a new child (the c2==0 branch). The c2-child is not running as a direct child of the main process, but its grand child. – user1934428 Mar 15 '21 at 09:17
  • Shouldn't the `setpgid(child, child)` call in the `else` block move the 1st child to a new process group and thus the subsequent created via the `c2` fork be the child of the 1st child in the new process group? – ANIRUDH BUVANESH Mar 15 '21 at 09:42
  • @ANIRUDHBUVANESH : First of all, you have not control over the time at which this `setpgid` is executed. The child process could have then already proceeded arbitrarily by then. Further, I find it confusing, that you pass the same PID for both parameters. The second one should be the PID of the process group, into which the first PID is supposed to be shifted. I would have expected something like `setpgid(child,0)`. Finally, you don't test the return value of setpgid, which is also a bit risky. – user1934428 Mar 15 '21 at 12:29