From 1d8ebf9e09bc29b4af588a211152e99a6c9c6402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Thu, 19 Dec 2024 18:31:19 +0100 Subject: [PATCH 1/2] vmupdate: fix waiting for the other apt-get process apt-get uses fcntl F_SETLK for locking, not flock(). Update the function accordingly. Fixes: 8fa2929 "vmupdate: wait for other apt-get to complete" --- vmupdate/agent/source/apt/apt_cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vmupdate/agent/source/apt/apt_cli.py b/vmupdate/agent/source/apt/apt_cli.py index 7516a8d..bbd5157 100644 --- a/vmupdate/agent/source/apt/apt_cli.py +++ b/vmupdate/agent/source/apt/apt_cli.py @@ -42,8 +42,8 @@ def wait_for_lock(self): """ Wait for any other apt-get instance to finish. """ - with open("/var/lib/apt/lists/lock") as f_lock: - fcntl.flock(f_lock.fileno(), fcntl.LOCK_EX) + with open("/var/lib/apt/lists/lock", "rb+") as f_lock: + fcntl.lockf(f_lock.fileno(), fcntl.LOCK_EX) def refresh(self, hard_fail: bool) -> ProcessResult: """ From d5759de1493057f8a0e88fbaca2d4dfd2385bf49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sun, 5 Jan 2025 03:02:50 +0100 Subject: [PATCH 2/2] vmupdate: improve waiting for apt-get update lock "apt-get update" doesn't support waiting for other instances to finish, it exits immediately instead. With CLI apt-get version, it's possible to get it waiting externally (by taking the lock manually, and then make apt-get not try to lock it again). Unfortunately the same cannot be done for the API version, because there is no way (I can find) to disable locking in the apt.Cache.update() function. Workaround for missing feature https://bugs.debian.org/1069167 --- vmupdate/agent/source/apt/apt_cli.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/vmupdate/agent/source/apt/apt_cli.py b/vmupdate/agent/source/apt/apt_cli.py index bbd5157..e6077f3 100644 --- a/vmupdate/agent/source/apt/apt_cli.py +++ b/vmupdate/agent/source/apt/apt_cli.py @@ -23,6 +23,7 @@ import fcntl import os +import contextlib from typing import List from source.common.package_manager import PackageManager @@ -38,12 +39,21 @@ def __init__(self, log_handler, log_level,): # to prevent a warning: `debconf: unable to initialize frontend: Dialog` os.environ['DEBIAN_FRONTEND'] = 'noninteractive' - def wait_for_lock(self): + @contextlib.contextmanager + def apt_lock(self): """ - Wait for any other apt-get instance to finish. + Contex manager for locking compatible with 'apt-get update' lock. """ with open("/var/lib/apt/lists/lock", "rb+") as f_lock: fcntl.lockf(f_lock.fileno(), fcntl.LOCK_EX) + yield + + def wait_for_lock(self): + """ + Wait for any other apt-get instance to finish. + """ + with self.apt_lock(): + pass def refresh(self, hard_fail: bool) -> ProcessResult: """ @@ -52,9 +62,12 @@ def refresh(self, hard_fail: bool) -> ProcessResult: :param hard_fail: raise error if some repo is unavailable :return: (exit_code, stdout, stderr) """ - self.wait_for_lock() - cmd = [self.package_manager, "-q", "update"] - result = self.run_cmd(cmd) + # apply lock externally to wait for it, until + # https://bugs.debian.org/1069167 gets implemented + with self.apt_lock(): + cmd = [self.package_manager, + "-o", "Debug::NoLocking=true", "-q", "update"] + result = self.run_cmd(cmd) # 'apt-get update' reports error with exit code 100, but updater as a # whole reserves it for "no updates" if result.code == 100: