# SPDX-License-Identifier: AGPL-3.0-or-later """Configuration helper for BitTorrent web client.""" import pathlib 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)