]> git.proxmox.com Git - mirror_lxc.git/blobdiff - src/lxc/lxclock.c
confile: cleanup parse_line()
[mirror_lxc.git] / src / lxc / lxclock.c
index 88a388182617939d3e450fa52610f03c7234442f..318e5bf5a32afa58b1f1411ec0de91f643be4041 100644 (file)
-/* liblxcapi
- *
- * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
- * Copyright © 2012 Canonical Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+/* SPDX-License-Identifier: LGPL-2.1+ */
 
-#include "lxclock.h"
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#include <errno.h>
+#include <fcntl.h>
 #include <malloc.h>
+#include <pthread.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <unistd.h>
+
+#include <lxc/lxccontainer.h>
+
+#include "config.h"
+#include "log.h"
+#include "lxclock.h"
+#include "utils.h"
+
+#ifdef MUTEX_DEBUGGING
+#include <execinfo.h>
+#endif
+
+#define MAX_STACKDEPTH 25
+
+lxc_log_define(lxclock, lxc);
+
+#ifdef MUTEX_DEBUGGING
+static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+
+static inline void dump_stacktrace(void)
+{
+       void *array[MAX_STACKDEPTH];
+       size_t size;
+       char **strings;
+       size_t i;
+
+       size = backtrace(array, MAX_STACKDEPTH);
+       strings = backtrace_symbols(array, size);
+
+       /* Using fprintf here as our logging module is not thread safe. */
+       fprintf(stderr, "\tObtained %zu stack frames\n", size);
+
+       for (i = 0; i < size; i++)
+               fprintf(stderr, "\t\t%s\n", strings[i]);
 
-#define OFLAG (O_CREAT | O_RDWR)
-#define SEMMODE 0660
-#define SEMVALUE 1
-#define SEMVALUE_LOCKED 0
-#define LXCLOCK_PREFIX "/lxcapi."
+       free(strings);
+}
+#else
+static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static inline void dump_stacktrace(void) {;}
+#endif
+
+static void lock_mutex(pthread_mutex_t *l)
+{
+       int ret;
+
+       ret = pthread_mutex_lock(l);
+       if (ret != 0) {
+               SYSERROR("Failed to acquire mutex");
+               dump_stacktrace();
+               _exit(EXIT_FAILURE);
+       }
+}
 
+static void unlock_mutex(pthread_mutex_t *l)
+{
+       int ret;
 
-static char *lxclock_name(const char *container)
+       ret = pthread_mutex_unlock(l);
+       if (ret != 0) {
+               SYSERROR("Failed to release mutex");
+               dump_stacktrace();
+               _exit(EXIT_FAILURE);
+       }
+}
+
+static char *lxclock_name(const char *p, const char *n)
 {
        int ret;
-       int len = strlen(container) + strlen(LXCLOCK_PREFIX) + 1;
-       char *dest = malloc(len);
-       if (!dest)
+       size_t len;
+       char *dest, *rundir;
+
+       /* lockfile will be:
+        * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root
+        * or
+        * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root
+        */
+
+       /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */
+       len = STRLITERALLEN("/lxc/lock/") + strlen(n) + strlen(p) + 3;
+
+       rundir = get_rundir();
+       if (!rundir)
+               return NULL;
+
+       len += strlen(rundir);
+
+       dest = malloc(len);
+       if (!dest) {
+               free(rundir);
+               return NULL;
+       }
+
+       ret = snprintf(dest, len, "%s/lxc/lock/%s", rundir, p);
+       if (ret < 0 || (size_t)ret >= len) {
+               free(dest);
+               free(rundir);
                return NULL;
-       ret = snprintf(dest, len, "%s%s", LXCLOCK_PREFIX, container);
-       if (ret < 0 || ret >= len) {
+       }
+
+       ret = mkdir_p(dest, 0755);
+       if (ret < 0) {
                free(dest);
+               free(rundir);
                return NULL;
        }
-       return dest;
-}
 
-static void lxcfree_name(char *name)
-{
-       if (name)
-               free(name);
+       ret = snprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n);
+       free(rundir);
+       if (ret < 0 || (size_t)ret >= len) {
+               free(dest);
+               return NULL;
+       }
+
+       return dest;
 }
 
 static sem_t *lxc_new_unnamed_sem(void)
 {
-       sem_t *s;
        int ret;
+       sem_t *s;
 
        s = malloc(sizeof(*s));
        if (!s)
                return NULL;
+
        ret = sem_init(s, 0, 1);
-       if (ret)
+       if (ret < 0) {
+               free(s);
                return NULL;
+       }
+
        return s;
 }
 
-sem_t *lxc_newlock(const char *name)
+struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name)
 {
-       char *lname;
-       sem_t *lock;
+       struct lxc_lock *l;
 
-       if (!name)
-               return lxc_new_unnamed_sem();
+       l = malloc(sizeof(*l));
+       if (!l)
+               goto on_error;
 
-       lname = lxclock_name(name);
-       if (!lname)
-               return NULL;
-       lock = sem_open(lname, OFLAG, SEMMODE, SEMVALUE);
-       lxcfree_name(lname);
-       if (lock == SEM_FAILED)
-               return NULL;
-       return lock;
+       if (!name) {
+               l->type = LXC_LOCK_ANON_SEM;
+               l->u.sem = lxc_new_unnamed_sem();
+               if (!l->u.sem) {
+                       free(l);
+                       l = NULL;
+               }
+
+               goto on_error;
+       }
+
+       l->type = LXC_LOCK_FLOCK;
+       l->u.f.fname = lxclock_name(lxcpath, name);
+       if (!l->u.f.fname) {
+               if (!name)
+                       free(l->u.sem);
+               free(l);
+               l = NULL;
+               goto on_error;
+       }
+
+       l->u.f.fd = -1;
+
+on_error:
+       return l;
 }
 
-int lxclock(sem_t *sem, int timeout)
+int lxclock(struct lxc_lock *l, int timeout)
 {
-       int ret;
+       struct flock lk;
+       int ret = -1, saved_errno = errno;
+
+       switch(l->type) {
+       case LXC_LOCK_ANON_SEM:
+               if (!timeout) {
+                       ret = sem_wait(l->u.sem);
+                       if (ret < 0)
+                               saved_errno = errno;
+               } else {
+                       struct timespec ts;
+
+                       ret = clock_gettime(CLOCK_REALTIME, &ts);
+                       if (ret < 0) {
+                               ret = -2;
+                               goto on_error;
+                       }
+
+                       ts.tv_sec += timeout;
+                       ret = sem_timedwait(l->u.sem, &ts);
+                       if (ret < 0)
+                               saved_errno = errno;
+               }
+
+               break;
+       case LXC_LOCK_FLOCK:
+               ret = -2;
+               if (timeout) {
+                       ERROR("Timeouts are not supported with file locks");
+                       goto on_error;
+               }
+
+               if (!l->u.f.fname) {
+                       ERROR("No filename set for file lock");
+                       goto on_error;
+               }
+
+               if (l->u.f.fd == -1) {
+                       l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR);
+                       if (l->u.f.fd == -1) {
+                               SYSERROR("Failed to open \"%s\"", l->u.f.fname);
+                               saved_errno = errno;
+                               goto on_error;
+                       }
+               }
+
+               memset(&lk, 0, sizeof(struct flock));
+
+               lk.l_type = F_WRLCK;
+               lk.l_whence = SEEK_SET;
+
+               ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk);
+               if (ret < 0) {
+                       if (errno == EINVAL)
+                               ret = flock(l->u.f.fd, LOCK_EX);
+                       saved_errno = errno;
+               }
 
-       if (!timeout) {
-               ret = sem_wait(sem);
-       } else {
-               struct timespec ts;
-               if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
-                       return -2;
-               ts.tv_sec += timeout;
-               ret = sem_timedwait(sem, &ts);
+               break;
        }
 
+on_error:
+       errno = saved_errno;
        return ret;
 }
 
-int lxcunlock(sem_t *sem)
+int lxcunlock(struct lxc_lock *l)
+{
+       struct flock lk;
+       int ret = 0, saved_errno = errno;
+
+       switch (l->type) {
+       case LXC_LOCK_ANON_SEM:
+               if (!l->u.sem) {
+                       ret = -2;
+               } else {
+                       ret = sem_post(l->u.sem);
+                       saved_errno = errno;
+               }
+
+               break;
+       case LXC_LOCK_FLOCK:
+               if (l->u.f.fd != -1) {
+                       memset(&lk, 0, sizeof(struct flock));
+
+                       lk.l_type = F_UNLCK;
+                       lk.l_whence = SEEK_SET;
+
+                       ret = fcntl(l->u.f.fd, F_OFD_SETLK, &lk);
+                       if (ret < 0) {
+                               if (errno == EINVAL)
+                                       ret = flock(l->u.f.fd, LOCK_EX | LOCK_NB);
+                               saved_errno = errno;
+                       }
+
+                       close(l->u.f.fd);
+                       l->u.f.fd = -1;
+               } else {
+                       ret = -2;
+               }
+
+               break;
+       }
+
+       errno = saved_errno;
+       return ret;
+}
+
+/*
+ * lxc_putlock() is only called when a container_new() fails,
+ * or during container_put(), which is already guaranteed to
+ * only be done by one task.
+ * So the only exclusion we need to provide here is for regular
+ * thread safety (i.e. file descriptor table changes).
+ */
+void lxc_putlock(struct lxc_lock *l)
+{
+       if (!l)
+               return;
+
+       switch(l->type) {
+       case LXC_LOCK_ANON_SEM:
+               if (l->u.sem) {
+                       sem_destroy(l->u.sem);
+                       free(l->u.sem);
+                       l->u.sem = NULL;
+               }
+
+               break;
+       case LXC_LOCK_FLOCK:
+               if (l->u.f.fd != -1) {
+                       close(l->u.f.fd);
+                       l->u.f.fd = -1;
+               }
+
+               free(l->u.f.fname);
+               l->u.f.fname = NULL;
+
+               break;
+       }
+
+       free(l);
+}
+
+void process_lock(void)
+{
+       lock_mutex(&thread_mutex);
+}
+
+void process_unlock(void)
+{
+       unlock_mutex(&thread_mutex);
+}
+
+int container_mem_lock(struct lxc_container *c)
+{
+       return lxclock(c->privlock, 0);
+}
+
+void container_mem_unlock(struct lxc_container *c)
+{
+       lxcunlock(c->privlock);
+}
+
+int container_disk_lock(struct lxc_container *c)
+{
+       int ret;
+
+       ret = lxclock(c->privlock, 0);
+       if (ret < 0)
+               return ret;
+
+       ret = lxclock(c->slock, 0);
+       if (ret < 0) {
+               lxcunlock(c->privlock);
+               return ret;
+       }
+
+       return 0;
+}
+
+void container_disk_unlock(struct lxc_container *c)
 {
-       if (!sem)
-               return -2;
-       return sem_post(sem);
+       lxcunlock(c->slock);
+       lxcunlock(c->privlock);
 }