An old Unix mistake you could make when signaling init (PID 1)

January 11, 2024

Init is the traditional name for the program that is run to be process ID 1, which is the ultimate ancestor of all Unix processes and historically in charge of managing the system. Process ID 1 is sufficiently crucial to the system that either it can't be killed or the system will reboot if it exits (or both, and this reboot is a hack). These days on Linux, PID 1 often isn't literally a binary and process called 'init', but the *BSDs have stuck with an 'init' binary.

Historically there have been a number of reasons for the system administrator to send signals to init, which you can still see documented for modern Unixes in places like the FreeBSD init(8) manual page. One of them was to reread the list of serial ports to offer login prompts on and often in the process to re-offer logins on any ports init had given up on, for example because the serial getty on them was starting and exiting too fast. Traditionally and even today, this is done by sending init a SIGHUP signal.

The kill program has supported sending signals by name for a long time, but sysadmins are lazy and we tend to have memorized that SIGHUP is signal 1 (and signal 9 is SIGKILL). So it was not unusual to type this as 'kill -1 1', sending signal 1 (SIGHUP) to process ID 1 (init). However, this version is a bit dangerous, because it's one extra repeated character away from a version with much different effects:

kill -1 -1

This is only one accidental unthinking repetition of '-1' (instead of typing '1') away from the version you want. Unfortunately the change is very bad.

(My view is that using 'kill -HUP 1' makes this much less likely because now you can't just repeat the '-1', although you can still reflexively type a '-' in front of both arguments.)

The destination process ID '-1' is very special, especially if you're root at the time. In both kill(1) and the kill(2) system call, using -1 as root means '(almost) all processes on the system'. So the addition of one extra character, a repeat of one you were just using, has turned this from sending a SIGHUP signal to init to sending a SIGHUP to pretty much every user and daemon process that's currently running. Some of them will have harmless reactions to this, like re-reading configuration files or re-executing themselves, but many processes will exit abruptly, including some number of daemon processes.

Back in the days when you were more likely to be SIGHUP'ing init in the first place, doing this by accident was not infrequently a good way to have to reboot your system. Even as recently as a decade ago, doing a 'kill -1 -1' as root by accident (for another reason) was a good way to have to reboot.

(At this point I can't remember if I ever accidentally made this mistake back in the old days, although I have typed 'kill -1 -1' in the wrong context.)


Comments on this page:

By R.Burns at 2024-01-12 10:36:48:

tend to have memorized that SIGHUP is signal 1 (and signal 9 is SIGKILL).

An odd bit of POSIX is that those (and five others) are well-defined arguments to the kill utility, and they're even called signal_number; however, they're not necessarily the values of SIGHUP and SIGKILL from signal.h. "1" is a string that can cause the utility to send SIGHUP, whatever its value might be.

Of course, the simplest way to implement that is to define SIGHUP as 1, and probably any reasonable implementor will do it. But this post is talking about historical behaviour, and I suspect there's some historical reason that POSIX was so careful to avoid giving actual numeric values (other than 0) for the signals. I wonder whether there's still some system lurking around where "kill -1 pid" sends a SIGHUP but SIGHUP is not signal 1.

Nit, for init(8) you'll get a slightly better online view with:

https://man.freebsd.org/cgi/man.cgi?query=init&sektion=8&manpath=freebsd-release

By cks at 2024-01-18 19:15:34:

I had a whole theory about why POSIX didn't nail down SIGHUP, but then I looked at the POSIX signal.h documentation and noticed the preface, so now I suspect it's simpler. I suspect that POSIX leaves signal numbers unspecified because ISO C leaves them unspecified, and POSIX wants to be aligned with ISO C.

Although I don't know for sure, I doubt that there was any Unix that redefined SIGHUP and other core signals present in V7. It would have been a bunch of work that'd inevitably cause a certain amount of bugs in code, and there's no clear reason to do so. However, additional signals added later could probably have different numbers in different strands of Unix (because they could have been added at different times and in a different order in the strands).

By root at 2024-04-28 09:18:52:
ps p0
By root at 2024-04-28 09:26:02:
$ uname
OpenBSD
$ ps p0
  PID TT  STAT        TIME COMMAND
    0 ??  DK       0:00.62 (swapper)
Written on 11 January 2024.
« MFA today is both 'simple' and non-trivial work
What we use ZFS on Linux's ZED 'zedlets' for »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Thu Jan 11 23:06:11 2024
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.