Bug 7713 - Graphical installer/tl-setup does not respond to SIGINT when relaunched through sudo
Summary: Graphical installer/tl-setup does not respond to SIGINT when relaunched throu...
Status: CLOSED FIXED
Alias: None
Product: ThinLinc
Classification: Unclassified
Component: Server Installer (show other bugs)
Version: trunk
Hardware: PC Unknown
: P2 Normal
Target Milestone: 4.13.0
Assignee: Pierre Ossman
URL:
Keywords: frifl_tester, prosaic
Depends on:
Blocks:
 
Reported: 2021-05-24 09:16 CEST by William Sjöblom
Modified: 2021-06-04 14:10 CEST (History)
1 user (show)

See Also:
Acceptance Criteria:
* Ctrl+C should terminate install-server/tl-setup in text mode * Ctrl+C should terminate install-server/tl-setup in GUI mode * GUI mode should still work without a terminal (e.g. via .desktop file) * GUI mode should not terminate if the terminal it was launched from dies


Attachments

Description William Sjöblom cendio 2021-05-24 09:16:47 CEST
Launching tlinstaller/tl-setup in graphical mode and interactively letting it restart itself as sudo results in SIGINTs sent by the shell not registering properly.

Steps to reproduce (Fedora 33, ThinLinc server 4.12.1):
 1. `./install-server`
 2. Accept to relaunch the process through sudo
 3. Enter password
 4. Return to the terminal and press Ctrl-C

One would expect that this would exit the newly started installer, but instead, it does nothing. This does not happen if we run `sudo ./install-server`.
Comment 1 William Sjöblom cendio 2021-05-25 16:43:44 CEST
In development builds of the server, this issue seem only to happen when
running the installer and not tl-setup. This, however, is not the whole
story.

When running `sudorelaunch` in graphical mode, we use `subprocess.Popen` to
relaunch ourselves as root. Popen differs slightly between python 2 and 3
where the former has the defaults the =close_fds= to `False` and the latter
to `True`. Since tlinstaller currently runs in python 2 and tl-setup in
python 3 this parameter is different for the two.

When starting the sudo subprocess with =close_fds=True= (as is the case for
tl-setup) and send a SIGINT to the parent process, the child sudo process,
and in turn our relaunched grandchild process gets signaled with a SIGHUP
and consequently terminates. When starting the sudo subprocess with
=close_fds=False= on the other hand (as is the case for tlinstaller) none of
the child processes are signaled when the parent receives a SIGINT and only
the parent terminates, leaving the children running.

None of the above scenarios are ideal as we want the re-launched grandchild
process to receive the SIGINT such that a KeyboardInterrupt is raised and
we can clean up after ourselves.

The root cause of this problem is that we, assuming we are running the
*graphical* installer/tl-setup, assign the subprocess a new ctty even
though we might already be running in a ctty.

Following is a potential solution for the problem:
- If we already have a ctty: start the sudo subprocess with a regular Popen
  call without assigning it a new ctty.
- If we do not have a ctty: start the sudo process assigning it a new ctty
  (as we are currently doing). Since we don't have a ctty there is
  practically no one that can send us a SIGINT, so not respecting these is
  not an issue.
Comment 2 William Sjöblom cendio 2021-05-25 17:17:45 CEST
When playing around with possible solutions for this bug I came across a couple of scenarios where starting the installer/tl-setup in the background (e.g `./install-server &`) would not work as intended. Doing this for long-running graphical applications is pretty common, so verifying that starting the process in the background works as intended is a good idea during testing.
Comment 3 Pierre Ossman cendio 2021-05-26 14:05:52 CEST
It is unclear when this ever worked, perhaps never? Or it worked before we started using sudo (bug 4191)? Or when sudo got its SIGINT bug fixed (unlikely, but see bug 7508)?

Still, we really don't want a SIGHUP as that is a very abrupt way of stopping the process.
Comment 4 Pierre Ossman cendio 2021-05-26 16:02:54 CEST
I can't fully reproduce this. On 4.12.1 the initial process dies when I press Ctrl-C, rather than nothing happening. The spawned sub process still carries on though.

This becomes extra weird when running tl-setup as then the parent process will pop up the dialog stating how to restart tl-setup. However closing this doesn't close tl-setup, which will happily continue in the background.
Comment 5 Pierre Ossman cendio 2021-05-26 16:18:32 CEST
(In reply to William Sjöblom from comment #1)
> 
> The root cause of this problem is that we, assuming we are running the
> *graphical* installer/tl-setup, assign the subprocess a new ctty even
> though we might already be running in a ctty.
> 

It doesn't seem like we ever need to create a tty. If an askpass program is provided then sudo (at least current version, even those on RHEL 7) is perfectly fine without a tty.
Comment 6 Pierre Ossman cendio 2021-05-27 16:44:15 CEST
This got complicated:

 * We need to disconnect sudo from our current tty or it will get a SIGHUP (and die) if the current terminal goes away (e.g. closing the xterm window)

 * Disconnecting sudo from our current tty means it doesn't get a SIGINT when Ctrl+C is pressed, so we need to help things out in that case

 * Sending signals to sudo is generally[1] not allowed as it is running as root, and the parent process is not.

[1] For some reason sudo on my Fedora 33 behaves differently and only shifts the effective uid to root (keeping the real uid), something that allows signals to still be sent to it. But we want things to work reliably so we cannot hope that this is common.


So to get a SIGINT to sudo it seems we need to go the terminal route. I.e. continue setting up our own tty and then use the master channel to pass a Ctrl+C down to sudo.

For this to work though we need to make sure it stops getting a SIGHUP if the parent process dies. The easiest way is probably to get back a bit of the Python 2 behaviour and make sure that sudo also gets a copy of the master fd.
Comment 11 Pierre Ossman cendio 2021-05-28 10:15:39 CEST
Works well now. Tested on RHEL 8 and Ubuntu 20.04.

> * Ctrl+C should terminate install-server/tl-setup in text mode

Yup.

> * Ctrl+C should terminate install-server/tl-setup in GUI mode

Yup.

> * GUI mode should still work without a terminal (e.g. via .desktop file)

Yup.

> * GUI mode should not terminate if the terminal it was launched from dies

Yup. Started via an xterm that I then closed.
Note that the initial sudo question still dies if the terminal goes away. But if you've gotten to the sudo prompt then things are fine.



Note that the code ended up being Python 3 specific. So graphical mode is broken for install-server until bug 7702 is fixed.
Comment 12 Frida Flodin cendio 2021-06-04 14:10:25 CEST
Works fine! Tested on SLES 15. 

> * Ctrl+C should terminate install-server/tl-setup in text mode
Yes.


> * Ctrl+C should terminate install-server/tl-setup in GUI mode
Yes, once you close the pop-up with information about how to restart tl-setup.


> * GUI mode should still work without a terminal (e.g. via .desktop file)
It does.


> * GUI mode should not terminate if the terminal it was launched from dies
It doesn't.

Note You need to log in before you can comment on or make changes to this bug.