views:

331

answers:

3

I have a small app written in C designed to run on Linux. Part of the app accepts user-input from the keyboard, and it uses non-canonical terminal mode so that it can respond to each keystroke.

The section of code that accepts input is a simple function which is called repeatedly in a loop:

char get_input()
{
 char c = 0;
 int res = read(input_terminal, &c, 1);
 if (res == 0) return 0;
 if (res == -1) { /* snip error handling */ }
 return c;
}

This reads a single character from the terminal. If no input is received within a certain timeframe, (specified by the c_cc[VTIME] value in the termios struct), read() returns 0, and get_input() is called again.

This all works great, except I recently discovered that if you run this app in a terminal window, and then close the terminal window without terminating the app, the app does not exit but launches into a CPU intensive infinite loop, where read() continuously returns 0 without waiting.

So how can I have the app exit gracefully if it is run from a terminal window, and then the terminal window is closed? The problem is that read() never returns -1, so the error condition is indistinguishable from a normal case where read() returns 0. So the only solution I see is to put in a timer, and assume there is an error condition if read returns 0 faster than the time specified in c_cc[V_TIME]. But that solution seems hacky at best, and I was hoping there is some better way to handle this situation.

Any ideas or suggestions?

+1  A: 

Are you catching signals and resetting things before your program exits? I think SIGHUP is the one you need to focus on. Possibly set a switch in the signal handler, if switch is on when returning from read() clean up and exit.

Duck
A: 
+1  A: 

You should handle timeout with select rather than with terminal settings. If the terminal is configured without timeout, then it will never return 0 on a read except on EOF.

Select gives you the timeout, and read gives you the 0 on close.

rc = select(...);
if(rc > 0) {
        char c = 0;
        int res = read(input_terminal, &c, 1);
        if (res == 0) {/* EOF detected, close your app ?*/}
        if (res == -1) { /* snip error handling */ }
        return c;
} else if (rc == 0) {
   /* timeout */
   return 0;
} else {
   /* handle select error */
}
shodanex