Systemd – Does It Protect Processes Against Acquiring a Controlling Terminal?

systemdtty

man 7 daemon

When a traditional SysV daemon starts, it should execute the following steps as part of the initialization. Note that these steps are
unnecessary for new-style daemons (see below), and should only be implemented if compatibility with SysV is essential.

[…]

6. In the child, call setsid() to detach from any terminal and create an independent session.

7. In the child, call fork() again, to ensure that the daemon can never re-acquire a terminal again.

but compare this to processes started with no vestige of SysV compatibility:

$ ps -efj
UID        PID  PPID  PGID   SID  C STIME TTY          TIME CMD
root         1     0     1     1  0 May10 ?        00:06:44 /sbin/init
...
root       185     1   185   185  0 May10 ?        00:09:48 /lib/systemd/systemd-journald
root     16434     1 16434 16434  0 May26 ?        00:00:11 /usr/sbin/rsyslogd -n

The processes for both rsyslog.service and systemd-journal.service are session leaders (SID = PID).

It seems that if such programs were configured to log to a TTY, they would gain the TTY as a controlling terminal, and receive an unwanted / fatal signal when the TTY is hung up / receives Ctrl+C, respectively. Unless they remember to set O_NOCTTY when opening the TTY file.

It seems this is a little pitfall when writing or converting a program to run as a systemd service w/o any SysV compatibility, if your program supports writing messages to custom files. It does not seem to be pointed out by this doc which advocates the systemd style. The doc rather implies the opposite, by insisting that double-fork is necessary to avoid this on SysV, and then not mentioning this as an issue when describing the steps a native systemd service would use.

Is that correct? Does systemd provide some protection against this I have overlooked, or is the issue pointed out elsewhere in the systemd doc?

Best Answer

Does systemd provide some protection against this […]?

You are assuming that it should. On the contrary, consider settings like TTYPath and services like getty@.service. The ability to gain a controlling terminal is actually necessary, in order that service management can encompass TTY login services, which need to do precisely that.

What actually protects against it is the move away from automatic allocation of a controlling terminal at open(), and discarding the old semantics. Or would protect against it. It is not the case on Linux, but on FreeBSD, NetBSD, OpenBSD, and Hurd nowadays the O_NOCTTY flag to open() is entirely superfluous. The only way to acquire a controlling terminal is by explicitly demanding it, with ioctl(…TIOSCTTY). This has actually been the case for approaching a quarter of a century, since the days of 4.4BSD.

In the meantime, the habit to get into on Linux is the habit that has also been the case for a long time, long before systemd: O_NOCTTY everywhere. ☺

(Yes, the GNU and musl C libraries do not give this to you for fopen(). This is one of several reasons that fdopen() is still a useful mechanism.)

Service management with the nosh toolset's service-manager takes a slightly different tack on this. Rather than always make dæmon processes into session leaders, each service being allocated its own kernel session object that then sees no use, only specific services also chain through setsid explicitly; such as ttylogin@* services that use open-controlling-tty, agetty@* services where of course agetty is setting the controlling terminal, and getty@* services. (As noted in the service source, mgetty calls setsid() itself.)

Further reading

Related Question