
The TTY demystified - luu
http://www.linusakesson.net/programming/tty/index.php
======
shwinted
Even after reading this, TTYs will surprise you. I've done my share of TTY
programming in Linux and here be dragons.

From modes to tty types to control chars and signals, the TTY system is itself
a bloated relic whose stink becomes apparent when you try to connect it to the
rest of the unix philosophy, where everything is supposed to be a stateless
text stream.

This stuff is really interesting for historical reasons and made sense 20
years ago, but I long for the day where the TTY system is gone and character
drivers work on some standard JSON stream or something.

~~~
ibisum
meh. TTY programming was hard in the 90's, and then it was done. If you find
it hard now, you're probably still just learning all about it. Its an old,
well and truly solved problem. The slayers of yesterdays TTY dragons are
todays WebSocket hackers, I surmise.

Still, I think the TTY - and UARTS in general - have a lot of utility in this
day and age, warty dragons and all. I've got a terminal server in my lab just
for the case of wiring up the small, light, cheap, efficient embedded systems
which still depend on the dragons to deliver the fire. Nothing quite like
having a functional 9600-baud term you can tap into with an Oscilloscope and
work out what the bits mean .. try doing that with a JSON-based interface. ;)

~~~
LukeShu
I disagree, it is still hard:
[https://bugs.freedesktop.org/show_bug.cgi?id=70290](https://bugs.freedesktop.org/show_bug.cgi?id=70290)

Further, there are still things for which there is not a right way to do it.
If you look at the man page for "unbuffer" (part of expect), there's a
"CAVEATS" section that describes a bug... that can't be fixed while PTYs are
in their current state. I wouldn't call it "well and truly solved".

------
thristian
Now I feel like I want to read a similar article describing how the Plan 9
terminal interface works, just to cleanse my palate.

------
Erwin
I learned something new from this: I was always under the impression that you
could not do anything when your process is stopped via ^Z. But apparently
there are two separate signals involved here: SIGTSTP (sent when ^Z is pressed
and blockable) and SIGSTOP (also suspending the process but NOT blockable)
(This reminds of the time SIGCLD / SIGCHLD were different).

I have a system where shell users run commands that can grab some locks, and
they like to press ^Z to suspend them which isn't compatible with a simple
fcntl-based lock.

~~~
mtdewcmu
vim and emacs both trap Ctrl-Z in the terminal and implement custom behavior.
If they didn't, I think, you would not be dropped gracefully into a shell
prompt after pressing Ctrl-Z; instead, you'd be left with the terminal in non-
canonical mode with the vim or emacs process suspended, and there would be
nothing graceful about that.

In your case, there should be a way to trap attempted Ctrl-Z suspends in your
process and break the locks (or do whatever you need to do).

------
McUsr
I have read a great book covering TTY program on ux machines, it is also great
for getting an understanding on how it all works. The book title is :
"Advanced Programming in the Unix Environment" By W. Richard Stevens and
Stephen A. Rago

It gives a thorough walk through of the ttys and pty's, IMHO it is a must read
for anybody wanting do to TTY programming, and it is also a great source for
understanding how the streams works in connection with the tty's/pty's.

------
gumby
> Line editing...a backspace key is often useful. This could of course be
> implemented by the applications themselves, but in accordance with the UNIX
> design philosophy, applications should be kept as simple as possible. So as
> a convenience, the operating system provides an editing buffer

Actually this is backwards.

Line discipline is a historical artifact. Typically mainframes had IO front
ends (AKA channel controllers) than managed IO so that the CPU (which BTW was
what was charged for) could be kept busy and not wait around for users (by
contrast CTSS, ITS and later machines like the Alto expressed an alternative,
radical idea which was that the computer was there to support the user).
Basically once a line was ready it was submitted by pressing the enter key and
then its process was unblocked and processed the data. (Nowadays we just queue
an event on the main thread...essentially the same thing!).

Likewise, a number of early network protocols like X.25 would charge per
"line" (more about that later).

Unix was originally written for the PDP-7 (& later 11) which were
minicomputers that didn't have channel controllers (in fact the earliest
arpanet interfaces that weren't IMPS were PDP-11s acting as networking channel
controllers for PDP-10 mainframes). Since IO was done by the kernel, you
didn't want to to an expensive context switch for the common case of just
reading a character and putting it into an input buffer.

So despite the comment quoted above, the true Unix model would have been for
the kernel to have been agnostic and for each program to decide how it wanted
to handle character input. But that would have been too expensive, so a more
pragmatic approach prevailed.

By the way there are all sorts of other fossils in the TTY system. Multics
used # to delete a character and @ to rub out the whole line -- a legacy of
printing characters that persisted into Unix for years ###### decades. This
seems weird today, but originally Multics (and later Unix) didn't have
traditional line disciplines, or even control characters, much less select().
So it was handy to be able to essentially do immediate mode editing right in
the input buffer.

Another fossil is that vi is just a visual mode grafted onto ed (clone of
Multics qed IIRC), which was an editor with commands based on line-at-a-time
IO.

ITS got proper IO virtualization first (and the predecessor to select, and
extended it to the network with SUPDUP, later also implemented by Multics and
eventually Unix, which made using Emacs possible) and actually had a more
general version of select() before Unix was written, but at the time it was
considered exotic.

Oh and the X.25 comment? Well in 1982 I was working in France and the only way
for me and my colleagues who had come from MIT to get back to our mail at the
MIT AI lab was a connect across the ocean on an international X.25 network
hooked into MIT Multics and thence connect to the AI lab. After using fully
interactive systems, it was painful to use a line-at-a-time system, so I
figured out how to reset the X.25 front end to ship a whole packet for every
character typed (packets had a maximum length so if you typed a really long
line it would send a packet's worth, so I hacked it into thinking that a
packet length was 1). Which was great, until shortly after the end of the
month when someone "upstairs" had a heart attack at the size of the networking
bill...

------
mtdewcmu
I recently wanted to give a command-line program the ability to wait for and
respond to keypresses instantly, without the user having to type Enter. It
sounded simple enough. Here's what you have to do to set this up:

    
    
      // headers for terminal control
      #include <termios.h>
      #include <unistd.h>
    
      // global variable
      struct termios saved_attributes;
    
      // prototype of atexit callback
      void reset_input_mode (void);
    
    
      // --- inside main()
    
      // local variable
      struct termios termios_data;
    
      // tell stdio not to buffer output
      setvbuf(stdout, NULL, _IONBF, 0);
    
      // incantation to the terminal
      tcgetattr(0, &termios_data);
      saved_attributes = termios_data;
      termios_data.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
      termios_data.c_cc[VMIN] = 1;
      tcsetattr(0, TCSANOW, &termios_data);
      
      // set atexit callback so we can restore the prior terminal mode upon exit
      atexit(reset_input_mode);
    
      // --- outside main()
    
      // implementation of atexit callback -- restores terminal to prior mode
      void reset_input_mode (void)
      {
        tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
      }
    
    

Now, you can read input one byte at a time (with, e.g. getchar(3)) and output
appears instantly.

------
wslh
Uh! It makes me remember some obscure and mystic stuff for the IBM 3151
terminal [1]. This terminal has an auxiliary port where you can connect the
printer, so it was a (not so) dumb terminal where you can connect other
peripherals. There was a government project to attach an individual printer
(instead of connecting it to the server) to some terminals but nobody knew the
escape codes to control that port. At some point the IBM guys sent a difficult
to find IBM 3151 with these details. Probably this manual is now already
available on Internet.

[1]
[http://ps-2.kev009.com/tl/techlib/manuals/adoclib/aixasync/a...](http://ps-2.kev009.com/tl/techlib/manuals/adoclib/aixasync/asycomgd/term3151.htm)

------
hollerith
Terminal.app and other terminal emulators are the prototypical case where a
TTY -- more precisely, a TTY/PTY pair -- are required these days.

Every virtual terminal in the Linux text-mode console also requires a TTY/PTY
pair.

Shell mode (and more generally any mode derived from Comint mode) in Emacs
also requires a TTY/PTY pair: although the Emacs documentation claims that
Comint-derived modes can be configured to use a pipe instead, a comment in
comint.el confesses that no one has gotten that to actually work.

And of course, SSHing into a remote host requires at least one TTY/PTY pair
(on the remote host).

Are there other place in the software on, e.g., a Mac reliant on a TTY? If
there are, please tell me what they are!

~~~
LukeShu
Both `ssh` and `sudo` will only ask you for a password from a TTY; they'll
refuse to work if they can't find a TTY (unless, of course you have a
graphical prompt configured).

The `expect` suite of tools uses a TTY/PTY pair to script programs that might
otherwise be unhappy with not having a PTY. Same with the `script` program,
which on my system is part of the 'util-linux' package.

On GNU/Linux, `systemd-nspawn` (a chroot-like tool) uses a TTY/PTY pair to
isolate the child process from the outside world.

------
BasDirks
This guy's other articles are gold too.

------
LukeShu
It may be worth noting that both of the kernel source files mentioned have
moved from `drivers/char/` to `drivers/tty/`. I _think_ that all of the other
details are up to date, though.

------
ch4s3
This was a great read, I hope it stays on the front page until morning so more
people see it. It filled some gaps left from my Operating Systems class about
TTY, especially the history with Teletypes.

------
Patient0
I wish he had said something about'^?' vs '^H'. To this day I still struggle
sometimes when logged into a Unix machine and run into this problem.

~~~
kps
ASCII DEL (^?) is natively a ‘forward delete’ operation; it erases the
character under the cursor while advancing to the next character. That's why
its value is 0x7F: on paper tape it's all holes, so it can punch over any
other character.

The problem arose from two later, non-Unix (and arguably deliberately anti-
Unix¹) influences.

One, as the Debian notes linked in a sibling comment mentions, was GNU Emacs,
whose bindings came from custom non-ASCII, bucky-bit² devices, and gleefully
stepped on ASCII control characters like Backspace (^H) and Device Control 3
(^S).

The other was DEC's new VT2x0 series of terminals, with the VMS-oriented LK201
keyboard³, that hid Backspace and Escape on unlabelled function keys (F12 and
F11 respectively) and made the key in the typist's backspace position send
DEL. (The VT100⁴ had had both Backspace, in the typist's location, and DEL,
where US PC keyboards have \|.) This was the big one, as BSD 4.2 for the VAX
spread and dragged the LK201 along with it.

¹ [http://richard.esplins.org/static/downloads/unix-haters-
hand...](http://richard.esplins.org/static/downloads/unix-haters-handbook.pdf)

²
[http://en.wikipedia.org/wiki/Bucky_bit](http://en.wikipedia.org/wiki/Bucky_bit)

³
[http://deskthority.net/wiki/DEC_LK201](http://deskthority.net/wiki/DEC_LK201)

⁴
[http://terminals.classiccmp.org/wiki/index.php/DEC_VT100](http://terminals.classiccmp.org/wiki/index.php/DEC_VT100)

------
codehero
All these years later and under Linux I still can't unambiguously read a UART
line break condition....

