From 0e698eb4b4a486e3fe91154512704e7d89fd2489 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Sun, 15 Mar 2026 09:39:26 -0400 Subject: [PATCH] apache: Use a Uwsgi native socket systemd unit for each app [Sunil]: - Drop Uwsgi component entirely. After the changes, it mostly looks like Daemon component minus some features. One change that Uwsgi component does is when component is disabled, it also stops and disables the .service unit. Stopping the service is useful and we can add this to Daemon component. - Use /run instead of /var/run/ as 1) /var/run is a symlink to /run 2) /run/ path is what is listed in uwsgi-app@.socket unit file. - Implement upgrade for apps from older version. Disable and mask uwsgi init.d script. Enable the daemon component if the webserver component is enabled. - Update manifest files to deal with .socket units instead of 'uwsgi' service. Backup the /var/lib/private directories as that is actual directory to backup with DynamicUser=yes. - For bepasty load the configuration as a systemd provided credential since DynamicUser=yes. - Remove the /var/lib/private directories during uninstall. - Don't create user/group for bepasty as it is not needed with DynamicUser=yes. Tests: - Radicale - Functional tests pass - Freshly install radicale. - Web interface works. - Create and edit calendars - Path of the storage directory is in /var/lib/private/radicale (after accessing web interface) - Permissions on the storage folder and files inside are set to nobody:nobody. - Uninstall removes the /var/lib/private/radicale directory. - Create a calender and backup the app. Uninstall the app. Re-install the app. The calendar is not available. After restoring the backup, the calendar is available. - Install radicale without patch and create a calendar. Apply patches and start plinth.service. Setup is run. UWSGI is disabled and masked. Service is running. Old calender is visible. - Install radicale without patch. Disable and apply patches and start plinth.service. Setup is run. UWSGI is disabled and masked. Service is not running. Enabling the service works. - After upgrade, data storage path got migrated to /var/lib/private/radicale. Old data is accessible. - After upgrade the directory is still owned by radicale:radicale. - Freshly install radicale with patch and restore an old backup. The data is available in the web interface and data was migrated to /var/lib/private/radicale. - Bepasty - Functional tests pass - Freshly install bepasy. - Enabling and disabling rapidly works. - Uploading files works. - Path of the storage directory is /var/lib/private/bepasty. - Permissions on the storage folder are as expect 755 but on the parent are 700. - Permissions on the stored files are 644 and owned by nobody:nobody. - Uninstall removes the /var/lib/private/bepasty directory. - Upload a picture and backup the app. Uninstall the app. Re-install the app. The uploaded file is not available. After restoring the backup, the uploaded file is available. - Install bepasty without patch and upload a file. Apply patches and start plinth.service. Setup is run. UWSGI is disabled and masked. Service is running. Old uploaded picture is visible. - Install bepasty without patch. Disable app. Apply patches and start plinth.service. Setup is run. UWSGI is disabled and masked. Service is not running. Enabling the service works. - After upgrade, data storage path got migrated to /var/lib/private/bepasty. Old data is accessible. - After upgrade the directory is still owned by bepasty:bepasty. - Freshly install bepasty with patch and restore an old backup. The uploaded file is available in the web interface and data was migrated to /var/lib/private/bepasty. Signed-off-by: James Valleroy Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- doc/dev/reference/components/webserver.rst | 3 - plinth/action_utils.py | 40 --------- plinth/modules/apache/__init__.py | 5 +- plinth/modules/apache/components.py | 36 -------- plinth/modules/apache/privileged.py | 12 --- .../modules/apache/tests/test_components.py | 83 ++----------------- plinth/modules/bepasty/__init__.py | 21 ++++- .../freedombox.conf | 5 ++ .../conf-available/bepasty-freedombox.conf | 2 +- .../apps-available/bepasty-freedombox.ini | 2 +- plinth/modules/bepasty/manifest.py | 4 +- plinth/modules/bepasty/privileged.py | 45 +++------- plinth/modules/radicale/__init__.py | 25 +++++- .../freedombox.conf | 4 + .../conf-available/radicale2-freedombox.conf | 4 +- plinth/modules/radicale/manifest.py | 4 +- plinth/modules/radicale/privileged.py | 14 +++- plinth/modules/searx/__init__.py | 20 ++++- .../conf-available/searx-freedombox-auth.conf | 2 +- .../conf-available/searx-freedombox.conf | 4 +- plinth/modules/searx/privileged.py | 3 +- 21 files changed, 107 insertions(+), 231 deletions(-) create mode 100644 plinth/modules/bepasty/data/usr/lib/systemd/system/uwsgi-app@bepasty-freedombox.service.d/freedombox.conf create mode 100644 plinth/modules/radicale/data/usr/lib/systemd/system/uwsgi-app@radicale.service.d/freedombox.conf diff --git a/doc/dev/reference/components/webserver.rst b/doc/dev/reference/components/webserver.rst index 987594342..1a42cd199 100644 --- a/doc/dev/reference/components/webserver.rst +++ b/doc/dev/reference/components/webserver.rst @@ -8,6 +8,3 @@ Webserver .. autoclass:: plinth.modules.apache.components.WebserverRoot :members: - -.. autoclass:: plinth.modules.apache.components.Uwsgi - :members: diff --git a/plinth/action_utils.py b/plinth/action_utils.py index f01aef15c..13563015f 100644 --- a/plinth/action_utils.py +++ b/plinth/action_utils.py @@ -18,9 +18,6 @@ from . import actions logger = logging.getLogger(__name__) -UWSGI_ENABLED_PATH = '/etc/uwsgi/apps-enabled/{config_name}.ini' -UWSGI_AVAILABLE_PATH = '/etc/uwsgi/apps-available/{config_name}.ini' - # Flag on disk to indicate if freedombox package was held by # plinth. This is a backup in case the process is interrupted and hold # is not released. @@ -318,43 +315,6 @@ class WebserverChange: self.actions_required.add(action_required) -def uwsgi_is_enabled(config_name): - """Return whether a uwsgi config is enabled.""" - enabled_path = UWSGI_ENABLED_PATH.format(config_name=config_name) - return os.path.exists(enabled_path) - - -def uwsgi_enable(config_name): - """Enable a uwsgi configuration that runs under uwsgi.""" - if uwsgi_is_enabled(config_name): - return - - # uwsgi is started/stopped using init script. We don't know if it can - # handle some configuration already running against newly enabled - # configuration. So, stop first before enabling new configuration. - service_stop('uwsgi') - - enabled_path = UWSGI_ENABLED_PATH.format(config_name=config_name) - available_path = UWSGI_AVAILABLE_PATH.format(config_name=config_name) - os.symlink(available_path, enabled_path) - - service_enable('uwsgi') - service_start('uwsgi') - - -def uwsgi_disable(config_name): - """Disable a uwsgi configuration that runs under uwsgi.""" - if not uwsgi_is_enabled(config_name): - return - - # If uwsgi is restarted later, it won't stop the just disabled - # configuration due to how init scripts are written for uwsgi. - service_stop('uwsgi') - enabled_path = UWSGI_ENABLED_PATH.format(config_name=config_name) - os.unlink(enabled_path) - service_start('uwsgi') - - def get_addresses() -> list[dict[str, str | bool]]: """Return a list of IP addresses and hostnames.""" addresses = get_ip_addresses() diff --git a/plinth/modules/apache/__init__.py b/plinth/modules/apache/__init__.py index d6268b9a3..2de1bcddc 100644 --- a/plinth/modules/apache/__init__.py +++ b/plinth/modules/apache/__init__.py @@ -97,8 +97,8 @@ class ApacheApp(app_module.App): self.add(info) packages = Packages('packages-apache', [ - 'apache2', 'php-fpm', 'ssl-cert', 'uwsgi', 'uwsgi-plugin-python3', - 'libapache2-mod-auth-openidc' + 'apache2', 'php-fpm', 'ssl-cert', 'uwsgi-core', + 'uwsgi-plugin-python3', 'libapache2-mod-auth-openidc' ]) self.add(packages) @@ -134,6 +134,7 @@ class ApacheApp(app_module.App): daemon = Daemon('daemon-apache', 'apache2') self.add(daemon) + # To be able to disable the old uwsgi init.d script. related_daemon = RelatedDaemon('related-daemon-apache', 'uwsgi') self.add(related_daemon) diff --git a/plinth/modules/apache/components.py b/plinth/modules/apache/components.py index e9149add2..05746a353 100644 --- a/plinth/modules/apache/components.py +++ b/plinth/modules/apache/components.py @@ -210,42 +210,6 @@ class WebserverRoot(app.FollowerComponent): kvstore.delete(self._key_get(), ignore_missing=True) -class Uwsgi(app.LeaderComponent): - """Component to enable/disable uWSGI configuration.""" - - def __init__(self, component_id: str, uwsgi_name: str): - """Initialize the uWSGI component. - - component_id should be a unique ID across all components of an app and - across all components. - - uwsgi_name is the primary part of the configuration file path which - must be enabled/disabled by this component. - - """ - super().__init__(component_id) - - self.uwsgi_name = uwsgi_name - - def is_enabled(self) -> bool: - """Return whether the uWSGI configuration is enabled.""" - return action_utils.uwsgi_is_enabled(self.uwsgi_name) \ - and action_utils.service_is_enabled('uwsgi') - - def enable(self) -> None: - """Enable the uWSGI configuration.""" - privileged.uwsgi_enable(self.uwsgi_name) - - def disable(self) -> None: - """Disable the uWSGI configuration.""" - privileged.uwsgi_disable(self.uwsgi_name) - - def is_running(self) -> bool: - """Return whether the uWSGI daemon is running with configuration.""" - return action_utils.uwsgi_is_enabled(self.uwsgi_name) \ - and action_utils.service_is_running('uwsgi') - - def diagnose_url(url: str, kind: str | None = None, env: dict[str, str] | None = None, check_certificate: bool = True, diff --git a/plinth/modules/apache/privileged.py b/plinth/modules/apache/privileged.py index e9df3135e..319aca804 100644 --- a/plinth/modules/apache/privileged.py +++ b/plinth/modules/apache/privileged.py @@ -362,18 +362,6 @@ def unlink_root(domain: str): action_utils.service_reload('apache2') -@privileged -def uwsgi_enable(name: str): - """Enable uWSGI configuration and reload.""" - action_utils.uwsgi_enable(name) - - -@privileged -def uwsgi_disable(name: str): - """Disable uWSGI configuration and reload.""" - action_utils.uwsgi_disable(name) - - @privileged def domain_setup(domain: str): """Add site specific configuration for a domain.""" diff --git a/plinth/modules/apache/tests/test_components.py b/plinth/modules/apache/tests/test_components.py index da59247cb..6c300e630 100644 --- a/plinth/modules/apache/tests/test_components.py +++ b/plinth/modules/apache/tests/test_components.py @@ -10,7 +10,7 @@ import pytest from plinth import app, kvstore from plinth.diagnostic_check import DiagnosticCheck, Result -from plinth.modules.apache.components import (Uwsgi, Webserver, WebserverRoot, +from plinth.modules.apache.components import (Webserver, WebserverRoot, check_url, diagnose_url, diagnose_url_on_all) @@ -326,81 +326,6 @@ def test_webserver_root_uninstall(component_app, enable, disable): assert kvstore.get_default('test-webserver_domain', 'x-value') == 'x-value' -def test_uwsgi_init(): - """Test that uWSGI component can be initialized.""" - with pytest.raises(ValueError): - Uwsgi(None, None) - - uwsgi = Uwsgi('test-uwsgi', 'test-config') - assert uwsgi.component_id == 'test-uwsgi' - assert uwsgi.uwsgi_name == 'test-config' - - -@patch('plinth.action_utils.service_is_enabled') -@patch('plinth.action_utils.uwsgi_is_enabled') -def test_uwsgi_is_enabled(uwsgi_is_enabled, service_is_enabled): - """Test that checking uwsgi configuration enabled works.""" - uwsgi = Uwsgi('test-uwsgi', 'test-config') - - uwsgi_is_enabled.return_value = True - service_is_enabled.return_value = True - assert uwsgi.is_enabled() - uwsgi_is_enabled.assert_has_calls([call('test-config')]) - service_is_enabled.assert_has_calls([call('uwsgi')]) - - service_is_enabled.return_value = False - assert not uwsgi.is_enabled() - - uwsgi_is_enabled.return_value = False - assert not uwsgi.is_enabled() - - service_is_enabled.return_value = False - assert not uwsgi.is_enabled() - - -@patch('plinth.modules.apache.privileged.uwsgi_enable') -def test_uwsgi_enable(enable): - """Test that enabling uwsgi configuration works.""" - uwsgi = Uwsgi('test-uwsgi', 'test-config') - - uwsgi.enable() - enable.assert_has_calls([call('test-config')]) - - -@patch('plinth.modules.apache.privileged.uwsgi_disable') -def test_uwsgi_disable(disable): - """Test that disabling uwsgi configuration works.""" - uwsgi = Uwsgi('test-uwsgi', 'test-config') - - uwsgi.disable() - disable.assert_has_calls([call('test-config')]) - - -@patch('plinth.action_utils.service_is_running') -@patch('plinth.action_utils.uwsgi_is_enabled') -def test_uwsgi_is_running(uwsgi_is_enabled, service_is_running): - """Test checking whether uwsgi is running with a configuration.""" - uwsgi = Uwsgi('test-uwsgi', 'test-config') - - uwsgi_is_enabled.return_value = True - service_is_running.return_value = True - assert uwsgi.is_running() - uwsgi_is_enabled.assert_has_calls([call('test-config')]) - service_is_running.assert_has_calls([call('uwsgi')]) - - uwsgi_is_enabled.return_value = False - service_is_running.return_value = True - assert not uwsgi.is_running() - - uwsgi_is_enabled.return_value = True - service_is_running.return_value = False - assert not uwsgi.is_running() - - uwsgi_is_enabled.return_value = False - service_is_running.return_value = False - assert not uwsgi.is_running() - - @patch('plinth.modules.apache.components.check_url') @patch('plinth.action_utils.get_addresses') def test_diagnose_url(get_addresses, check): @@ -473,8 +398,10 @@ def test_diagnose_url(get_addresses, check): def test_check_url(run): """Test checking whether a URL is accessible.""" url = 'http://localhost/test' - basic_command = ['curl', '--location', '--cookie', '', '--fail', - '--write-out', '%{response_code}'] + basic_command = [ + 'curl', '--location', '--cookie', '', '--fail', '--write-out', + '%{response_code}' + ] extra_args = {'env': None, 'check': True, 'stdout': -1, 'stderr': -1} # Basic diff --git a/plinth/modules/bepasty/__init__.py b/plinth/modules/bepasty/__init__.py index fea754764..6d0fa5781 100644 --- a/plinth/modules/bepasty/__init__.py +++ b/plinth/modules/bepasty/__init__.py @@ -6,10 +6,12 @@ from django.utils.translation import gettext_lazy as _ from plinth import app as app_module from plinth import frontpage, menu from plinth.config import DropinConfigs -from plinth.modules.apache.components import Uwsgi, Webserver +from plinth.daemon import Daemon +from plinth.modules.apache.components import Webserver from plinth.modules.backups.components import BackupRestore from plinth.modules.firewall.components import Firewall from plinth.package import Packages +from plinth.privileged import service as service_privileged from . import manifest, privileged @@ -48,7 +50,7 @@ class BepastyApp(app_module.App): app_id = 'bepasty' - _version = 3 + _version = 4 def __init__(self) -> None: """Create components for the app.""" @@ -83,8 +85,9 @@ class BepastyApp(app_module.App): ports=['http', 'https'], is_external=True) self.add(firewall) - uwsgi = Uwsgi('uwsgi-bepasty', 'bepasty-freedombox') - self.add(uwsgi) + daemon = Daemon('daemon-bepasty', + 'uwsgi-app@bepasty-freedombox.socket') + self.add(daemon) webserver = Webserver('webserver-bepasty', 'bepasty-freedombox', urls=['https://{host}/bepasty/']) @@ -107,6 +110,16 @@ class BepastyApp(app_module.App): # value. privileged.set_default(['read']) + if old_version and old_version <= 3: + webserver = self.get_component('webserver-bepasty') + daemon = self.get_component('daemon-bepasty') + if webserver.is_enabled(): + daemon.enable() + + # Vanquish the old uwsgi init.d script. + service_privileged.disable('uwsgi') + service_privileged.mask('uwsgi') + def uninstall(self): """De-configure and uninstall the app.""" super().uninstall() diff --git a/plinth/modules/bepasty/data/usr/lib/systemd/system/uwsgi-app@bepasty-freedombox.service.d/freedombox.conf b/plinth/modules/bepasty/data/usr/lib/systemd/system/uwsgi-app@bepasty-freedombox.service.d/freedombox.conf new file mode 100644 index 000000000..518d1d7f5 --- /dev/null +++ b/plinth/modules/bepasty/data/usr/lib/systemd/system/uwsgi-app@bepasty-freedombox.service.d/freedombox.conf @@ -0,0 +1,5 @@ +[Service] +User=bepasty +Group=bepasty +StateDirectory=bepasty +LoadCredential=bepasty-freedombox.conf:/etc/bepasty-freedombox.conf diff --git a/plinth/modules/bepasty/data/usr/share/freedombox/etc/apache2/conf-available/bepasty-freedombox.conf b/plinth/modules/bepasty/data/usr/share/freedombox/etc/apache2/conf-available/bepasty-freedombox.conf index 7ca23e624..f121f4de7 100644 --- a/plinth/modules/bepasty/data/usr/share/freedombox/etc/apache2/conf-available/bepasty-freedombox.conf +++ b/plinth/modules/bepasty/data/usr/share/freedombox/etc/apache2/conf-available/bepasty-freedombox.conf @@ -12,5 +12,5 @@ - ProxyPass unix:/run/uwsgi/app/bepasty-freedombox/socket|uwsgi://bepasty/ + ProxyPass unix:/run/uwsgi/bepasty-freedombox.socket|uwsgi://bepasty/ diff --git a/plinth/modules/bepasty/data/usr/share/freedombox/etc/uwsgi/apps-available/bepasty-freedombox.ini b/plinth/modules/bepasty/data/usr/share/freedombox/etc/uwsgi/apps-available/bepasty-freedombox.ini index 8fada8377..8a90bc74c 100644 --- a/plinth/modules/bepasty/data/usr/share/freedombox/etc/uwsgi/apps-available/bepasty-freedombox.ini +++ b/plinth/modules/bepasty/data/usr/share/freedombox/etc/uwsgi/apps-available/bepasty-freedombox.ini @@ -25,7 +25,7 @@ lazy-apps = true # Module to import module = bepasty.wsgi -env = BEPASTY_CONFIG=/etc/bepasty-freedombox.conf +env = BEPASTY_CONFIG=$(CREDENTIALS_DIRECTORY)/bepasty-freedombox.conf pythonpath = /usr/lib/python3/dist-packages/ buffer-size = 32768 diff --git a/plinth/modules/bepasty/manifest.py b/plinth/modules/bepasty/manifest.py index 754532584..a2f6655cd 100644 --- a/plinth/modules/bepasty/manifest.py +++ b/plinth/modules/bepasty/manifest.py @@ -15,9 +15,9 @@ backup = { 'files': ['/etc/bepasty-freedombox.conf'] }, 'data': { - 'directories': ['/var/lib/bepasty'] + 'directories': ['/var/lib/bepasty', '/var/lib/private/bepasty'] }, - 'services': ['uwsgi'], + 'services': ['uwsgi-app@bepasty-freedombox.socket'], } tags = [_('File sharing'), _('Pastebin')] diff --git a/plinth/modules/bepasty/privileged.py b/plinth/modules/bepasty/privileged.py index 11a09c5e6..80bdcb439 100644 --- a/plinth/modules/bepasty/privileged.py +++ b/plinth/modules/bepasty/privileged.py @@ -2,11 +2,8 @@ """Configuration helper for bepasty.""" import collections -import grp import json -import os import pathlib -import pwd import secrets import shutil import string @@ -17,11 +14,10 @@ from plinth import action_utils from plinth.actions import privileged, secret_str from plinth.modules import bepasty -DATA_DIR = '/var/lib/bepasty' - -PASSWORD_LENGTH = 20 - CONF_FILE = pathlib.Path('/etc/bepasty-freedombox.conf') +DATA_DIR = '/var/lib/private/bepasty' +PASSWORD_LENGTH = 20 +SERVICE_NAME = 'uwsgi-app@bepasty-freedombox.service' def _augeas_load(): @@ -66,26 +62,6 @@ def conf_file_write(conf): @privileged def setup(domain_name: str): """Post installation actions for bepasty.""" - # Create bepasty group if needed. - try: - grp.getgrnam('bepasty') - except KeyError: - action_utils.run(['addgroup', '--system', 'bepasty'], check=True) - - # Create bepasty user if needed. - try: - pwd.getpwnam('bepasty') - except KeyError: - action_utils.run([ - 'adduser', '--system', '--ingroup', 'bepasty', '--home', - '/var/lib/bepasty', '--gecos', 'bepasty file sharing', 'bepasty' - ], check=True) - - # Create data directory if needed. - if not os.path.exists(DATA_DIR): - os.makedirs(DATA_DIR, mode=0o750) - shutil.chown(DATA_DIR, user='bepasty', group='bepasty') - # Create configuration file if needed. if not CONF_FILE.is_file(): passwords = [_generate_password() for _ in range(3)] @@ -112,7 +88,11 @@ def setup(domain_name: str): } conf_file_write(conf) CONF_FILE.chmod(0o640) - shutil.chown(CONF_FILE, user='bepasty', group='bepasty') + + # Migrate from old bepasty:bepasty ownership to root:root + shutil.chown(CONF_FILE, user='root', group='root') + action_utils.run(['deluser', 'bepasty'], check=False) + action_utils.run(['delgroup', 'bepasty'], check=False) @privileged @@ -132,7 +112,8 @@ def add_password(permissions: list[str], comment: str | None = None): conf['PERMISSION_COMMENTS'][password] = comment conf_file_write(conf) - action_utils.service_try_restart('uwsgi') + # Service is started again by socket. + action_utils.service_stop(SERVICE_NAME) @privileged @@ -145,7 +126,7 @@ def remove_password(password: secret_str): if password in conf['PERMISSION_COMMENTS']: del conf['PERMISSION_COMMENTS'][password] conf_file_write(conf) - action_utils.service_try_restart('uwsgi') + action_utils.service_stop(SERVICE_NAME) @privileged @@ -153,7 +134,7 @@ def set_default(permissions: list[str]): """Set default permissions.""" conf = {'DEFAULT_PERMISSIONS': _format_permissions(permissions)} conf_file_write(conf) - action_utils.service_try_restart('uwsgi') + action_utils.service_stop(SERVICE_NAME) def _format_permissions(permissions=None): @@ -173,5 +154,3 @@ def uninstall(): """Remove bepasty user, group and data.""" shutil.rmtree(DATA_DIR, ignore_errors=True) CONF_FILE.unlink(missing_ok=True) - action_utils.run(['deluser', 'bepasty'], check=False) - action_utils.run(['delgroup', 'bepasty'], check=False) diff --git a/plinth/modules/radicale/__init__.py b/plinth/modules/radicale/__init__.py index b5d88505e..f8b355615 100644 --- a/plinth/modules/radicale/__init__.py +++ b/plinth/modules/radicale/__init__.py @@ -11,11 +11,13 @@ from django.utils.translation import gettext_lazy as _ from plinth import app as app_module from plinth import cfg, frontpage, menu from plinth.config import DropinConfigs -from plinth.modules.apache.components import Uwsgi, Webserver +from plinth.daemon import Daemon +from plinth.modules.apache.components import Webserver from plinth.modules.backups.components import BackupRestore from plinth.modules.firewall.components import Firewall from plinth.modules.users.components import UsersAndGroups from plinth.package import Packages, install +from plinth.privileged import service as service_privileged from plinth.utils import Version, format_lazy from . import manifest, privileged @@ -43,7 +45,7 @@ class RadicaleApp(app_module.App): app_id = 'radicale' - _version = 4 + _version = 5 def __init__(self) -> None: """Create components for the app.""" @@ -84,8 +86,8 @@ class RadicaleApp(app_module.App): urls=['https://{host}/radicale']) self.add(webserver) - uwsgi = Uwsgi('uwsgi-radicale', 'radicale') - self.add(uwsgi) + daemon = Daemon('daemon-radicale', 'uwsgi-app@radicale.socket') + self.add(daemon) users_and_groups = UsersAndGroups('users-and-groups-radicale', reserved_usernames=['radicale']) @@ -107,6 +109,16 @@ class RadicaleApp(app_module.App): if not old_version: self.enable() + if old_version and old_version <= 4: + webserver = self.get_component('webserver-radicale') + daemon = self.get_component('daemon-radicale') + if webserver.is_enabled(): + daemon.enable() + + # Vanquish the old uwsgi init.d script. + service_privileged.disable('uwsgi') + service_privileged.mask('uwsgi') + def force_upgrade(self, packages): """Force upgrade radicale to resolve conffile prompt.""" if 'radicale' not in packages: @@ -124,6 +136,11 @@ class RadicaleApp(app_module.App): return True + def uninstall(self): + """De-configure and uninstall the app.""" + super().uninstall() + privileged.uninstall() + def load_augeas(): """Prepares the augeas.""" diff --git a/plinth/modules/radicale/data/usr/lib/systemd/system/uwsgi-app@radicale.service.d/freedombox.conf b/plinth/modules/radicale/data/usr/lib/systemd/system/uwsgi-app@radicale.service.d/freedombox.conf new file mode 100644 index 000000000..259c335dd --- /dev/null +++ b/plinth/modules/radicale/data/usr/lib/systemd/system/uwsgi-app@radicale.service.d/freedombox.conf @@ -0,0 +1,4 @@ +[Service] +User=radicale +Group=radicale +StateDirectory=radicale diff --git a/plinth/modules/radicale/data/usr/share/freedombox/etc/apache2/conf-available/radicale2-freedombox.conf b/plinth/modules/radicale/data/usr/share/freedombox/etc/apache2/conf-available/radicale2-freedombox.conf index d3501f622..c798791c1 100644 --- a/plinth/modules/radicale/data/usr/share/freedombox/etc/apache2/conf-available/radicale2-freedombox.conf +++ b/plinth/modules/radicale/data/usr/share/freedombox/etc/apache2/conf-available/radicale2-freedombox.conf @@ -17,8 +17,8 @@ Redirect 301 /.well-known/caldav /radicale/ Include includes/freedombox-auth-ldap.conf Require valid-user - ProxyPass unix:/run/uwsgi/app/radicale/socket|uwsgi://radicale/ - ProxyPassReverse unix:/run/uwsgi/app/radicale/socket|uwsgi://radicale/ + ProxyPass unix:/run/uwsgi/radicale.socket|uwsgi://radicale/ + ProxyPassReverse unix:/run/uwsgi/radicale.socket|uwsgi://radicale/ RequestHeader set X-Script-Name /radicale/ RequestHeader set X-Remote-User expr=%{REMOTE_USER} diff --git a/plinth/modules/radicale/manifest.py b/plinth/modules/radicale/manifest.py index 1a12e0e81..0b6f0f7b9 100644 --- a/plinth/modules/radicale/manifest.py +++ b/plinth/modules/radicale/manifest.py @@ -83,9 +83,9 @@ backup = { 'directories': ['/etc/radicale/'] }, 'data': { - 'directories': ['/var/lib/radicale/'] + 'directories': ['/var/lib/private/radicale/', '/var/lib/radicale/'] }, - 'services': ['uwsgi'] + 'services': ['uwsgi-app@radicale.socket'] } tags = [_('Calendar'), _('Contacts'), _('Server'), _('CalDAV'), _('CardDAV')] diff --git a/plinth/modules/radicale/privileged.py b/plinth/modules/radicale/privileged.py index 8508fd56d..75e9bfe17 100644 --- a/plinth/modules/radicale/privileged.py +++ b/plinth/modules/radicale/privileged.py @@ -2,6 +2,7 @@ """Configure Radicale.""" import os +import shutil import augeas @@ -10,6 +11,7 @@ from plinth.actions import privileged CONFIG_FILE = '/etc/radicale/config' LOG_PATH = '/var/log/radicale' +SERVICE_NAME = 'uwsgi-app@radicale.service' @privileged @@ -23,7 +25,8 @@ def setup(): aug = load_augeas() aug.set('auth/type', 'remote_user') aug.save() - action_utils.service_try_restart('uwsgi') + # Service is started again by socket. + action_utils.service_stop(SERVICE_NAME) @privileged @@ -36,8 +39,7 @@ def configure(rights_type: str): aug = load_augeas() aug.set('rights/type', rights_type) aug.save() - - action_utils.service_try_restart('uwsgi') + action_utils.service_stop(SERVICE_NAME) @privileged @@ -57,3 +59,9 @@ def load_augeas(): aug.set('/augeas/context', '/files' + CONFIG_FILE) aug.load() return aug + + +@privileged +def uninstall(): + """Remove all radicale collections.""" + shutil.rmtree('/var/lib/private/radicale/', ignore_errors=True) diff --git a/plinth/modules/searx/__init__.py b/plinth/modules/searx/__init__.py index 44bab52ed..7b13e67ef 100644 --- a/plinth/modules/searx/__init__.py +++ b/plinth/modules/searx/__init__.py @@ -8,11 +8,13 @@ from django.utils.translation import gettext_lazy as _ from plinth import app as app_module from plinth import frontpage, menu from plinth.config import DropinConfigs -from plinth.modules.apache.components import Uwsgi, Webserver +from plinth.daemon import Daemon +from plinth.modules.apache.components import Webserver from plinth.modules.backups.components import BackupRestore from plinth.modules.firewall.components import Firewall from plinth.modules.users.components import UsersAndGroups from plinth.package import Packages +from plinth.privileged import service as service_privileged from . import manifest, privileged @@ -29,7 +31,7 @@ class SearxApp(app_module.App): app_id = 'searx' - _version = 6 + _version = 7 def __init__(self) -> None: """Create components for the app.""" @@ -78,8 +80,8 @@ class SearxApp(app_module.App): 'searx-freedombox-auth') self.add(webserver) - uwsgi = Uwsgi('uwsgi-searx', 'searx') - self.add(uwsgi) + daemon = Daemon('daemon-searx', 'uwsgi-app@searx.socket') + self.add(daemon) users_and_groups = UsersAndGroups('users-and-groups-searx', groups=groups) @@ -102,6 +104,16 @@ class SearxApp(app_module.App): self.enable() self.set_shortcut_login_required(True) + if old_version and old_version <= 6: + webserver = self.get_component('webserver-searx') + daemon = self.get_component('daemon-searx') + if webserver.is_enabled(): + daemon.enable() + + # Vanquish the old uwsgi init.d script. + service_privileged.disable('uwsgi') + service_privileged.mask('uwsgi') + def uninstall(self): """De-configure and uninstall the app.""" super().uninstall() diff --git a/plinth/modules/searx/data/usr/share/freedombox/etc/apache2/conf-available/searx-freedombox-auth.conf b/plinth/modules/searx/data/usr/share/freedombox/etc/apache2/conf-available/searx-freedombox-auth.conf index b65db00ba..eed17132b 100644 --- a/plinth/modules/searx/data/usr/share/freedombox/etc/apache2/conf-available/searx-freedombox-auth.conf +++ b/plinth/modules/searx/data/usr/share/freedombox/etc/apache2/conf-available/searx-freedombox-auth.conf @@ -1,5 +1,5 @@ - ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/ + ProxyPass unix:/run/uwsgi/searx.socket|uwsgi://uwsgi-uds-searx/ Use AuthOpenIDConnect Use RequireGroup web-search diff --git a/plinth/modules/searx/data/usr/share/freedombox/etc/apache2/conf-available/searx-freedombox.conf b/plinth/modules/searx/data/usr/share/freedombox/etc/apache2/conf-available/searx-freedombox.conf index ceaa0b176..6e2411a2e 100644 --- a/plinth/modules/searx/data/usr/share/freedombox/etc/apache2/conf-available/searx-freedombox.conf +++ b/plinth/modules/searx/data/usr/share/freedombox/etc/apache2/conf-available/searx-freedombox.conf @@ -9,12 +9,12 @@ - ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/ + ProxyPass unix:/run/uwsgi/searx.socket|uwsgi://uwsgi-uds-searx/ # This exclusion rule is to allow Searx to be added as a search engine in Firefox. Require all granted - ProxyPassMatch "unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/$1" + ProxyPassMatch "unix:/run/uwsgi/searx.socket|uwsgi://uwsgi-uds-searx/$1" diff --git a/plinth/modules/searx/privileged.py b/plinth/modules/searx/privileged.py index f91a8634b..16b4a3972 100644 --- a/plinth/modules/searx/privileged.py +++ b/plinth/modules/searx/privileged.py @@ -139,7 +139,8 @@ def setup(): _update_search_engines(settings) write_settings(settings) - action_utils.service_restart('uwsgi') + # Service is started again by socket. + action_utils.service_stop('uwsgi-app@searx.service') @privileged