đŸ Archived View for gemini.omarpolo.com âș post âș suspend-unix-process.gmi captured on 2023-07-10 at 13:52:40. Gemini links have been rewritten to link to archived content
âŹ ïž Previous capture (2023-01-29)
-=-=-=-=-=-=-
I want to break free!
Written while listening to âI want to break freeâ by The Queen.
Published: 2021-07-15
Tagged with:
On UNIX when you type C-z (hold control and press âzâ) at your terminal the program you have opened, letâs say ed(1), will be suspended and put into the background. Later, with âfgâ it can be bring back into foreground.
But how it works?
When the tty driver sees that youâve typed control-z itâll send a SIGTSTP signal to the process in the foreground. Then, the shell should do its part and handle the situation, usually by printing something like âSuspended $prognameâ and the prompt.
Actually, the exact key binding is probably customizable via stty(1), but thatâs not important.
The tty driver is not the only one thatâs entitled to send signals. Any program can send a SIGTSTP to any other program (OK, as long as they are running as the same user, or youâre root); so, for instance, you can kill ed with âkill -TSTP $pidâ from another terminal to achieve the same thing as pressing C-z.
In a ideal world a program shouldnât know anything about SIGTSTP. This is not an ideal world though. If you donât trust me, the proof is that ed is not the most used text editor. Most interactive terminal applications uses ncurses (or similar).
By calling âraw()â when initialising ncurses, the program wonât receive signals for special keys (like control-c, control-z, âŠ), so it can handle those keys by itself. (Think how funny must be running emacs in a terminal and seeing it being killed every time you press control-c.)
So, how can ncurses applications (like vi, tmux & co) suspend themselves? The answer is pretty easy given the context, yet it took me a while to figure it out:
#include <ncurses.h> #include <signal.h> #include <unistd.h> /* if youâre using ncurses: */ endwin(); /* kill the current program */ kill(getpid(), SIGSTOP); /* if youâre using ncurses, redraw the UI */ refresh(); clear(); redraw_my_awesome_ui();
Yes, by killing yourself. (UNIX terminology leads to funny sentences, see?)
But why SIGSTOP and not SIGTSTP as previously stated? Well, the manpage for signal(3) says that SIGSTOP and SIGTSTP have the same default action, but while a SIGTSTP can be caught or ignored, SIGSTOP cannot.
The astute reader may have read the kill(2) man page and saw that if pid is zero, the signal is sent to the current process; so we could have wrote âkill(0, SIGSTOP)â. Unfortunately, what kill(2) *exactly* says is
If pid is zero:
sig is sent to all processes whose group ID is equal to the process group ID of the sender, and for which the process has permission; this is a variant of killpg(3).
If your program is made by multiple process, youâd stop all of them! This may be preferred, or it may not; I chosen to explicitly kill the current process (and only that!) for this very reason.
Itâs fun to see how things works, especially when you are able to figure it up by yourself. I was trying to handle C-z in telescope, a gemini client Iâm writing, and it took me a while to understand how suspend the current process. I searched a bit the internet but everything I found boiled down to âcontrol-z, bg/fg or kill it with SIGTSTPâ; which turns out was the correct solution, but wasnât clear to me at first. I tend to forget that I can send signals to myself.
curs_inopts(3) manpage â talks about cbreak, raw and other interesting stuff
-- text: CC0 1.0; code: public domain (unless specified otherwise). No copyright here.