]>
Commit | Line | Data |
---|---|---|
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 | 28 | lxc_log_define(lxclock, lxc); |
72d0e1cb | 29 | |
052616eb | 30 | #ifdef MUTEX_DEBUGGING |
74a3920a | 31 | static pthread_mutex_t thread_mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; |
41f68357 | 32 | |
74a3920a | 33 | static 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 | 52 | static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; |
41f68357 | 53 | |
74a3920a | 54 | static inline void dump_stacktrace(void) {;} |
052616eb | 55 | #endif |
df271a59 | 56 | |
74a3920a | 57 | static 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 | 69 | static 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 | 81 | static 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 | ||
132 | static 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 | 150 | struct 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 | 179 | on_error: |
df271a59 | 180 | return l; |
72d0e1cb SG |
181 | } |
182 | ||
df271a59 | 183 | int 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 | 246 | on_error: |
df271a59 | 247 | errno = saved_errno; |
72d0e1cb SG |
248 | return ret; |
249 | } | |
250 | ||
df271a59 | 251 | int 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 |
300 | void 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 | 329 | void process_lock(void) |
5cee8c50 | 330 | { |
41f68357 | 331 | lock_mutex(&thread_mutex); |
5cee8c50 SH |
332 | } |
333 | ||
334 | void process_unlock(void) | |
335 | { | |
41f68357 ÇO |
336 | unlock_mutex(&thread_mutex); |
337 | } | |
052616eb | 338 | |
5cee8c50 SH |
339 | int container_mem_lock(struct lxc_container *c) |
340 | { | |
341 | return lxclock(c->privlock, 0); | |
342 | } | |
343 | ||
344 | void container_mem_unlock(struct lxc_container *c) | |
345 | { | |
346 | lxcunlock(c->privlock); | |
347 | } | |
348 | ||
349 | int 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 | ||
366 | void container_disk_unlock(struct lxc_container *c) | |
367 | { | |
368 | lxcunlock(c->slock); | |
369 | lxcunlock(c->privlock); | |
370 | } |