SIGTTOU and switching to canonical mode

Here's an interesting behaviour that, as far as I can tell, is completley undocumented, sightly consfusing but fairly logical. Your program should receive a SIGTTOU when it is running in the background and attempts to output to the terminal -- the idea being that you shouldn't scramble the output by mixing it in while the shell is trying to operate. Here's what the bash manual has to say

Background processes are those whose process group ID differs from the
terminal's; such processes are immune to key- board-generated signals.
Only foreground processes are allowed to read from or write to the
terminal.  Background processes which attempt to read from (write to)
the terminal are sent a SIGTTIN (SIGTTOU) signal by the terminal
driver, which, unless caught, suspends the process.

So, consider the following short program, which writes some output and catches any SIGTTOU's, with an optional flag to switch between canonical and non-canonical mode.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>

static void sig_ttou(int signo) {
   printf("caught SIGTTOU\n");
   signal(SIGTTOU, SIG_DFL);
   kill(getpid(), SIGTTOU);
}

int main(int argc, char *argv[]) {

   signal(SIGTTOU, sig_ttou);

   if (argc != 1) {
      struct termios tty;

      printf("setting non-canoncial mode\n");
      tcgetattr(fileno(stdout), &tty);
      tty.c_lflag &= ~(ICANON);
      tcsetattr(fileno(stdout), TCSANOW, &tty);
   }

   int i = 0;
   while (1) {
      printf("  *** %d ***\n", i++);
      sleep(1);
   }
}

This program ends up operating in an interesting manner.

  1. Run in the background, canonical mode : no SIGTTOU and output gets multiplexed with shell.

    $ ./sigttou &
      *** 0 ***
    [1] 26171
    $   *** 1 ***
      *** 2 ***
      *** 3 ***
    
  2. Run in the background, non-canonical mode : SIGTTOU delivered

    $ ./sigttou 1 &
    [1] 26494
    ianw@jj:/tmp$ setting non-canoncial mode
    caught SIGTTOU
    
    
    [1]+  Stopped                 ./sigttou 1
    
  3. Run in the background, canonical mode, tostop set via stty : SIGTTOU delivered, seemingly after a write proceeds

    $ stty tostop
    $ ./sigttou &
    [2] 26531
    ianw@jj:/tmp$   *** 0 ***
    caught SIGTTOU
    
    
    [2]+  Stopped                 ./sigttou
    

You can see a practical example of this by comparing the difference between cat file & and more file &. The semantics make some sense -- anything switching off canonical mode is like to be going to really scramble your terminal, so it's good to stop it and let it's terminal handling functions run. I'm not sure why canoncial background is considered useful mixed in with your prompt, but someone, somewhere must have decided it was so.

Update: upon further investigation, it is the switching of terminal modes that invokes the SIGTTOU. To follow the logic through more, see the various users of tty_check_change in the tty driver.