diff options
| author | Christian Breunig <christian@breunig.cc> | 2026-05-18 17:02:57 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-18 17:02:57 +0200 |
| commit | edccb2fc91163a626ece24c8be14a8bf3d444d8f (patch) | |
| tree | 0d2d243bf800a1f66d1b81b239c173c4eb29c78d /python | |
| parent | 964a01697f3c2361ce4324fb8333f5fe4d2b0434 (diff) | |
| parent | abd22ba6c3852fbf1002204a416b9de83e405caa (diff) | |
| download | vyos-1x-edccb2fc91163a626ece24c8be14a8bf3d444d8f.tar.gz vyos-1x-edccb2fc91163a626ece24c8be14a8bf3d444d8f.zip | |
Merge pull request #5200 from c-po/commit-in-progress
vyos.commit: T8781: move from O(n) to O(1) for commit_in_progress() checks
Diffstat (limited to 'python')
| -rw-r--r-- | python/vyos/utils/commit.py | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/python/vyos/utils/commit.py b/python/vyos/utils/commit.py index 4147c7fba..1bbb236e4 100644 --- a/python/vyos/utils/commit.py +++ b/python/vyos/utils/commit.py @@ -18,6 +18,63 @@ from typing import IO +def _commit_lock_busy(lock_path: str) -> bool: + """Return True if another process holds a POSIX advisory lock on lock_path. + + Uses libc lockf(F_TEST): never acquires or releases a lock (no observer window + where this code holds LOCK_EX). Compatible with locks taken via fcntl.lockf / + fcntl F_SETLK on Linux. + """ + import ctypes + import errno + import os + + libc = ctypes.CDLL('libc.so.6', use_errno=True) + lockf_fn = libc.lockf + lockf_fn.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_long] + lockf_fn.restype = ctypes.c_int + + # Defined in glibc <unistd.h> / <fcntl.h> as F_TEST. + _F_TEST = 3 + + try: + fd = os.open(lock_path, os.O_RDONLY) + except FileNotFoundError: + # Lost a race with unlink or commit teardown. + return False + try: + os.lseek(fd, 0, os.SEEK_SET) + ctypes.set_errno(0) + ret = lockf_fn(fd, _F_TEST, 0) + err = ctypes.get_errno() + if ret == 0: + return False + if err in (errno.EACCES, errno.EAGAIN): + return True + raise OSError(err, os.strerror(err), lock_path) + finally: + os.close(fd) + +def commit_in_progress2(): + """ + Modern implementation of commit_in_progress() which is O(1) instead of O(n) + + The reason not everything is moved to this new implementation yet is to + give it heavy testing in vyos-netlinkd first. + """ + # Query advisory locks without acquiring them (see _commit_lock_busy). + # Requires read access to the lock file. + # If there is no read access otherwise os.open raises PermissionError. + + from pathlib import Path + from vyos.defaults import commit_lock + + lock_path = Path(commit_lock) + if not lock_path.exists(): + return False + + return _commit_lock_busy(str(lock_path)) + def commit_in_progress(): """Not to be used in normal op mode scripts!""" |
