From eccc3552036fcc37cdb971a6a761f4c6c806ff39 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Tue, 22 Jun 2021 22:29:11 -0400 Subject: [PATCH 1/6] action_utils: Separate function to hold freedombox package Signed-off-by: James Valleroy --- actions/packages | 4 ++-- actions/upgrades | 8 ++++---- plinth/action_utils.py | 18 ++++++++++++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/actions/packages b/actions/packages index ad6390a11..ff15e9df6 100755 --- a/actions/packages +++ b/actions/packages @@ -18,7 +18,7 @@ import apt.cache import apt_inst import apt_pkg from plinth import cfg -from plinth.action_utils import apt_hold, run_apt_command +from plinth.action_utils import apt_hold_freedombox, run_apt_command LOCK_FILE = '/var/lib/dpkg/lock' @@ -93,7 +93,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) diff --git a/actions/upgrades b/actions/upgrades index 401fb6299..242a87093 100755 --- a/actions/upgrades +++ b/actions/upgrades @@ -14,9 +14,9 @@ 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_freedombox, + debconf_set_selections, 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 @@ -432,7 +432,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']) diff --git a/plinth/action_utils.py b/plinth/action_utils.py index 676c3fafb..8ee808233 100644 --- a/plinth/action_utils.py +++ b/plinth/action_utils.py @@ -413,11 +413,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 +423,16 @@ 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: + yield current_hold or subprocess.check_call( + ['apt-mark', 'hold', 'freedombox']) + finally: + if not current_hold: + subprocess.check_call(['apt-mark', 'unhold', 'freedombox']) From 9c57347931e135dffd040cc0fd04f5211ea76821 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Wed, 23 Jun 2021 18:12:47 -0400 Subject: [PATCH 2/6] action_utils: Use flag to indicate freedombox package has been held In case the plinth process is interrupted, the "finally" block that is meant to unhold the package may not be executed, and the package will stay held. The flag is used to indicate this situation, so it can be resolved the next time apt_hold_freedombox is used. Tests: - (normal) No hold on freedombox package, and flag is not set. Install any app. Afterwards, there is no hold, and flag is not set. - (admin preference) Place hold on freedombox package, but flag is not set. Install any app. Afterwards, there is still a hold, but flag is not set. - (recovery) Place hold on freedombox package, but flag is set. Install any app. Afterwards, there is no hold, and flag is not set. Signed-off-by: James Valleroy --- plinth/action_utils.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plinth/action_utils.py b/plinth/action_utils.py index 8ee808233..aed8e5a3f 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 @@ -428,11 +429,23 @@ def apt_hold(packages, ignore_errors=False): @contextmanager def apt_hold_freedombox(): """Prevent freedombox package from being removed during apt operations.""" + # This flag is a backup indicator that we held the package, in + # case the process is interrupted and the 'finally' is not run. + apt_hold_flag = pathlib.Path('/var/lib/freedombox/package-held') current_hold = subprocess.check_output( ['apt-mark', 'showhold', 'freedombox']) try: - yield current_hold or subprocess.check_call( - ['apt-mark', 'hold', 'freedombox']) + 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: - if not current_hold: + # Was the package held, either in this process or a previous one? + if not current_hold or apt_hold_flag.exists(): subprocess.check_call(['apt-mark', 'unhold', 'freedombox']) + # Clear the flag. + if apt_hold_flag.exists(): + apt_hold_flag.unlink() From 967d68cb923dff8999925acc35bfc251d63478b7 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Thu, 24 Jun 2021 12:54:34 -0400 Subject: [PATCH 3/6] upgrades: Check for held freedombox package in manual update This is to recover from a situation where the package is held, and then plinth process is interrupted so it cannot release the hold. We check for and recover from this situation when installing new apps, and when running dist upgrade. This provides another way to recover from the problem, by running manual update. Tests: - (normal) No hold on freedombox package, and flag is not set. Run manual update. Afterwards, there is no hold, and flag is not set. - (admin preference) Place hold on freedombox package, but flag is not set. Run manual update. Afterwards, there is still a hold, but flag is not set. - (recovery) Place hold on freedombox package, but flag is set. Run manual update. Afterwards, there is no hold, and flag is not set. Signed-off-by: James Valleroy --- actions/upgrades | 13 ++++++++++--- plinth/action_utils.py | 20 +++++++++++++------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/actions/upgrades b/actions/upgrades index 242a87093..313adcf45 100755 --- a/actions/upgrades +++ b/actions/upgrades @@ -14,9 +14,10 @@ import subprocess import sys import time -from plinth.action_utils import (apt_hold, apt_hold_freedombox, - 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, + 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(): + apt_unhold_freedombox() + subprocess.Popen(['systemctl', 'start', 'freedombox-manual-upgrade'], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, close_fds=True, diff --git a/plinth/action_utils.py b/plinth/action_utils.py index aed8e5a3f..8a77db120 100644 --- a/plinth/action_utils.py +++ b/plinth/action_utils.py @@ -16,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.""" @@ -429,9 +434,6 @@ def apt_hold(packages, ignore_errors=False): @contextmanager def apt_hold_freedombox(): """Prevent freedombox package from being removed during apt operations.""" - # This flag is a backup indicator that we held the package, in - # case the process is interrupted and the 'finally' is not run. - apt_hold_flag = pathlib.Path('/var/lib/freedombox/package-held') current_hold = subprocess.check_output( ['apt-mark', 'showhold', 'freedombox']) try: @@ -445,7 +447,11 @@ def apt_hold_freedombox(): finally: # Was the package held, either in this process or a previous one? if not current_hold or apt_hold_flag.exists(): - subprocess.check_call(['apt-mark', 'unhold', 'freedombox']) - # Clear the flag. - if apt_hold_flag.exists(): - apt_hold_flag.unlink() + apt_unhold_freedombox() + + +def apt_unhold_freedombox(): + """Remove any hold on freedombox package, and clear flag.""" + subprocess.check_call(['apt-mark', 'unhold', 'freedombox']) + if apt_hold_flag.exists(): + apt_hold_flag.unlink() From a24fcb2c90db4cbfa90a6f61377fff9643baa338 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Mon, 28 Jun 2021 16:53:56 -0400 Subject: [PATCH 4/6] upgrades: Check for held freedombox package daily Guard against removing a hold while the package manager is busy. Test: 1. Place hold on freedombox package. 2. Wait 3 minutes in development mode. - Package is held. 3. Touch /var/lib/freedombox/package-held. 4. Wait 3 minutes in development mode. - Package is not held. - Flag is removed. Signed-off-by: James Valleroy --- actions/packages | 15 ++++++--------- actions/upgrades | 11 ++++++++--- plinth/action_utils.py | 12 ++++++++++++ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/actions/packages b/actions/packages index ff15e9df6..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_freedombox, 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__) @@ -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 313adcf45..5278c403c 100755 --- a/actions/upgrades +++ b/actions/upgrades @@ -16,8 +16,8 @@ import time from plinth.action_utils import (apt_hold, apt_hold_flag, apt_hold_freedombox, apt_unhold_freedombox, debconf_set_selections, - run_apt_command, service_daemon_reload, - service_restart) + 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 @@ -145,7 +145,7 @@ def _run(): # In case freedombox package was left in held state by an # interrupted process, release it. - if apt_hold_flag.exists(): + if apt_hold_flag.exists() and not is_package_manager_busy(): apt_unhold_freedombox() subprocess.Popen(['systemctl', 'start', 'freedombox-manual-upgrade'], @@ -538,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/plinth/action_utils.py b/plinth/action_utils.py index 8a77db120..216d3c9ff 100644 --- a/plinth/action_utils.py +++ b/plinth/action_utils.py @@ -455,3 +455,15 @@ def apt_unhold_freedombox(): subprocess.check_call(['apt-mark', 'unhold', 'freedombox']) 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 From af1d21ce3b1b7daa5c7eb9755302b59a1ece743b Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Sun, 4 Jul 2021 20:37:32 -0400 Subject: [PATCH 5/6] action_utils: Don't print when unholding freedombox package Some actions that use this function are expected to output JSON. Any output from apt-mark can interfere with this. Signed-off-by: James Valleroy --- plinth/action_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plinth/action_utils.py b/plinth/action_utils.py index 216d3c9ff..9d78db755 100644 --- a/plinth/action_utils.py +++ b/plinth/action_utils.py @@ -452,7 +452,8 @@ def apt_hold_freedombox(): def apt_unhold_freedombox(): """Remove any hold on freedombox package, and clear flag.""" - subprocess.check_call(['apt-mark', 'unhold', 'freedombox']) + subprocess.run(['apt-mark', 'unhold', 'freedombox'], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if apt_hold_flag.exists(): apt_hold_flag.unlink() From 4696acccf150b3280a76f6553fcd044530ef2679 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Fri, 16 Jul 2021 09:08:38 -0400 Subject: [PATCH 6/6] Release v21.4.4 to unstable Signed-off-by: James Valleroy --- debian/changelog | 10 ++++++++++ plinth/__init__.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 7c48afbef..25e2efc69 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) unstable; urgency=medium [ Andreas Beckmann ] 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'