diff --git a/actions/packages b/actions/packages index ad6390a11..d9415f5eb 100755 --- a/actions/packages +++ b/actions/packages @@ -18,9 +18,8 @@ import apt.cache import apt_inst import apt_pkg from plinth import cfg -from plinth.action_utils import apt_hold, run_apt_command - -LOCK_FILE = '/var/lib/dpkg/lock' +from plinth.action_utils import (apt_hold_freedombox, is_package_manager_busy, + run_apt_command) logger = logging.getLogger(__name__) @@ -93,7 +92,7 @@ def subcommand_install(arguments): extra_arguments += ['-o', 'Dpkg::Options::=--force-confmiss'] subprocess.run(['dpkg', '--configure', '-a']) - with apt_hold(): + with apt_hold_freedombox(): run_apt_command(['--fix-broken', 'install']) returncode = run_apt_command(['install'] + extra_arguments + arguments.packages) @@ -115,12 +114,10 @@ def _assert_managed_packages(module, packages): def subcommand_is_package_manager_busy(_): - """Return whether package manager is busy. - This command uses the `lsof` command to check whether the dpkg lock file - is open which indicates that the package manager is busy""" - try: - subprocess.check_output(['lsof', LOCK_FILE]) - except subprocess.CalledProcessError: + """Check whether package manager is busy. + + An exit code of zero indicates that package manager is busy.""" + if not is_package_manager_busy(): sys.exit(-1) diff --git a/actions/upgrades b/actions/upgrades index 401fb6299..5278c403c 100755 --- a/actions/upgrades +++ b/actions/upgrades @@ -14,9 +14,10 @@ import subprocess import sys import time -from plinth.action_utils import (apt_hold, debconf_set_selections, - run_apt_command, service_daemon_reload, - service_restart) +from plinth.action_utils import (apt_hold, apt_hold_flag, apt_hold_freedombox, + apt_unhold_freedombox, debconf_set_selections, + is_package_manager_busy, run_apt_command, + service_daemon_reload, service_restart) from plinth.modules.apache.components import check_url from plinth.modules.snapshot import (is_apt_snapshots_enabled, is_supported as snapshot_is_supported, load_augeas as @@ -141,6 +142,12 @@ def _run(): """Run unattended-upgrades""" subprocess.run(['dpkg', '--configure', '-a']) run_apt_command(['--fix-broken', 'install']) + + # In case freedombox package was left in held state by an + # interrupted process, release it. + if apt_hold_flag.exists() and not is_package_manager_busy(): + apt_unhold_freedombox() + subprocess.Popen(['systemctl', 'start', 'freedombox-manual-upgrade'], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, close_fds=True, @@ -432,7 +439,7 @@ def _perform_dist_upgrade(): # Hold freedombox package during entire dist upgrade. print('Holding freedombox package...', flush=True) - with apt_hold(): + with apt_hold_freedombox(): print('Updating Apt cache...', flush=True) run_apt_command(['update']) @@ -531,6 +538,11 @@ def subcommand_start_dist_upgrade(arguments): Check if a new stable release is available, and start dist-upgrade process if updates are enabled. """ + # In case freedombox package was left in held state by an + # interrupted process, release it. + if apt_hold_flag.exists() and not is_package_manager_busy(): + apt_unhold_freedombox() + upgrade_ready, reason = _check_dist_upgrade(arguments.test) if upgrade_ready: with open(DIST_UPGRADE_SERVICE_PATH, 'w') as service_file: diff --git a/debian/changelog b/debian/changelog index 8a4fbebaa..11d6f2b66 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +freedombox (21.4.4) unstable; urgency=medium + + * action_utils: Separate function to hold freedombox package + * action_utils: Use flag to indicate freedombox package has been held + * upgrades: Check for held freedombox package in manual update + * upgrades: Check for held freedombox package daily + * action_utils: Don't print when unholding freedombox package + + -- James Valleroy Fri, 16 Jul 2021 09:07:51 -0400 + freedombox (21.4.3~bpo10+1) buster-backports; urgency=medium * Rebuild for buster-backports. diff --git a/plinth/__init__.py b/plinth/__init__.py index 2a12e1d7f..a688c5a94 100644 --- a/plinth/__init__.py +++ b/plinth/__init__.py @@ -3,4 +3,4 @@ Package init file. """ -__version__ = '21.4.3' +__version__ = '21.4.4' diff --git a/plinth/action_utils.py b/plinth/action_utils.py index 676c3fafb..9d78db755 100644 --- a/plinth/action_utils.py +++ b/plinth/action_utils.py @@ -5,6 +5,7 @@ Python action utility functions. import logging import os +import pathlib import shutil import subprocess import tempfile @@ -15,6 +16,11 @@ 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. +apt_hold_flag = pathlib.Path('/var/lib/freedombox/package-held') + def is_systemd_running(): """Return if we are running under systemd.""" @@ -413,11 +419,8 @@ def run_apt_command(arguments): @contextmanager -def apt_hold(packages=None, ignore_errors=False): +def apt_hold(packages, ignore_errors=False): """Prevent packages from being removed during apt operations.""" - if not packages: - packages = ['freedombox'] - current_hold = subprocess.check_output(['apt-mark', 'showhold'] + packages) try: yield current_hold or subprocess.run(['apt-mark', 'hold'] + packages, @@ -426,3 +429,42 @@ def apt_hold(packages=None, ignore_errors=False): if not current_hold: subprocess.run(['apt-mark', 'unhold'] + packages, check=not ignore_errors) + + +@contextmanager +def apt_hold_freedombox(): + """Prevent freedombox package from being removed during apt operations.""" + current_hold = subprocess.check_output( + ['apt-mark', 'showhold', 'freedombox']) + try: + if current_hold: + # Package is already held, possibly by administrator. + yield current_hold + else: + # Set the flag. + apt_hold_flag.touch(mode=0o660) + yield subprocess.check_call(['apt-mark', 'hold', 'freedombox']) + finally: + # Was the package held, either in this process or a previous one? + if not current_hold or apt_hold_flag.exists(): + apt_unhold_freedombox() + + +def apt_unhold_freedombox(): + """Remove any hold on freedombox package, and clear flag.""" + subprocess.run(['apt-mark', 'unhold', 'freedombox'], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + if apt_hold_flag.exists(): + apt_hold_flag.unlink() + + +def is_package_manager_busy(): + """Return whether package manager is busy. + This command uses the `lsof` command to check whether the dpkg lock file + is open which indicates that the package manager is busy""" + LOCK_FILE = '/var/lib/dpkg/lock' + try: + subprocess.check_output(['lsof', LOCK_FILE]) + return True + except subprocess.CalledProcessError: + return False