summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-05-18 17:02:57 +0200
committerGitHub <noreply@github.com>2026-05-18 17:02:57 +0200
commitedccb2fc91163a626ece24c8be14a8bf3d444d8f (patch)
tree0d2d243bf800a1f66d1b81b239c173c4eb29c78d
parent964a01697f3c2361ce4324fb8333f5fe4d2b0434 (diff)
parentabd22ba6c3852fbf1002204a416b9de83e405caa (diff)
downloadvyos-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
-rw-r--r--python/vyos/utils/commit.py57
-rwxr-xr-xsrc/services/vyos-netlinkd6
2 files changed, 60 insertions, 3 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!"""
diff --git a/src/services/vyos-netlinkd b/src/services/vyos-netlinkd
index 368824bf9..addfc8ef9 100755
--- a/src/services/vyos-netlinkd
+++ b/src/services/vyos-netlinkd
@@ -29,7 +29,7 @@ from typing import Optional
from vyos.configquery import op_mode_config_dict
from vyos.ifconfig import Section
from vyos.utils.boot import boot_configuration_complete
-from vyos.utils.commit import commit_in_progress
+from vyos.utils.commit import commit_in_progress2
from vyos.utils.dict import dict_search
from vyos.utils.process import cmd
from vyos.utils.process import is_systemd_service_active
@@ -127,8 +127,8 @@ def main():
# Check if a config commit is in progress before processing any
# messages. This avoids blocking per-message and reduces unnecessary
- # commit_in_progress() calls.
- if commit_in_progress():
+ # calls to commit_in_progress2()
+ if commit_in_progress2():
syslog.syslog(syslog.LOG_DEBUG,
'Config commit in progress, skipping netlink events')
sleep(1)