From 47d80ded722151bdbea10d1b670c4b1a2262fb95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Derzsi=20D=C3=A1niel?= Date: Fri, 29 Nov 2024 18:33:22 +0200 Subject: [PATCH] libalpm: Ignore database locks if the process that locked the database no longer exists It's quite common to get the following message after aborting a pacman command, often after system restarts: :: Synchronizing package databases... error: failed to synchronize all databases (unable to lock database) Right now, the locking mechanism is relatively rudimentary: if db.lck exists, then the database is considered locked. If something happens to the pacman process and it cannot remove the db.lck, it will be locked indefinitely, or until somebody Googles "pacman unable to lock database" and removes the lock. This commit makes db.lck keep track of the process that locked it. If subsequent pacman processes cannot find the process that originally locked the database, the database will be considered unlocked. --- lib/libalpm/handle.c | 54 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c index e2f919f6..4d42119d 100644 --- a/lib/libalpm/handle.c +++ b/lib/libalpm/handle.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -135,10 +136,59 @@ int _alpm_handle_lock(alpm_handle_t *handle) FREE(dir); do { - handle->lockfd = open(handle->lockfile, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0000); + handle->lockfd = open(handle->lockfile, O_RDWR | O_CREAT | O_CLOEXEC, 0000); } while(handle->lockfd == -1 && errno == EINTR); - return (handle->lockfd >= 0 ? 0 : -1); + if(handle->lockfd < 0) { + /* failed to open lock for writing */ + return -1; + } + + /* lock opened */ + /* check the pid */ + pid_t pid = getpid(); + char buf[12]; + ssize_t bytesread = read(handle->lockfd, buf, sizeof(buf) - 1); + + buf[bytesread] = '\0'; + pid_t lockedpid; + + if(sscanf(buf, "%d", &lockedpid) == 1 && kill(lockedpid, 0) == 0 && errno != ESRCH) { + /* could not grab lock */ + /* old pid is still active */ + close(handle->lockfd); + handle->lockfd = -1; + return -1; + } + + /* write pid to lock */ + /* first allocate string for pid */ + char *pidstr = NULL; + int len = asprintf(&pidstr, "%d\n", pid); + + if(len == -1) { + _alpm_log(handle, ALPM_LOG_ERROR, "alloc failure: could not allocate pid"); + close(handle->lockfd); + unlink(handle->lockfile); + handle->lockfd = -1; + return -1; + } + + /* finally write pid to lock */ + int writtenbytes = write(handle->lockfd, pidstr, len); + + /* cleanup */ + FREE(pidstr); + + if(writtenbytes != len) { + _alpm_log(handle, ALPM_LOG_ERROR, "could not write pid to lockfile"); + close(handle->lockfd); + unlink(handle->lockfile); + handle->lockfd = -1; + return -1; + } + + return 0; } int SYMEXPORT alpm_unlock(alpm_handle_t *handle)