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