Museum

Home

Lab Overview

Retrotechnology Articles

⇒ Online Manual

Media Vault

Software Library

Restoration Projects

Artifacts Sought

Related Articles

ioctl(2)

select(2)

signal(5)

termio(7)

pty(7)

NAME

pty − pseudo terminal driver

SYNOPSIS

pseudo-device pty

DESCRIPTION

The pty driver provides support for a device-pair termed a pseudo terminal.  A pseudo terminal is a pair of character devices, a master device and a slave device.  The slave device provides to application processes an interface identical to that described in termio(7). Unlike all other devices that provide the interface described in termio(7), the slave device does not have a hardware device behind it. Instead, it has another process manipulating it through the master half of the pseudo terminal. Thus anything written on the master device is given to the slave device as input, and anything written on the slave device is presented as input on the master device.

_ _

pty functions

Application <—> _ _ <—> Server
Processes Slave Master Process
side side

_ _

Open and Close Processing

The slave side of the pty interprets opening or closing the master side as a modem connection or disconnection on a real terminal.  Only one open to the master side of a pty is permitted.  An attempt to open an already open master side returns -1 and sets the external variable errno to EBUSY.  An attempt to open the master side of a pty that has a slave with an open file descriptor returns -1 and sets errno to EBUSY.  The potential problem of ptys being found busy at opens can be avoided by using the clone open functionality discussed in the next section. 

An attempt to open a non-existent pty returns -1 and sets errno to ENXIO.  If O_NDELAY is not specified, opens on the slave side hang until the master side is opened.  If O_NDELAY is specified, opens on the slave side return error if the master side is closed.  Any ioctl() (see ioctl(2)) or write() (see write(2)) request made on the slave side of a pty after the master side is closed returns -1 and sets the external variable errno to EIO.  A read() request made on the slave side of a pty after the master side is closed returns 0 bytes (see read(2)). Closing the master side of a pty sends a SIGHUP hangup signal to the tty process group number of the corresponding slave side and flushes pending input and output. 

Clone Open

In typical pty usage, there is no preference among pty pairs.  Thus, it is useful to be able to issue a single open() that internally opens any available pty (see open(2)). An open on /dev/ptym/clone returns an open file descriptor of a free master pty device.  If there are no free devices, the open returns -1 and sets errno to EBUSY.  The name of the slave device corresponding to the opened master device can be found through a ptsname() request (see ptsname(3C)).

Processing ioctl() Requests

By default, any ioctl() request defined by termio(7) is recognized by both the master and slave sides of a pty. These ioctl() requests are processed by the pty driver as specified by termio(7). In addition, the ioctl() requests defined below are recognized by the master side of a pty.  The slave side only recognizes ioctl() requests defined by termio(7). An ioctl() request made on the slave side of a pty after the master side is closed returns -1 and sets the external variable errno to EIO.  An ioctl() request not recognized by the pty returns -1 and sets the external variable errno to EINVAL.  Note that some of the master-side-only ioctl() requests affect which ioctl() requests are recognized by the master and slave side of the pty.  These master-side-only ioctl() requests also affect the way recognized ioctl() requests, open() requests, and close() requests are processed by the pty driver (see close(2)).

The following ioctl() requests, defined in <sys/ptyio.h>, apply only to the master side of pty:

TIOCSIGSEND Cause a signal to be sent from the slave side of the pty to the current tty process group of the slave side.  The value of the parameter is taken to be the signal number sent.  An EINVAL error is returned and no signal is sent if the specified signal number does not refer to a legitimate signal (see signal(5)). Note that this request allows the server process to send signals to processes not owned by the same user ID. 

TIOCTTY Enable or disable all termio processing by a pty.  termio processing is enabled if the int addressed by arg is nonzero and disabled if the int addressed by arg is zero.  By default, termio processing is enabled.  termio processing refers to processing of input and output described by termio(7) (such as tab expansion), as well as the processing of the ioctl() requests described by termio(7). When disabled, all input and output data is passed through the pty without modification. Issuing a TIOCTTY ioctl() request flushes all data buffered in the pseudo terminal and releases any processes blocked waiting for data.  Enabling and disabling TIOCTTY affects the operation of the following ioctl() requests: TIOCPKT, TIOCREMOTE, TIOCBREAK, TIOCSTOP, TIOCSTART, TIOCTRAP, and TIOCMONITOR. 

When TIOCTTY is enabled, all termio ioctl() requests execute as specified in termio(7), regardless of the side from which the ioctl() request is made.  When TIOCTTY is disabled, master side termio ioctl() requests set and return the the external variable errno to EINVAL.  Slave side termio ioctl() requests are processed like any other ioctl() request when TIOCTTY is disabled.  In particular, slave side termio ioctl() requests set and return the external variable errno to EINVAL when both TIOCTTY and TIOCTRAP are disabled.  (See the discussion of ioctl(), open(), close() trapping below).  ioctl() requests not defined by termio(7) are not affected by the state of TIOCTTY. 

Data written through a pseudo terminal with TIOCTTY disabled is handled in a manner similar to data flowing through a pipe.  A write request blocks in the pty until all data has been written into the pty.  A read request blocks if there is no data available unless the O_NDELAY flag is set (see fcntl(2)). When data is available to be read, the read request returns whatever is available, and does not wait for the number of bytes requested to be satisfied. The number of bytes a pty can contain in its internal memory is implementation dependent, but is at least 256 bytes in each direction. For example, a write on the slave side of a pty of 1024 bytes might be read on the master side by four read requests returning 256 bytes each. The size of the chunks of data that are read is not guaranteed to be consistent, but no data is lost.

The following ioctl() requests, defined in <sys/ptyio.h>, apply only to the master side of a pty.  In particular, these ioctl() requests enable/disable specific modes of pty driver operation.  These ioctl() requests work in series with TIOCTTY; that is, the mode must be enabled by its ioctl() request and TIOCTTY must be enabled for the mode to operate.  The mode can be enabled or disabled regardless of the state of TIOCTTY. 

TIOCPKT Enable or disable packet mode.  Packet mode is enabled if the int addressed by arg is nonzero and disabled if the int addressed by arg is zero.  By default, packet mode is disabled.  When applied to the master side of a pseudo terminal, each subsequent read() from the master side returns data written on the slave part of the pseudo terminal preceded by a zero byte (symbolically defined as TIOCPKT_DATA ), or a single byte reflecting control status information.  The value of such a status byte is composed of zero or more bit flags:

TIOCPKT_FLUSHREAD
The read queue for the slave side has been flushed.

TIOCPKT_FLUSHWRITE
The write queue for the slave side has been flushed.

TIOCPKT_STOP
Data flowing from the slave side of the pty to the master side has been stopped by means of ^S, TIOCSTOP, or TCXONC. 

TIOCPKT_START
Data flowing from the slave side of the pty to the master side has been restarted.

TIOCPKT_DOSTOP
Stop and start characters have been set to ^S or ^Q. 

TIOCPKT_NOSTOP
Stop and start characters are set to something other than ^S or ^Q. 

TIOCREMOTE Enable or disable remote mode.  Remote mode is enabled if the int value of arg is nonzero and disabled if the int value of arg is zero.  By default, remote mode is disabled.  Remote mode is independent of packet mode.  This mode causes input to the pseudo terminal to be flow controlled and not input edited (regardless of the terminal mode).  Each write to the master side produces a record boundary for the process reading the slave side.  In normal usage, writing data is like typing the data as a line on a terminal; writing zero bytes is equivalent to typing an end-of-file character (that is, the EOF character as defined in termio(7)). The data read by the slave side is identical to the data written on the master side. Data written on the slave side and read on the master side with TIOCREMOTE enabled is still subject to the normal termio(7) processing. TIOCREMOTE can be used when doing remote line editing in a window manager, or whenever flow-controlled input is required.  Issuing a TIOCMONITOR ioctl() request flushes all data buffered in the pseudo terminal. 

The following ioctl() requests, defined in <sys/ptyio.h>, apply only to the master side of pty.  In particular, these ioctl() requests are only recognized when TIOCTTY is enabled.  When TIOCTTY is disabled, these ioctl() requests set and return the external variable errno to EINVAL. 

TIOCBREAK Cause a break operation to be done on the slave side of the pty, as if a user had pressed the break key on a real terminal.  Takes no parameter. 

TIOCSTOP Stop data flowing from the slave side of the pty to the master side (equivalent to typing ^S).  Takes no parameter. 

TIOCSTART Restart output (stopped by TIOCSTOP or by typing ^S).  Takes no parameter. 

Flow-Control Input and Output Processing

The following terms are used to describe the flow of data through psuedo terminals.  INPUT refers to data flowing from the master side of a pty to the slave side.  OUTPUT refers to data flowing from the slave side of a pty to the master side. 

When packet mode (TIOCPKT) is disabled and INPUT is stopped (see IXOFF, input modes, in termio(7)), the next read() from the master side of a pty returns a STOP character.  When INPUT is restarted, the next read() from the master side returns a START character.  If packet mode (TIOCPKT) is enabled, the STOP or START character is preceded by a data packet indicator (TIOCPKTDATA).  select() should be used by the master-side server before each write() request to properly handle INPUT flow control (see select(2)).

When INPUT flow control is enabled, write() and select() are handled as follows: Write-selects on the master side of a pty return true only if INPUT has not been stopped.  If INPUT becomes stopped while data is being written into the master side of a pty, the write returns with the number of bytes written before INPUT was stopped.  Writes done after INPUT is stopped return immediately with zero bytes written. 

When packet mode (TIOCPKT) is disabled and OUTPUT is stopped (see IXON, input modes in termio(7)), each subsequent read() from the master side of a pty returns with no data read.  When OUTPUT is restarted, each subsequent read() from the master side returns data written on the slave side.  If packet mode (TIOCPKT) is enabled, the first read() after OUTPUT has been stopped returns a TIOCPKTSTOP packet.  All subsequent reads from the master side while OUTPUT is stopped returns a TIOCPKTDATA packet with no data.  When OUTPUT is restarted, the next read() from the master side returns a TIOCPKTSTART packet.  All subsequent reads from the master side return data written on the slave side preceded by a TIOCPKTDATA packet.  select() should be used by the master-side server before each read() to properly handle OUTPUT flow control.  Otherwise, reads from the master side of a pty will not be prevented when OUTPUT is stopped. 

Trapping ioctl(), open(), close() Requests

When trapping is enabled, the master side is notified when the application on its slave side makes an ioctl(), open(), or close() request.  For trapped ioctl() and open() requests, the slave side is blocked (that is, the request does not complete) until the server on its master side acknowledges the trapped request.  For trapped close() requests, the slave slave does not block for an acknowledgement. 

select() should be used by the master side server to receive notification of trapped ioctl(), open(), and close() requests.  When one of these requests is trapped, the select() returns with an "exceptional condition" indicated for the slave side’s file descriptor.  Other mechanisms for receiving notification of trapped requests are defined below, but these mechanisms should be used only if select() is not available. 

When trapping is disabled (default condition), ioctl() requests made to the slave side set fail and return the external variable errno to EINVAL if they are not recognized by the slave side of the pty driver.  The only ioctl() requests recognized by the slave side are those defined by termio(7) and only when TIOCTTY is enabled.  When TIOCTTY is disabled, no ioctl() requests are recognized by the slave side.  If trapping is enabled and the master side closes, trapping is disabled.  If the master closes during the middle of a handshake with the slave, the handshake is done automatically. 

Trapping occurs in two forms that are identified by the ioctl() requests that enable or disable them — TIOCTRAP and TIOCMONITOR.  These two forms are distinguished by the types of requests they affect and by the capabilities they provide.  Trapping open() and close() requests is enabled or disabled by TIOCTRAP.  Trapping ioctl() requests not defined by termio(7) are enabled or disabled by TIOCTRAP.  Trapping ioctl() requests defined by termio(7) are enabled or disabled by TIOCTRAP only when TIOCTTY is also disabled.  When TIOCTTY is enabled, trapping ioctl() requests defined by termio(7) are enabled or disabled by TIOCMONITOR.  Briefly, both TIOCTRAP and TIOCMONITOR trapping allow the server on the master side to examine the request’s parameters, the pid making the request, etc.  In addition, TIOCTRAP trapping allows the server to modify the parameters and return values of an ioctl() request. 

The following ioctl() calls apply only to the master side of a pty and pertain to trapping ioctl(), open(), and close() requests.  They are defined in <sys/ptyio.h>:

TIOCTRAP Enable or disable trapping of ioctl(), open(), and close() requests made by the application on the slave side of a pty.  Trapping is enabled if the int addressed by arg is nonzero and disabled if the int addressed by arg is zero.  By default, TIOCTRAP trapping is disabled. 

TIOCTRAPSTATUS
Check for a pending ioctl(), open(), or close() trap.  The argument points to an int that is set to one if a trap is pending and to zero if nothing is pending.  Use TIOCTRAPSTATUS when the preferred method of a select() "exceptional condition" is not available. 

TIOCREQCHECK Return the trapped ioctl(), open(), or close() information to the master side.  Use TIOCREQCHECK in response to either a select() "exceptional condition" or a TIOCTRAPSTATUS indicating that a trap is pending.  A TIOCREQCHECK reads the pending ioctl(), open(), or close() information into the memory pointed to by the arg of TIOCREQCHECK.  The information takes the form of the following request_info structure, defined in <sys/ptyio.h>:

struct request_info {
    int request;
    int argget;
    int argset;
    short pgrp;
    short pid;
    int errno_error;
    int return_value;
};

All elements of request_info refer to the slave side of the pty and include the following:

request The ioctl() command received. 

argget The ioctl() request applied to master side to receive the trapped ioctl() structure, if one exists (a zero value means there is none).  (When nonzero, argget is a TIOCARGGET request with the size field precomputed.) 

argset The ioctl() request applied to master side to send back the resulting ioctl() structure, if one exists (a zero value means there is none).  (When nonzero, argset is a TIOCARGSET request with the size field precomputed.) 

pgrp The process group number of the process doing the operation. 

pid The process ID of the process doing the operation. 

errno_error The errno external variable error code (initialized to zero) returned by ioctl() on the slave side. 

return_value The success value (initialized to zero) returned by ioctl() on the slave side when errno_error is not set. 

When the ioctl() argument received on the slave side is not a pointer, its value is stored as four bytes retrievable with an ioctl() request to the master side equal to argget. 

When an open() or close() is being passed, request is set to TIOCOPEN or TIOCCLOSE, respectively.  For TIOCOPEN and TIOCCLOSE, both argget and argset are zero because there is no ioctl() structure.  When TIOCTTY is enabled, the termio(7) definition of open/close is executed first before being passed to the master side. Note that while all opens are trapped, only the last close on a particular inode for a pty slave side is trapped by the pty.

A TIOCREQCHECK returns the external variable errno error EINVAL if no ioctl(), open(), or close() trap is pending.  Accordingly, a TIOCREQCHECK that returns EINVAL in response to a select() "exceptional condition" indicates that the trapped ioctl(), open(), or close() request was terminated by a signal after select() returned. 

TIOCREQGET Identical to TIOCREQCHECK except when no ioctl(), open(), or close() trap is pending.  A TIOCREQGET blocks until a slave side ioctl(), open(), or close() is trapped; whereas a TIOCREQCHECK returns EINVAL.  Use TIOCREQGET when neither the preferred method of a select (2) "exceptional condition" nor the master side ioctl() TIOCTRAPSTATUS is available. 

TIOCREQSET Complete the handshake started by a previous TIOCREQCHECK or TIOCREQGET.  The argument should point to the request_info structure, as defined by the TIOCREQCHECK. 

Before doing this ioctl() request to complete the handshake, the server should set errno_error to an external variable errno error value to be passed back to the slave side.  If there is no error, errno_error can be left alone because the pty initializes it to zero.  Also, when there is no error, return_value should be set if other than a zero result is desired.  The server can set return_value and errno_error if the trapped request is an ioctl().  Setting either return_value or errno_error for a trapped open() or close() affects neither the return value of the request nor the external variable errno value of the slave side.  Further, setting either return_value or errno_error does not cause TIOCREQSET to return an error to the server. 

If the TIOCREQSET request is made and the request value in the passed request_info structure does not equal the trapped value, the external variable errno is set and returned as EINVAL.  EINVAL is also returned if there are no trapped ioctl(), open(), or close() requests.  If the trapped request has been interrupted by a signal between the time that the server has done the TIOCREQGET and the TIOCREQSET, the TIOCREQSET request returns EINVAL. 

TIOCMONITOR Enable or disable read-only trapping of termio ioctl() requests.  TIOCMONITOR trapping is enabled if the int addressed by arg is nonzero and disabled if the int addressed by arg is zero.  By default, TIOCMONITOR trapping is disabled.  TIOCMONITOR works in series with TIOCTTY; that is, the TIOCMONITOR trapping must be enabled and TIOCTTY must be enabled for termio ioctl() requests to be trapped by TIOCMONITOR.  TIOCMONITOR trapping can be enabled or disabled regardless of the state of TIOCTTY. 

When TIOCTTY is disabled, termio ioctl() requests are not trapped by TIOCMONITOR.  However, ioctl() requests are trapped by TIOCTRAP if TIOCTTY is disabled and TIOCTRAP is enabled.  TIOCTRAP trapping allows the master side server to modify the parameters and return values of an ioctl() request, whereas TIOCTMONITOR trapping does not. 

TIOCMONITOR trapping allows the server on the master side to know when characteristics of the line discipline in the pty are changed by an application on its slave side.  The mechanism for handshaking termio requests trapped by TIOCMONITOR is the same as the mechanism described above for requests trapped by TIOCTRAP.  (It is recommended that termio ioctl() requests be used on the master side to interrogate the configured state of the line discipline in the pty.  This compensates for the window of time before TIOCMONITOR is enabled, when termio ioctl() requests are not trapped.) 

When using select() on the master side of a pty, the "exceptional condition" refers to an open(), close(), or ioctl() request pending on the slave side, while "ready for reading or writing" refers to a read() or write() request pending. 

Of the ioctl() requests subject to being trapped, only one-per-pty can be handled at a time.  This means that when an application does a non-termio ioctl() request to the slave side, all other ioctl() requests to the same pty slave side are blocked until the first one is handshaked back by the master side.  (ioctl() requests that are not trapped, such as termio when TIOCTTY is enabled and TIOCMONITOR is disabled, are not blocked.)  This permits the implementation of indivisible operations by an ioctl() call on the slave side that is passed to the server process. 

In summary, the following method of handling trapped ioctl(), open(), and close() requests is preferred:

1.  Call select().  This system call blocks the master side until a slave side ioctl(), open(), or close() request is trapped. 

2.  Make TIOCREQCHECK ioctl() request.  This step returns information about a trapped ioctl(), open(), or close() request.  If TIOCREQCHECK returnsthe external variable errno error EINVAL, loop back to the select() call. 

3.  Make argget ioctl() request.  This optional step is used if argget is nonzero and the server wants to do more than just reject the trapped slave ioctl() request. 

4.  Make argset ioctl() request.  This optional step is done if argset is nonzero and the server wants to pass back a modified ioctl() structure.  It is done after the trapped ioctl() request is processed via the server on the master side. 

5.  Set errno_error and return_error.  If the trapped request is an ioctl(), set errno_error appropriately.  If the appropriate value for errno_error is zero, return_error must be set. 

6.  Make TIOCREQSET ioctl() request.  This step completes the trapped ioctl(), open(), close() request. 

While a process is waiting in the slave side of the pty for the server to complete a handshake, it is susceptible to receiving signals.  The following master side ioctl() request allows the server process to control how the pty responds when a signal attempts to interrupt a trapped open() or ioctl() request:

TIOCSIGMODE Set the signal handling state of the pty to the mode specified as the argument.  The mode can have three values, which are TIOCSIGBLOCK, TIOCSIGABORT, and TIOCSIGNORMAL. 

TIOCSIGBLOCK
Cause some signals to be postponed that are destined for the slave-side process whose open() or ioctl() request is trapped.  Signals are postponed if they would otherwise cause the process to jump to an installed signal handler.  Signals are not postponed if they would otherwise cause the process to abort or if they are being ignored.  When the server process completes the handshake by means of the TIOCREQSET ioctl() request, the process returns to the calling program and any pending signals are then acted upon.  Any signals that the user has blocked by means of sigblock() continues to be blocked. 

TIOCSIGABORT
Prevent a trapped open() or ioctl() request from being restarted.  The server process sets this mode when it wants the interrupted requests to return to the calling program with an EINTR error. 

TIOCSIGNORMAL
This is the default mode of the pty. If a signal interrupts a trapped open() or ioctl() request, the user’s signal handler routine can specify whether the request is to be restarted.  If the request is restarted, it executes again from the beginning and the server has to make another TIOCREQGET request to start the handshake over again.  If the user’s signal handler routine specifies that the interrupted request should not be restarted, the request returns to the calling program with EINTR upon completion of the signal handler.  Note that the restarted request is not necessarily the very next one to be trapped. 

WARNINGS

The slave side cannot indicate an end-of-file condition to the master side. 

When using TIOCREMOTE, a single write() request to the master side of greater than 256 bytes may result in multiple smaller records being read from the slave side instead of only one record. 

DEPENDENCIES

Series 300/400

The clone open functionality is currently supported on the Series 800 systems only. 

The largest ioctl() argument passable between master and slave sides is currently limited to 128 bytes. 

Slave-side non-termio ioctl() requests either go unrecognized or are passed to the master side, depending upon the state of the TIOCTRAP. 

Series 700

The clone open functionality is currently supported on the Series 800 systems only. 

AUTHOR

pty was developed by the University of California, Berkeley. 

FILES

/dev/ptym/pty[a-ce-z][0-9][0-9] master pseudo terminals

/dev/ptym/pty[a-ce-z][0-9a-f] master pseudo terminals

/dev/pty[pqr][0-9a-f] master pseudo terminals

/dev/pty/tty[a-ce-z][0-9][0-9] slave pseudo terminals

/dev/pty/tty[a-ce-z][0-9a-f] slave pseudo terminals

/dev/tty[pqr][0-9a-f] slave pseudo terminals

SEE ALSO

ioctl(2), select(2), signal(5), termio(7). 

Hewlett-Packard Company  —  HP-UX Release 9.0: August 1992

Typewritten Software • bear@typewritten.org • Edmonds, WA 98026