From 8fa2929622baca03148ae8aeb93e775205d0491b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Sun, 15 Dec 2024 13:02:42 +0100 Subject: [PATCH] vmupdate: wait for other apt-get to complete If there is an `apt-get update` running in the background (for example periodical updates check), running another would fail. Since `apt-get` itself doesn't have an option to wait for the lock, wait for it in the updater tool. It's not perfect - there is still a window where another apt-get instance can start, but at least this will handle the (more common) situation when it's already running. --- vmupdate/agent/source/apt/apt_api.py | 1 + vmupdate/agent/source/apt/apt_cli.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/vmupdate/agent/source/apt/apt_api.py b/vmupdate/agent/source/apt/apt_api.py index 5bd38f8..59b6ac3 100644 --- a/vmupdate/agent/source/apt/apt_api.py +++ b/vmupdate/agent/source/apt/apt_api.py @@ -53,6 +53,7 @@ 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() result = ProcessResult() try: self.log.debug("Refreshing available packages...") diff --git a/vmupdate/agent/source/apt/apt_cli.py b/vmupdate/agent/source/apt/apt_cli.py index 7f6b0dc..7516a8d 100644 --- a/vmupdate/agent/source/apt/apt_cli.py +++ b/vmupdate/agent/source/apt/apt_cli.py @@ -21,6 +21,7 @@ # pylint: disable=unused-argument +import fcntl import os from typing import List @@ -37,6 +38,13 @@ 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): + """ + 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) + def refresh(self, hard_fail: bool) -> ProcessResult: """ Use package manager to refresh available packages. @@ -44,6 +52,7 @@ 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) # 'apt-get update' reports error with exit code 100, but updater as a