From: Michael Tremer Date: Thu, 9 Dec 2021 14:46:13 +0000 (+0000) Subject: Implement a simple locking mechanism to avoid concurrent transactions X-Git-Tag: 0.9.28~820 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b5cc64243af575aac514f52afdcd203582f8216e;p=pakfire.git Implement a simple locking mechanism to avoid concurrent transactions Signed-off-by: Michael Tremer --- diff --git a/configure.ac b/configure.ac index 4a19f3e41..2632006f8 100644 --- a/configure.ac +++ b/configure.ac @@ -177,6 +177,7 @@ AC_CHECK_HEADERS([ \ string.h \ syslog.h \ sys/epoll.h \ + sys/file.h \ sys/ioctl.h \ sys/mount.h \ sys/personality.h \ @@ -200,6 +201,7 @@ AC_CHECK_FUNCS([ \ epoll_ctl \ epoll_create1 \ epoll_wait \ + flock \ fnctl \ fts_close \ fts_open \ diff --git a/src/libpakfire/include/pakfire/pakfire.h b/src/libpakfire/include/pakfire/pakfire.h index 52ee69b11..0f33b7084 100644 --- a/src/libpakfire/include/pakfire/pakfire.h +++ b/src/libpakfire/include/pakfire/pakfire.h @@ -142,6 +142,10 @@ void pakfire_log(struct pakfire* pakfire, int priority, const char *file, int pakfire_has_flag(struct pakfire* pakfire, int flag); +// Locking +int pakfire_acquire_lock(struct pakfire* pakfire); +void pakfire_release_lock(struct pakfire* pakfire); + struct pakfire_config* pakfire_get_config(struct pakfire* pakfire); int pakfire_is_mountpoint(struct pakfire* pakfire, const char* path); diff --git a/src/libpakfire/pakfire.c b/src/libpakfire/pakfire.c index 94505c2c3..576f2adda 100644 --- a/src/libpakfire/pakfire.c +++ b/src/libpakfire/pakfire.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,7 @@ #include #define KEYSTORE_DIR "/etc/pakfire/trusted.keys.d" +#define LOCK_PATH PAKFIRE_PRIVATE_DIR "/.lock" #define CCACHE_DIR "/var/cache/ccache" struct mountpoint { @@ -73,12 +75,16 @@ struct pakfire { int nrefs; char path[PATH_MAX]; + char lock_path[PATH_MAX]; char cache_path[PATH_MAX]; char arch[ARCH_MAX]; char keystore_path[PATH_MAX]; int flags; + // Lock + FILE* lock; + // Pool Pool* pool; @@ -532,6 +538,9 @@ static void pakfire_free(struct pakfire* pakfire) { if (pakfire->gpgctx) pakfire_keystore_destroy(pakfire, &pakfire->gpgctx); + // Release lock (if not already done so) + pakfire_release_lock(pakfire); + // umount everything pakfire_umount(pakfire); @@ -964,6 +973,13 @@ PAKFIRE_EXPORT int pakfire_create(struct pakfire** pakfire, const char* path, if (*p->distro.slogan) DEBUG(p, " slogan = %s\n", p->distro.slogan); + // Set lock path + r = pakfire_make_path(p, p->lock_path, LOCK_PATH); + if (r < 0) { + ERROR(p, "Could not set lock path: %m\n"); + goto ERROR; + } + // Set cache path pakfire_string_format(p->cache_path, "%s/%s/%s/%s", PAKFIRE_CACHE_DIR, p->distro.id, p->distro.version_id, p->arch); @@ -1064,6 +1080,61 @@ PAKFIRE_EXPORT const char* pakfire_get_path(struct pakfire* pakfire) { return pakfire->path; } +int pakfire_acquire_lock(struct pakfire* pakfire) { + int r; + + // Check if the lock is already held + if (pakfire->lock) { + ERROR(pakfire, "Lock is already been acquired by this process\n"); + errno = ENOLCK; + return 1; + } + + DEBUG(pakfire, "Acquire lock...\n"); + + // Ensure the parent directory exists + pakfire_mkparentdir(pakfire->lock_path, 0); + + // Open the lock file + pakfire->lock = fopen(pakfire->lock_path, "w"); + if (!pakfire->lock) { + ERROR(pakfire, "Could not open lock file %s: %m\n", pakfire->lock_path); + return 1; + } + + // Attempt to lock the file exclusively + while (1) { + r = flock(fileno(pakfire->lock), LOCK_EX|LOCK_NB); + + // Success! + if (r == 0) + goto SUCCESS; + + DEBUG(pakfire, "Could not acquire lock %s: %m\n", pakfire->lock_path); + + // Wait 500ms until the next attempt + usleep(500000); + } + +SUCCESS: + DEBUG(pakfire, "Lock acquired\n"); + + return 0; +} + +void pakfire_release_lock(struct pakfire* pakfire) { + if (!pakfire->lock) + return; + + DEBUG(pakfire, "Releasing lock\n"); + + fclose(pakfire->lock); + pakfire->lock = NULL; + + // Attempt to unlink the lock file + unlink(pakfire->lock_path); +} + void pakfire_call_status_callback(struct pakfire* pakfire, const char* message, ...) { char* buffer = NULL; va_list args; @@ -1768,6 +1839,11 @@ static int pakfire_perform_transaction(struct pakfire* pakfire, int solver_flags return 1; } + // Acquire lock + r = pakfire_acquire_lock(pakfire); + if (r) + goto ERROR; + // Create a new request r = pakfire_request_create(&request, pakfire, solver_flags); if (r) @@ -1838,6 +1914,10 @@ static int pakfire_perform_transaction(struct pakfire* pakfire, int solver_flags r = 0; ERROR: + // Release lock + pakfire_release_lock(pakfire); + + // Cleanup if (problems) { for (struct pakfire_problem** problem = problems; *problem; problem++) pakfire_problem_unref(*problem); @@ -1871,6 +1951,11 @@ static int pakfire_perform_transaction_simple(struct pakfire* pakfire, int solve struct pakfire_problem** problems = NULL; int r = 1; + // Acquire lock + r = pakfire_acquire_lock(pakfire); + if (r) + goto ERROR; + // Create a new request r = pakfire_request_create(&request, pakfire, solver_flags); if (r) @@ -1926,6 +2011,9 @@ static int pakfire_perform_transaction_simple(struct pakfire* pakfire, int solve r = 0; ERROR: + // Release lock + pakfire_release_lock(pakfire); + if (problems) { for (struct pakfire_problem** problem = problems; *problem; problem++) pakfire_problem_unref(*problem); diff --git a/src/libpakfire/transaction.c b/src/libpakfire/transaction.c index 57ef20c4c..e2026381f 100644 --- a/src/libpakfire/transaction.c +++ b/src/libpakfire/transaction.c @@ -1250,6 +1250,7 @@ PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transacti goto ERROR; ERROR: + // Cleanup free(dump); return r;