nbenedek 615c47e49f
deluge: Completely uninstall app
Remove deluge default file, config directory, and systemd service file

Test:
1. In a Vagrant box manually install the app
2. Modify the default download path and change the default password
3. Uninstall the app
4. Confirm that the download path and password are reset to default
5. Also test enabling and disabling the app after reinstallation

Signed-off-by: nbenedek <contact@nbenedek.me>
[sunil: Update docstings and make uninstall fail-safe]
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
2023-04-11 10:45:16 +05:30

167 lines
5.4 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""Configuration helper for BitTorrent web client."""
import pathlib
import shutil
import subprocess
import time
import augeas
from plinth import action_utils
from plinth.actions import privileged
from plinth.modules.deluge.utils import Config
DELUGED_DEFAULT_FILE = '/etc/default/deluged'
DELUGE_CONF_DIR = pathlib.Path('/var/lib/deluged/.config/deluge/')
DELUGE_WEB_SYSTEMD_SERVICE_PATH = '/etc/systemd/system/deluge-web.service'
DELUGE_WEB_SYSTEMD_SERVICE = '''
#
# This file is managed and overwritten by Plinth. If you wish to edit
# it, disable Deluge in Plinth, remove this file and manage it manually.
#
[Unit]
Description=Deluge Web Interface
Documentation=man:deluge-web(1)
After=network.target
[Service]
ExecStart=bash -c "/usr/bin/deluge-web --base=deluge $(/usr/bin/deluge-web --version | grep deluge-web | cut -f2 -d' ' | grep -q '^1.' && echo '' || echo '--do-not-daemonize')"
Restart=on-failure
User=debian-deluged
Group=debian-deluged
LockPersonality=yes
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
ProtectControlGroups=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectSystem=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictRealtime=yes
StateDirectory=deluged
SystemCallArchitectures=native
[Install]
WantedBy=multi-user.target
''' # noqa: E501
def _set_configuration(filename, parameter, value):
"""Set the configuration parameter."""
deluged_is_running = action_utils.service_is_running('deluged')
if deluged_is_running:
action_utils.service_stop('deluged')
deluge_web_is_running = action_utils.service_is_running('deluge-web')
if deluge_web_is_running:
action_utils.service_stop('deluge-web')
with Config(DELUGE_CONF_DIR / filename) as config:
config.content[parameter] = value
if deluged_is_running:
action_utils.service_start('deluged')
if deluge_web_is_running:
action_utils.service_start('deluge-web')
def _get_host_id():
"""Get default host id."""
try:
with Config(DELUGE_CONF_DIR / 'hostlist.conf') as config:
return config.content['hosts'][0][0]
except FileNotFoundError:
with Config(DELUGE_CONF_DIR / 'hostlist.conf.1.2') as config:
return config.content['hosts'][0][0]
def _set_deluged_daemon_options():
"""Set deluged daemon options."""
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
augeas.Augeas.NO_MODL_AUTOLOAD)
aug.set('/augeas/load/Shellvars/lens', 'Shellvars.lns')
aug.set('/augeas/load/Shellvars/incl[last() + 1]', DELUGED_DEFAULT_FILE)
aug.load()
aug.set('/files' + DELUGED_DEFAULT_FILE + '/ENABLE_DELUGED', '1')
# overwrite daemon args, use default config directory (same as deluge-web)
aug.set('/files' + DELUGED_DEFAULT_FILE + '/DAEMON_ARGS',
'"-d -l /var/log/deluged/daemon.log -L info"')
aug.save()
@privileged
def get_configuration() -> dict[str, str]:
"""Return the current deluged configuration."""
with Config(DELUGE_CONF_DIR / 'core.conf') as config:
download_location = config.content['download_location']
return {'download_location': download_location}
@privileged
def set_configuration(download_location: str):
"""Set the deluged configuration."""
_set_configuration('core.conf', 'download_location', download_location)
@privileged
def setup():
"""Perform initial setup for deluge."""
with open(DELUGE_WEB_SYSTEMD_SERVICE_PATH, 'w',
encoding='utf-8') as file_handle:
file_handle.write(DELUGE_WEB_SYSTEMD_SERVICE)
_set_deluged_daemon_options()
subprocess.check_call(['systemctl', 'daemon-reload'])
# Restarting an old deluge-web service stops also possible deluged process
# that was started from the web interface.
action_utils.service_try_restart('deluge-web')
# Wait until core configuration is available so that status of the app can
# be shown properly.
_wait_for_configuration('deluged', 'core.conf')
# Configure deluge-web to autoconnect to the default deluged daemon.
_wait_for_configuration('deluge-web', 'web.conf')
host_id = _get_host_id()
_set_configuration('web.conf', 'default_daemon', host_id)
def _wait_for_configuration(service, file_name):
"""Wait until configuration file has been created."""
conf_file = DELUGE_CONF_DIR / file_name
if conf_file.exists():
return
# deluge-web creates files on first run. deluged on the other than differs
# in version. Older version in Debian Buster creates the files after a
# restart while newer versions create the files on first run. The following
# approach is slightly better for create-on-exit case.
is_running = action_utils.service_is_running(service)
for interval in range(7):
action_utils.service_restart(service)
if conf_file.exists():
break
print('Waiting for {service} configuration')
time.sleep(2**interval) # Exponentially increase the time waited
else:
raise Exception(f'Unable to setup {service}.')
if not is_running:
action_utils.service_stop(service)
@privileged
def uninstall():
"""Remove configuration and service files."""
for item in DELUGED_DEFAULT_FILE, DELUGE_WEB_SYSTEMD_SERVICE_PATH:
pathlib.Path(item).unlink(missing_ok=True)
shutil.rmtree(DELUGE_CONF_DIR, ignore_errors=True)