Go Tidbit: Putting The Terminal Into Raw Mode
I learned something new today. It helped solve a long-standing bug in Bullet.
Bullet is an application deployment tool that I wrote several years ago. It is a simple tool that SSHs into a server and uses Docker to run applications.
I use it in production for some of my projects.
About the Bug
Bullet can SSH into a remote server, spin up a one-off container and attach the terminal to it. You could run commands in that container as if you were SSH’ed directly into that environment.
Except control characters didn’t work.
You couldn’t press Ctrl+C
to interrupt the currently running program. You couldn’t press Ctrl+D
to signal EOF.
In fact, for example, pressing Ctrl+C
would kill Bullet, ending the SSH session.
And why didn’t control characters work? Because I didn’t put the terminal into raw mode before connecting to the container over SSH.
SSH Client in Go
Here is a simplified Go code that connects to a remote server over SSH and acts like an SSH client:
|
|
It is almost like running ssh carrot.local
.
In fact, after running the Go code, I was greeted with a familiar prompt.
[hjr265@Potato gossh]$ go run .
[hjr265@Carrot ~]$
But, unlike the ssh
program, pressing Ctrl+C
here terminated the Go program instead of sending a SIGINT
to the remote server.
[hjr265@Potato gossh]$ go run .
[hjr265@Carrot ~]$ signal: interrupt
[hjr265@Potato gossh]$
The Fix: Put The Terminal Into Raw Mode
The solution turned out to be fairly simple. But it is something that I only came to learn about today.
Terminals can run in raw mode or cooked mode.
In cooked mode data is preprocessed before being given to a program, while raw mode passes the data as-is to the program without interpreting any of the special characters. […]
What cooked mode means is dependent on the operating system. However, in cooked mode, control characters are handled by the operating system.
When Ctrl+C
is pressed, the operating system sends SIGINT
to the currently running program. In most cases, the program aborts immediately.
But what we want here is to handle that control character ourselves.
And we can do that by putting the terminal into raw mode:
|
|
The function term.MakeRaw
comes with the golang.org/x/term
package.
A call to this function will put the terminal into raw mode and return a copy of the old state. This old state can be used to restore the terminal to its previous mode with the term.Restore
function.
Working SSH Client in Go
|
|
Now I can send SIGINT
s to my heart’s content:
[hjr265@Potato gossh]$ go run .
[hjr265@Carrot ~]$ sleep 5
^C
[hjr265@Carrot ~]$
This post is 47th of my #100DaysToOffload challenge. Want to get involved? Find out more at 100daystooffload.com.
comments powered by Disqus