-/* liblxcapi
- *
- * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
- * Copyright © 2012 Canonical Ltd.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
-
- * This library 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
- * Lesser General Public License for more details.
-
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; 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+ */
-#define _GNU_SOURCE
-#include "lxclock.h"
-#include <malloc.h>
-#include <stdio.h>
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
#include <errno.h>
-#include <unistd.h>
#include <fcntl.h>
-#include <stdlib.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 "utils.h"
+#include "config.h"
#include "log.h"
+#include "lxclock.h"
+#include "utils.h"
#ifdef MUTEX_DEBUGGING
#include <execinfo.h>
#define MAX_STACKDEPTH 25
-lxc_log_define(lxc_lock, lxc);
+lxc_log_define(lxclock, lxc);
#ifdef MUTEX_DEBUGGING
static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
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);
+ /* 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]);
{
int ret;
- if ((ret = pthread_mutex_lock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
+ ret = pthread_mutex_lock(l);
+ if (ret != 0) {
+ SYSERROR("Failed to acquire mutex");
dump_stacktrace();
- exit(1);
+ _exit(EXIT_FAILURE);
}
}
{
int ret;
- if ((ret = pthread_mutex_unlock(l)) != 0) {
- fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
+ ret = pthread_mutex_unlock(l);
+ if (ret != 0) {
+ SYSERROR("Failed to release mutex");
dump_stacktrace();
- exit(1);
+ _exit(EXIT_FAILURE);
}
}
static char *lxclock_name(const char *p, const char *n)
{
int ret;
- int len;
- char *dest;
- char *rundir;
+ size_t len;
+ char *dest, *rundir;
/* lockfile will be:
* "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root
*/
/* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */
- len = strlen("/lxc/lock/") + strlen(n) + strlen(p) + 3;
+ len = STRLITERALLEN("/lxc/lock/") + strlen(n) + strlen(p) + 3;
+
rundir = get_rundir();
if (!rundir)
return NULL;
+
len += strlen(rundir);
- if ((dest = malloc(len)) == NULL) {
+ dest = malloc(len);
+ if (!dest) {
free(rundir);
return NULL;
}
ret = snprintf(dest, len, "%s/lxc/lock/%s", rundir, p);
- if (ret < 0 || ret >= len) {
+ if (ret < 0 || (size_t)ret >= len) {
free(dest);
free(rundir);
return NULL;
}
+
ret = mkdir_p(dest, 0755);
if (ret < 0) {
free(dest);
ret = snprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n);
free(rundir);
- if (ret < 0 || ret >= len) {
+ 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;
}
l = malloc(sizeof(*l));
if (!l)
- goto out;
+ goto on_error;
if (!name) {
l->type = LXC_LOCK_ANON_SEM;
free(l);
l = NULL;
}
- goto out;
+
+ 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 out;
+ goto on_error;
}
+
l->u.f.fd = -1;
-out:
+on_error:
return l;
}
int lxclock(struct lxc_lock *l, int timeout)
{
- int ret = -1, saved_errno = errno;
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 == -1)
+ if (ret < 0)
saved_errno = errno;
} else {
struct timespec ts;
- if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
+
+ ret = clock_gettime(CLOCK_REALTIME, &ts);
+ if (ret < 0) {
ret = -2;
- goto out;
+ goto on_error;
}
+
ts.tv_sec += timeout;
ret = sem_timedwait(l->u.sem, &ts);
- if (ret == -1)
+ if (ret < 0)
saved_errno = errno;
}
+
break;
case LXC_LOCK_FLOCK:
ret = -2;
if (timeout) {
- ERROR("Error: timeout not supported with flock");
- ret = -2;
- goto out;
+ ERROR("Timeouts are not supported with file locks");
+ goto on_error;
}
+
if (!l->u.f.fname) {
- ERROR("Error: filename not set for flock");
- ret = -2;
- goto out;
+ 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_RDWR|O_CREAT,
- S_IWUSR | S_IRUSR);
+ 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) {
- ERROR("Error opening %s", l->u.f.fname);
+ SYSERROR("Failed to open \"%s\"", l->u.f.fname);
saved_errno = errno;
- goto out;
+ goto on_error;
}
}
+
+ memset(&lk, 0, sizeof(struct flock));
+
lk.l_type = F_WRLCK;
lk.l_whence = SEEK_SET;
- lk.l_start = 0;
- lk.l_len = 0;
- ret = fcntl(l->u.f.fd, F_SETLKW, &lk);
- if (ret == -1)
+
+ 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;
+ }
+
break;
}
-out:
+on_error:
errno = saved_errno;
return ret;
}
int lxcunlock(struct lxc_lock *l)
{
- int ret = 0, saved_errno = errno;
struct flock lk;
+ int ret = 0, saved_errno = errno;
- switch(l->type) {
+ switch (l->type) {
case LXC_LOCK_ANON_SEM:
- if (!l->u.sem)
+ if (!l->u.sem) {
ret = -2;
- else {
+ } 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;
- lk.l_start = 0;
- lk.l_len = 0;
- ret = fcntl(l->u.f.fd, F_SETLK, &lk);
- if (ret < 0)
+
+ 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
+ } else {
ret = -2;
+ }
+
break;
}
{
if (!l)
return;
+
switch(l->type) {
case LXC_LOCK_ANON_SEM:
if (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);
}
unlock_mutex(&thread_mutex);
}
-/* One thread can do fork() while another one is holding a mutex.
- * There is only one thread in child just after the fork(), so no one will ever release that mutex.
- * We setup a "child" fork handler to unlock the mutex just after the fork().
- * For several mutex types, unlocking an unlocked mutex can lead to undefined behavior.
- * One way to deal with it is to setup "prepare" fork handler
- * to lock the mutex before fork() and both "parent" and "child" fork handlers
- * to unlock the mutex.
- * This forbids doing fork() while explicitly holding the lock.
- */
-#ifdef HAVE_PTHREAD_ATFORK
-__attribute__((constructor))
-static void process_lock_setup_atfork(void)
-{
- pthread_atfork(process_lock, process_unlock, process_unlock);
-}
-#endif
-
int container_mem_lock(struct lxc_container *c)
{
return lxclock(c->privlock, 0);
{
int ret;
- if ((ret = lxclock(c->privlock, 0)))
+ ret = lxclock(c->privlock, 0);
+ if (ret < 0)
return ret;
- if ((ret = lxclock(c->slock, 0))) {
+
+ ret = lxclock(c->slock, 0);
+ if (ret < 0) {
lxcunlock(c->privlock);
return ret;
}
+
return 0;
}