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.
This commit is contained in:
Derzsi Dániel 2024-11-29 18:33:22 +02:00 committed by Disyer
parent 9154600490
commit 47d80ded72

View file

@ -21,6 +21,7 @@
*/
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
@ -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)