How Can I Write to Console During Shutdown?

Standard output and error of services under service management — be it s6, runit, perp, daemontools, nosh service management, or systemd — is not the console. It is a pipe connected to some form of log writer.

For a systemd service you need a TTYPath=/dev/console and a StandardOutput=tty in the .INI file to change this, StandardInput=tty if you want to read (but you do not) as well as write. Witness systemd's pre-supplied debug-shell.service.

This is a general principle that is not systemd specific. Dæmon context involves (amongst other things) not having a controlling terminal and not having open file descriptors for terminals, and under proper service management (such as all of the daemontools family) this is where one starts from, the state that a service process begins in when the supervisor/service manager forks it. So to use the console the service has to explicitly open it.

In systemd, the aforementioned TTYPath and StandardInput settings cause the forked child process to open the console before it executes the service program proper. This is hidden inside systemd and you do not really get to see it. In the run program of a similar nosh service, the run program explicitly uses some of the nosh toolset chain-loading tools to do the same thing before executing the main program (emergency-login in this case):

% cat /etc/service-bundles/services/emergency-login@console/service/run
#!/bin/nosh
#Emergency super-user login on console
setsid
vc-get-tty console
open-controlling-tty
vc-reset-tty --hard-reset
line-banner "Emergency mode log-in."
emergency-login
%

Ironically, you do not need the logger command, or any syslog dependencies. There is no point in writing this interactive prompt to a log. But you really should run this service unprivileged, on principle. It does not need superuser privileges, for anything that it does.

On another principle, don't make your script use #!/bin/bash unless you really are going to use Bashisms. One of the greatest speedups to system bootstrap/shutdown in the past couple of decades on Debian Linux and Ubuntu Linux was the switch of /bin/sh from the Bourne Again shell to the Debian Almquist shell. If you are going to write a script as simple as this, keep it POSIX-conformant and use #!/bin/sh anyway, even if you are not using Debian/Ubuntu, and on Debian/Ubuntu you'll get the Debian Almquist shell benefit as a bonus.

Moreover, if you decide to have more than a glass TTY message, with a tool like dialog, you will need to set the TERM environment variable so that your programs can look up the right escape and control sequences to emit in the terminfo database. Again, witness debug-shell.service. (In the aforegiven run program, for comparison, the vc-get-tty tool sets TERM.)

Similarly, you will want script errors to be logged. So standard error should be left pointing at the journal with StandardError=journal. Here's a nosh service run program that illustrates the equivalent of this, and also shows dropping user privileges for a program that really does not need them, which in a systemd .INI file would be User=daemon:

% cat /etc/service-bundles/services/monitor-fsck-progress/service/run
#!/bin/nosh
#local socket used for monitor-fsck-progress
local-stream-socket-listen --systemd-compatibility --backlog 2 --mode 0644 /run/fsck.progress
setsid
setlogin -- daemon
vc-get-tty console
fdmove -c 4 2
open-controlling-tty
fdmove 2 4
setuidgid -- daemon
./service
%

The program run by ./service in this case presents a full-screen TUI on the console, whilst its errors are sent to the logging service. This is the stuff that one needs to do, under service managers in general, in order to run such programs as services, talking to the console.

Of course, any such full-screen TUI program will conflict with systemd's "A stop job is running", also written to the console. But that is your problem. ☺

Further reading

  • https://unix.stackexchange.com/a/468457/5132
  • https://unix.stackexchange.com/a/250965/5132
  • https://unix.stackexchange.com/a/499148/5132
  • https://unix.stackexchange.com/a/233855/5132
  • whiptail or dialog