]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/lxclock.c
Merge pull request #3235 from xinhua9569/master
[mirror_lxc.git] / src / lxc / lxclock.c
CommitLineData
cc73685d 1/* SPDX-License-Identifier: LGPL-2.1+ */
72d0e1cb 2
d38dd64a
CB
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE 1
5#endif
df271a59 6#include <errno.h>
93dc5327 7#include <fcntl.h>
56474555 8#include <malloc.h>
052616eb 9#include <pthread.h>
56474555
CB
10#include <stdio.h>
11#include <stdlib.h>
12#include <sys/file.h>
13#include <unistd.h>
f2363e38 14
5cee8c50 15#include <lxc/lxccontainer.h>
72d0e1cb 16
d38dd64a
CB
17#include "config.h"
18#include "log.h"
ccfc84ca 19#include "lxclock.h"
f2363e38 20#include "utils.h"
f2363e38 21
41f68357
ÇO
22#ifdef MUTEX_DEBUGGING
23#include <execinfo.h>
24#endif
25
5b28d063
SH
26#define MAX_STACKDEPTH 25
27
ac2cecc4 28lxc_log_define(lxclock, lxc);
72d0e1cb 29
052616eb 30#ifdef MUTEX_DEBUGGING
74a3920a 31static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
41f68357 32
74a3920a 33static inline void dump_stacktrace(void)
41f68357
ÇO
34{
35 void *array[MAX_STACKDEPTH];
36 size_t size;
37 char **strings;
38 size_t i;
39
40 size = backtrace(array, MAX_STACKDEPTH);
41 strings = backtrace_symbols(array, size);
42
1a0e70ac 43 /* Using fprintf here as our logging module is not thread safe. */
b19aabf5 44 fprintf(stderr, "\tObtained %zu stack frames\n", size);
41f68357
ÇO
45
46 for (i = 0; i < size; i++)
47 fprintf(stderr, "\t\t%s\n", strings[i]);
48
157ad8d4 49 free(strings);
41f68357 50}
052616eb 51#else
74a3920a 52static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER;
41f68357 53
74a3920a 54static inline void dump_stacktrace(void) {;}
052616eb 55#endif
df271a59 56
74a3920a 57static void lock_mutex(pthread_mutex_t *l)
41f68357
ÇO
58{
59 int ret;
60
7249588c
CB
61 ret = pthread_mutex_lock(l);
62 if (ret != 0) {
79cda71d 63 SYSERROR("Failed to acquire mutex");
41f68357 64 dump_stacktrace();
7249588c 65 _exit(EXIT_FAILURE);
41f68357
ÇO
66 }
67}
68
74a3920a 69static void unlock_mutex(pthread_mutex_t *l)
41f68357
ÇO
70{
71 int ret;
72
7249588c
CB
73 ret = pthread_mutex_unlock(l);
74 if (ret != 0) {
79cda71d 75 SYSERROR("Failed to release mutex");
41f68357 76 dump_stacktrace();
7249588c 77 _exit(EXIT_FAILURE);
41f68357
ÇO
78 }
79}
80
df271a59 81static char *lxclock_name(const char *p, const char *n)
72d0e1cb
SG
82{
83 int ret;
ecd8cb99
CB
84 size_t len;
85 char *dest, *rundir;
71b0fed6 86
469b5787 87 /* lockfile will be:
72cf81f6 88 * "/run" + "/lxc/lock/$lxcpath/$lxcname + '\0' if root
469b5787 89 * or
72cf81f6 90 * $XDG_RUNTIME_DIR + "/lxc/lock/$lxcpath/$lxcname + '\0' if non-root
469b5787
SH
91 */
92
72cf81f6 93 /* length of "/lxc/lock/" + $lxcpath + "/" + "." + $lxcname + '\0' */
6333c915 94 len = STRLITERALLEN("/lxc/lock/") + strlen(n) + strlen(p) + 3;
ecd8cb99 95
9e60f51d 96 rundir = get_rundir();
97a696c6
SG
97 if (!rundir)
98 return NULL;
79cda71d 99
469b5787
SH
100 len += strlen(rundir);
101
ecd8cb99
CB
102 dest = malloc(len);
103 if (!dest) {
44b9ae4b 104 free(rundir);
72d0e1cb 105 return NULL;
44b9ae4b 106 }
469b5787 107
72cf81f6 108 ret = snprintf(dest, len, "%s/lxc/lock/%s", rundir, p);
ecd8cb99 109 if (ret < 0 || (size_t)ret >= len) {
72d0e1cb 110 free(dest);
44b9ae4b 111 free(rundir);
72d0e1cb
SG
112 return NULL;
113 }
79cda71d 114
5cee8c50 115 ret = mkdir_p(dest, 0755);
5cee8c50 116 if (ret < 0) {
72cf81f6
SH
117 free(dest);
118 free(rundir);
119 return NULL;
120 }
72d0e1cb 121
72cf81f6 122 ret = snprintf(dest, len, "%s/lxc/lock/%s/.%s", rundir, p, n);
44b9ae4b 123 free(rundir);
ecd8cb99 124 if (ret < 0 || (size_t)ret >= len) {
df271a59
SH
125 free(dest);
126 return NULL;
127 }
79cda71d 128
df271a59 129 return dest;
72d0e1cb
SG
130}
131
132static sem_t *lxc_new_unnamed_sem(void)
133{
87677950 134 int ret;
ecd8cb99 135 sem_t *s;
72d0e1cb 136
87677950
SH
137 s = malloc(sizeof(*s));
138 if (!s)
139 return NULL;
79cda71d 140
87677950 141 ret = sem_init(s, 0, 1);
ecd8cb99 142 if (ret < 0) {
bdb539b8 143 free(s);
87677950 144 return NULL;
bdb539b8 145 }
79cda71d 146
87677950 147 return s;
72d0e1cb
SG
148}
149
df271a59 150struct lxc_lock *lxc_newlock(const char *lxcpath, const char *name)
72d0e1cb 151{
df271a59 152 struct lxc_lock *l;
72d0e1cb 153
df271a59
SH
154 l = malloc(sizeof(*l));
155 if (!l)
ecd8cb99 156 goto on_error;
72d0e1cb 157
df271a59
SH
158 if (!name) {
159 l->type = LXC_LOCK_ANON_SEM;
160 l->u.sem = lxc_new_unnamed_sem();
41c3b7c7
DE
161 if (!l->u.sem) {
162 free(l);
163 l = NULL;
164 }
79cda71d 165
ecd8cb99 166 goto on_error;
df271a59
SH
167 }
168
169 l->type = LXC_LOCK_FLOCK;
170 l->u.f.fname = lxclock_name(lxcpath, name);
171 if (!l->u.f.fname) {
172 free(l);
173 l = NULL;
ecd8cb99 174 goto on_error;
df271a59 175 }
79cda71d 176
df271a59
SH
177 l->u.f.fd = -1;
178
ecd8cb99 179on_error:
df271a59 180 return l;
72d0e1cb
SG
181}
182
df271a59 183int lxclock(struct lxc_lock *l, int timeout)
72d0e1cb 184{
93dc5327 185 struct flock lk;
ecd8cb99 186 int ret = -1, saved_errno = errno;
72d0e1cb 187
df271a59
SH
188 switch(l->type) {
189 case LXC_LOCK_ANON_SEM:
190 if (!timeout) {
191 ret = sem_wait(l->u.sem);
b19aabf5 192 if (ret < 0)
df271a59
SH
193 saved_errno = errno;
194 } else {
195 struct timespec ts;
79cda71d 196
ecd8cb99
CB
197 ret = clock_gettime(CLOCK_REALTIME, &ts);
198 if (ret < 0) {
df271a59 199 ret = -2;
ecd8cb99 200 goto on_error;
df271a59 201 }
79cda71d 202
df271a59
SH
203 ts.tv_sec += timeout;
204 ret = sem_timedwait(l->u.sem, &ts);
b19aabf5 205 if (ret < 0)
df271a59
SH
206 saved_errno = errno;
207 }
ecd8cb99 208
df271a59
SH
209 break;
210 case LXC_LOCK_FLOCK:
211 ret = -2;
212 if (timeout) {
ecd8cb99
CB
213 ERROR("Timeouts are not supported with file locks");
214 goto on_error;
df271a59 215 }
79cda71d 216
df271a59 217 if (!l->u.f.fname) {
ecd8cb99
CB
218 ERROR("No filename set for file lock");
219 goto on_error;
df271a59 220 }
79cda71d 221
df271a59 222 if (l->u.f.fd == -1) {
b19aabf5 223 l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR);
df271a59 224 if (l->u.f.fd == -1) {
ecd8cb99 225 SYSERROR("Failed to open \"%s\"", l->u.f.fname);
98375790 226 saved_errno = errno;
ecd8cb99 227 goto on_error;
df271a59
SH
228 }
229 }
79cda71d 230
b19aabf5 231 memset(&lk, 0, sizeof(struct flock));
79cda71d 232
93dc5327
SH
233 lk.l_type = F_WRLCK;
234 lk.l_whence = SEEK_SET;
79cda71d 235
b19aabf5
CB
236 ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk);
237 if (ret < 0) {
238 if (errno == EINVAL)
239 ret = flock(l->u.f.fd, LOCK_EX);
df271a59 240 saved_errno = errno;
b19aabf5 241 }
ecd8cb99 242
df271a59 243 break;
72d0e1cb
SG
244 }
245
ecd8cb99 246on_error:
df271a59 247 errno = saved_errno;
72d0e1cb
SG
248 return ret;
249}
250
df271a59 251int lxcunlock(struct lxc_lock *l)
72d0e1cb 252{
93dc5327 253 struct flock lk;
ecd8cb99 254 int ret = 0, saved_errno = errno;
df271a59 255
ecd8cb99 256 switch (l->type) {
df271a59 257 case LXC_LOCK_ANON_SEM:
ecd8cb99 258 if (!l->u.sem) {
df271a59 259 ret = -2;
ecd8cb99 260 } else {
df271a59
SH
261 ret = sem_post(l->u.sem);
262 saved_errno = errno;
659aa061 263 }
ecd8cb99 264
df271a59
SH
265 break;
266 case LXC_LOCK_FLOCK:
267 if (l->u.f.fd != -1) {
b19aabf5 268 memset(&lk, 0, sizeof(struct flock));
79cda71d 269
93dc5327
SH
270 lk.l_type = F_UNLCK;
271 lk.l_whence = SEEK_SET;
79cda71d 272
b19aabf5
CB
273 ret = fcntl(l->u.f.fd, F_OFD_SETLK, &lk);
274 if (ret < 0) {
275 if (errno == EINVAL)
276 ret = flock(l->u.f.fd, LOCK_EX | LOCK_NB);
df271a59 277 saved_errno = errno;
b19aabf5 278 }
79cda71d 279
df271a59
SH
280 close(l->u.f.fd);
281 l->u.f.fd = -1;
ecd8cb99 282 } else {
df271a59 283 ret = -2;
ecd8cb99
CB
284 }
285
df271a59
SH
286 break;
287 }
288
df271a59
SH
289 errno = saved_errno;
290 return ret;
291}
292
5cee8c50
SH
293/*
294 * lxc_putlock() is only called when a container_new() fails,
295 * or during container_put(), which is already guaranteed to
296 * only be done by one task.
297 * So the only exclusion we need to provide here is for regular
298 * thread safety (i.e. file descriptor table changes).
299 */
df271a59
SH
300void lxc_putlock(struct lxc_lock *l)
301{
df271a59 302 if (!l)
5cee8c50 303 return;
79cda71d 304
df271a59
SH
305 switch(l->type) {
306 case LXC_LOCK_ANON_SEM:
41c3b7c7 307 if (l->u.sem) {
527dacf6 308 sem_destroy(l->u.sem);
41c3b7c7
DE
309 free(l->u.sem);
310 l->u.sem = NULL;
311 }
ecd8cb99 312
df271a59
SH
313 break;
314 case LXC_LOCK_FLOCK:
315 if (l->u.f.fd != -1) {
316 close(l->u.f.fd);
317 l->u.f.fd = -1;
318 }
79cda71d 319
f10fad2f
ME
320 free(l->u.f.fname);
321 l->u.f.fname = NULL;
ecd8cb99 322
df271a59
SH
323 break;
324 }
ecd8cb99 325
41c3b7c7 326 free(l);
5cee8c50
SH
327}
328
025ed0f3 329void process_lock(void)
5cee8c50 330{
41f68357 331 lock_mutex(&thread_mutex);
5cee8c50
SH
332}
333
334void process_unlock(void)
335{
41f68357
ÇO
336 unlock_mutex(&thread_mutex);
337}
052616eb 338
5cee8c50
SH
339int container_mem_lock(struct lxc_container *c)
340{
341 return lxclock(c->privlock, 0);
342}
343
344void container_mem_unlock(struct lxc_container *c)
345{
346 lxcunlock(c->privlock);
347}
348
349int container_disk_lock(struct lxc_container *c)
350{
351 int ret;
352
ecd8cb99
CB
353 ret = lxclock(c->privlock, 0);
354 if (ret < 0)
5cee8c50 355 return ret;
79cda71d 356
ecd8cb99
CB
357 ret = lxclock(c->slock, 0);
358 if (ret < 0) {
5cee8c50
SH
359 lxcunlock(c->privlock);
360 return ret;
361 }
79cda71d 362
5cee8c50
SH
363 return 0;
364}
365
366void container_disk_unlock(struct lxc_container *c)
367{
368 lxcunlock(c->slock);
369 lxcunlock(c->privlock);
370}