SIGQUIT and STDIN (unexpected dump of read function)

Viewed 192

i got problem with stdin and sigquit signal handling. The task is to ignore sigquit signal. Everything is fine, except all 'read' buffer is gone, and i don't know how to avoid it . Here's my code:

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <strings.h>

int main(void)
{
    char buffer[4096];
    int byte;

    bzero(buffer, 4096);
    signal(SIGQUIT, SIG_IGN);
    while ((byte = read(0, &buffer, 4096)))
    {
        if (strlen(buffer) == 5 && !strncmp(buffer, "exit", 4))
            break ;
        write(1, "STDOUT | ", strlen("STDOUT | "));
        write(1, buffer, strlen(buffer));
        bzero(buffer, 4096);
    }
    write(1, "Program is finished\n", strlen("Program is finished\n"));
    return (0);
}

Here's my output:

$ ./test
hello world
STDOUT | hello world
hello world^\
STDOUT |
The way it should be
STDOUT | The way it should be
And how it is now^\
STDOUT |
i need help
STDOUT | i need help
really^\
STDOUT |
exit
Program is finished

OS: MacOS Mojave - 10.14.6 Clang verison: 11.0.1

2 Answers

This is not a bug in your code. You have correctly ignored SIGQUIT, and your read-write loop is mostly fine (the only actual bug I see is that you're assigning the return value of read to a variable of the wrong type, and then not paying enough attention to it; but that's not the cause of your immediate problem).

What's going on is, when you type ^\, the tty driver generates a SIGQUIT and also discards any pending line of input. This behavior is specified by POSIX but only by implication: the definition of the NOFLSH local-mode flag reads

If NOFLSH is set, the normal flush of the input and output queues associated with the INTR, QUIT, and SUSP characters shall not be done.

(Open Group Base Specifications Issue 7, 2018 edition, §11.2.5 Local Modes.)

So if NOFLSH is not set, which is the default, then both the input and output queues are to be flushed (contents discarded) upon processing ^C, ^\, and ^Z.

You can set the NOFLSH flag with tcsetattr, but you have to take care to unset it again on exit. I recommend using the readline or curses library instead of trying to wrangle the low-level tty API yourself.

You tty layer is flushing your input when you hit ^C (intr), ^Z (susp) and ^\ (quit).

The solution is to call stty noflsh before running your program.

If you need to do it programmatically, see the NOFLSH flag in this termios manual page

Related