Skip to content
  • Will Thompson's avatar
    monitor: use a PTY to send SIGINT to privileged child · d2316f2b
    Will Thompson authored
    Previously, we sent SIGKILL directly to the child process. If we're
    monitoring the system bus, the child process is owned by root, so the
    parent process can't send it signals. In this case, we relied on the
    child process dying with "Broken pipe" when it next tries to write to
    stdout (which we close).
    If you run `pkexec dbus-monitor --system` in a terminal, you are able to
    press Ctrl-C to send SIGINT to that privileged child process. This is
    because the signal is not sent directly. Instead, the terminal emulator
    writes ^C to the child's controlling terminal; the kernel turns this
    into SIGINT and send that to the child.
    We can do the same thing here. Here are the steps:
    * Create a pseudo-terminal (PTY) master/slave (not my terminology) pair
    * Make this PTY the controlling terminal for the child process:
      * Make the slave FD the stdin for the child
      * In a GSubprocessLauncher child_setup function, which runs between
        fork() and exec():
        * Move the process to a new session with setsid(), removing any
          existing controlling terminal
        * Call ioctl(STDIN_FILENO, TIOCSCTTY, 0) to set the stdin FD as the
          controlling terminal
    * When it comes time to kill the child, write ^C into the master side of
      the PTY
    We continue to send SIGINT (rather than SIGKILL; it seems kinder) the
    old-fashioned way (in case something goes wrong setting the controlling
    terminal) and closing the pipe so that the child eventually dies with
    EPIPE (in case the old-fashioned way fails too).
    A potential fly in the works is that, in the Flatpak case, the immediate
    child is a flatpak-spawn process; `pkexec dbus-monitor --system` is
    actually launched from the session helper. Happily, the session helper
    already calls setsid() + TIOCSCTTY if any of stdin/stdout/stderr on the
    spawned process are TTYs
    so we just skip the child_setup function in that case.
    See for some useful
    background reading on controlling terminals.