]>
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) { | |
b8a6a00c TA |
172 | if (!name) |
173 | free(l->u.sem); | |
df271a59 SH |
174 | free(l); |
175 | l = NULL; | |
ecd8cb99 | 176 | goto on_error; |
df271a59 | 177 | } |
79cda71d | 178 | |
df271a59 SH |
179 | l->u.f.fd = -1; |
180 | ||
ecd8cb99 | 181 | on_error: |
df271a59 | 182 | return l; |
72d0e1cb SG |
183 | } |
184 | ||
df271a59 | 185 | int lxclock(struct lxc_lock *l, int timeout) |
72d0e1cb | 186 | { |
93dc5327 | 187 | struct flock lk; |
ecd8cb99 | 188 | int ret = -1, saved_errno = errno; |
72d0e1cb | 189 | |
df271a59 SH |
190 | switch(l->type) { |
191 | case LXC_LOCK_ANON_SEM: | |
192 | if (!timeout) { | |
193 | ret = sem_wait(l->u.sem); | |
b19aabf5 | 194 | if (ret < 0) |
df271a59 SH |
195 | saved_errno = errno; |
196 | } else { | |
197 | struct timespec ts; | |
79cda71d | 198 | |
ecd8cb99 CB |
199 | ret = clock_gettime(CLOCK_REALTIME, &ts); |
200 | if (ret < 0) { | |
df271a59 | 201 | ret = -2; |
ecd8cb99 | 202 | goto on_error; |
df271a59 | 203 | } |
79cda71d | 204 | |
df271a59 SH |
205 | ts.tv_sec += timeout; |
206 | ret = sem_timedwait(l->u.sem, &ts); | |
b19aabf5 | 207 | if (ret < 0) |
df271a59 SH |
208 | saved_errno = errno; |
209 | } | |
ecd8cb99 | 210 | |
df271a59 SH |
211 | break; |
212 | case LXC_LOCK_FLOCK: | |
213 | ret = -2; | |
214 | if (timeout) { | |
ecd8cb99 CB |
215 | ERROR("Timeouts are not supported with file locks"); |
216 | goto on_error; | |
df271a59 | 217 | } |
79cda71d | 218 | |
df271a59 | 219 | if (!l->u.f.fname) { |
ecd8cb99 CB |
220 | ERROR("No filename set for file lock"); |
221 | goto on_error; | |
df271a59 | 222 | } |
79cda71d | 223 | |
df271a59 | 224 | if (l->u.f.fd == -1) { |
b19aabf5 | 225 | l->u.f.fd = open(l->u.f.fname, O_CREAT | O_RDWR | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY, S_IWUSR | S_IRUSR); |
df271a59 | 226 | if (l->u.f.fd == -1) { |
ecd8cb99 | 227 | SYSERROR("Failed to open \"%s\"", l->u.f.fname); |
98375790 | 228 | saved_errno = errno; |
ecd8cb99 | 229 | goto on_error; |
df271a59 SH |
230 | } |
231 | } | |
79cda71d | 232 | |
b19aabf5 | 233 | memset(&lk, 0, sizeof(struct flock)); |
79cda71d | 234 | |
93dc5327 SH |
235 | lk.l_type = F_WRLCK; |
236 | lk.l_whence = SEEK_SET; | |
79cda71d | 237 | |
b19aabf5 CB |
238 | ret = fcntl(l->u.f.fd, F_OFD_SETLKW, &lk); |
239 | if (ret < 0) { | |
240 | if (errno == EINVAL) | |
241 | ret = flock(l->u.f.fd, LOCK_EX); | |
df271a59 | 242 | saved_errno = errno; |
b19aabf5 | 243 | } |
ecd8cb99 | 244 | |
df271a59 | 245 | break; |
72d0e1cb SG |
246 | } |
247 | ||
ecd8cb99 | 248 | on_error: |
df271a59 | 249 | errno = saved_errno; |
72d0e1cb SG |
250 | return ret; |
251 | } | |
252 | ||
df271a59 | 253 | int lxcunlock(struct lxc_lock *l) |
72d0e1cb | 254 | { |
93dc5327 | 255 | struct flock lk; |
ecd8cb99 | 256 | int ret = 0, saved_errno = errno; |
df271a59 | 257 | |
ecd8cb99 | 258 | switch (l->type) { |
df271a59 | 259 | case LXC_LOCK_ANON_SEM: |
ecd8cb99 | 260 | if (!l->u.sem) { |
df271a59 | 261 | ret = -2; |
ecd8cb99 | 262 | } else { |
df271a59 SH |
263 | ret = sem_post(l->u.sem); |
264 | saved_errno = errno; | |
659aa061 | 265 | } |
ecd8cb99 | 266 | |
df271a59 SH |
267 | break; |
268 | case LXC_LOCK_FLOCK: | |
269 | if (l->u.f.fd != -1) { | |
b19aabf5 | 270 | memset(&lk, 0, sizeof(struct flock)); |
79cda71d | 271 | |
93dc5327 SH |
272 | lk.l_type = F_UNLCK; |
273 | lk.l_whence = SEEK_SET; | |
79cda71d | 274 | |
b19aabf5 CB |
275 | ret = fcntl(l->u.f.fd, F_OFD_SETLK, &lk); |
276 | if (ret < 0) { | |
277 | if (errno == EINVAL) | |
278 | ret = flock(l->u.f.fd, LOCK_EX | LOCK_NB); | |
df271a59 | 279 | saved_errno = errno; |
b19aabf5 | 280 | } |
79cda71d | 281 | |
df271a59 SH |
282 | close(l->u.f.fd); |
283 | l->u.f.fd = -1; | |
ecd8cb99 | 284 | } else { |
df271a59 | 285 | ret = -2; |
ecd8cb99 CB |
286 | } |
287 | ||
df271a59 SH |
288 | break; |
289 | } | |
290 | ||
df271a59 SH |
291 | errno = saved_errno; |
292 | return ret; | |
293 | } | |
294 | ||
5cee8c50 SH |
295 | /* |
296 | * lxc_putlock() is only called when a container_new() fails, | |
297 | * or during container_put(), which is already guaranteed to | |
298 | * only be done by one task. | |
299 | * So the only exclusion we need to provide here is for regular | |
300 | * thread safety (i.e. file descriptor table changes). | |
301 | */ | |
df271a59 SH |
302 | void lxc_putlock(struct lxc_lock *l) |
303 | { | |
df271a59 | 304 | if (!l) |
5cee8c50 | 305 | return; |
79cda71d | 306 | |
df271a59 SH |
307 | switch(l->type) { |
308 | case LXC_LOCK_ANON_SEM: | |
41c3b7c7 | 309 | if (l->u.sem) { |
527dacf6 | 310 | sem_destroy(l->u.sem); |
41c3b7c7 DE |
311 | free(l->u.sem); |
312 | l->u.sem = NULL; | |
313 | } | |
ecd8cb99 | 314 | |
df271a59 SH |
315 | break; |
316 | case LXC_LOCK_FLOCK: | |
317 | if (l->u.f.fd != -1) { | |
318 | close(l->u.f.fd); | |
319 | l->u.f.fd = -1; | |
320 | } | |
79cda71d | 321 | |
f10fad2f ME |
322 | free(l->u.f.fname); |
323 | l->u.f.fname = NULL; | |
ecd8cb99 | 324 | |
df271a59 SH |
325 | break; |
326 | } | |
ecd8cb99 | 327 | |
41c3b7c7 | 328 | free(l); |
5cee8c50 SH |
329 | } |
330 | ||
025ed0f3 | 331 | void process_lock(void) |
5cee8c50 | 332 | { |
41f68357 | 333 | lock_mutex(&thread_mutex); |
5cee8c50 SH |
334 | } |
335 | ||
336 | void process_unlock(void) | |
337 | { | |
41f68357 ÇO |
338 | unlock_mutex(&thread_mutex); |
339 | } | |
052616eb | 340 | |
5cee8c50 SH |
341 | int container_mem_lock(struct lxc_container *c) |
342 | { | |
343 | return lxclock(c->privlock, 0); | |
344 | } | |
345 | ||
346 | void container_mem_unlock(struct lxc_container *c) | |
347 | { | |
348 | lxcunlock(c->privlock); | |
349 | } | |
350 | ||
351 | int container_disk_lock(struct lxc_container *c) | |
352 | { | |
353 | int ret; | |
354 | ||
ecd8cb99 CB |
355 | ret = lxclock(c->privlock, 0); |
356 | if (ret < 0) | |
5cee8c50 | 357 | return ret; |
79cda71d | 358 | |
ecd8cb99 CB |
359 | ret = lxclock(c->slock, 0); |
360 | if (ret < 0) { | |
5cee8c50 SH |
361 | lxcunlock(c->privlock); |
362 | return ret; | |
363 | } | |
79cda71d | 364 | |
5cee8c50 SH |
365 | return 0; |
366 | } | |
367 | ||
368 | void container_disk_unlock(struct lxc_container *c) | |
369 | { | |
370 | lxcunlock(c->slock); | |
371 | lxcunlock(c->privlock); | |
372 | } |