As @AndyDalton and @muru pointed out(thank you!), SysVinit and Systemd work differently.
In systemd daemon processes are spawned only by the init process. cli-tools to control daemons(like systemctl) send commands to the init process via IPC and the init process does the job. They use sd-bus, their own implementation of D-bus. And probably can fall back to using sockets if D-bus System Message Bus doesn't work properly, but this requires root privileges. (I'm not sure if it really can fall back. It's my guess based on the description of this case and this discussion)
In the case of SysVinit, sending signals to daemons and spawning daemon processes are the job of daemon-specific bash-scripts. The init process runs this scripts on boot. But if you want to start a daemon manually via cli you run this scripts yourself. Then the daemon becomes a child of your bash process and has a controlling terminal. For more details look at sysvinit daemon scripts examples on the internert and the "service" cli-tool. "service" is just a bash script. It can be found inside debian package "sysvinit-utils". It seems the init process keeps track of running daemons only using PID-files.
That's why SysVinit daemons need to deatach themselves from controlling terminal. And New-Style daemons (systemd, upstart, etc.) don't need it. This also explains this part from manual:
- As new-style daemons are invoked without a controlling TTY
(but as their own session leaders) care should be taken to
always specify
O_NOCTTY
on open()
calls that possibly
reference a TTY device node, so that no controlling TTY is
accidentally acquired.
Conclusion:
Systemd supports SysVinit-style double-forking daemons(type=forking), but this is not the recomended way to write daemons. Double forking is not needed.
It seems systemd does setsid() for you:
Note that new-style init systems guarantee execution of daemon
processes in a clean process context: it is guaranteed that the
environment block is sanitized, that the signal handlers and mask
is reset and that no left-over file descriptors are passed.
Daemons will be executed in their own session, with standard
input connected to /dev/null and standard output/error connected
to the systemd-journald.service(8) logging service, unless
otherwise configured. The umask is reset
This is another reason why daemons don't need to detach themselves from controlling terminal. They are already in a new session.
daemon process is the leader of its session. So daemon should be careful with opening control terminals to not acqure controlling terminal. But do this accidently is a little bit harder on modern OS And acquiring controlling terminal probably not that big problem if SIGHOP is only a request to reload configuration.
I'm not sure but it seems systemd uses control groups to track and kill child processes of daemon Why it need setsid then? My be this is instead sessions now or control groups need separated sessions -- I don't know. May be sessions were never used for this. But now it uses control groups.
UPD:
- @Uncle Billy mentioned other signals that can be sent to a process via terminal. I did a little research. SIGINT, SIGQUIT and SIGSTP are sent by tty line discipline. These signals are sent if the flag ISIG in c_lflag in termios is set. Line discipline sends the signal to foreground process group. Not to the shell. Shell reports the pid of foreground process group to line discipline in advance. I couldn't find default value for ISIG. But most likely if foreground process group is not set manually(i.e. our process is not a job aware shell), a process won't get these signals.
Probably this also have nothing to do with whether the terminal is controlling or not. This is more about job control. But I could be wrong.
I wonder job-unaware shells (like Bourne shell) deal with SIGINT. the command "trap" indicates that they have a different mechanism for this.
systemd
can manage a daemon, there's nothing that says it must manage all daemons.