]> git.ipfire.org Git - pakfire.git/commitdiff
Implement a simple locking mechanism to avoid concurrent transactions
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 9 Dec 2021 14:46:13 +0000 (14:46 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 9 Dec 2021 14:46:13 +0000 (14:46 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
configure.ac
src/libpakfire/include/pakfire/pakfire.h
src/libpakfire/pakfire.c
src/libpakfire/transaction.c

index 4a19f3e410a4c11d5901c984d583cb04405442da..2632006f8f85ec2fbe410c1574eb50dc44604cf4 100644 (file)
@@ -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 \
index 52ee69b117fbe50e1958522738086b1fdc06f28c..0f33b70845307e673ac19c380b59f5fefcd22a48 100644 (file)
@@ -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);
 
index 94505c2c3d43387236b49312de9df6719a0d8b28..576f2adda0f6a61f4919ecae84c9d1dab54b3ea0 100644 (file)
@@ -25,6 +25,7 @@
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/file.h>
 #include <sys/mount.h>
 #include <sys/queue.h>
 #include <sys/stat.h>
@@ -62,6 +63,7 @@
 #include <pakfire/util.h>
 
 #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);
index 57ef20c4ccbe59fe39763e8366c1a9ad9b71f3e0..e2026381fc451a6837cbc6b353d262001b856574 100644 (file)
@@ -1250,6 +1250,7 @@ PAKFIRE_EXPORT int pakfire_transaction_run(struct pakfire_transaction* transacti
                goto ERROR;
 
 ERROR:
+       // Cleanup
        free(dump);
 
        return r;