Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
C.  STREAMS-Based Terminal Subsystem STREAMS-based Pseudo-Terminal Subsystem Pseudo-TTY Emulation Module: ptem  Previous   Contents   Next 
   
 

ptem Data Structure

Each instantiation of ptem(7M) is associated with a local area. These data are held in a structure called ptem that has the following format:

struct ptem
{
	long cflags;					/* copy of c_flags */
	mblk_t *dack_ptr;			/* pointer to preallocated msg blk
									   used to send disconnect */
	queue_t *q_ptr;				/* pointer to ptem's read queue */
	struct winsize wsz;			/*struct to hold windowing info*/
	unsigned short state;		/* state of ptem entry */
};

When ptem(7M) is pushed onto the slave side stream, a search of the ptem structure is made for a free entry (state is not set to INUSE). The c_cflags of the termio(7I) structure and the windowing variables are stored in cflags and wsz respectively. The dack_ptr is a pointer to a message block used to send a zero-length message whenever a hang-up occurs on the slave side.


Note - ptem(7M) internal implementation might change. This structure should be relevant only to people wanting to change the module.


open and close Routines


Caution - The following information is implementation-dependent.


In the open routine of ptem(7M) a STREAMS message block is allocated for a zero-length message for delivering a hangup message. This allocation of a buffer is done before it is needed to ensure that a buffer is available. An M_SETOPTS message is sent upstream to set the read-side stream head queues, to assign high-water and low-water marks (1024 and 256 respectively), and to establish a controlling terminal.

The same default values as for the line-discipline module are assigned to cflags, and INUSE to the state field.


Note - These default values are currently being examined and may change in the future.


The open routine fails if:

  • No free entries are found when the ptem(7M) structure is searched

  • sflag is not set to MODOPEN

  • A zero-length message cannot be allocated (no buffer is available)

  • A stroptions(9S) structure cannot be allocated

The close routine is called on the last close of the slave-side stream. Pointers to read and write queues are cleared and the buffer for the zero-length message is freed.

Remote Mode

Remote mode available with the pseudo-TTY subsystem, is used for applications that perform the canonical function normally done by ldterm(7M) and the TTY driver. The remote mode enables applications on the master side to turn off the canonical processing. An TIOCREMOTE ioctl(2) with a nonzero parameter (ioctl(fd, TIOCREMOTE, 1)) is issued on the master side to enter the remote mode. When this occurs, an M_CTL message with the command MC_NO_CANON is sent to ldterm(7M), indicating that data should be passed when received on the read side and that no canonical processing is to take place. The remote mode may be disabled by ioctl(fd, TIOCREMOTE, 0).

Packet Mode

In the STREAMS-based pseudo-terminal subsystem packet mode is used to inform the process on the master side when state changes have occurred in the pseudo-TTY. Packet mode is enabled by pushing the pckt module on the master side. Data written on the master side is processed normally. When data is written on the slave side, or when other messages are encountered by the pckt module, a header is added to the message so it can be subsequently retrieved by the master side with a getmsg operation.

pckt(7M) does the following:

  • When a message is passed to this module on its write queue, the module does no processing and passes the message to the next module or driver.

  • pckt creates an M_PROTO message when one of the following messages is passed to it: M_DATA, M_IOCTL, M_PROTO/M_PCPROTO, M_FLUSH, M_READ, M_START/M_STOP, and M_STARTI/M_STOPI.

    All other messages are passed through. The M_PROTO message is passed upstream and retrieved when the user issues getmsg(2).

  • If the message is an M_FLUSH message, pckt(7M) does the following:

    • If the flag is FLUSHW, it is changed to FLUSHR (because FLUSHR was the original flag before the pts(7D) driver changed it), changed into an M_PROTO message, and passed upstream. To prevent the stream head's read queue from being flushed, the original M_FLUSH message must not be passed upstream.

    • If the flag is FLUSHR, it is changed to FLUSHW, packetized into an M_PROTO message, and passed upstream. To flush both of the write queues properly, an M_FLUSH message with the FLUSHW flag set is also sent upstream.

    • If the flag is FLUSHRW, the message with both flags set is packetized and passed upstream. An M_FLUSH message with the FLUSHW flag set is also sent upstream.

Pseudo-TTY Drivers: ptm and pts

To use the pseudo-TTY subsystem, a node for the master side driver /dev/ptmx and N number of slave drivers must be installed (N is determined at installation). The names of the slave devices are /dev/pts/M where M has the values 0 through N-1. A user accesses a pseudo-TTY device through the master device (called ptm) that in turn is accessed through the clone driver. The master device is set up as a clone device where its major device number is the major for the clone device and its minor device number is the major for the ptm(7D) driver.

The master pseudo driver is opened by calling open(2) with /dev/ptmx as the device to be opened. The clone open finds the next available minor device for that major device. A master device is available only if it, and its corresponding slave device, are not already open. There are no nodes in the file system for master devices.

When the master device is opened, the corresponding slave device is automatically locked out. No user may open that slave device until it is unlocked. A user may invoke a function grantpt to change the owner of the slave device to that of the user who is running this process, change the group ID to TTY, and change the mode of the device to 0620. Once the permissions have been changed, the device may be unlocked by the user. Only the owner or the root user can access the slave device. The user must then invoke the unlockpt function to unlock the slave device. Before opening the slave device, the user must call the ptsname function to obtain the name of the slave device. The functions grantpt, unlockpt, and ptsname are called with the file descriptor of the master device. The user may then invoke the open system call with the name that was returned by the ptsname function to open the slave device.

The following example shows how a user may invoke the pseudo-TTY subsystem:

int fdm fds;
char *slavename;
extern char *ptsname();

fdm = open("/dev/ptmx", O_RDWR);  /* open master */
grantpt(fdm);                     /* change permission of slave */
unlockpt(fdm);                    /* unlock slave */
slavename = ptsname(fdm);         /* get name of slave */
fds = open(slavename, O_RDWR);    /* open slave */
ioctl(fds, I_PUSH, "ptem");       /* push ptem */
ioctl(fds, I_PUSH, "ldterm");    /* push ldterm */

Unrelated processes may open the pseudo device. The initial user may pass the master file descriptor using a STREAMS-based pipe or a slave name to another process to enable it to open the slave. After the slave device is open, the owner is free to change the permissions.


Note - Certain programs such as write and wall are set group ID (setgid(2)) to TTY and are also able to access the slave device.


After both the master and slave have been opened, the user has two file descriptors that provide full-duplex communication using two streams. The two streams are automatically connected. The user may then push modules onto either side of the stream. The user also needs to push the ptem and ldterm modules onto the slave side of the pseudo-terminal subsystem to get terminal semantics.

The master and slave drivers pass all STREAMS messages to their adjacent queues. Only the M_FLUSH needs some processing. Because the read queue of one side is connected to the write queue of the other, the FLUSHR flag is changed to FLUSHW and vice versa.

When the master device is closed, an M_HANGUP message is sent to the slave device to render the device unusable. The process on the slave side gets the errno ENXIO when attempting to write on that stream, but it will be able to read any data remaining on the stream head read queue. When all the data has been read, read(2) returns 0, indicating that the stream can no longer be used.

On the last close of the slave device, a zero-length message is sent to the master device. When the application on the master side issues a read or getmsg and 0 is returned, the user of the master device decides whether to issue a close that dismantles the pseudo-terminal subsystem. If the master device is not closed, the pseudo-TTY subsystem is available to another user to open the slave device.

Since zero-length messages are used to indicate that the process on the slave side has closed (and should be interpreted that way by the process on the master side), applications on the slave side should not write zero-length messages. If that occurs, the write returns 0, and the zero-length message is discarded by the ptem module.

The standard STREAMS system calls can access the pseudo-TTY devices. The slave devices support the O_NDELAY and O_NONBLOCK flags. Because the master side does not act like the terminal, if O_NONBLOCK or O_NDELAY is set, read on the master side returns -1 with errno set to EAGAIN if no data is available, and write(2) returns -1 with errno set to EAGAIN if there is internal flow control.

The master driver supports the ISPTM and UNLKPT ioctl(2) that are used by the functions grantpt(3C), unlockpt(3C), and ptsname(3C). The ISPTM ioctl(2) determines whether the file descriptor is that of an open master device. On success, it returns the major/minor number (type dev_t) of the master device which can be used to determine the name of the corresponding slave device. The UNLKPT ioctl(2) unlocks the master and slave devices. It returns 0 on success. On failure, the errno is set to EINVAL indicating that the master device is not open.

The format of these commands is:

int ioctl (int fd, int command, int arg)

where command is either ISPTM or UNLKPT and arg is 0. On failure, -1 is returned.

When data is written to the master side, the entire block of data written is treated as a single line. The slave-side process reading the terminal receives the entire block of data. Data is not edited by the ldterm module at input, regardless of the terminal mode. The master-side application is responsible for detecting an interrupt character and sending an interrupt signal SIGINT to the process in the slave side. This can be done as follows:

ioctl (fd, TIOCSIGNAL, SIGINT)

where SIGINT is defined in the file signal.h. When a process on the master side issues this ioctl(2), the argument is the number of the signal that should be sent. The specified signal is then sent to the process group on the slave side.

 
 
 
  Previous   Contents   Next