3

All sources I've read about writing a daemon say that daemons should detach themselves from controlling terminal so that if the terminal is closed, the daemon won't get SIGHUP.

But daemons are always started by the init process. htop shows that my init process (systemd) doesn't have a controlling terminal, so daemons it spawns don't have one either. I don't know how it works in SysVinit, but there are hints in changelogs that it either doesn't have a controlling terminal either or uses "/dev/console". I could be wrong, but it seems this is a special kernel tty that always exists and can't be closed.

Do I miss something?

I have a guess that this is more about starting a new process session for each daemon than detaching from a controlling terminal. Sessions can help in cases when a daemon spawns his child processes. A session tracks these child processes. This allows us to kill them all together.

7
  • 5
    It is not true that "daemons are always started by the init process". I can start/stop/restart daemon processes via the command line. Commented Jan 14, 2021 at 22:43
  • @AndyDalton, don't cli tools only ask the init process to start/stop/restart the daemon via IPC? Commented Jan 14, 2021 at 23:02
  • 1
    It's an issue of "control". Rather than trusting the initiating process to exist forever (a Bad Idea), the daemon takes control and detaches. The daemon does not want to be affected by the terminal at all. Except in debugging the daemon
    – waltinator
    Commented Jan 15, 2021 at 1:12
  • 3
    @Alexey true of systemd, upstart, etc., not true of sysvinit, where the init scripts are actual scripts that start the daemons.
    – muru
    Commented Jan 15, 2021 at 4:53
  • 1
    Also, note that because something like systemd can manage a daemon, there's nothing that says it must manage all daemons. Commented Jan 15, 2021 at 22:46

1 Answer 1

2

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:

  1. 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:

  1. Systemd supports SysVinit-style double-forking daemons(type=forking), but this is not the recomended way to write daemons. Double forking is not needed.

  2. 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.

  1. 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.

  2. 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:

  1. @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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .