6.5 Terminal Control

Team-FLY

Many special files represent devices with characteristics that are platform dependent, making standardization difficult. However, since terminal control was thought to be essential on all systems, the POSIX standards committee decided to include library functions for manipulating special files representing terminals and asynchronous communication ports. This section describes these functions and the way to use them.

The stty command reports or sets terminal I/O characteristics. When executed without any arguments or with the -a or -g options, the stty command outputs information about the current terminal to standard output. The -a produces a longer form of the readable information produced by stty without arguments; the -g option produces the information in a form that can be used by a program. The second form of stty allows operands to change the behavior of the terminal associated with a shell.

  SYNOPSIS  stty [-a  -g]    stty operands  POSIX:Shell and Utilities  
Exercise 6.16

Execute stty , stty -a and stty -g on your system. Try to interpret the results.

Answer:

The stty command outputs the following under Sun Solaris 9.

 speed 9600 baud; -parity rows = 34; columns = 80; ypixels = 680; xpixels = 808; swtch = <undef>; brkint -inpck -istrip icrnl -ixany imaxbel onlcr tab3 echo echoe echok echoctl echoke iexten 

The stty -a command on the same system outputs a more complete listing of the terminal settings.

 speed 9600 baud; rows = 34; columns = 80; ypixels = 680; xpixels = 808; csdata ? eucw 1:0:0:0, scrw 1:0:0:0 intr = ^c; quit = ^\; erase = ^?; kill = ^u; eof = ^d; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^q; stop = ^s; susp = ^z; dsusp = ^y; rprnt = ^r; flush = ^o; werase = ^w; lnext = ^v; -parenb -parodd cs8 -cstopb -hupcl cread -clocal -loblk -crtscts -crtsxoff -parext -ignbrk brkint ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc ixon -ixany -ixoff imaxbel isig icanon -xcase echo echoe echok -echonl -noflsh -tostop echoctl -echoprt echoke -defecho -flusho -pendin iexten opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel tab3 

The stty -g command outputs the following on a single line.

 2506:1805:d00bd:8a3b:3:1c:7f:15:4:0:0:0:11:13:1a:19:12:f: 17:16:0:0:1:1:0:00:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0: 0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0 

The interpretation of the fields closely follows the flags in the struct termios structure described below.

The stty -a command displays the current terminal settings, and the second form of stty allows you to change them. One important operand of stty is sane . This operand sets all modes to reasonable values and is useful if you terminate a program that has set the modes in an inconvenient way. You can use stty sane to recover when, for example, local echo has been turned off and you cannot see what you are typing. Sometimes you will have to terminate the line containing the stty command with a Ctrl-J rather than pressing the Return key if Return has been set to send a carriage return rather than a newline.

Programs access terminal characteristics through the struct termios structure, which includes at least the following members .

 tcflag_t  c_iflag;      /* input modes */ tcflag_t  c_oflag;      /* output modes */ tcflag_t  c_cflag;      /* control modes */ tcflag_t  c_lflag;      /* local modes */ cc_t      c_cc[NCCS];   /* control characters */ 

The c_cc array of the struct termios structure holds the values of the characters that have special meaning to the terminal device drivers, for example, the end of input or program break characters. Table 6.1 on page 206 lists the special characters and their default settings.

The c_iflag member of the struct termios structure controls the way a terminal handles input; the c_oflag controls the way a terminal handles output. The c_cflag specifies hardware control information for the terminal, and the c_lflag controls the editing functions of the terminal. Table 6.2 on page 210 lists the POSIX values that these flags can take on. You can set an action by performing a bitwise OR of the appropriate struct termios field with the corresponding flag, and you can clear it by performing a bitwise AND with the complement of the flag.

Example 6.17

The ECHO value of the c_lflag field of struct termios specifies that characters typed at standard input should be echoed to standard output of the terminal. The following code segment clears the ECHO flag in a struct termios structure.

 struct termio term; term.c_lflag &= ~ECHO; 

The tcgetattr function retrieves the attributes associated with the terminal referenced by the open file descriptor fildes . The attributes are returned in a struct termios structure pointed to by termios_p . The tcsetattr function sets the parameters of the terminal referenced by the open file descriptor fildes from the struct termios structure pointed to by termios_p . The optional_actions parameter controls the point at which the changes take effect: TCSANOW signifies that changes occur immediately, and TCSADRAIN signifies that changes occur after all output to fildes is transmitted. If optional_actions is TCSAFLUSH , the changes occur after all output to fildes is transmitted. In this case, all input received but not read is discarded.

  SYNOPSIS  #include <termios.h>   int tcgetattr(int fildes, struct termios *termios_p);   int tcsetattr(int fildes, int optional_actions,                 const struct termios *termios_p);  POSIX  

These functions return 0 if successful. If unsuccessful , these functions return 1 and set errno . The following table lists the mandatory errors for these functions.

errno

cause

EBADF

fildes is not a valid file descriptor

EINTR

a signal interrupted tcsetattr

EINVAL

optional_actions is not a supported value, or attempt to change attribute represented in struct termios to an unsupported value

ENOTTY

file associated with fildes is not a terminal

Program 6.11 shows a ttysetchar function that sets a particular character. The ttsetchar function first calls tcgetattr to read the current settings of the terminal into a struct termios structure. After modifying the desired characters, ttysetchar calls tcsetattr to change the actual terminal settings. It is possible for tcsetattr to be interrupted by a signal while it is waiting for output to drain, so we restart it in this case.

Example 6.18

The following code segment calls the ttysetchar function of Program 6.11 to set the character that indicates end of terminal input to Ctrl-G. (The usual default is Ctrl-D.)

 if (ttysetchar(STDIN_FILENO, VEOF, 0x07) == -1)    perror("Failed to change end-of-file character"); 
Table 6.1. The POSIX special control characters

canonical mode

noncanonical mode

description

usual default

VEOF

 

EOF character

Ctrl-D

VEOL

 

EOL character

none

VERASE

 

ERASE character

backspace or delete

VINTR

VINTR

INTR character

Ctrl-C

VKILL

 

KILL character

Ctrl-U

 

VMIN

MIN value

1

VQUIT

VQUIT

QUIT character

Ctrl- \

VSUSP

VSUSP

SUSP character

Ctrl-Z

 

VTIME

TIME value

VSTART

VSTART

START character

Ctrl-Q

VSTOP

VSTOP

STOP character

Ctrl-S

Program 6.11 ttysetchar.c

A function that sets a particular terminal control character to be a particular value .

 #include <errno.h> #include <termios.h> #include <unistd.h> int ttysetchar(int fd, int flagname, char c) {    int error;    struct termios term;    if (tcgetattr(fd, &term) == -1)       return -1;    term.c_cc[flagname] = (cc_t)c;    while (((error = tcsetattr(fd, TCSAFLUSH, &term)) == -1) &&            (errno == EINTR)) ;    return error; } 

Program 6.12 shows a function that uses tcgetattr and tcsetattr to turn echoing on or off. When echoing is turned off, the characters that you type do not appear on the screen.

Exercise 6.19

Why did Program 6.12 use tcgetattr to read the existing struct termios structure before setting the echo flags?

Answer:

The code shouldn't change any of the other settings, so it reads the existing struct termios structure before modifying it.

Program 6.12 setecho.c

A function to turn terminal echo on or off .

 #include <errno.h> #include <termios.h> #include <unistd.h> #define ECHOFLAGS (ECHO  ECHOE  ECHOK  ECHONL) int setecho(int fd, int onflag) {    int error;    struct termios term;    if (tcgetattr(fd, &term) == -1)       return -1;    if (onflag)                                        /* turn echo on */       term.c_lflag = ECHOFLAGS;    else                                              /* turn echo off */       term.c_lflag &= ~ECHOFLAGS;    while (((error = tcsetattr(fd, TCSAFLUSH, &term)) == -1) &&            (errno == EINTR)) ;    return error; } 
Exercise 6.20

What happens when you run the following program? Under what circumstances might such behavior be useful?

 #include <unistd.h> int setecho(int fd, int onflag); int main(void) {    setecho(STDIN_FILENO, 0);    return 0; } 

Answer:

After you run this program, you will not see anything that you type on the computer screen. You can log out or use stty sane to set the echo back on. Turning off echoing is used for entering passwords and other secrets.

Program 6.13 shows the passwordnosigs function that retrieves the password entered at the controlling terminal of a process. It returns 0 if successful. On failure it returns 1 and sets errno . Notice that passwordnosigs sets the errno based on the first error that occurs. While most functions return immediately after an error, functions that must always restore state have to clean up before they return. The program calls the setecho function of Program 6.12 to turn echoing off and on. It must turn the terminal echo back on before returning or the user won't be able to see what is typed.

Program 6.13 passwordnosigs.c

A function that prompts for and reads a password, assuming that no signals will occur .

 #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <termios.h> #include <unistd.h> #include "restart.h" int readline(int fd, char *buf, int nbytes); int setecho(int fd, int onflag); int passwordnosigs(char *prompt, char *passbuf, int passmax) {    int fd; int firsterrno = 0;    int passlen;    char termbuf[L_ctermid];    if (ctermid(termbuf) == NULL) {                /* find the terminal name */       errno = ENODEV;       return -1;    }    if ((fd = r_open2(termbuf, O_RDWR)) == -1)         /* open the terminal  */       return -1;    if (setecho(fd, 0) == -1)                               /* turn echo off */       firsterrno = errno;    else if (r_write(fd, prompt, strlen(prompt)) == -1)      /* write prompt */       firsterrno = errno;    else if ((passlen = readline(fd, passbuf, passmax)) == 0)       firsterrno = EINVAL;    else if (passlen == -1)       firsterrno = errno;    else       passbuf[passlen-1] = ' 
 #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <termios.h> #include <unistd.h> #include "restart.h" int readline(int fd, char *buf, int nbytes); int setecho(int fd, int onflag); int passwordnosigs(char *prompt, char *passbuf, int passmax) { int fd; int firsterrno = 0; int passlen; char termbuf[L_ctermid]; if ( ctermid (termbuf) == NULL) { /* find the terminal name */ errno = ENODEV; return -1; } if ((fd = r_open2(termbuf, O_RDWR)) == -1) /* open the terminal */ return -1; if (setecho(fd, 0) == -1) /* turn echo off */ firsterrno = errno; else if (r_write(fd, prompt, strlen(prompt)) == -1) /* write prompt */ firsterrno = errno; else if ((passlen = readline(fd, passbuf, passmax)) == 0) firsterrno = EINVAL; else if (passlen == -1) firsterrno = errno; else passbuf[passlen-1] = '\0'; /* remove newline */ if ((setecho(fd, 1) == -1) && !firsterrno) /* always turn echo back on */ firsterrno = errno; if ((r_write(fd,"\n",1) == -1) && !firsterrno) firsterrno = errno; if ((r_close(fd) == -1) && !firsterrno) firsterrno = errno; if (firsterrno) errno = firsterrno; return firsterrno ? -1 : 0; } 
'; /* remove newline */ if ((setecho(fd, 1) == -1) && !firsterrno) /* always turn echo back on */ firsterrno = errno; if ((r_write(fd,"\n",1) == -1) && !firsterrno) firsterrno = errno; if ((r_close(fd) == -1) && !firsterrno) firsterrno = errno; if (firsterrno) errno = firsterrno; return firsterrno ? -1 : 0; }

The passwordnosigs uses readline of Program 4.1 on page 95 to read in a line from the terminal. We were able to use it here because it was written to use a general file descriptor rather than just reading from standard input.

The passwordnosigs function uses the controlling terminal as determined by the ctermid function rather than using standard input. The controlling terminal is usually something like /dev/tty and often shares the same physical devices as standard input and standard output, which are usually the keyboard and screen. One of the consequences of using a controlling terminal rather than standard input and standard output is that controlling terminals cannot be redirected from the command line. This is often used for passwords to discourage users from storing passwords in a file.

Exercise 6.21

What happens if a signal aborts a program that is executing passwordnosigs ? This could happen if the user enters Ctrl-C after being prompted for the password.

Answer:

If the signal comes in after passwordnosigs turns off echoing, the user won't be able to see subsequent typing at the terminal. If you do this, try typing stty sane followed by Return to get the terminal back to echo mode. Chapter 8 addresses this issue more carefully in Program 8.4 on page 266.

Table 6.2 lists the flags for terminal control. Chapter 8 discusses some of the issues related to terminals and signals. The project of Chapter 11 explores many aspects of terminal configuration and the interaction of terminal devices with user processes.

6.5.1 Canonical and noncanonical input processing

A common misconception is that somehow the keyboard and screen are connected, so everything that you type automatically appears on the screen. The keyboard and screen are, in fact, separate devices that communicate with terminal device drivers running on the computer. The device drivers receive bytes from the keyboard, buffering and editing them as specified by the settings for these devices.

The usual method of handling terminal input, canonical mode , processes input one line at a time. The special characters of Table 6.1 are used for terminating input and simple editing such as erasing the last character typed. A line is a sequence of bytes delimited by a newline ( NL ), an end-of-file ( EOF ) or an end-of-line ( EOL ).

In canonical mode, read requests do not return until the user enters a line delimiter (or the process receives a signal). The ERASE and KILL characters work only on the portion of a line that has not yet been delimited. A read request can return only one line, regardless of the number of bytes requested . If the system defines the POSIX constant MAX_CANON for the terminal, input lines cannot be longer than MAX_CANON .

A consequence of canonical mode processing is that input from a terminal behaves differently from input from other devices such as disks. In noncanonical mode , input is not assembled into lines. The device driver does not respond to the ERASE and KILL characters. Noncanonical input processing has two controlling parameters MIN and TIME . The MIN parameter controls the smallest number of bytes that should be gathered before read returns. The TIME parameter refers to a timer with a 0.1-second granularity used for timing out bursty transmissions. Table 6.3 summarizes the settings for MIN and TIME .

Table 6.2. The POSIX values of flags for terminal control.

field

flag

description

c_iflag

BRKINT

signal interrupt on break

 

ICRNL

map CR to NL on input

 

IGNBRK

ignore break condition

 

IGNCR

ignore CR

 

IGNPAR

ignore characters with parity errors

 

INLCR

map NL to CR on input

 

INPCK

enable input parity check

 

ISTRIP

strip character

 

IXOFF

enable start/stop input control

 

IXON

enable start/stop output control

 

PARMRK

mark parity errors

c_oflag

OPOST

postprocess output

 

OCRNL

map CR to NL on output (POSIX:XSI Extension)

 

ONOCR

no CR output at column 0 (POSIX:XSI Extension)

 

ONLRET

NL performs CR function (POSIX:XSI Extension)

c_cflag

CSIZE

character size ( CS5CS8 for 5 to 8 bits, respectively)

 

CSTOPB

send two stop bits, else one

 

CREAD

enable receiver

 

PARENB

enable parity

 

PARODD

odd parity, else even

 

HUPCL

hang up on last close

 

CLOCAL

ignore modem status lines

c_lflag

ECHO

enable echo

 

ECHOE

echo ERASE as an error-correcting backspace

 

ECHOK

enable KILL

 

ECHONL

echo a newline

 

ICANON

canonical input (erase and kill processing)

 

IEXTEN

enable extended (implementation-defined) functions

 

ISIG

enable signals

 

NOFLSH

disable flush after interrupt, quit, or suspend

 

TOSTOP

send SIGTTOU for background output

Program 6.14 shows a function that sets the current terminal to be in noncanonical mode with single-character input. After a setnoncanonical call, the terminal device driver delivers each character as typed, treating the ERASE and KILL characters as ordinary characters. The function returns 0 on success. If an error occurs, setnoncanonical returns 1 and sets errno .

Exercise 6.22

How would you set the terminal back to canonical mode after a call to the function setnoncanonical ?

Answer:

This may be a problem on some systems. POSIX allows c_cc[MIN] and c_cc[TIME] to be used for VEOF and VEOL in canonical mode. On some systems, a call to setnoncanonical will overwrite these values. Unless these values have been saved, there is no way to restore them to their original values. If you just set the ICANON bit in the c_lflag of the struct termios structure, it may not return the terminal to the previous canonical mode state. Program 6.15 provides a method for handling this.

Table 6.3. Parameters for noncanonical mode processing.

case

meaning

MIN > 0, TIME > 0

TIME is an interbyte timer If TIME expires or MIN bytes are received, read is satisfied.

MIN > 0, TIME = 0

read blocks until at least MIN bytes received

MIN = 0, TIME > 0

read is satisfied when a single byte arrives or TIME expires

MIN = 0, TIME = 0

minimum of number of bytes requested or number of bytes available returned

Exercise 6.23

Suppose that standard input has been set to noncanonical mode. Five characters have been typed at the keyboard. You try to read 10 bytes from standard input. What happens in each of the following cases?

  1. MIN = 5 and TIME = 0

  2. MIN = 0 and TIME = 100

  3. MIN = 20 and TIME = 100

  4. MIN = 3 and TIME = 100

  5. MIN = 20 and TIME = 0

  6. MIN = 0 and TIME = 0

Answer:

  1. You receive 5 bytes immediately.

  2. You receive 5 bytes immediately.

  3. You receive 5 bytes after a delay of 10 seconds.

  4. You receive 5 bytes immediately.

  5. You block until at least 5 more characters are entered.

  6. You receive 5 bytes immediately.

Program 6.14 setnoncanonical.c

A function that sets the terminal associated with the caller to perform single character input (rather than line processing) .

 #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <termios.h> #include <unistd.h> #include "restart.h" int ttysetchar(int fd, int flagname, char c); int setnoncanonical(void) {    int error;    int fd;    int firsterrno = 0;    struct termios term;    char termbuf[L_ctermid];    if (ctermid(termbuf) == NULL) {               /* find the terminal name */       errno = ENODEV;       return -1;    }    if ((fd = r_open2(termbuf, O_RDONLY)) == -1)       /* open the terminal */       return -1;    if (tcgetattr(fd, &term) == -1)                  /* get its termios */       firsterrno = errno;    else {       term.c_lflag &= ~ICANON;       while (((error = tcsetattr(fd, TCSAFLUSH, &term)) == -1) &&               (errno == EINTR)) ;       if (error)          firsterrno = errno;    }    if (!firsterrno && (ttysetchar(fd, VMIN, 1)  ttysetchar(fd, VTIME, 0)))       firsterrno = errno;    if ((r_close(fd) == -1) && !firsterrno)       firsterrno = errno;    if (firsterrno)       errno = firsterrno;    return firsterrno ? -1 : 0; } 

Program 6.15 shows two functions for saving and restoring the struct termios structure. Each takes a pointer to a struct termios structure as a parameter and returns 0 on success. On error these functions return 1 with errno set. The correct way to temporarily set noncanonical mode is as follows.

  1. Call gettermios to save struct termios structure in a local variable.

  2. Call setnoncanonical .

  3. Do the noncanonical mode processing.

  4. Restore the original terminal mode by calling settermios .

Program 6.15 savetermios.c

Functions for saving and restoring the terminal mode .

 #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <termios.h> #include <unistd.h> #include "restart.h" int gettermios(struct termios *termp) {    int fd;    int firsterrno = 0;    char termbuf[L_ctermid];    if (ctermid(termbuf) == NULL) {                /* find the terminal name */       errno = ENODEV;       return -1;    }    if ((fd = r_open2(termbuf, O_RDONLY)) == -1)        /* open the terminal */       return -1;    if (tcgetattr(fd, termp) == -1)                       /* get its termios */       firsterrno = errno;    if ((r_close(fd) == -1) && !firsterrno)       firsterrno = errno;    if (firsterrno) {       errno = firsterrno;       return -1;    }    return 0; } int settermios(struct termios *termp) {    int error;    int fd;    int firsterrno = 0;    char termbuf[L_ctermid];    if (ctermid(termbuf) == NULL) {                /* find the terminal name */       errno = ENODEV;       return -1;    }    if ((fd = r_open2(termbuf, O_RDONLY)) == -1)        /* open the terminal */       return -1;    while (((error = tcsetattr(fd, TCSAFLUSH, termp)) == -1) &&            (errno == EINTR)) ;    if (error)       firsterrno = errno;    if ((r_close(fd) == -1) && !firsterrno)       firsterrno = errno;    if (firsterrno) {       errno = firsterrno;       return -1;    }    return 0; } 
Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net