Terminals are the windows to the Linux operating system. Like in the real world, windows sometimes get broken. Whether due to a bad command, random data, key combination, or a bug, our current terminal may break and stop functioning properly.
In this tutorial, we explore ways in which terminals can break and how to reset them. First, we discuss how a terminal can become problematic. Next, we tackle the problems that can occur one by one, presenting some universal solutions along the way. Finally, we deal with commands to reset a broken terminal and conclude with some radical terminal restart methods.
We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.
2. Breaking Terminals
As with any problem, before looking at how to fix it, it’s best to first understand it.
Terminals can break in many ways: misbehaving processes, TTY or PTY misconfiguration, shell issues, or even kernel problems. Let’s look at some common ways a terminal can stop functioning correctly.
2.1. Long Running or Hung Commands
Indeed, a terminal can look broken but work as expected. For example, the current foreground job can run for a long time or hang. This may leave the terminal in an unresponsive state or prevent users from entering commands:
$ sleep 666 [blank]
Above is a simple demonstration with a long pause via sleep, but a misbehaving process can produce similar results:
$ ./badscript.sh [blank]
Usually, it’s not easy to distinguish between a hung and a long-running process.
2.2. Control Characters
Due to their nature, terminals are sensitive to text. The latter can come in many forms, all of them containing characters.
Importantly, characters can belong to several categories, depending on their type. For example, we have regular printable characters. However, there are other types:
How does the terminal receive unexpected characters? There are many ways, but a very common one is to output a binary file to stdout with a command like cat file.bin. In essence, when we output a binary file to it, the terminal can perform the functions of many characters as they are interpreted from the binary data.
In fact, some control and special character shortcuts can easily cause problematic behavior, such as:
Further, the last issue can also happen due to signaling.
Of course, part of the control characters and shortcuts are related to signals.
$ kill -SIGSTOP $$ [blank]
Actually, it’s easy for the above to occur when using a PID number instead of the $$ special variable. In that case, entering an incorrect PID may send the signal to a wrong but existing process.
Naturally, the methods we discussed do not comprise all possible ways to make terminals behave badly. However, we’ll discuss many ways to reset a terminal, so we can cover our bases.
Now, after we managed to ruin both our TTY/PTY and our shell, let’s see what we can do to correct them.
3. Dealing With the Blocking Process
When a running job prevents us from using the terminal, the remedy is simple: deal with that problematic process. There are many ways to do this:
- Ctrl+C should interrupt the currently running process
- Ctrl+Z should background the currently running process
- identify the process as a child of the current terminal and kill it
Importantly, the first options require both job control and terminal input to work. Furthermore, we can detect processes by name, port, file usage, or other criteria as well. Another easy way is by getting the child processes the problematic terminal.
Unlike the next solution, this one should be applied from a separate terminal, which is still working.
4. Undo Control Character and Signal Issues
Which control character or signal we use for a correction depends on the initial problem.
For example, there is the Ctrl+S flow control shortcut, which freezes the terminal. After it, we should use the Ctrl+Q shortcut to unfreeze it. Similar behavior occurs with the SIGSTP and SIGCONT signals.
In addition, some common signals with associated special control characters include:
- Ctrl+C for SIGINT to interrupt
- Ctrl+Z for SIGSTP to stop or background
- Ctrl+\ for SIGQUIT to quit with a dump
$ trap 'trap "" EXIT; sleep 666' EXIT STP INT QUIT HUP PIPE TERM
Here, our trap handler runs sleep for a long time. This means none of the signals will get processed before the sleep concludes, if at all. Such a trap could hinder our attempts to restore a terminal’s function via both control characters and signals, leaving us with a blocking process.
On the other hand, there are untrappable control characters like Ctrl+J and Ctrl+D, which may replace the usual Return key for sending commands. In this case, any reset commands need to end with those characters instead of Return.
Also, in most vt100-compatible terminals, the 0x0e byte causes much of the text output after it to come out as unexpected characters. To reset this, 0x0f can be used.
As can be observed, issues of this kind come in many flavors and for many reasons. Their roots or fixes are not always obvious. In fact, once we reach a point that allows us to do so, we can use a single command to reset our terminal to its default settings.
5. Reset Commands
When the terminal still accepts regular and command input, we can apply one of several reset commands.
The reset command passes several arguments to tput:
- clear to clear the screen
- rmacs to end alternate character set mode
- rmm to end meta mode
- rmul to end underscore mode
- rs1, rs2, rs3 to reset to sane modes
Further, we can check all mode and switch definitions.
In addition, tput has the reset flag, which sends its own reset strings and initializes the same way as tput init.
Finally, there is the stty command for printing and changing terminal settings. tput reset also calls stty sane, which applies some “sane defaults” to the terminal configuration.
The point of such commands is the ability to skip the details of each flag in favor of just restoring the terminal. However, we always have at least one radical way.
6. TTY Generator
Recent Linux distribution versions often provide systemd as their initialization system. It enables easier TTY management via the getty services.
In short, [email protected] handles /dev/tty* terminals by generating, providing, and monitoring them. Instead of all TTYs being instantiated upon start, they get generated on demand. Thus, we can actually use a command to dynamically restart a terminal via its responsible service:
$ systemctl restart [email protected]
This is important because TTYs are often the backbone of terminal activity, and having an unresponsive one may pose a problem.
Of course, another radical but simple solution that should work most of the time is to kill a TTY’s shell from another terminal with kill -SIGKILL.
In this article, we discussed ways to break a terminal, as well as possible solutions for getting back and restoring an already broken terminal.
In conclusion, it may not always be possible to reset or restore a terminal from within itself, but we can at least rescue or restart it via another TTY.