/usr/lib/python3/dist-packages/uaclient
NameSizeModeActions
api/-0755rm
cli/-0755rm
clouds/-0755rm
daemon/-0755rm
entitlements/-0755rm
files/-0755rm
http/-0755rm
messages/-0755rm
timer/-0755rm
__pycache__/-0755rm
actions.py151870644editdlrm
apt.py371260644editdlrm
apt_news.py85870644editdlrm
config.py193610644editdlrm
contract.py367430644editdlrm
contract_data_types.py101740644editdlrm
data_types.py143290644editdlrm
defaults.py32690644editdlrm
event_logger.py82570644editdlrm
exceptions.py187240644editdlrm
gpg.py8360644editdlrm
livepatch.py131600644editdlrm
lock.py45250644editdlrm
log.py50170644editdlrm
secret_manager.py6480644editdlrm
security_status.py267850644editdlrm
snap.py64150644editdlrm
status.py292120644editdlrm
system.py287930644editdlrm
types.py3080644editdlrm
update_contract_info.py15860644editdlrm
upgrade_lts_contract.py32950644editdlrm
util.py158200644editdlrm
version.py26990644editdlrm
yaml.py8400644editdlrm
__init__.py00644editdlrm
Edit: /usr/lib/python3/dist-packages/uaclient/lock.py (4525B)
import logging import os import time from typing import Tuple from uaclient import exceptions, system, util from uaclient.data_types import DataObject, Field, StringDataValue from uaclient.files import notices from uaclient.files.data_types import DataObjectFile, DataObjectFileFormat from uaclient.files.files import UAFile from uaclient.files.notices import Notice LOG = logging.getLogger(util.replace_top_level_logger_name(__name__)) class LockData(DataObject): fields = [ Field("lock_pid", StringDataValue), Field("lock_holder", StringDataValue), ] def __init__(self, lock_pid: str, lock_holder: str): self.lock_pid = lock_pid self.lock_holder = lock_holder lock_data_file = DataObjectFile( LockData, UAFile("lock", private=False), DataObjectFileFormat.JSON, ) def check_lock_info() -> Tuple[int, str]: """Return lock info if lock file is present the lock is active. If process claiming the lock is no longer present, remove the lock file and log a warning. :return: A tuple (pid, string describing lock holder) If no active lock, pid will be -1. """ try: lock_data_obj = lock_data_file.read() except exceptions.InvalidFileFormatError: raise exceptions.InvalidLockFile(lock_file_path=lock_data_file.path) no_lock = (-1, "") if not lock_data_obj: return no_lock lock_pid = lock_data_obj.lock_pid lock_holder = lock_data_obj.lock_holder try: system.subp(["ps", lock_pid]) return (int(lock_pid), lock_holder) except exceptions.ProcessExecutionError: if not util.we_are_currently_root(): LOG.debug( "Found stale lock file previously held by %s:%s", lock_pid, lock_holder, ) return (int(lock_pid), lock_holder) LOG.warning( "Removing stale lock file previously held by %s:%s", lock_pid, lock_holder, ) system.ensure_file_absent(lock_data_file.path) return no_lock def clear_lock_file_if_present(): lock_data_file.delete() class RetryLock: """ Context manager for gaining exclusive access to the lock file. Create a lock file if absent. The lock file will contain a pid of the running process, and a customer-visible description of the lock holder. The RetryLock will try several times to acquire the lock before giving up. The number of times to try and how long to sleep in between tries is configurable. :param lock_holder: String with the service name or command which is holding the lock. This lock_holder string will be customer visible in status.json. :param sleep_time: Number of seconds to sleep before retrying if the lock is already held. :param max_retries: Maximum number of times to try to grab the lock before giving up and raising a LockHeldError. :raises: LockHeldError if lock is held after (sleep_time * max_retries) """ def __init__( self, *_args, lock_holder: str, sleep_time: int = 10, max_retries: int = 12 ): self.lock_holder = lock_holder self.sleep_time = sleep_time self.max_retries = max_retries def grab_lock(self): (lock_pid, cur_lock_holder) = check_lock_info() if lock_pid > 0: raise exceptions.LockHeldError( lock_request=self.lock_holder, lock_holder=cur_lock_holder, pid=lock_pid, ) lock_data_file.write( LockData(lock_pid=str(os.getpid()), lock_holder=self.lock_holder) ) notices.add( Notice.OPERATION_IN_PROGRESS, operation=self.lock_holder, ) def __enter__(self): LOG.debug("spin lock starting for %s", self.lock_holder) tries = 0 while True: try: self.grab_lock() break except exceptions.LockHeldError as e: LOG.debug( "RetryLock Attempt %d. %s. Spinning...", tries + 1, e.msg ) tries += 1 if tries >= self.max_retries: raise e else: time.sleep(self.sleep_time) def __exit__(self, _exc_type, _exc_value, _traceback): lock_data_file.delete() notices.remove(Notice.OPERATION_IN_PROGRESS)