I am want to stream a linux terminal to my own program, and as far as I understand this is done by opening /dev/ptmx to start a new pts, I have tested this and this does indeed work (it creates a new file in /dev/pts). But I am not sure how I am supposed to actually read and write to this terminal. Writing directly to /dev/pts/(pts number) I just get an input/output error. Also, am I supposed to open /dev/ptmx and /dev/pts/(pts number) at the same time with the same program. Am I supposed to somehow open a shell first?

I find this stuff kinda confusing and I have not been able to find much information except for this man page http://man7.org/linux/man-pages/man4/pts.4.html

  • 1
    from the man page you're linking to: "Before opening the pseudoterminal slave, you must pass the master's file descriptor to grantpt(3) and unlockpt(3).". But I think you better use the functions from -lutil (openpty(3), login_tty(3), forkpty(3)).
    – user313992
    Commented Sep 22, 2018 at 18:57

1 Answer 1


There are two distinct parts to this sort of thing.

  1. As you have determined, you open the master side of the pseudo-terminal and this creates a slave-side device file that can be opened. The ptsname() library function allows something with an open file descriptor for the master side to determine this device name.
  2. The same or another program, in a different process, opens the slave side, treating it exactly as real and virtual terminals are treated by the login subsystem: setting that process as a session leader; setting the slave side as the controlling terminal for the session; and setting standard input, output, and error as open file descriptors to the slave side. It then chain loads whatever interactive program is appropriate, which can indeed be a shell amongst other things.

The second part cannot proceed, on several operating systems, until the first part has called the grantpt() and unlockpt() library functions. Interlocks in the kernel prevent the slave side from being openable until these have happened on the master side.

Interestingly, these functions (which date from AT&T Unix System 5 Release 4) have proven to be unnecessary. They result from implementations where the slave side device is created owned by the wrong user account and with the wrong permissions, or even older implementations where slave side devices are persistent character device nodes (whose permissions and ownership persist from what they were last set to) and are not created on the fly, resulting in windows of opportunity in various circumstances for an attacker program run by other users to gain access to the terminal. But nowadays some operating system kernels simply give the slave side devices the appropriate ownership and permissions right from the get-go, allowing these functions to essentially be no-operations as a consequence. FreeBSD and OpenBSD both work this way nowadays. Unfortunately, despite several rumblings to this effect by kernel developers over the years, Linux is not one such kernel.

The second part is intentionally vague about exactly what different process this is. A common architecture is for the master-side process to fork(), call ptsname(), open the slave device, and close the master side file descriptor. This is what pty-run in the nosh toolset, out of which one can build tools such as ptybandage and ptyrun, does. It's how script, GNU Screen, tmux, and GUI terminal emulators such as XTerm work.

But this is not a necessary thing. As long as it knows the filename to open, the slave side process does not need to be a fork()ed child of the master side process. Indeed, in not being it does away with any need for knowledge about the master side or that the terminal is a pseudo-terminal in particular.

In my user-space virtual terminal subsystem, for example, the process running console-terminal-emulator creates a symbolic link with a known fixed name pointing to the slave device filename. Entirely separate service processes use the known fixed name to open the slave side device without need to know the exact /dev/pts/N name that the kernel happened to use each time. These service processes operate identically to similar service processes attached to kernel virtual terminals, and largely the same as service processes attached to real terminals. The slave side of a pseudo-terminal is, after all, designed to work just like the other two sorts of terminals.

Further reading

Not the answer you're looking for? Browse other questions tagged .